aether-colony 3.1.17 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/.aether/aether-utils.sh +5354 -0
  2. package/.aether/agents-claude/aether-ambassador.md +265 -0
  3. package/.aether/agents-claude/aether-archaeologist.md +327 -0
  4. package/.aether/agents-claude/aether-architect.md +236 -0
  5. package/.aether/agents-claude/aether-auditor.md +271 -0
  6. package/.aether/agents-claude/aether-builder.md +224 -0
  7. package/.aether/agents-claude/aether-chaos.md +269 -0
  8. package/.aether/agents-claude/aether-chronicler.md +305 -0
  9. package/.aether/agents-claude/aether-gatekeeper.md +330 -0
  10. package/.aether/agents-claude/aether-includer.md +374 -0
  11. package/.aether/agents-claude/aether-keeper.md +272 -0
  12. package/.aether/agents-claude/aether-measurer.md +322 -0
  13. package/.aether/agents-claude/aether-oracle.md +237 -0
  14. package/.aether/agents-claude/aether-probe.md +211 -0
  15. package/.aether/agents-claude/aether-queen.md +330 -0
  16. package/.aether/agents-claude/aether-route-setter.md +178 -0
  17. package/.aether/agents-claude/aether-sage.md +418 -0
  18. package/.aether/agents-claude/aether-scout.md +179 -0
  19. package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
  20. package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
  21. package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
  22. package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
  23. package/.aether/agents-claude/aether-tracker.md +270 -0
  24. package/.aether/agents-claude/aether-watcher.md +280 -0
  25. package/.aether/agents-claude/aether-weaver.md +248 -0
  26. package/.aether/commands/archaeology.yaml +653 -0
  27. package/.aether/commands/build.yaml +1221 -0
  28. package/.aether/commands/chaos.yaml +653 -0
  29. package/.aether/commands/colonize.yaml +438 -0
  30. package/.aether/commands/continue.yaml +1484 -0
  31. package/.aether/commands/council.yaml +304 -0
  32. package/.aether/commands/data-clean.yaml +80 -0
  33. package/.aether/commands/dream.yaml +275 -0
  34. package/.aether/commands/entomb.yaml +863 -0
  35. package/.aether/commands/export-signals.yaml +64 -0
  36. package/.aether/commands/feedback.yaml +158 -0
  37. package/.aether/commands/flag.yaml +160 -0
  38. package/.aether/commands/flags.yaml +177 -0
  39. package/.aether/commands/focus.yaml +112 -0
  40. package/.aether/commands/help.yaml +167 -0
  41. package/.aether/commands/history.yaml +137 -0
  42. package/.aether/commands/import-signals.yaml +79 -0
  43. package/.aether/commands/init.yaml +469 -0
  44. package/.aether/commands/insert-phase.yaml +98 -0
  45. package/.aether/commands/interpret.yaml +285 -0
  46. package/.aether/commands/lay-eggs.yaml +224 -0
  47. package/.aether/commands/maturity.yaml +122 -0
  48. package/.aether/commands/memory-details.yaml +74 -0
  49. package/.aether/commands/migrate-state.yaml +174 -0
  50. package/.aether/commands/oracle.yaml +1224 -0
  51. package/.aether/commands/organize.yaml +446 -0
  52. package/.aether/commands/patrol.yaml +621 -0
  53. package/.aether/commands/pause-colony.yaml +424 -0
  54. package/.aether/commands/phase.yaml +124 -0
  55. package/.aether/commands/pheromones.yaml +153 -0
  56. package/.aether/commands/plan.yaml +1313 -0
  57. package/.aether/commands/preferences.yaml +63 -0
  58. package/.aether/commands/redirect.yaml +123 -0
  59. package/.aether/commands/resume-colony.yaml +373 -0
  60. package/.aether/commands/resume.yaml +398 -0
  61. package/.aether/commands/run.yaml +193 -0
  62. package/.aether/commands/seal.yaml +1205 -0
  63. package/.aether/commands/skill-create.yaml +337 -0
  64. package/.aether/commands/status.yaml +364 -0
  65. package/.aether/commands/swarm.yaml +352 -0
  66. package/.aether/commands/tunnels.yaml +814 -0
  67. package/.aether/commands/update.yaml +131 -0
  68. package/.aether/commands/verify-castes.yaml +159 -0
  69. package/.aether/commands/watch.yaml +454 -0
  70. package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
  71. package/.aether/docs/QUEEN-SYSTEM.md +211 -0
  72. package/.aether/docs/README.md +98 -0
  73. package/.aether/docs/caste-system.md +48 -0
  74. package/.aether/docs/command-playbooks/README.md +23 -0
  75. package/.aether/docs/command-playbooks/build-complete.md +349 -0
  76. package/.aether/docs/command-playbooks/build-context.md +282 -0
  77. package/.aether/docs/command-playbooks/build-full.md +1682 -0
  78. package/.aether/docs/command-playbooks/build-prep.md +283 -0
  79. package/.aether/docs/command-playbooks/build-verify.md +405 -0
  80. package/.aether/docs/command-playbooks/build-wave.md +749 -0
  81. package/.aether/docs/command-playbooks/continue-advance.md +524 -0
  82. package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
  83. package/.aether/docs/command-playbooks/continue-full.md +1724 -0
  84. package/.aether/docs/command-playbooks/continue-gates.md +686 -0
  85. package/.aether/docs/command-playbooks/continue-verify.md +406 -0
  86. package/.aether/docs/context-continuity.md +84 -0
  87. package/{runtime → .aether/docs/disciplines}/DISCIPLINES.md +13 -11
  88. package/.aether/docs/error-codes.md +268 -0
  89. package/.aether/docs/known-issues.md +94 -0
  90. package/{runtime → .aether}/docs/pheromones.md +86 -6
  91. package/.aether/docs/plans/pheromone-display-plan.md +257 -0
  92. package/.aether/docs/queen-commands.md +98 -0
  93. package/.aether/docs/source-of-truth-map.md +132 -0
  94. package/.aether/docs/xml-utilities.md +47 -0
  95. package/{runtime → .aether}/exchange/pheromone-xml.sh +2 -1
  96. package/{runtime → .aether}/exchange/registry-xml.sh +7 -3
  97. package/{runtime → .aether}/exchange/wisdom-xml.sh +11 -4
  98. package/.aether/rules/aether-colony.md +144 -0
  99. package/.aether/schemas/example-prompt-builder.xml +234 -0
  100. package/.aether/scripts/incident-test-add.sh +47 -0
  101. package/.aether/scripts/weekly-audit.sh +79 -0
  102. package/.aether/skills/.index.json +649 -0
  103. package/.aether/skills/colony/.manifest.json +16 -0
  104. package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
  105. package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
  106. package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
  107. package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
  108. package/.aether/skills/colony/context-management/SKILL.md +80 -0
  109. package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
  110. package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
  111. package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
  112. package/.aether/skills/colony/state-safety/SKILL.md +84 -0
  113. package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
  114. package/.aether/skills/domain/.manifest.json +24 -0
  115. package/.aether/skills/domain/README.md +33 -0
  116. package/.aether/skills/domain/django/SKILL.md +49 -0
  117. package/.aether/skills/domain/docker/SKILL.md +52 -0
  118. package/.aether/skills/domain/golang/SKILL.md +52 -0
  119. package/.aether/skills/domain/graphql/SKILL.md +51 -0
  120. package/.aether/skills/domain/html-css/SKILL.md +48 -0
  121. package/.aether/skills/domain/nextjs/SKILL.md +45 -0
  122. package/.aether/skills/domain/nodejs/SKILL.md +53 -0
  123. package/.aether/skills/domain/postgresql/SKILL.md +53 -0
  124. package/.aether/skills/domain/prisma/SKILL.md +59 -0
  125. package/.aether/skills/domain/python/SKILL.md +50 -0
  126. package/.aether/skills/domain/rails/SKILL.md +52 -0
  127. package/.aether/skills/domain/react/SKILL.md +45 -0
  128. package/.aether/skills/domain/rest-api/SKILL.md +58 -0
  129. package/.aether/skills/domain/svelte/SKILL.md +47 -0
  130. package/.aether/skills/domain/tailwind/SKILL.md +45 -0
  131. package/.aether/skills/domain/testing/SKILL.md +53 -0
  132. package/.aether/skills/domain/typescript/SKILL.md +58 -0
  133. package/.aether/skills/domain/vue/SKILL.md +49 -0
  134. package/.aether/templates/QUEEN.md.template +61 -0
  135. package/.aether/templates/colony-state-reset.jq.template +23 -0
  136. package/.aether/templates/colony-state.template.json +39 -0
  137. package/.aether/templates/constraints.template.json +9 -0
  138. package/.aether/templates/crowned-anthill.template.md +36 -0
  139. package/.aether/templates/handoff-build-error.template.md +30 -0
  140. package/.aether/templates/handoff-build-success.template.md +39 -0
  141. package/.aether/templates/handoff.template.md +40 -0
  142. package/.aether/templates/learning-observations.template.json +6 -0
  143. package/.aether/templates/midden.template.json +13 -0
  144. package/.aether/templates/pheromones.template.json +6 -0
  145. package/.aether/templates/session.template.json +9 -0
  146. package/{runtime → .aether}/utils/atomic-write.sh +68 -22
  147. package/{runtime → .aether}/utils/chamber-compare.sh +23 -10
  148. package/.aether/utils/chamber-utils.sh +440 -0
  149. package/.aether/utils/emoji-audit.sh +166 -0
  150. package/{runtime → .aether}/utils/error-handler.sh +34 -8
  151. package/.aether/utils/file-lock.sh +313 -0
  152. package/.aether/utils/flag.sh +267 -0
  153. package/.aether/utils/hive.sh +572 -0
  154. package/.aether/utils/learning.sh +1928 -0
  155. package/.aether/utils/midden.sh +342 -0
  156. package/.aether/utils/oracle/oracle.md +168 -0
  157. package/.aether/utils/oracle/oracle.sh +1023 -0
  158. package/.aether/utils/pheromone.sh +2029 -0
  159. package/.aether/utils/queen.sh +1698 -0
  160. package/.aether/utils/scan.sh +860 -0
  161. package/.aether/utils/semantic-cli.sh +415 -0
  162. package/.aether/utils/session.sh +552 -0
  163. package/.aether/utils/skills.sh +509 -0
  164. package/.aether/utils/spawn-tree.sh +260 -0
  165. package/.aether/utils/spawn.sh +260 -0
  166. package/.aether/utils/state-api.sh +199 -0
  167. package/{runtime → .aether}/utils/state-loader.sh +8 -6
  168. package/.aether/utils/suggest.sh +611 -0
  169. package/{runtime → .aether}/utils/swarm-display.sh +10 -1
  170. package/.aether/utils/swarm.sh +1004 -0
  171. package/{runtime → .aether}/utils/watch-spawn-tree.sh +11 -2
  172. package/{runtime → .aether}/utils/xml-compose.sh +9 -3
  173. package/.aether/utils/xml-convert.sh +277 -0
  174. package/{runtime → .aether}/utils/xml-core.sh +5 -9
  175. package/.aether/utils/xml-query.sh +201 -0
  176. package/.aether/utils/xml-utils.sh +110 -0
  177. package/{runtime → .aether}/workers.md +97 -81
  178. package/.claude/agents/ant/aether-ambassador.md +265 -0
  179. package/.claude/agents/ant/aether-archaeologist.md +327 -0
  180. package/.claude/agents/ant/aether-architect.md +236 -0
  181. package/.claude/agents/ant/aether-auditor.md +271 -0
  182. package/.claude/agents/ant/aether-builder.md +224 -0
  183. package/.claude/agents/ant/aether-chaos.md +269 -0
  184. package/.claude/agents/ant/aether-chronicler.md +305 -0
  185. package/.claude/agents/ant/aether-gatekeeper.md +330 -0
  186. package/.claude/agents/ant/aether-includer.md +374 -0
  187. package/.claude/agents/ant/aether-keeper.md +272 -0
  188. package/.claude/agents/ant/aether-measurer.md +322 -0
  189. package/.claude/agents/ant/aether-oracle.md +237 -0
  190. package/.claude/agents/ant/aether-probe.md +211 -0
  191. package/.claude/agents/ant/aether-queen.md +330 -0
  192. package/.claude/agents/ant/aether-route-setter.md +178 -0
  193. package/.claude/agents/ant/aether-sage.md +418 -0
  194. package/.claude/agents/ant/aether-scout.md +179 -0
  195. package/.claude/agents/ant/aether-surveyor-disciplines.md +417 -0
  196. package/.claude/agents/ant/aether-surveyor-nest.md +355 -0
  197. package/.claude/agents/ant/aether-surveyor-pathogens.md +289 -0
  198. package/.claude/agents/ant/aether-surveyor-provisions.md +360 -0
  199. package/.claude/agents/ant/aether-tracker.md +270 -0
  200. package/.claude/agents/ant/aether-watcher.md +280 -0
  201. package/.claude/agents/ant/aether-weaver.md +248 -0
  202. package/.claude/commands/ant/archaeology.md +16 -14
  203. package/.claude/commands/ant/build.md +43 -1028
  204. package/.claude/commands/ant/chaos.md +19 -23
  205. package/.claude/commands/ant/colonize.md +52 -31
  206. package/.claude/commands/ant/continue.md +40 -1016
  207. package/.claude/commands/ant/council.md +21 -18
  208. package/.claude/commands/ant/data-clean.md +81 -0
  209. package/.claude/commands/ant/dream.md +27 -15
  210. package/.claude/commands/ant/entomb.md +317 -225
  211. package/.claude/commands/ant/export-signals.md +57 -0
  212. package/.claude/commands/ant/feedback.md +48 -26
  213. package/.claude/commands/ant/flag.md +30 -10
  214. package/.claude/commands/ant/flags.md +34 -12
  215. package/.claude/commands/ant/focus.md +45 -19
  216. package/.claude/commands/ant/help.md +50 -8
  217. package/.claude/commands/ant/history.md +13 -0
  218. package/.claude/commands/ant/import-signals.md +71 -0
  219. package/.claude/commands/ant/init.md +345 -194
  220. package/.claude/commands/ant/insert-phase.md +101 -0
  221. package/.claude/commands/ant/interpret.md +26 -4
  222. package/.claude/commands/ant/lay-eggs.md +184 -127
  223. package/.claude/commands/ant/maturity.md +32 -11
  224. package/.claude/commands/ant/memory-details.md +77 -0
  225. package/.claude/commands/ant/migrate-state.md +20 -2
  226. package/.claude/commands/ant/oracle.md +337 -74
  227. package/.claude/commands/ant/organize.md +39 -25
  228. package/.claude/commands/ant/patrol.md +620 -0
  229. package/.claude/commands/ant/pause-colony.md +23 -27
  230. package/.claude/commands/ant/phase.md +40 -42
  231. package/.claude/commands/ant/pheromones.md +156 -0
  232. package/.claude/commands/ant/plan.md +185 -51
  233. package/.claude/commands/ant/preferences.md +65 -0
  234. package/.claude/commands/ant/redirect.md +45 -30
  235. package/.claude/commands/ant/resume-colony.md +51 -27
  236. package/.claude/commands/ant/resume.md +314 -94
  237. package/.claude/commands/ant/run.md +195 -0
  238. package/.claude/commands/ant/seal.md +650 -221
  239. package/.claude/commands/ant/skill-create.md +286 -0
  240. package/.claude/commands/ant/status.md +196 -31
  241. package/.claude/commands/ant/swarm.md +16 -46
  242. package/.claude/commands/ant/tunnels.md +280 -105
  243. package/.claude/commands/ant/update.md +73 -89
  244. package/.claude/commands/ant/verify-castes.md +100 -42
  245. package/.claude/commands/ant/watch.md +14 -12
  246. package/.opencode/agents/aether-ambassador.md +63 -20
  247. package/.opencode/agents/aether-archaeologist.md +29 -12
  248. package/.opencode/agents/aether-architect.md +103 -36
  249. package/.opencode/agents/aether-auditor.md +51 -18
  250. package/.opencode/agents/aether-builder.md +70 -20
  251. package/.opencode/agents/aether-chaos.md +29 -12
  252. package/.opencode/agents/aether-chronicler.md +60 -18
  253. package/.opencode/agents/aether-gatekeeper.md +27 -18
  254. package/.opencode/agents/aether-includer.md +27 -18
  255. package/.opencode/agents/aether-keeper.md +89 -18
  256. package/.opencode/agents/aether-measurer.md +27 -18
  257. package/.opencode/agents/aether-oracle.md +137 -0
  258. package/.opencode/agents/aether-probe.md +60 -18
  259. package/.opencode/agents/aether-queen.md +172 -24
  260. package/.opencode/agents/aether-route-setter.md +57 -12
  261. package/.opencode/agents/aether-sage.md +26 -18
  262. package/.opencode/agents/aether-scout.md +28 -20
  263. package/.opencode/agents/aether-surveyor-disciplines.md +59 -2
  264. package/.opencode/agents/aether-surveyor-nest.md +59 -2
  265. package/.opencode/agents/aether-surveyor-pathogens.md +57 -2
  266. package/.opencode/agents/aether-surveyor-provisions.md +59 -2
  267. package/.opencode/agents/aether-tracker.md +64 -18
  268. package/.opencode/agents/aether-watcher.md +66 -19
  269. package/.opencode/agents/aether-weaver.md +61 -18
  270. package/.opencode/commands/ant/archaeology.md +7 -14
  271. package/.opencode/commands/ant/build.md +437 -257
  272. package/.opencode/commands/ant/chaos.md +7 -24
  273. package/.opencode/commands/ant/colonize.md +8 -17
  274. package/.opencode/commands/ant/continue.md +661 -142
  275. package/.opencode/commands/ant/council.md +11 -22
  276. package/.opencode/commands/ant/data-clean.md +77 -0
  277. package/.opencode/commands/ant/dream.md +15 -17
  278. package/.opencode/commands/ant/entomb.md +133 -62
  279. package/.opencode/commands/ant/export-signals.md +54 -0
  280. package/.opencode/commands/ant/feedback.md +24 -5
  281. package/.opencode/commands/ant/flag.md +16 -4
  282. package/.opencode/commands/ant/flags.md +24 -10
  283. package/.opencode/commands/ant/focus.md +22 -5
  284. package/.opencode/commands/ant/help.md +41 -8
  285. package/.opencode/commands/ant/history.md +9 -0
  286. package/.opencode/commands/ant/import-signals.md +68 -0
  287. package/.opencode/commands/ant/init.md +374 -167
  288. package/.opencode/commands/ant/insert-phase.md +107 -0
  289. package/.opencode/commands/ant/interpret.md +16 -0
  290. package/.opencode/commands/ant/lay-eggs.md +184 -112
  291. package/.opencode/commands/ant/maturity.md +18 -2
  292. package/.opencode/commands/ant/memory-details.md +83 -0
  293. package/.opencode/commands/ant/migrate-state.md +12 -0
  294. package/.opencode/commands/ant/oracle.md +322 -67
  295. package/.opencode/commands/ant/organize.md +18 -16
  296. package/.opencode/commands/ant/patrol.md +626 -0
  297. package/.opencode/commands/ant/pause-colony.md +12 -29
  298. package/.opencode/commands/ant/phase.md +30 -40
  299. package/.opencode/commands/ant/pheromones.md +162 -0
  300. package/.opencode/commands/ant/plan.md +184 -56
  301. package/.opencode/commands/ant/preferences.md +71 -0
  302. package/.opencode/commands/ant/redirect.md +22 -5
  303. package/.opencode/commands/ant/resume-colony.md +38 -27
  304. package/.opencode/commands/ant/resume.md +385 -0
  305. package/.opencode/commands/ant/run.md +201 -0
  306. package/.opencode/commands/ant/seal.md +259 -45
  307. package/.opencode/commands/ant/skill-create.md +63 -0
  308. package/.opencode/commands/ant/status.md +135 -31
  309. package/.opencode/commands/ant/swarm.md +3 -345
  310. package/.opencode/commands/ant/tunnels.md +152 -9
  311. package/.opencode/commands/ant/update.md +70 -91
  312. package/.opencode/commands/ant/verify-castes.md +96 -42
  313. package/.opencode/commands/ant/watch.md +7 -0
  314. package/CHANGELOG.md +356 -0
  315. package/README.md +203 -573
  316. package/bin/cli.js +455 -527
  317. package/bin/generate-commands.js +186 -0
  318. package/bin/generate-commands.sh +127 -88
  319. package/bin/lib/init.js +13 -3
  320. package/bin/lib/spawn-logger.js +0 -15
  321. package/bin/lib/update-transaction.js +392 -140
  322. package/bin/npx-install.js +178 -0
  323. package/bin/sync-to-runtime.sh +5 -137
  324. package/bin/validate-package.sh +166 -0
  325. package/package.json +14 -7
  326. package/.opencode/agents/aether-guardian.md +0 -107
  327. package/.opencode/agents/workers.md +0 -1034
  328. package/bin/lib/model-profiles.js +0 -445
  329. package/bin/lib/model-verify.js +0 -288
  330. package/bin/lib/proxy-health.js +0 -253
  331. package/bin/lib/telemetry.js +0 -441
  332. package/runtime/CONTEXT.md +0 -160
  333. package/runtime/QUEEN_ANT_ARCHITECTURE.md +0 -402
  334. package/runtime/aether-utils.sh +0 -3879
  335. package/runtime/data/signatures.json +0 -41
  336. package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +0 -1343
  337. package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +0 -2642
  338. package/runtime/docs/PHEROMONE-INJECTION.md +0 -240
  339. package/runtime/docs/PHEROMONE-INTEGRATION.md +0 -192
  340. package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +0 -426
  341. package/runtime/docs/README.md +0 -94
  342. package/runtime/docs/VISUAL-OUTPUT-SPEC.md +0 -219
  343. package/runtime/docs/biological-reference.md +0 -272
  344. package/runtime/docs/codebase-review.md +0 -399
  345. package/runtime/docs/command-sync.md +0 -164
  346. package/runtime/docs/constraints.md +0 -116
  347. package/runtime/docs/implementation-learnings.md +0 -89
  348. package/runtime/docs/known-issues.md +0 -217
  349. package/runtime/docs/namespace.md +0 -148
  350. package/runtime/docs/pathogen-schema-example.json +0 -36
  351. package/runtime/docs/pathogen-schema.md +0 -111
  352. package/runtime/docs/planning-discipline.md +0 -159
  353. package/runtime/docs/progressive-disclosure.md +0 -184
  354. package/runtime/lib/queen-utils.sh +0 -729
  355. package/runtime/model-profiles.yaml +0 -100
  356. package/runtime/planning.md +0 -159
  357. package/runtime/recover.sh +0 -136
  358. package/runtime/templates/QUEEN.md.template +0 -79
  359. package/runtime/utils/chamber-utils.sh +0 -285
  360. package/runtime/utils/file-lock.sh +0 -122
  361. package/runtime/utils/spawn-tree.sh +0 -428
  362. package/runtime/utils/spawn-with-model.sh +0 -56
  363. package/runtime/utils/xml-utils.sh +0 -2196
  364. package/runtime/workers-new-castes.md +0 -516
  365. /package/{runtime → .aether/docs/disciplines}/coding-standards.md +0 -0
  366. /package/{runtime → .aether/docs/disciplines}/debugging.md +0 -0
  367. /package/{runtime → .aether/docs/disciplines}/learning.md +0 -0
  368. /package/{runtime → .aether/docs/disciplines}/tdd.md +0 -0
  369. /package/{runtime → .aether/docs/disciplines}/verification-loop.md +0 -0
  370. /package/{runtime → .aether/docs/disciplines}/verification.md +0 -0
  371. /package/{runtime → .aether}/schemas/aether-types.xsd +0 -0
  372. /package/{runtime → .aether}/schemas/colony-registry.xsd +0 -0
  373. /package/{runtime → .aether}/schemas/pheromone.xsd +0 -0
  374. /package/{runtime → .aether}/schemas/prompt.xsd +0 -0
  375. /package/{runtime → .aether}/schemas/queen-wisdom.xsd +0 -0
  376. /package/{runtime → .aether}/schemas/worker-priming.xsd +0 -0
  377. /package/{runtime → .aether}/utils/colorize-log.sh +0 -0
  378. /package/{runtime → .aether}/utils/queen-to-md.xsl +0 -0
package/bin/cli.js CHANGED
@@ -22,31 +22,8 @@ const { logError, logActivity } = require('./lib/logger');
22
22
  const { UpdateTransaction, UpdateError, UpdateErrorCodes } = require('./lib/update-transaction');
23
23
  const { initializeRepo, isInitialized } = require('./lib/init');
24
24
  const { syncStateFromPlanning, reconcileStates } = require('./lib/state-sync');
25
- const { createVerificationReport } = require('./lib/model-verify');
26
- const {
27
- loadModelProfiles,
28
- getAllAssignments,
29
- getProviderForModel,
30
- validateCaste,
31
- validateModel,
32
- setModelOverride,
33
- resetModelOverride,
34
- getEffectiveModel,
35
- getUserOverrides,
36
- getModelMetadata,
37
- getProxyConfig,
38
- } = require('./lib/model-profiles');
39
- const {
40
- checkProxyHealth,
41
- verifyModelRouting,
42
- formatProxyStatusColored,
43
- } = require('./lib/proxy-health');
44
25
  const { findNestmates, formatNestmates, loadNestmateTodos } = require('./lib/nestmate-loader');
45
26
  const { logSpawn, formatSpawnTree } = require('./lib/spawn-logger');
46
- const {
47
- getTelemetrySummary,
48
- getModelPerformance,
49
- } = require('./lib/telemetry');
50
27
 
51
28
  // Color palette
52
29
  const c = require('./lib/colors');
@@ -65,14 +42,22 @@ if (!HOME) {
65
42
  }
66
43
 
67
44
  // Claude Code paths (global)
68
- const COMMANDS_SRC = path.join(PACKAGE_DIR, 'commands', 'ant');
45
+ const COMMANDS_SRC = path.join(PACKAGE_DIR, '.claude', 'commands', 'ant');
69
46
  const COMMANDS_DEST = path.join(HOME, '.claude', 'commands', 'ant');
47
+ const AGENTS_DEST = path.join(HOME, '.claude', 'agents', 'ant');
48
+
49
+ // OpenCode paths (global)
50
+ const OPENCODE_COMMANDS_DEST = path.join(HOME, '.opencode', 'command');
51
+ const OPENCODE_AGENTS_DEST = path.join(HOME, '.opencode', 'agent');
70
52
 
71
53
  // Hub paths
72
54
  const HUB_DIR = path.join(HOME, '.aether');
73
- const HUB_COMMANDS_CLAUDE = path.join(HUB_DIR, 'commands', 'claude');
74
- const HUB_COMMANDS_OPENCODE = path.join(HUB_DIR, 'commands', 'opencode');
75
- const HUB_AGENTS = path.join(HUB_DIR, 'agents');
55
+ const HUB_SYSTEM_DIR = path.join(HUB_DIR, 'system');
56
+ const HUB_COMMANDS_CLAUDE = path.join(HUB_SYSTEM_DIR, 'commands', 'claude');
57
+ const HUB_COMMANDS_OPENCODE = path.join(HUB_SYSTEM_DIR, 'commands', 'opencode');
58
+ const HUB_AGENTS = path.join(HUB_SYSTEM_DIR, 'agents');
59
+ const HUB_AGENTS_CLAUDE = path.join(HUB_SYSTEM_DIR, 'agents-claude');
60
+ const HUB_RULES = path.join(HUB_SYSTEM_DIR, 'rules');
76
61
  const HUB_REGISTRY = path.join(HUB_DIR, 'registry.json');
77
62
  const HUB_VERSION = path.join(HUB_DIR, 'version.json');
78
63
 
@@ -374,41 +359,15 @@ function removeDirSync(dir) {
374
359
  return count;
375
360
  }
376
361
 
377
- // System files allowlist only these are copied during updates (never colony data)
378
- const SYSTEM_FILES = [
379
- 'aether-utils.sh',
380
- 'coding-standards.md',
381
- 'debugging.md',
382
- 'DISCIPLINES.md',
383
- 'learning.md',
384
- 'planning.md',
385
- 'QUEEN_ANT_ARCHITECTURE.md',
386
- 'tdd.md',
387
- 'verification-loop.md',
388
- 'verification.md',
389
- 'workers.md',
390
- 'docs/constraints.md',
391
- 'docs/pathogen-schema-example.json',
392
- 'docs/pathogen-schema.md',
393
- 'docs/pheromones.md',
394
- 'docs/progressive-disclosure.md',
395
- 'utils/atomic-write.sh',
396
- 'utils/colorize-log.sh',
397
- 'utils/file-lock.sh',
398
- 'utils/watch-spawn-tree.sh',
399
- ];
400
-
401
- function copySystemFiles(srcDir, destDir) {
362
+ // Remove only files from dest that exist in source (safe for shared directories)
363
+ function removeFilesFromSource(sourceDir, destDir) {
364
+ if (!fs.existsSync(sourceDir) || !fs.existsSync(destDir)) return 0;
402
365
  let count = 0;
403
- for (const file of SYSTEM_FILES) {
404
- const srcPath = path.join(srcDir, file);
366
+ const sourceFiles = fs.readdirSync(sourceDir).filter(f => f.endsWith('.md'));
367
+ for (const file of sourceFiles) {
405
368
  const destPath = path.join(destDir, file);
406
- if (fs.existsSync(srcPath)) {
407
- fs.mkdirSync(path.dirname(destPath), { recursive: true });
408
- fs.copyFileSync(srcPath, destPath);
409
- if (file.endsWith('.sh')) {
410
- fs.chmodSync(destPath, 0o755);
411
- }
369
+ if (fs.existsSync(destPath)) {
370
+ fs.unlinkSync(destPath);
412
371
  count++;
413
372
  }
414
373
  }
@@ -585,65 +544,15 @@ function computeFileHash(filePath) {
585
544
  }
586
545
  }
587
546
 
588
- function syncSystemFilesWithCleanup(srcDir, destDir, opts) {
589
- opts = opts || {};
590
- const dryRun = opts.dryRun || false;
591
-
592
- let copied = 0;
593
- let skipped = 0;
594
- for (const file of SYSTEM_FILES) {
595
- const srcPath = path.join(srcDir, file);
596
- const destPath = path.join(destDir, file);
597
- if (fs.existsSync(srcPath)) {
598
- if (!dryRun) {
599
- // Compute hashes to determine if copy is needed
600
- const srcHash = computeFileHash(srcPath);
601
- const destHash = fs.existsSync(destPath) ? computeFileHash(destPath) : null;
602
-
603
- if (srcHash === destHash) {
604
- // Files are identical, skip copying
605
- skipped++;
606
- continue;
607
- }
608
-
609
- fs.mkdirSync(path.dirname(destPath), { recursive: true });
610
- fs.copyFileSync(srcPath, destPath);
611
- if (file.endsWith('.sh')) {
612
- fs.chmodSync(destPath, 0o755);
613
- }
614
- }
615
- copied++;
616
- }
617
- }
618
-
619
- // Remove allowlisted files that no longer exist in src
620
- const removed = [];
621
- for (const file of SYSTEM_FILES) {
622
- const srcPath = path.join(srcDir, file);
623
- const destPath = path.join(destDir, file);
624
- if (!fs.existsSync(srcPath) && fs.existsSync(destPath)) {
625
- removed.push(file);
626
- if (!dryRun) {
627
- fs.unlinkSync(destPath);
628
- }
629
- }
630
- }
631
-
632
- if (!dryRun && removed.length > 0) {
633
- cleanEmptyDirs(destDir);
634
- }
635
-
636
- return { copied, removed, skipped };
637
- }
638
-
639
547
  // Checkpoint allowlist - only these files are captured in checkpoints
640
548
  // NEVER include: data/, dreams/, oracle/, TO-DOs.md (user data)
549
+ // Note: runtime/ was removed in v4.0 — .aether/ is published directly
641
550
  const CHECKPOINT_ALLOWLIST = [
642
551
  '.aether/*.md', // All .md files directly in .aether/
643
552
  '.claude/commands/ant/**', // All files in .claude/commands/ant/ recursively
553
+ '.claude/agents/ant/**', // All files in .claude/agents/ant/ recursively
644
554
  '.opencode/commands/ant/**', // All files in .opencode/commands/ant/ recursively
645
555
  '.opencode/agents/**', // All files in .opencode/agents/ recursively
646
- 'runtime/**', // All files in runtime/ recursively
647
556
  'bin/cli.js', // Specific file: bin/cli.js
648
557
  ];
649
558
 
@@ -805,6 +714,64 @@ function isGitRepo(repoPath) {
805
714
  }
806
715
  }
807
716
 
717
+ // Paths that updates should preserve (never overwrite/stash as "managed")
718
+ const UPDATE_PROTECTED_AETHER_DIRS = new Set([
719
+ 'data',
720
+ 'dreams',
721
+ 'oracle',
722
+ 'midden',
723
+ 'checkpoints',
724
+ 'locks',
725
+ 'temp',
726
+ 'archive',
727
+ 'chambers',
728
+ 'exchange',
729
+ ]);
730
+
731
+ const UPDATE_MANAGED_PREFIXES = [
732
+ '.claude/commands/ant',
733
+ '.claude/agents/ant',
734
+ '.claude/rules',
735
+ '.opencode/commands/ant',
736
+ '.opencode/agents',
737
+ ];
738
+
739
+ function normalizePorcelainPath(filePath) {
740
+ let normalized = filePath;
741
+
742
+ // For rename entries, keep only destination path: "old -> new"
743
+ if (normalized.includes(' -> ')) {
744
+ normalized = normalized.split(' -> ').pop();
745
+ }
746
+
747
+ // Handle quoted porcelain paths (spaces, escaped chars)
748
+ if (normalized.startsWith('"') && normalized.endsWith('"')) {
749
+ normalized = normalized
750
+ .slice(1, -1)
751
+ .replace(/\\"/g, '"')
752
+ .replace(/\\\\/g, '\\');
753
+ }
754
+
755
+ return normalized;
756
+ }
757
+
758
+ function isManagedUpdatePath(filePath) {
759
+ const normalized = normalizePorcelainPath(filePath);
760
+
761
+ if (normalized === '.aether' || normalized.startsWith('.aether/')) {
762
+ const rel = normalized === '.aether' ? '' : normalized.slice('.aether/'.length);
763
+ if (!rel) return true;
764
+
765
+ const first = rel.split('/')[0];
766
+ if (!first || first.startsWith('.')) return false;
767
+ if (UPDATE_PROTECTED_AETHER_DIRS.has(first)) return false;
768
+ if (rel === 'QUEEN.md') return false;
769
+ return true;
770
+ }
771
+
772
+ return UPDATE_MANAGED_PREFIXES.some(prefix => normalized === prefix || normalized.startsWith(`${prefix}/`));
773
+ }
774
+
808
775
  function getGitDirtyFiles(repoPath, targetDirs) {
809
776
  try {
810
777
  const args = targetDirs.filter(d => fs.existsSync(path.join(repoPath, d)));
@@ -814,7 +781,14 @@ function getGitDirtyFiles(repoPath, targetDirs) {
814
781
  stdio: 'pipe',
815
782
  encoding: 'utf8',
816
783
  });
817
- return result.trim().split('\n').filter(Boolean).map(line => line.slice(3));
784
+ const files = [];
785
+ for (const line of result.split('\n')) {
786
+ if (!line || line.length < 4) continue;
787
+ const filePath = line.slice(3);
788
+ if (!isManagedUpdatePath(filePath)) continue;
789
+ files.push(normalizePorcelainPath(filePath));
790
+ }
791
+ return [...new Set(files)];
818
792
  } catch {
819
793
  return [];
820
794
  }
@@ -835,7 +809,13 @@ function gitStashFiles(repoPath, files) {
835
809
  }
836
810
 
837
811
  // Directories to exclude from hub sync (user data, local state)
838
- const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp'];
812
+ // 'rules' is excluded here because it is synced via a dedicated step (rulesSrc below)
813
+ const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp', 'rules'];
814
+ // Directories excluded only when they are the FIRST path segment under .aether/
815
+ // (e.g., .aether/oracle/ is excluded but .aether/utils/oracle/ is NOT)
816
+ const HUB_EXCLUDE_FIRST_SEGMENT = ['oracle'];
817
+ // Files to exclude from hub sync (repo-local state, not distributable)
818
+ const HUB_EXCLUDE_FILES = ['CONTEXT.md', 'HANDOFF.md'];
839
819
 
840
820
  /**
841
821
  * Check if a path should be excluded from hub sync
@@ -844,8 +824,13 @@ const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp'];
844
824
  */
845
825
  function shouldExcludeFromHub(relPath) {
846
826
  const parts = relPath.split(path.sep);
827
+ const basename = path.basename(relPath);
847
828
  // Exclude if any part of the path is in the exclude list
848
- return parts.some(part => HUB_EXCLUDE_DIRS.includes(part));
829
+ if (parts.some(part => HUB_EXCLUDE_DIRS.includes(part))) return true;
830
+ // Exclude if first segment matches first-segment-only list (position-aware)
831
+ if (parts.length > 0 && HUB_EXCLUDE_FIRST_SEGMENT.includes(parts[0])) return true;
832
+ if (HUB_EXCLUDE_FILES.includes(basename)) return true;
833
+ return false;
849
834
  }
850
835
 
851
836
  /**
@@ -870,6 +855,7 @@ function syncAetherToHub(srcDir, destDir) {
870
855
  const fullPath = path.join(dir, entry.name);
871
856
  const relPath = path.relative(base, fullPath);
872
857
 
858
+ // Exclude non-distributable paths from source collection.
873
859
  if (shouldExcludeFromHub(relPath)) continue;
874
860
 
875
861
  if (entry.isDirectory()) {
@@ -920,7 +906,10 @@ function syncAetherToHub(srcDir, destDir) {
920
906
  const fullPath = path.join(dir, entry.name);
921
907
  const relPath = path.relative(base, fullPath);
922
908
 
923
- if (shouldExcludeFromHub(relPath)) continue;
909
+ // Skip protected directories during cleanup, but allow excluded files
910
+ // (e.g. CONTEXT.md/HANDOFF.md) to be removed from the hub if stale.
911
+ const parts = relPath.split(path.sep);
912
+ if (parts.some(part => HUB_EXCLUDE_DIRS.includes(part))) continue;
924
913
 
925
914
  if (entry.isDirectory()) {
926
915
  collectDestFiles(fullPath, base);
@@ -950,11 +939,116 @@ function syncAetherToHub(srcDir, destDir) {
950
939
  return { copied, removed, skipped };
951
940
  }
952
941
 
942
+ /**
943
+ * Sync skills from source to hub using manifest-aware strategy.
944
+ * Only syncs skills listed in .manifest.json (Aether-managed).
945
+ * User-created skills (not in manifest) are never modified or deleted.
946
+ *
947
+ * @param {string} skillsSrc - Source skills directory (e.g., .aether/skills)
948
+ * @param {string} hubSkillsDir - Hub skills directory (e.g., ~/.aether/skills)
949
+ * @returns {{ synced: string[], skipped: string[], notices: string[] }}
950
+ */
951
+ function syncSkillsToHub(skillsSrc, hubSkillsDir) {
952
+ const result = { synced: [], skipped: [], notices: [] };
953
+
954
+ if (!fs.existsSync(skillsSrc)) {
955
+ return result;
956
+ }
957
+
958
+ for (const category of ['colony', 'domain']) {
959
+ const srcCat = path.join(skillsSrc, category);
960
+ const hubCat = path.join(hubSkillsDir, category);
961
+
962
+ if (!fs.existsSync(srcCat)) continue;
963
+ fs.mkdirSync(hubCat, { recursive: true });
964
+
965
+ // Copy manifest
966
+ const manifestSrc = path.join(srcCat, '.manifest.json');
967
+ if (fs.existsSync(manifestSrc)) {
968
+ fs.copyFileSync(manifestSrc, path.join(hubCat, '.manifest.json'));
969
+ }
970
+
971
+ // Read manifest for owned skills list
972
+ let manifest = { skills: [] };
973
+ try {
974
+ manifest = JSON.parse(fs.readFileSync(manifestSrc, 'utf8'));
975
+ } catch (e) { /* no manifest = no managed skills */ }
976
+
977
+ // Sync only managed skills (skip user-created)
978
+ const srcDirs = fs.readdirSync(srcCat, { withFileTypes: true })
979
+ .filter(d => d.isDirectory());
980
+
981
+ for (const dir of srcDirs) {
982
+ if (manifest.skills.includes(dir.name)) {
983
+ // Managed skill — overwrite via syncDirWithCleanup
984
+ const srcSkill = path.join(srcCat, dir.name);
985
+ const hubSkill = path.join(hubCat, dir.name);
986
+ syncDirWithCleanup(srcSkill, hubSkill);
987
+ result.synced.push(`${category}/${dir.name}`);
988
+ } else if (fs.existsSync(path.join(hubCat, dir.name))) {
989
+ // User-created skill exists with same name — skip and log notice
990
+ const notice = `Skipped skill '${dir.name}' — user version exists. Run 'aether skill-diff ${dir.name}' to compare.`;
991
+ result.notices.push(notice);
992
+ result.skipped.push(`${category}/${dir.name}`);
993
+ }
994
+ }
995
+
996
+ // Copy README if present
997
+ const readmeSrc = path.join(srcCat, 'README.md');
998
+ if (fs.existsSync(readmeSrc)) {
999
+ fs.copyFileSync(readmeSrc, path.join(hubCat, 'README.md'));
1000
+ }
1001
+ }
1002
+
1003
+ return result;
1004
+ }
1005
+
953
1006
  function setupHub() {
954
1007
  // Create ~/.aether/ directory structure and populate from package
955
1008
  try {
956
1009
  fs.mkdirSync(HUB_DIR, { recursive: true });
957
1010
 
1011
+ // MIGRATION: Check for old structure and migrate to system/
1012
+ const oldStructureFiles = [
1013
+ path.join(HUB_DIR, 'aether-utils.sh'),
1014
+ path.join(HUB_DIR, 'workers.md'),
1015
+ ];
1016
+ const hasOldStructure = oldStructureFiles.some(f => fs.existsSync(f));
1017
+ const hasNewStructure = fs.existsSync(HUB_SYSTEM_DIR);
1018
+
1019
+ if (hasOldStructure && !hasNewStructure) {
1020
+ log(' Migrating hub to new structure...');
1021
+ fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
1022
+
1023
+ // Move system files to system/
1024
+ const systemFiles = ['aether-utils.sh', 'workers.md'];
1025
+ const systemDirs = ['docs', 'utils', 'commands', 'agents', 'schemas', 'exchange', 'templates', 'lib'];
1026
+
1027
+ for (const file of systemFiles) {
1028
+ const oldPath = path.join(HUB_DIR, file);
1029
+ if (fs.existsSync(oldPath)) {
1030
+ fs.renameSync(oldPath, path.join(HUB_SYSTEM_DIR, file));
1031
+ }
1032
+ }
1033
+
1034
+ for (const dir of systemDirs) {
1035
+ const oldPath = path.join(HUB_DIR, dir);
1036
+ if (fs.existsSync(oldPath)) {
1037
+ fs.renameSync(oldPath, path.join(HUB_SYSTEM_DIR, dir));
1038
+ }
1039
+ }
1040
+
1041
+ log(' Migration complete: system files moved to ~/.aether/system/');
1042
+ }
1043
+
1044
+ // Create system/ directory structure
1045
+ fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
1046
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'commands', 'claude'), { recursive: true });
1047
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'commands', 'opencode'), { recursive: true });
1048
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'agents'), { recursive: true });
1049
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'agents-claude'), { recursive: true });
1050
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'rules'), { recursive: true });
1051
+
958
1052
  // Read previous manifest for delta reporting
959
1053
  const prevManifestRaw = readJsonSafe(path.join(HUB_DIR, 'manifest.json'));
960
1054
  const prevManifest = prevManifestRaw && validateManifest(prevManifestRaw).valid ? prevManifestRaw : null;
@@ -962,21 +1056,32 @@ function setupHub() {
962
1056
  log(` Warning: previous manifest is invalid, regenerating`);
963
1057
  }
964
1058
 
965
- // Sync runtime/ -> ~/.aether/ (clean production files)
966
- // runtime/ is the staging area - explicit allowlist via sync-to-runtime.sh
967
- const runtimeSrc = path.join(PACKAGE_DIR, 'runtime');
968
- if (fs.existsSync(runtimeSrc)) {
969
- const result = syncAetherToHub(runtimeSrc, HUB_DIR);
970
- log(` Hub system: ${result.copied} files, ${result.skipped} unchanged -> ${HUB_DIR}`);
1059
+ // Sync .aether/ -> ~/.aether/system/ (direct packaging, no staging)
1060
+ // v4.0: .aether/ is published directly runtime/ staging removed
1061
+ const aetherSrc = path.join(PACKAGE_DIR, '.aether');
1062
+ if (fs.existsSync(aetherSrc)) {
1063
+ const result = syncAetherToHub(aetherSrc, HUB_SYSTEM_DIR);
1064
+ log(` Hub system: ${result.copied} files, ${result.skipped} unchanged -> ${HUB_SYSTEM_DIR}`);
971
1065
  if (result.removed.length > 0) {
972
1066
  log(` Hub system: removed ${result.removed.length} stale files`);
973
1067
  for (const f of result.removed) log(` - ${f}`);
974
1068
  }
975
1069
  }
976
1070
 
977
- // Clean up legacy directories from old hub structure
1071
+ // Migration message for users upgrading from pre-4.0 (runtime/ era)
1072
+ const prevManifestForMigration = readJsonSafe(path.join(HUB_DIR, 'manifest.json'));
1073
+ if (prevManifestForMigration && prevManifestForMigration.version && prevManifestForMigration.version.startsWith('3.')) {
1074
+ log('');
1075
+ log(' Distribution pipeline simplified (v4.0 change):');
1076
+ log(' - runtime/ staging directory has been removed');
1077
+ log(' - .aether/ is now published directly (private dirs excluded)');
1078
+ log(' - Your colony state and data are unaffected');
1079
+ log(' - See CHANGELOG.md for details');
1080
+ log('');
1081
+ }
1082
+
1083
+ // Clean up legacy directories from very old hub structure (pre-system/)
978
1084
  const legacyDirs = [
979
- path.join(HUB_DIR, 'system'),
980
1085
  path.join(HUB_DIR, '.aether'),
981
1086
  path.join(HUB_DIR, 'visualizations'),
982
1087
  ];
@@ -991,7 +1096,7 @@ function setupHub() {
991
1096
  }
992
1097
  }
993
1098
 
994
- // Sync .claude/commands/ant/ -> ~/.aether/commands/claude/
1099
+ // Sync .claude/commands/ant/ -> ~/.aether/system/commands/claude/
995
1100
  const claudeCmdSrc = fs.existsSync(COMMANDS_SRC)
996
1101
  ? COMMANDS_SRC
997
1102
  : path.join(PACKAGE_DIR, '.claude', 'commands', 'ant');
@@ -1004,7 +1109,7 @@ function setupHub() {
1004
1109
  }
1005
1110
  }
1006
1111
 
1007
- // Sync .opencode/commands/ant/ -> ~/.aether/commands/opencode/
1112
+ // Sync .opencode/commands/ant/ -> ~/.aether/system/commands/opencode/
1008
1113
  const opencodeCmdSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
1009
1114
  if (fs.existsSync(opencodeCmdSrc)) {
1010
1115
  const result = syncDirWithCleanup(opencodeCmdSrc, HUB_COMMANDS_OPENCODE);
@@ -1015,7 +1120,7 @@ function setupHub() {
1015
1120
  }
1016
1121
  }
1017
1122
 
1018
- // Sync .opencode/agents/ -> ~/.aether/agents/
1123
+ // Sync .opencode/agents/ -> ~/.aether/system/agents/
1019
1124
  const agentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
1020
1125
  if (fs.existsSync(agentsSrc)) {
1021
1126
  const result = syncDirWithCleanup(agentsSrc, HUB_AGENTS);
@@ -1026,7 +1131,44 @@ function setupHub() {
1026
1131
  }
1027
1132
  }
1028
1133
 
1029
- // Create/preserve registry.json
1134
+ // Sync .claude/agents/ant/ -> ~/.aether/system/agents-claude/
1135
+ const claudeAgentsSrc = path.join(PACKAGE_DIR, '.claude', 'agents', 'ant');
1136
+ if (fs.existsSync(claudeAgentsSrc)) {
1137
+ const result = syncDirWithCleanup(claudeAgentsSrc, HUB_AGENTS_CLAUDE);
1138
+ log(` Hub agents (claude): ${result.copied} files, ${result.skipped} unchanged -> ${HUB_AGENTS_CLAUDE}`);
1139
+ if (result.removed.length > 0) {
1140
+ log(` Hub agents (claude): removed ${result.removed.length} stale files`);
1141
+ for (const f of result.removed) log(` - ${f}`);
1142
+ }
1143
+ }
1144
+
1145
+ // Sync rules/ from .aether/ -> ~/.aether/system/rules/
1146
+ // v4.0: source is .aether/rules/ directly (no runtime/ staging)
1147
+ const rulesSrc = path.join(PACKAGE_DIR, '.aether', 'rules');
1148
+ if (fs.existsSync(rulesSrc)) {
1149
+ const result = syncDirWithCleanup(rulesSrc, HUB_RULES);
1150
+ log(` Hub rules: ${result.copied} files -> ${HUB_RULES}`);
1151
+ if (result.removed.length > 0) {
1152
+ log(` Hub rules: removed ${result.removed.length} stale files`);
1153
+ for (const f of result.removed) log(` - ${f}`);
1154
+ }
1155
+ }
1156
+
1157
+ // Sync skills to hub (~/.aether/skills/)
1158
+ // Skills install at hub root (NOT in system/) so users can find and create their own
1159
+ const skillsSrc = path.join(aetherSrc, 'skills');
1160
+ const HUB_SKILLS_DIR = path.join(HUB_DIR, 'skills');
1161
+ if (fs.existsSync(skillsSrc)) {
1162
+ const skillsResult = syncSkillsToHub(skillsSrc, HUB_SKILLS_DIR);
1163
+ if (skillsResult.synced.length > 0) {
1164
+ log(` Hub skills: ${skillsResult.synced.length} managed skills synced -> ${HUB_SKILLS_DIR}`);
1165
+ }
1166
+ for (const notice of skillsResult.notices) {
1167
+ log(` ${notice}`);
1168
+ }
1169
+ }
1170
+
1171
+ // Create/preserve registry.json (at root, not in system/)
1030
1172
  if (!fs.existsSync(HUB_REGISTRY)) {
1031
1173
  writeJsonSync(HUB_REGISTRY, { schema_version: 1, repos: [] });
1032
1174
  log(` Registry: initialized ${HUB_REGISTRY}`);
@@ -1034,7 +1176,7 @@ function setupHub() {
1034
1176
  log(` Registry: preserved existing ${HUB_REGISTRY}`);
1035
1177
  }
1036
1178
 
1037
- // Generate and write manifest
1179
+ // Generate and write manifest (at root, tracks everything)
1038
1180
  const manifest = generateManifest(HUB_DIR);
1039
1181
  const manifestPath = path.join(HUB_DIR, 'manifest.json');
1040
1182
  writeJsonSync(manifestPath, manifest);
@@ -1053,7 +1195,7 @@ function setupHub() {
1053
1195
  }
1054
1196
  }
1055
1197
 
1056
- // Write version.json
1198
+ // Write version.json (at root)
1057
1199
  writeJsonSync(HUB_VERSION, { version: VERSION, updated_at: new Date().toISOString() });
1058
1200
  log(` Hub version: ${VERSION}`);
1059
1201
  } catch (err) {
@@ -1079,21 +1221,23 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1079
1221
  const currentVer = currentVersion ? currentVersion.version : 'unknown';
1080
1222
 
1081
1223
  // Target directories for git safety checks
1082
- const targetDirs = ['.aether', '.claude/commands/ant', '.opencode/commands/ant', '.opencode/agents'];
1224
+ const targetDirs = ['.aether', '.claude/commands/ant', '.claude/agents/ant', '.claude/rules', '.opencode/commands/ant', '.opencode/agents'];
1083
1225
 
1084
- // Git safety: check for dirty files in target directories (skip in dry-run mode)
1226
+ // Use UpdateTransaction for two-phase commit with automatic rollback
1227
+ const transaction = new UpdateTransaction(repoPath, { sourceVersion, quiet, force });
1228
+
1229
+ // Git safety: only warn about dirty files the update would actually overwrite
1085
1230
  let dirtyFiles = [];
1086
- if (isGitRepo(repoPath)) {
1087
- dirtyFiles = getGitDirtyFiles(repoPath, targetDirs);
1088
- if (dirtyFiles.length > 0 && !force) {
1231
+ if (isGitRepo(repoPath) && !force) {
1232
+ const wouldOverwrite = new Set(transaction.getConflictingFiles());
1233
+ const allDirty = getGitDirtyFiles(repoPath, targetDirs);
1234
+ dirtyFiles = allDirty.filter(f => wouldOverwrite.has(f));
1235
+ if (dirtyFiles.length > 0) {
1089
1236
  return { status: 'dirty', files: dirtyFiles };
1090
1237
  }
1091
1238
  // Note: --force handling is now done via checkpoint stash in UpdateTransaction
1092
1239
  }
1093
1240
 
1094
- // Use UpdateTransaction for two-phase commit with automatic rollback
1095
- const transaction = new UpdateTransaction(repoPath, { sourceVersion, quiet, force });
1096
-
1097
1241
  try {
1098
1242
  const result = await transaction.execute(sourceVersion, { dryRun });
1099
1243
 
@@ -1101,17 +1245,25 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1101
1245
  const systemCopied = result.sync_result?.system?.copied || 0;
1102
1246
  const commandsCopied = (result.sync_result?.commands?.copied || 0);
1103
1247
  const agentsCopied = result.sync_result?.agents?.copied || 0;
1248
+ const rulesCopied = result.sync_result?.rules?.copied || 0;
1249
+ const agentsClaudeCopied = result.sync_result?.agents_claude?.copied || 0;
1104
1250
 
1105
1251
  const systemRemoved = result.sync_result?.system?.removed?.length || 0;
1106
1252
  const commandsRemoved = result.sync_result?.commands?.removed?.length || 0;
1107
1253
  const agentsRemoved = result.sync_result?.agents?.removed?.length || 0;
1254
+ const rulesRemoved = result.sync_result?.rules?.removed?.length || 0;
1255
+ const agentsClaudeRemoved = result.sync_result?.agents_claude?.removed?.length || 0;
1108
1256
 
1109
1257
  const allRemovedFiles = [
1110
1258
  ...(result.sync_result?.system?.removed || []),
1111
1259
  ...(result.sync_result?.commands?.removed || []).map(f => `.claude/commands/ant/${f}`),
1112
1260
  ...(result.sync_result?.agents?.removed || []).map(f => `.opencode/agents/${f}`),
1261
+ ...(result.sync_result?.rules?.removed || []).map(f => `.claude/rules/${f}`),
1262
+ ...(result.sync_result?.agents_claude?.removed || []).map(f => `.claude/agents/ant/${f}`),
1113
1263
  ];
1114
1264
 
1265
+ const cleanupResult = result.cleanup_result || { cleaned: [], failed: [] };
1266
+
1115
1267
  return {
1116
1268
  status: result.status,
1117
1269
  from: currentVer,
@@ -1119,10 +1271,13 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1119
1271
  system: systemCopied,
1120
1272
  commands: commandsCopied,
1121
1273
  agents: agentsCopied,
1122
- removed: systemRemoved + commandsRemoved + agentsRemoved,
1274
+ rules: rulesCopied,
1275
+ agentsClaude: agentsClaudeCopied,
1276
+ removed: systemRemoved + commandsRemoved + agentsRemoved + rulesRemoved + agentsClaudeRemoved,
1123
1277
  removedFiles: allRemovedFiles,
1124
1278
  stashCreated: !!transaction.checkpoint?.stashRef,
1125
1279
  checkpoint_id: result.checkpoint_id,
1280
+ cleanup: cleanupResult,
1126
1281
  };
1127
1282
  } catch (error) {
1128
1283
  // Handle UpdateError with recovery commands
@@ -1161,7 +1316,7 @@ program.on('option:quiet', () => {
1161
1316
  // Install command
1162
1317
  program
1163
1318
  .command('install')
1164
- .description('Install slash-commands to ~/.claude/commands/ant/ and set up distribution hub')
1319
+ .description('Install commands and agents to ~/.claude/ and set up distribution hub')
1165
1320
  .action(wrapCommand(async () => {
1166
1321
  log(c.header(`aether-colony v${VERSION} — installing...`));
1167
1322
 
@@ -1188,6 +1343,39 @@ program
1188
1343
  }
1189
1344
  }
1190
1345
 
1346
+ // Sync agents to ~/.claude/agents/ant/ (with orphan cleanup)
1347
+ const repoAgents = path.join(PACKAGE_DIR, '.claude', 'agents', 'ant');
1348
+ if (fs.existsSync(repoAgents)) {
1349
+ const result = syncDirWithCleanup(repoAgents, AGENTS_DEST);
1350
+ log(` Agents (claude): ${result.copied} files -> ${AGENTS_DEST}`);
1351
+ if (result.removed.length > 0) {
1352
+ log(` Agents (claude): removed ${result.removed.length} stale files`);
1353
+ for (const f of result.removed) log(` - ${f}`);
1354
+ }
1355
+ }
1356
+
1357
+ // Sync OpenCode commands to ~/.opencode/command/ (with orphan cleanup)
1358
+ const opencodeCmdsSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
1359
+ if (fs.existsSync(opencodeCmdsSrc)) {
1360
+ const result = syncDirWithCleanup(opencodeCmdsSrc, OPENCODE_COMMANDS_DEST);
1361
+ log(` Commands (opencode): ${result.copied} files -> ${OPENCODE_COMMANDS_DEST}`);
1362
+ if (result.removed.length > 0) {
1363
+ log(` Commands (opencode): removed ${result.removed.length} stale files`);
1364
+ for (const f of result.removed) log(` - ${f}`);
1365
+ }
1366
+ }
1367
+
1368
+ // Sync OpenCode agents to ~/.opencode/agent/ (with orphan cleanup)
1369
+ const opencodeAgentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
1370
+ if (fs.existsSync(opencodeAgentsSrc)) {
1371
+ const result = syncDirWithCleanup(opencodeAgentsSrc, OPENCODE_AGENTS_DEST);
1372
+ log(` Agents (opencode): ${result.copied} files -> ${OPENCODE_AGENTS_DEST}`);
1373
+ if (result.removed.length > 0) {
1374
+ log(` Agents (opencode): removed ${result.removed.length} stale files`);
1375
+ for (const f of result.removed) log(` - ${f}`);
1376
+ }
1377
+ }
1378
+
1191
1379
  // Set up distribution hub at ~/.aether/
1192
1380
  log('');
1193
1381
  log(c.colony('Setting up distribution hub...'));
@@ -1196,6 +1384,7 @@ program
1196
1384
  log('');
1197
1385
  log(c.success('Install complete.'));
1198
1386
  log(` ${c.queen('Claude Code:')} run /ant to get started`);
1387
+ log(` ${c.colony('OpenCode:')} run /ant to get started`);
1199
1388
  log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} (for coordinated updates across repos)`);
1200
1389
  }));
1201
1390
 
@@ -1288,19 +1477,31 @@ program
1288
1477
  console.error(` Skipping. Use --force to stash and update.`);
1289
1478
  dirty++;
1290
1479
  } else if (result.status === 'dry-run') {
1291
- log(` Would update: ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents]`);
1480
+ log(` Would update: ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents, ${result.agentsClaude} claude agents]`);
1292
1481
  if (result.removed > 0) {
1293
1482
  log(` Would remove ${result.removed} stale files:`);
1294
1483
  for (const f of result.removedFiles) log(` - ${f}`);
1295
1484
  }
1296
1485
  updated++;
1297
1486
  } else if (result.status === 'updated') {
1298
- log(` ${c.success('Updated:')} ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents]`);
1487
+ log(` ${c.success('Updated:')} ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents, ${result.agentsClaude} claude agents]`);
1299
1488
  if (result.removed > 0) {
1300
1489
  log(` Removed ${result.removed} stale files:`);
1301
1490
  for (const f of result.removedFiles) log(` - ${f}`);
1302
1491
  totalRemoved += result.removed;
1303
1492
  }
1493
+ // Distribution chain cleanup reporting
1494
+ if (result.cleanup && result.cleanup.cleaned.length > 0) {
1495
+ for (const label of result.cleanup.cleaned) {
1496
+ log(` ${c.success('\u2713')} Removed ${label}`);
1497
+ }
1498
+ }
1499
+ for (const failure of (result.cleanup?.failed || [])) {
1500
+ log(` ${c.error('\u2717')} Failed to remove ${failure.label}: ${failure.error}`);
1501
+ }
1502
+ if (result.cleanup && result.cleanup.cleaned.length === 0 && result.cleanup.failed.length === 0) {
1503
+ log(` Distribution chain: ${c.success('\u2713')} clean`);
1504
+ }
1304
1505
  if (result.stashCreated) {
1305
1506
  log(` Stash created. Recover with: cd ${repo.path} && git stash pop`);
1306
1507
  }
@@ -1324,7 +1525,7 @@ program
1324
1525
  }
1325
1526
 
1326
1527
  const label = dryRun ? 'would update' : 'updated';
1327
- let summary = `\nSummary: ${updated} ${label}, ${upToDate} up-to-date, ${pruned} pruned`;
1528
+ let summary = `\nSummary: ${updated} ${label}, ${upToDate} up to date, ${pruned} pruned`;
1328
1529
  if (dirty > 0) summary += `, ${dirty} dirty (skipped)`;
1329
1530
  if (totalRemoved > 0) summary += `, ${totalRemoved} stale files removed`;
1330
1531
  console.log(summary);
@@ -1343,11 +1544,19 @@ program
1343
1544
  process.exit(getExitCode(error.code));
1344
1545
  }
1345
1546
 
1547
+ const pendingPath = path.join(repoAether, '.update-pending');
1548
+ const hasPending = fs.existsSync(pendingPath);
1549
+
1550
+ if (hasPending) {
1551
+ console.log('Detected incomplete update, re-syncing...');
1552
+ try { fs.unlinkSync(pendingPath); } catch { /* ignore */ }
1553
+ }
1554
+
1346
1555
  const currentVersion = readJsonSafe(path.join(repoAether, 'version.json'));
1347
1556
  const currentVer = currentVersion ? currentVersion.version : 'unknown';
1348
1557
 
1349
- if (!forceFlag && !dryRun && currentVer === sourceVersion) {
1350
- console.log(c.info(`Already up-to-date (v${sourceVersion}).`));
1558
+ if (!hasPending && !forceFlag && !dryRun && currentVer === sourceVersion) {
1559
+ console.log(c.info(`Already up to date (v${sourceVersion}).`));
1351
1560
  return;
1352
1561
  }
1353
1562
 
@@ -1371,7 +1580,7 @@ program
1371
1580
 
1372
1581
  if (result.status === 'dry-run') {
1373
1582
  console.log(`Would update: ${result.from} -> ${result.to}`);
1374
- console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files`);
1583
+ console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files, ${result.agentsClaude} claude agent files`);
1375
1584
  if (result.removed > 0) {
1376
1585
  console.log(` Would remove ${result.removed} stale files:`);
1377
1586
  for (const f of result.removedFiles) console.log(` - ${f}`);
@@ -1381,11 +1590,23 @@ program
1381
1590
  }
1382
1591
 
1383
1592
  console.log(c.success(`Updated: ${result.from} -> ${result.to}`));
1384
- console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files, ${result.visualizations} visualization files`);
1593
+ console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files, ${result.agentsClaude} claude agent files`);
1385
1594
  if (result.removed > 0) {
1386
1595
  console.log(` Removed ${result.removed} stale files:`);
1387
1596
  for (const f of result.removedFiles) console.log(` - ${f}`);
1388
1597
  }
1598
+ // Distribution chain cleanup reporting
1599
+ if (result.cleanup && result.cleanup.cleaned.length > 0) {
1600
+ for (const label of result.cleanup.cleaned) {
1601
+ console.log(` ${c.success('\u2713')} Removed ${label}`);
1602
+ }
1603
+ }
1604
+ for (const failure of (result.cleanup?.failed || [])) {
1605
+ console.log(` ${c.error('\u2717')} Failed to remove ${failure.label}: ${failure.error}`);
1606
+ }
1607
+ if (result.cleanup && result.cleanup.cleaned.length === 0 && result.cleanup.failed.length === 0) {
1608
+ console.log(` Distribution chain: ${c.success('\u2713')} clean`);
1609
+ }
1389
1610
  if (result.stashCreated) {
1390
1611
  console.log(' Git stash created. Recover with: git stash pop');
1391
1612
  }
@@ -1432,7 +1653,7 @@ program
1432
1653
  // Uninstall command
1433
1654
  program
1434
1655
  .command('uninstall')
1435
- .description('Remove slash-commands from ~/.claude/commands/ant/ (preserves project state and hub)')
1656
+ .description('Remove slash-commands from ~/.claude/commands/ant/ and ~/.opencode/ (preserves project state and hub)')
1436
1657
  .action(wrapCommand(async () => {
1437
1658
  log(c.header(`aether-colony v${VERSION} — uninstalling...`));
1438
1659
 
@@ -1444,6 +1665,26 @@ program
1444
1665
  log(' Claude Code commands already removed.');
1445
1666
  }
1446
1667
 
1668
+ // Remove Claude Code agents
1669
+ if (fs.existsSync(AGENTS_DEST)) {
1670
+ const n = removeDirSync(AGENTS_DEST);
1671
+ log(` Removed: ${n} agent files from ${AGENTS_DEST}`);
1672
+ }
1673
+
1674
+ // Remove OpenCode commands (only our files, preserve others)
1675
+ const opencodeCmdsSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
1676
+ if (fs.existsSync(OPENCODE_COMMANDS_DEST) && fs.existsSync(opencodeCmdsSrc)) {
1677
+ const n = removeFilesFromSource(opencodeCmdsSrc, OPENCODE_COMMANDS_DEST);
1678
+ log(` Removed: ${n} command files from ${OPENCODE_COMMANDS_DEST}`);
1679
+ }
1680
+
1681
+ // Remove OpenCode agents (only our files, preserve others)
1682
+ const opencodeAgentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
1683
+ if (fs.existsSync(OPENCODE_AGENTS_DEST) && fs.existsSync(opencodeAgentsSrc)) {
1684
+ const n = removeFilesFromSource(opencodeAgentsSrc, OPENCODE_AGENTS_DEST);
1685
+ log(` Removed: ${n} agent files from ${OPENCODE_AGENTS_DEST}`);
1686
+ }
1687
+
1447
1688
  log('');
1448
1689
  log(c.success('Uninstall complete. Per-project .aether/data/ directories are untouched.'));
1449
1690
  log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} preserved (remove manually if desired).`);
@@ -1681,261 +1922,6 @@ program
1681
1922
  }
1682
1923
  }));
1683
1924
 
1684
- // Caste emoji mapping for display
1685
- const CASTE_EMOJIS = {
1686
- builder: '🔨',
1687
- watcher: '👁️',
1688
- scout: '🔍',
1689
- chaos: '🎲',
1690
- oracle: '🔮',
1691
- architect: '🏗️',
1692
- prime: '🏛️',
1693
- colonizer: '🌱',
1694
- route_setter: '🧭',
1695
- archaeologist: '📜',
1696
- };
1697
-
1698
- /**
1699
- * Format context window for display
1700
- * @param {number} contextWindow - Context window size
1701
- * @returns {string} Formatted string (e.g., "256K")
1702
- */
1703
- function formatContextWindow(contextWindow) {
1704
- if (!contextWindow) return '-';
1705
- if (contextWindow >= 1000) {
1706
- return `${Math.round(contextWindow / 1000)}K`;
1707
- }
1708
- return String(contextWindow);
1709
- }
1710
-
1711
- // Caste-models command - Manage caste-to-model assignments
1712
- const casteModelsCmd = program
1713
- .command('caste-models')
1714
- .description('Manage caste-to-model assignments');
1715
-
1716
- // list subcommand
1717
- casteModelsCmd
1718
- .command('list')
1719
- .description('List current model assignments per caste')
1720
- .option('--verify', 'Verify model availability on proxy')
1721
- .action(wrapCommand(async (options) => {
1722
- const repoPath = process.cwd();
1723
- const profiles = loadModelProfiles(repoPath);
1724
- const overrides = getUserOverrides(profiles);
1725
- const proxyConfig = getProxyConfig(profiles);
1726
-
1727
- // Check proxy health
1728
- let proxyHealth = null;
1729
- let proxyModels = null;
1730
- if (proxyConfig?.endpoint) {
1731
- proxyHealth = await checkProxyHealth(proxyConfig.endpoint);
1732
- if (proxyHealth.healthy && proxyHealth.models) {
1733
- proxyModels = proxyHealth.models;
1734
- }
1735
- }
1736
-
1737
- console.log(c.header('Caste Model Assignments\n'));
1738
-
1739
- // Display proxy status
1740
- if (proxyConfig?.endpoint) {
1741
- const proxyStatus = formatProxyStatusColored(proxyHealth, c) + c.dim(` @ ${proxyConfig.endpoint}`);
1742
- console.log(`Proxy: ${proxyStatus}`);
1743
- if (!proxyHealth?.healthy) {
1744
- console.log(c.warning('Warning: Using default model (kimi-k2.5) for all castes'));
1745
- }
1746
- console.log('');
1747
- }
1748
-
1749
- // Table header - add Verify column if --verify flag
1750
- const verifyFlag = options.verify;
1751
- const header = verifyFlag
1752
- ? `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Verify Status`
1753
- : `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Status`;
1754
- console.log(header);
1755
- console.log(verifyFlag ? '─'.repeat(70) : '─'.repeat(60));
1756
-
1757
- // Get all assignments
1758
- const assignments = getAllAssignments(profiles);
1759
-
1760
- for (const assignment of assignments) {
1761
- const emoji = CASTE_EMOJIS[assignment.caste] || '•';
1762
- const casteName = assignment.caste.charAt(0).toUpperCase() + assignment.caste.slice(1);
1763
- const casteDisplay = `${emoji} ${casteName}`;
1764
-
1765
- // Check for override
1766
- const hasOverride = overrides[assignment.caste] !== undefined;
1767
- const effectiveModel = getEffectiveModel(profiles, assignment.caste);
1768
- const modelDisplay = effectiveModel.model + (hasOverride ? ' (override)' : '');
1769
-
1770
- // Get model metadata
1771
- const metadata = getModelMetadata(profiles, effectiveModel.model);
1772
- const provider = metadata?.provider || assignment.provider || '-';
1773
- const contextWindow = formatContextWindow(metadata?.context_window);
1774
-
1775
- // Status indicator based on proxy health
1776
- const status = proxyHealth?.healthy ? '✓' : '⚠';
1777
-
1778
- // Verify flag - check if model is available on proxy
1779
- let verifyStatus = '';
1780
- if (verifyFlag) {
1781
- if (proxyModels) {
1782
- const isAvailable = proxyModels.includes(effectiveModel.model);
1783
- verifyStatus = isAvailable ? '✓' : '✗';
1784
- } else {
1785
- verifyStatus = '?';
1786
- }
1787
- console.log(
1788
- `${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${verifyStatus.padEnd(7)} ${status}`
1789
- );
1790
- } else {
1791
- console.log(
1792
- `${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${status}`
1793
- );
1794
- }
1795
- }
1796
-
1797
- // Show overrides summary if any exist
1798
- const overrideCount = Object.keys(overrides).length;
1799
- if (overrideCount > 0) {
1800
- console.log('');
1801
- console.log(c.info(`Active overrides: ${overrideCount}`));
1802
- for (const [caste, model] of Object.entries(overrides)) {
1803
- console.log(` ${caste}: ${model}`);
1804
- }
1805
- }
1806
- }));
1807
-
1808
- // set subcommand
1809
- casteModelsCmd
1810
- .command('set')
1811
- .description('Set model override for a caste')
1812
- .argument('<assignment>', 'caste=model (e.g., builder=glm-5)')
1813
- .action(wrapCommand(async (assignment) => {
1814
- // Parse caste=model format
1815
- const match = assignment.match(/^([^=]+)=(.+)$/);
1816
- if (!match) {
1817
- const error = new ValidationError(
1818
- `Invalid assignment format: '${assignment}'`,
1819
- { received: assignment },
1820
- 'Use format: caste=model (e.g., builder=glm-5)'
1821
- );
1822
- throw error;
1823
- }
1824
-
1825
- const [, caste, model] = match;
1826
-
1827
- const repoPath = process.cwd();
1828
-
1829
- // Validate and set
1830
- try {
1831
- const result = setModelOverride(repoPath, caste, model);
1832
-
1833
- if (result.previous) {
1834
- console.log(c.success(`Updated ${caste}: ${result.previous} → ${model}`));
1835
- } else {
1836
- console.log(c.success(`Set ${caste} to ${model}`));
1837
- }
1838
- } catch (error) {
1839
- if (error.name === 'ValidationError') {
1840
- // Add helpful suggestions
1841
- if (error.details?.validCastes) {
1842
- console.error(c.error(`Error: ${error.message}`));
1843
- console.error('\nValid castes:');
1844
- for (const casteName of error.details.validCastes) {
1845
- const emoji = CASTE_EMOJIS[casteName] || '•';
1846
- console.error(` ${emoji} ${casteName}`);
1847
- }
1848
- } else if (error.details?.validModels) {
1849
- console.error(c.error(`Error: ${error.message}`));
1850
- console.error('\nValid models:');
1851
- for (const modelName of error.details.validModels) {
1852
- console.error(` • ${modelName}`);
1853
- }
1854
- }
1855
- process.exit(1);
1856
- }
1857
- throw error;
1858
- }
1859
- }));
1860
-
1861
- // reset subcommand
1862
- casteModelsCmd
1863
- .command('reset')
1864
- .description('Reset caste to default model (remove override)')
1865
- .argument('<caste>', 'caste name (e.g., builder)')
1866
- .action(wrapCommand(async (caste) => {
1867
- const repoPath = process.cwd();
1868
-
1869
- try {
1870
- const result = resetModelOverride(repoPath, caste);
1871
-
1872
- if (result.hadOverride) {
1873
- console.log(c.success(`Reset ${caste} to default model`));
1874
- } else {
1875
- console.log(c.info(`${caste} was already using default model`));
1876
- }
1877
- } catch (error) {
1878
- if (error.name === 'ValidationError' && error.details?.validCastes) {
1879
- console.error(c.error(`Error: ${error.message}`));
1880
- console.error('\nValid castes:');
1881
- for (const casteName of error.details.validCastes) {
1882
- const emoji = CASTE_EMOJIS[casteName] || '•';
1883
- console.error(` ${emoji} ${casteName}`);
1884
- }
1885
- process.exit(1);
1886
- }
1887
- throw error;
1888
- }
1889
- }));
1890
-
1891
- // Verify-models command - Verify model routing configuration
1892
- program
1893
- .command('verify-models')
1894
- .description('Verify model routing configuration is active')
1895
- .action(wrapCommand(async () => {
1896
- const repoPath = process.cwd();
1897
- const report = await createVerificationReport(repoPath);
1898
-
1899
- console.log('=== Model Routing Verification ===\n');
1900
-
1901
- // Proxy status
1902
- console.log(`LiteLLM Proxy: ${report.proxy.running ? '✓ Running' : '✗ Not running'}`);
1903
- if (report.proxy.running) {
1904
- console.log(` Latency: ${report.proxy.latency}ms`);
1905
- }
1906
-
1907
- // Environment
1908
- console.log(`\nEnvironment:`);
1909
- console.log(` ANTHROPIC_MODEL: ${report.env.model || '(not set)'}`);
1910
- console.log(` ANTHROPIC_BASE_URL: ${report.env.baseUrl || '(not set)'}`);
1911
-
1912
- // Caste assignments
1913
- console.log(`\nCaste Model Assignments:`);
1914
- for (const [caste, info] of Object.entries(report.castes)) {
1915
- const status = info.assigned ? '✓' : '✗';
1916
- console.log(` ${status} ${caste}: ${info.model || 'default'}`);
1917
- }
1918
-
1919
- // Model profiles file
1920
- console.log(`\nModel Profiles File:`);
1921
- if (report.profilesFile.exists) {
1922
- console.log(` ✓ Found: ${report.profilesFile.path}`);
1923
- const profileCount = Object.keys(report.profilesFile.profiles).length;
1924
- console.log(` Profiles: ${profileCount} castes configured`);
1925
- } else {
1926
- console.log(` ✗ Not found: ${report.profilesFile.path}`);
1927
- }
1928
-
1929
- // Issues
1930
- if (report.issues.length > 0) {
1931
- console.log(`\nIssues Found:`);
1932
- report.issues.forEach(issue => console.log(` ⚠ ${issue}`));
1933
- }
1934
-
1935
- // Recommendation
1936
- console.log(`\n${report.recommendation}`);
1937
- }));
1938
-
1939
1925
  // Spawn-log command - Log a worker spawn event
1940
1926
  program
1941
1927
  .command('spawn-log')
@@ -1969,156 +1955,100 @@ program
1969
1955
  console.log(tree);
1970
1956
  }));
1971
1957
 
1972
- // Nestmates command - List sibling colonies
1958
+ // Status command - Show colony status
1973
1959
  program
1974
- .command('nestmates')
1975
- .description('List sibling colonies (nestmates)')
1976
- .action(wrapCommand(async () => {
1960
+ .command('status')
1961
+ .description('Show colony status')
1962
+ .option('-j, --json', 'Output as JSON')
1963
+ .action(wrapCommand(async (options) => {
1977
1964
  const repoPath = process.cwd();
1978
- const nestmates = findNestmates(repoPath);
1965
+ const colonyStatePath = path.join(repoPath, '.aether', 'data', 'COLONY_STATE.json');
1979
1966
 
1980
- if (nestmates.length === 0) {
1981
- console.log('No nestmates found (sibling directories with .aether/).');
1967
+ // Check if colony exists
1968
+ if (!fs.existsSync(colonyStatePath)) {
1969
+ console.log(c.warning('No colony found in current directory.'));
1970
+ console.log(c.dim('Run /ant:init to create a new colony, or cd to a project with an existing colony.'));
1982
1971
  return;
1983
1972
  }
1984
1973
 
1985
- console.log(`Found ${nestmates.length} nestmate(s):\n`);
1986
- console.log(formatNestmates(nestmates));
1987
- }));
1988
-
1989
- // Telemetry command - View model performance telemetry
1990
- const telemetryCmd = program
1991
- .command('telemetry')
1992
- .description('View model performance telemetry')
1993
- .action(wrapCommand(async () => {
1994
- // Default action: show summary
1995
- const repoPath = process.cwd();
1996
- const summary = getTelemetrySummary(repoPath);
1997
-
1998
- console.log(c.header('Model Performance Telemetry\n'));
1999
- console.log(`Total Spawns: ${summary.total_spawns}`);
2000
- console.log(`Models Used: ${summary.total_models}\n`);
2001
-
2002
- if (summary.total_spawns === 0) {
2003
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
1974
+ // Load colony state
1975
+ let state;
1976
+ try {
1977
+ state = JSON.parse(fs.readFileSync(colonyStatePath, 'utf8'));
1978
+ } catch (err) {
1979
+ console.log(c.error('Could not read colony state.'));
1980
+ console.log(c.dim(`Error: ${err.message}`));
1981
+ console.log(c.dim('The colony state file may be corrupted. Consider running /ant:init to reinitialize.'));
2004
1982
  return;
2005
1983
  }
2006
1984
 
2007
- console.log('Model Performance:');
2008
- console.log('─'.repeat(60));
2009
- for (const [model, stats] of Object.entries(summary.models)) {
2010
- const rate = (stats.success_rate * 100).toFixed(1);
2011
- const rateColor = stats.success_rate >= 0.9 ? c.success :
2012
- stats.success_rate >= 0.7 ? c.warning : c.error;
2013
- console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
2014
- }
2015
-
2016
- if (summary.recent_decisions.length > 0) {
2017
- console.log('\nRecent Routing Decisions:');
2018
- console.log('─'.repeat(60));
2019
- for (const decision of summary.recent_decisions.slice(-5)) {
2020
- console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
2021
- }
1985
+ // JSON output
1986
+ if (options.json) {
1987
+ console.log(JSON.stringify(state, null, 2));
1988
+ return;
2022
1989
  }
2023
- }));
2024
1990
 
2025
- // summary subcommand (explicit)
2026
- telemetryCmd
2027
- .command('summary')
2028
- .description('Show overall telemetry summary')
2029
- .action(wrapCommand(async () => {
2030
- const repoPath = process.cwd();
2031
- const summary = getTelemetrySummary(repoPath);
1991
+ // Dashboard output
1992
+ console.log(c.header('Colony Status\n'));
2032
1993
 
2033
- console.log(c.header('Model Performance Telemetry\n'));
2034
- console.log(`Total Spawns: ${summary.total_spawns}`);
2035
- console.log(`Models Used: ${summary.total_models}\n`);
2036
-
2037
- if (summary.total_spawns === 0) {
2038
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
2039
- return;
1994
+ // Goal
1995
+ if (state.goal) {
1996
+ console.log(`${c.queen('Goal:')} ${state.goal}`);
2040
1997
  }
2041
1998
 
2042
- console.log('Model Performance:');
2043
- console.log('─'.repeat(60));
2044
- for (const [model, stats] of Object.entries(summary.models)) {
2045
- const rate = (stats.success_rate * 100).toFixed(1);
2046
- const rateColor = stats.success_rate >= 0.9 ? c.success :
2047
- stats.success_rate >= 0.7 ? c.warning : c.error;
2048
- console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
1999
+ // State
2000
+ if (state.state) {
2001
+ const stateDisplay = state.state === 'BUILDING' ? c.success(state.state) :
2002
+ state.state === 'PLANNING' ? c.warning(state.state) :
2003
+ state.state === 'COMPLETED' ? c.success(state.state) :
2004
+ state.state;
2005
+ console.log(`${c.colony('State:')} ${stateDisplay}`);
2049
2006
  }
2050
2007
 
2051
- if (summary.recent_decisions.length > 0) {
2052
- console.log('\nRecent Routing Decisions:');
2053
- console.log(''.repeat(60));
2054
- for (const decision of summary.recent_decisions.slice(-5)) {
2055
- console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
2056
- }
2008
+ // Phase
2009
+ if (state.current_phase !== undefined) {
2010
+ console.log(`${c.info('Phase:')} ${state.current_phase}`);
2057
2011
  }
2058
- }));
2059
2012
 
2060
- // model subcommand
2061
- telemetryCmd
2062
- .command('model <model-name>')
2063
- .description('Show detailed performance for a specific model')
2064
- .action(wrapCommand(async (modelName) => {
2065
- const repoPath = process.cwd();
2066
- const performance = getModelPerformance(repoPath, modelName);
2067
-
2068
- if (!performance) {
2069
- console.log(c.warning(`No data for model: ${modelName}`));
2070
- return;
2013
+ // Version
2014
+ if (state.version) {
2015
+ console.log(`${c.dim('Version:')} ${state.version}`);
2071
2016
  }
2072
2017
 
2073
- console.log(c.header(`Model Performance: ${modelName}\n`));
2074
- console.log(`Total Spawns: ${performance.total_spawns}`);
2075
- console.log(`Success Rate: ${(performance.success_rate * 100).toFixed(1)}%`);
2076
- console.log(` ✓ Completed: ${performance.successful_completions}`);
2077
- console.log(` ✗ Failed: ${performance.failed_completions}`);
2078
- console.log(` 🚫 Blocked: ${performance.blocked}`);
2018
+ // Last updated
2019
+ if (state.last_updated) {
2020
+ const lastUpdated = new Date(state.last_updated);
2021
+ const now = new Date();
2022
+ const hoursAgo = Math.round((now - lastUpdated) / (1000 * 60 * 60));
2023
+ const timeDisplay = hoursAgo < 1 ? 'just now' :
2024
+ hoursAgo < 24 ? `${hoursAgo} hours ago` :
2025
+ `${Math.round(hoursAgo / 24)} days ago`;
2026
+ console.log(`${c.dim('Last updated:')} ${timeDisplay}`);
2027
+ }
2079
2028
 
2080
- if (Object.keys(performance.by_caste).length > 0) {
2081
- console.log('\nPerformance by Caste:');
2082
- console.log(''.repeat(50));
2083
- for (const [caste, stats] of Object.entries(performance.by_caste)) {
2084
- const casteRate = stats.spawns > 0 ? (stats.success / stats.spawns * 100).toFixed(1) : '0.0';
2085
- console.log(` ${caste.padEnd(12)} ${String(stats.spawns).padStart(4)} spawns ${casteRate}% success`);
2086
- }
2029
+ // Event count
2030
+ if (state.events && state.events.length > 0) {
2031
+ console.log(`${c.dim('Events:')} ${state.events.length}`);
2087
2032
  }
2033
+
2034
+ console.log('');
2088
2035
  }));
2089
2036
 
2090
- // performance subcommand
2091
- telemetryCmd
2092
- .command('performance')
2093
- .description('Show models ranked by performance')
2037
+ // Nestmates command - List sibling colonies
2038
+ program
2039
+ .command('nestmates')
2040
+ .description('List sibling colonies (nestmates)')
2094
2041
  .action(wrapCommand(async () => {
2095
2042
  const repoPath = process.cwd();
2096
- const summary = getTelemetrySummary(repoPath);
2097
-
2098
- console.log(c.header('Model Performance Ranking\n'));
2043
+ const nestmates = findNestmates(repoPath);
2099
2044
 
2100
- if (summary.total_spawns === 0) {
2101
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
2045
+ if (nestmates.length === 0) {
2046
+ console.log('No nestmates found (sibling directories with .aether/).');
2102
2047
  return;
2103
2048
  }
2104
2049
 
2105
- // Sort models by success rate
2106
- const ranked = Object.entries(summary.models)
2107
- .map(([model, stats]) => ({ model, ...stats }))
2108
- .sort((a, b) => b.success_rate - a.success_rate);
2109
-
2110
- console.log(`${'Rank'.padEnd(6)} ${'Model'.padEnd(15)} ${'Spawns'.padStart(6)} ${'Success'.padStart(8)} ${'Rate'.padStart(6)}`);
2111
- console.log('─'.repeat(60));
2112
-
2113
- ranked.forEach((m, i) => {
2114
- const rank = `${i + 1}.`.padEnd(6);
2115
- const rate = (m.success_rate * 100).toFixed(1);
2116
- const rateColor = m.success_rate >= 0.9 ? c.success :
2117
- m.success_rate >= 0.7 ? c.warning : c.error;
2118
- console.log(`${rank} ${m.model.padEnd(15)} ${String(m.total_spawns).padStart(6)} ${String(m.successful_completions || 0).padStart(8)} ${rateColor(rate.padStart(5) + '%')}`);
2119
- });
2120
-
2121
- console.log('\n' + c.dim('Tip: Use "aether telemetry model <name>" for detailed stats'));
2050
+ console.log(`Found ${nestmates.length} nestmate(s):\n`);
2051
+ console.log(formatNestmates(nestmates));
2122
2052
  }));
2123
2053
 
2124
2054
  // Context command - Show auto-loaded context
@@ -2191,8 +2121,7 @@ program
2191
2121
  console.log('Next steps:');
2192
2122
  console.log(' 1. Define your colony goal in .aether/data/COLONY_STATE.json');
2193
2123
  console.log(' 2. Run: aether sync-state');
2194
- console.log(' 3. Run: aether verify-models');
2195
- console.log(' 4. Start building: /ant:init');
2124
+ console.log(' 3. Start building: /ant:init');
2196
2125
  }
2197
2126
  }));
2198
2127
 
@@ -2204,7 +2133,6 @@ program.on('--help', () => {
2204
2133
  console.log(' install Install slash-commands and set up distribution hub');
2205
2134
  console.log(' update Update current repo from hub');
2206
2135
  console.log(' sync-state Synchronize COLONY_STATE.json with .planning/STATE.md');
2207
- console.log(' verify-models Verify model routing configuration');
2208
2136
  console.log(' version Show installed version');
2209
2137
  console.log(' uninstall Remove slash-commands (preserves project state and hub)');
2210
2138
  console.log('');
@@ -2242,7 +2170,7 @@ module.exports = {
2242
2170
  saveCheckpointMetadata,
2243
2171
  isUserData,
2244
2172
  syncDirWithCleanup,
2245
- syncSystemFilesWithCleanup,
2173
+ syncSkillsToHub,
2246
2174
  listFilesRecursive,
2247
2175
  cleanEmptyDirs
2248
2176
  };