aether-colony 5.0.0 → 5.2.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 (317) hide show
  1. package/.aether/aether-utils.sh +3226 -3345
  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 +442 -0
  30. package/.aether/commands/continue.yaml +1484 -0
  31. package/.aether/commands/council.yaml +509 -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 +502 -0
  44. package/.aether/commands/insert-phase.yaml +102 -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 +1364 -0
  57. package/.aether/commands/preferences.yaml +63 -0
  58. package/.aether/commands/quick.yaml +104 -0
  59. package/.aether/commands/redirect.yaml +123 -0
  60. package/.aether/commands/resume-colony.yaml +375 -0
  61. package/.aether/commands/resume.yaml +407 -0
  62. package/.aether/commands/run.yaml +229 -0
  63. package/.aether/commands/seal.yaml +1214 -0
  64. package/.aether/commands/skill-create.yaml +337 -0
  65. package/.aether/commands/status.yaml +408 -0
  66. package/.aether/commands/swarm.yaml +352 -0
  67. package/.aether/commands/tunnels.yaml +814 -0
  68. package/.aether/commands/update.yaml +131 -0
  69. package/.aether/commands/verify-castes.yaml +159 -0
  70. package/.aether/commands/watch.yaml +454 -0
  71. package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
  72. package/.aether/docs/QUEEN-SYSTEM.md +11 -11
  73. package/.aether/docs/README.md +32 -2
  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 +1683 -0
  78. package/.aether/docs/command-playbooks/build-prep.md +284 -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 +1725 -0
  84. package/.aether/docs/command-playbooks/continue-gates.md +686 -0
  85. package/.aether/docs/command-playbooks/continue-verify.md +407 -0
  86. package/.aether/docs/context-continuity.md +84 -0
  87. package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
  88. package/.aether/docs/error-codes.md +1 -1
  89. package/.aether/docs/known-issues.md +34 -173
  90. package/.aether/docs/pheromones.md +86 -6
  91. package/.aether/docs/plans/pheromone-display-plan.md +257 -0
  92. package/.aether/docs/queen-commands.md +10 -9
  93. package/.aether/docs/source-of-truth-map.md +132 -0
  94. package/.aether/docs/xml-utilities.md +47 -0
  95. package/.aether/rules/aether-colony.md +23 -13
  96. package/.aether/scripts/incident-test-add.sh +47 -0
  97. package/.aether/scripts/weekly-audit.sh +79 -0
  98. package/.aether/skills/.index.json +649 -0
  99. package/.aether/skills/colony/.manifest.json +16 -0
  100. package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
  101. package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
  102. package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
  103. package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
  104. package/.aether/skills/colony/context-management/SKILL.md +80 -0
  105. package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
  106. package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
  107. package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
  108. package/.aether/skills/colony/state-safety/SKILL.md +84 -0
  109. package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
  110. package/.aether/skills/domain/.manifest.json +24 -0
  111. package/.aether/skills/domain/README.md +33 -0
  112. package/.aether/skills/domain/django/SKILL.md +49 -0
  113. package/.aether/skills/domain/docker/SKILL.md +52 -0
  114. package/.aether/skills/domain/golang/SKILL.md +52 -0
  115. package/.aether/skills/domain/graphql/SKILL.md +51 -0
  116. package/.aether/skills/domain/html-css/SKILL.md +48 -0
  117. package/.aether/skills/domain/nextjs/SKILL.md +45 -0
  118. package/.aether/skills/domain/nodejs/SKILL.md +53 -0
  119. package/.aether/skills/domain/postgresql/SKILL.md +53 -0
  120. package/.aether/skills/domain/prisma/SKILL.md +59 -0
  121. package/.aether/skills/domain/python/SKILL.md +50 -0
  122. package/.aether/skills/domain/rails/SKILL.md +52 -0
  123. package/.aether/skills/domain/react/SKILL.md +45 -0
  124. package/.aether/skills/domain/rest-api/SKILL.md +58 -0
  125. package/.aether/skills/domain/svelte/SKILL.md +47 -0
  126. package/.aether/skills/domain/tailwind/SKILL.md +45 -0
  127. package/.aether/skills/domain/testing/SKILL.md +53 -0
  128. package/.aether/skills/domain/typescript/SKILL.md +58 -0
  129. package/.aether/skills/domain/vue/SKILL.md +49 -0
  130. package/.aether/templates/QUEEN.md.template +23 -41
  131. package/.aether/templates/colony-state-reset.jq.template +1 -0
  132. package/.aether/templates/colony-state.template.json +4 -0
  133. package/.aether/templates/learning-observations.template.json +6 -0
  134. package/.aether/templates/midden.template.json +13 -0
  135. package/.aether/templates/pheromones.template.json +6 -0
  136. package/.aether/templates/session.template.json +9 -0
  137. package/.aether/utils/atomic-write.sh +63 -17
  138. package/.aether/utils/chamber-utils.sh +145 -2
  139. package/.aether/utils/council.sh +425 -0
  140. package/.aether/utils/emoji-audit.sh +166 -0
  141. package/.aether/utils/error-handler.sh +21 -7
  142. package/.aether/utils/file-lock.sh +182 -27
  143. package/.aether/utils/flag.sh +278 -0
  144. package/.aether/utils/hive.sh +572 -0
  145. package/.aether/utils/immune.sh +508 -0
  146. package/.aether/utils/learning.sh +1928 -0
  147. package/.aether/utils/midden.sh +520 -0
  148. package/.aether/utils/oracle/oracle.md +168 -0
  149. package/.aether/utils/oracle/oracle.sh +1023 -0
  150. package/.aether/utils/pheromone.sh +2029 -0
  151. package/.aether/utils/queen.sh +1710 -0
  152. package/.aether/utils/scan.sh +860 -0
  153. package/.aether/utils/semantic-cli.sh +10 -8
  154. package/.aether/utils/session.sh +816 -0
  155. package/.aether/utils/skills.sh +509 -0
  156. package/.aether/utils/spawn-tree.sh +103 -271
  157. package/.aether/utils/spawn.sh +260 -0
  158. package/.aether/utils/state-api.sh +389 -0
  159. package/.aether/utils/state-loader.sh +8 -6
  160. package/.aether/utils/suggest.sh +611 -0
  161. package/.aether/utils/swarm-display.sh +10 -1
  162. package/.aether/utils/swarm.sh +1004 -0
  163. package/.aether/utils/watch-spawn-tree.sh +11 -2
  164. package/.aether/utils/xml-compose.sh +2 -2
  165. package/.aether/utils/xml-convert.sh +9 -5
  166. package/.aether/utils/xml-core.sh +5 -9
  167. package/.aether/utils/xml-query.sh +4 -4
  168. package/.aether/workers.md +86 -67
  169. package/.claude/agents/ant/aether-ambassador.md +2 -1
  170. package/.claude/agents/ant/aether-archaeologist.md +6 -1
  171. package/.claude/agents/ant/aether-architect.md +236 -0
  172. package/.claude/agents/ant/aether-auditor.md +6 -1
  173. package/.claude/agents/ant/aether-builder.md +38 -1
  174. package/.claude/agents/ant/aether-chaos.md +2 -1
  175. package/.claude/agents/ant/aether-chronicler.md +1 -0
  176. package/.claude/agents/ant/aether-gatekeeper.md +6 -1
  177. package/.claude/agents/ant/aether-includer.md +1 -0
  178. package/.claude/agents/ant/aether-keeper.md +1 -0
  179. package/.claude/agents/ant/aether-measurer.md +6 -1
  180. package/.claude/agents/ant/aether-oracle.md +237 -0
  181. package/.claude/agents/ant/aether-probe.md +2 -1
  182. package/.claude/agents/ant/aether-queen.md +6 -1
  183. package/.claude/agents/ant/aether-route-setter.md +6 -1
  184. package/.claude/agents/ant/aether-sage.md +68 -3
  185. package/.claude/agents/ant/aether-scout.md +38 -1
  186. package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
  187. package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
  188. package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
  189. package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
  190. package/.claude/agents/ant/aether-tracker.md +6 -1
  191. package/.claude/agents/ant/aether-watcher.md +37 -1
  192. package/.claude/agents/ant/aether-weaver.md +2 -1
  193. package/.claude/commands/ant/archaeology.md +1 -8
  194. package/.claude/commands/ant/build.md +43 -1159
  195. package/.claude/commands/ant/chaos.md +1 -14
  196. package/.claude/commands/ant/colonize.md +3 -14
  197. package/.claude/commands/ant/continue.md +40 -1026
  198. package/.claude/commands/ant/council.md +213 -15
  199. package/.claude/commands/ant/data-clean.md +81 -0
  200. package/.claude/commands/ant/dream.md +12 -9
  201. package/.claude/commands/ant/entomb.md +62 -87
  202. package/.claude/commands/ant/export-signals.md +57 -0
  203. package/.claude/commands/ant/feedback.md +18 -0
  204. package/.claude/commands/ant/flag.md +12 -0
  205. package/.claude/commands/ant/flags.md +22 -8
  206. package/.claude/commands/ant/focus.md +18 -0
  207. package/.claude/commands/ant/help.md +40 -8
  208. package/.claude/commands/ant/history.md +3 -0
  209. package/.claude/commands/ant/import-signals.md +71 -0
  210. package/.claude/commands/ant/init.md +349 -191
  211. package/.claude/commands/ant/insert-phase.md +105 -0
  212. package/.claude/commands/ant/interpret.md +11 -0
  213. package/.claude/commands/ant/lay-eggs.md +167 -158
  214. package/.claude/commands/ant/maturity.md +22 -11
  215. package/.claude/commands/ant/memory-details.md +77 -0
  216. package/.claude/commands/ant/migrate-state.md +6 -0
  217. package/.claude/commands/ant/oracle.md +317 -62
  218. package/.claude/commands/ant/organize.md +10 -5
  219. package/.claude/commands/ant/patrol.md +620 -0
  220. package/.claude/commands/ant/pause-colony.md +8 -22
  221. package/.claude/commands/ant/phase.md +26 -37
  222. package/.claude/commands/ant/pheromones.md +156 -0
  223. package/.claude/commands/ant/plan.md +199 -50
  224. package/.claude/commands/ant/preferences.md +65 -0
  225. package/.claude/commands/ant/quick.md +100 -0
  226. package/.claude/commands/ant/redirect.md +18 -0
  227. package/.claude/commands/ant/resume-colony.md +37 -22
  228. package/.claude/commands/ant/resume.md +60 -7
  229. package/.claude/commands/ant/run.md +231 -0
  230. package/.claude/commands/ant/seal.md +506 -78
  231. package/.claude/commands/ant/skill-create.md +286 -0
  232. package/.claude/commands/ant/status.md +171 -1
  233. package/.claude/commands/ant/swarm.md +11 -23
  234. package/.claude/commands/ant/tunnels.md +1 -0
  235. package/.claude/commands/ant/update.md +58 -135
  236. package/.claude/commands/ant/verify-castes.md +90 -42
  237. package/.claude/commands/ant/watch.md +1 -0
  238. package/.opencode/agents/aether-ambassador.md +1 -1
  239. package/.opencode/agents/aether-architect.md +133 -0
  240. package/.opencode/agents/aether-builder.md +3 -3
  241. package/.opencode/agents/aether-oracle.md +137 -0
  242. package/.opencode/agents/aether-queen.md +1 -1
  243. package/.opencode/agents/aether-route-setter.md +1 -1
  244. package/.opencode/agents/aether-scout.md +1 -1
  245. package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
  246. package/.opencode/agents/aether-surveyor-nest.md +6 -1
  247. package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
  248. package/.opencode/agents/aether-surveyor-provisions.md +6 -1
  249. package/.opencode/agents/aether-tracker.md +1 -1
  250. package/.opencode/agents/aether-watcher.md +1 -1
  251. package/.opencode/agents/aether-weaver.md +1 -1
  252. package/.opencode/commands/ant/archaeology.md +7 -14
  253. package/.opencode/commands/ant/build.md +54 -88
  254. package/.opencode/commands/ant/chaos.md +7 -24
  255. package/.opencode/commands/ant/colonize.md +10 -17
  256. package/.opencode/commands/ant/continue.md +595 -66
  257. package/.opencode/commands/ant/council.md +150 -18
  258. package/.opencode/commands/ant/data-clean.md +77 -0
  259. package/.opencode/commands/ant/dream.md +15 -17
  260. package/.opencode/commands/ant/entomb.md +28 -18
  261. package/.opencode/commands/ant/export-signals.md +54 -0
  262. package/.opencode/commands/ant/feedback.md +24 -5
  263. package/.opencode/commands/ant/flag.md +16 -4
  264. package/.opencode/commands/ant/flags.md +24 -10
  265. package/.opencode/commands/ant/focus.md +22 -5
  266. package/.opencode/commands/ant/help.md +41 -8
  267. package/.opencode/commands/ant/history.md +9 -0
  268. package/.opencode/commands/ant/import-signals.md +68 -0
  269. package/.opencode/commands/ant/init.md +396 -154
  270. package/.opencode/commands/ant/insert-phase.md +111 -0
  271. package/.opencode/commands/ant/interpret.md +16 -0
  272. package/.opencode/commands/ant/lay-eggs.md +184 -112
  273. package/.opencode/commands/ant/maturity.md +18 -2
  274. package/.opencode/commands/ant/memory-details.md +83 -0
  275. package/.opencode/commands/ant/migrate-state.md +12 -0
  276. package/.opencode/commands/ant/oracle.md +322 -67
  277. package/.opencode/commands/ant/organize.md +14 -12
  278. package/.opencode/commands/ant/patrol.md +626 -0
  279. package/.opencode/commands/ant/pause-colony.md +12 -29
  280. package/.opencode/commands/ant/phase.md +30 -40
  281. package/.opencode/commands/ant/pheromones.md +162 -0
  282. package/.opencode/commands/ant/plan.md +210 -57
  283. package/.opencode/commands/ant/preferences.md +71 -0
  284. package/.opencode/commands/ant/quick.md +91 -0
  285. package/.opencode/commands/ant/redirect.md +22 -5
  286. package/.opencode/commands/ant/resume-colony.md +41 -29
  287. package/.opencode/commands/ant/resume.md +80 -20
  288. package/.opencode/commands/ant/run.md +237 -0
  289. package/.opencode/commands/ant/seal.md +230 -25
  290. package/.opencode/commands/ant/skill-create.md +63 -0
  291. package/.opencode/commands/ant/status.md +125 -30
  292. package/.opencode/commands/ant/swarm.md +3 -345
  293. package/.opencode/commands/ant/tunnels.md +3 -9
  294. package/.opencode/commands/ant/update.md +63 -127
  295. package/.opencode/commands/ant/verify-castes.md +96 -42
  296. package/.opencode/commands/ant/watch.md +7 -0
  297. package/CHANGELOG.md +368 -1
  298. package/README.md +195 -324
  299. package/bin/cli.js +236 -429
  300. package/bin/generate-commands.js +186 -0
  301. package/bin/generate-commands.sh +128 -89
  302. package/bin/lib/spawn-logger.js +0 -15
  303. package/bin/lib/update-transaction.js +285 -35
  304. package/bin/npx-install.js +178 -0
  305. package/bin/validate-package.sh +85 -3
  306. package/package.json +16 -4
  307. package/.aether/CONTEXT.md +0 -160
  308. package/.aether/docs/QUEEN.md +0 -84
  309. package/.aether/exchange/colony-registry.xml +0 -11
  310. package/.aether/exchange/pheromones.xml +0 -87
  311. package/.aether/exchange/queen-wisdom.xml +0 -14
  312. package/.aether/model-profiles.yaml +0 -100
  313. package/.aether/utils/spawn-with-model.sh +0 -56
  314. package/bin/lib/model-profiles.js +0 -445
  315. package/bin/lib/model-verify.js +0 -288
  316. package/bin/lib/proxy-health.js +0 -253
  317. package/bin/lib/telemetry.js +0 -441
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,10 +42,14 @@ 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');
70
47
  const AGENTS_DEST = path.join(HOME, '.claude', 'agents', 'ant');
71
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');
52
+
72
53
  // Hub paths
73
54
  const HUB_DIR = path.join(HOME, '.aether');
74
55
  const HUB_SYSTEM_DIR = path.join(HUB_DIR, 'system');
@@ -378,6 +359,21 @@ function removeDirSync(dir) {
378
359
  return count;
379
360
  }
380
361
 
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;
365
+ let count = 0;
366
+ const sourceFiles = fs.readdirSync(sourceDir).filter(f => f.endsWith('.md'));
367
+ for (const file of sourceFiles) {
368
+ const destPath = path.join(destDir, file);
369
+ if (fs.existsSync(destPath)) {
370
+ fs.unlinkSync(destPath);
371
+ count++;
372
+ }
373
+ }
374
+ return count;
375
+ }
376
+
381
377
  function readJsonSafe(filePath) {
382
378
  try {
383
379
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
@@ -718,6 +714,64 @@ function isGitRepo(repoPath) {
718
714
  }
719
715
  }
720
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
+
721
775
  function getGitDirtyFiles(repoPath, targetDirs) {
722
776
  try {
723
777
  const args = targetDirs.filter(d => fs.existsSync(path.join(repoPath, d)));
@@ -727,7 +781,14 @@ function getGitDirtyFiles(repoPath, targetDirs) {
727
781
  stdio: 'pipe',
728
782
  encoding: 'utf8',
729
783
  });
730
- 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)];
731
792
  } catch {
732
793
  return [];
733
794
  }
@@ -750,6 +811,11 @@ function gitStashFiles(repoPath, files) {
750
811
  // Directories to exclude from hub sync (user data, local state)
751
812
  // 'rules' is excluded here because it is synced via a dedicated step (rulesSrc below)
752
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'];
753
819
 
754
820
  /**
755
821
  * Check if a path should be excluded from hub sync
@@ -758,8 +824,13 @@ const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp', 'rul
758
824
  */
759
825
  function shouldExcludeFromHub(relPath) {
760
826
  const parts = relPath.split(path.sep);
827
+ const basename = path.basename(relPath);
761
828
  // Exclude if any part of the path is in the exclude list
762
- 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;
763
834
  }
764
835
 
765
836
  /**
@@ -784,6 +855,7 @@ function syncAetherToHub(srcDir, destDir) {
784
855
  const fullPath = path.join(dir, entry.name);
785
856
  const relPath = path.relative(base, fullPath);
786
857
 
858
+ // Exclude non-distributable paths from source collection.
787
859
  if (shouldExcludeFromHub(relPath)) continue;
788
860
 
789
861
  if (entry.isDirectory()) {
@@ -834,7 +906,10 @@ function syncAetherToHub(srcDir, destDir) {
834
906
  const fullPath = path.join(dir, entry.name);
835
907
  const relPath = path.relative(base, fullPath);
836
908
 
837
- 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;
838
913
 
839
914
  if (entry.isDirectory()) {
840
915
  collectDestFiles(fullPath, base);
@@ -864,6 +939,70 @@ function syncAetherToHub(srcDir, destDir) {
864
939
  return { copied, removed, skipped };
865
940
  }
866
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
+
867
1006
  function setupHub() {
868
1007
  // Create ~/.aether/ directory structure and populate from package
869
1008
  try {
@@ -882,7 +1021,7 @@ function setupHub() {
882
1021
  fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
883
1022
 
884
1023
  // Move system files to system/
885
- const systemFiles = ['aether-utils.sh', 'workers.md', 'CONTEXT.md', 'model-profiles.yaml'];
1024
+ const systemFiles = ['aether-utils.sh', 'workers.md'];
886
1025
  const systemDirs = ['docs', 'utils', 'commands', 'agents', 'schemas', 'exchange', 'templates', 'lib'];
887
1026
 
888
1027
  for (const file of systemFiles) {
@@ -1015,6 +1154,20 @@ function setupHub() {
1015
1154
  }
1016
1155
  }
1017
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
+
1018
1171
  // Create/preserve registry.json (at root, not in system/)
1019
1172
  if (!fs.existsSync(HUB_REGISTRY)) {
1020
1173
  writeJsonSync(HUB_REGISTRY, { schema_version: 1, repos: [] });
@@ -1070,19 +1223,21 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1070
1223
  // Target directories for git safety checks
1071
1224
  const targetDirs = ['.aether', '.claude/commands/ant', '.claude/agents/ant', '.claude/rules', '.opencode/commands/ant', '.opencode/agents'];
1072
1225
 
1073
- // 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
1074
1230
  let dirtyFiles = [];
1075
- if (isGitRepo(repoPath)) {
1076
- dirtyFiles = getGitDirtyFiles(repoPath, targetDirs);
1077
- 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) {
1078
1236
  return { status: 'dirty', files: dirtyFiles };
1079
1237
  }
1080
1238
  // Note: --force handling is now done via checkpoint stash in UpdateTransaction
1081
1239
  }
1082
1240
 
1083
- // Use UpdateTransaction for two-phase commit with automatic rollback
1084
- const transaction = new UpdateTransaction(repoPath, { sourceVersion, quiet, force });
1085
-
1086
1241
  try {
1087
1242
  const result = await transaction.execute(sourceVersion, { dryRun });
1088
1243
 
@@ -1199,6 +1354,28 @@ program
1199
1354
  }
1200
1355
  }
1201
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
+
1202
1379
  // Set up distribution hub at ~/.aether/
1203
1380
  log('');
1204
1381
  log(c.colony('Setting up distribution hub...'));
@@ -1207,6 +1384,7 @@ program
1207
1384
  log('');
1208
1385
  log(c.success('Install complete.'));
1209
1386
  log(` ${c.queen('Claude Code:')} run /ant to get started`);
1387
+ log(` ${c.colony('OpenCode:')} run /ant to get started`);
1210
1388
  log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} (for coordinated updates across repos)`);
1211
1389
  }));
1212
1390
 
@@ -1475,7 +1653,7 @@ program
1475
1653
  // Uninstall command
1476
1654
  program
1477
1655
  .command('uninstall')
1478
- .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)')
1479
1657
  .action(wrapCommand(async () => {
1480
1658
  log(c.header(`aether-colony v${VERSION} — uninstalling...`));
1481
1659
 
@@ -1487,6 +1665,26 @@ program
1487
1665
  log(' Claude Code commands already removed.');
1488
1666
  }
1489
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
+
1490
1688
  log('');
1491
1689
  log(c.success('Uninstall complete. Per-project .aether/data/ directories are untouched.'));
1492
1690
  log(` ${c.colony('Hub:')} ${c.dim('~/.aether/')} preserved (remove manually if desired).`);
@@ -1724,261 +1922,6 @@ program
1724
1922
  }
1725
1923
  }));
1726
1924
 
1727
- // Caste emoji mapping for display
1728
- const CASTE_EMOJIS = {
1729
- builder: '🔨',
1730
- watcher: '👁️',
1731
- scout: '🔍',
1732
- chaos: '🎲',
1733
- oracle: '🔮',
1734
- architect: '🏗️',
1735
- prime: '🏛️',
1736
- colonizer: '🌱',
1737
- route_setter: '🧭',
1738
- archaeologist: '📜',
1739
- };
1740
-
1741
- /**
1742
- * Format context window for display
1743
- * @param {number} contextWindow - Context window size
1744
- * @returns {string} Formatted string (e.g., "256K")
1745
- */
1746
- function formatContextWindow(contextWindow) {
1747
- if (!contextWindow) return '-';
1748
- if (contextWindow >= 1000) {
1749
- return `${Math.round(contextWindow / 1000)}K`;
1750
- }
1751
- return String(contextWindow);
1752
- }
1753
-
1754
- // Caste-models command - Manage caste-to-model assignments
1755
- const casteModelsCmd = program
1756
- .command('caste-models')
1757
- .description('Manage caste-to-model assignments');
1758
-
1759
- // list subcommand
1760
- casteModelsCmd
1761
- .command('list')
1762
- .description('List current model assignments per caste')
1763
- .option('--verify', 'Verify model availability on proxy')
1764
- .action(wrapCommand(async (options) => {
1765
- const repoPath = process.cwd();
1766
- const profiles = loadModelProfiles(repoPath);
1767
- const overrides = getUserOverrides(profiles);
1768
- const proxyConfig = getProxyConfig(profiles);
1769
-
1770
- // Check proxy health
1771
- let proxyHealth = null;
1772
- let proxyModels = null;
1773
- if (proxyConfig?.endpoint) {
1774
- proxyHealth = await checkProxyHealth(proxyConfig.endpoint);
1775
- if (proxyHealth.healthy && proxyHealth.models) {
1776
- proxyModels = proxyHealth.models;
1777
- }
1778
- }
1779
-
1780
- console.log(c.header('Caste Model Assignments\n'));
1781
-
1782
- // Display proxy status
1783
- if (proxyConfig?.endpoint) {
1784
- const proxyStatus = formatProxyStatusColored(proxyHealth, c) + c.dim(` @ ${proxyConfig.endpoint}`);
1785
- console.log(`Proxy: ${proxyStatus}`);
1786
- if (!proxyHealth?.healthy) {
1787
- console.log(c.warning('Warning: Using default model (kimi-k2.5) for all castes'));
1788
- }
1789
- console.log('');
1790
- }
1791
-
1792
- // Table header - add Verify column if --verify flag
1793
- const verifyFlag = options.verify;
1794
- const header = verifyFlag
1795
- ? `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Verify Status`
1796
- : `${'Caste'.padEnd(14)} ${'Model'.padEnd(14)} ${'Provider'.padEnd(10)} ${'Context'.padEnd(8)} Status`;
1797
- console.log(header);
1798
- console.log(verifyFlag ? '─'.repeat(70) : '─'.repeat(60));
1799
-
1800
- // Get all assignments
1801
- const assignments = getAllAssignments(profiles);
1802
-
1803
- for (const assignment of assignments) {
1804
- const emoji = CASTE_EMOJIS[assignment.caste] || '•';
1805
- const casteName = assignment.caste.charAt(0).toUpperCase() + assignment.caste.slice(1);
1806
- const casteDisplay = `${emoji} ${casteName}`;
1807
-
1808
- // Check for override
1809
- const hasOverride = overrides[assignment.caste] !== undefined;
1810
- const effectiveModel = getEffectiveModel(profiles, assignment.caste);
1811
- const modelDisplay = effectiveModel.model + (hasOverride ? ' (override)' : '');
1812
-
1813
- // Get model metadata
1814
- const metadata = getModelMetadata(profiles, effectiveModel.model);
1815
- const provider = metadata?.provider || assignment.provider || '-';
1816
- const contextWindow = formatContextWindow(metadata?.context_window);
1817
-
1818
- // Status indicator based on proxy health
1819
- const status = proxyHealth?.healthy ? '✓' : '⚠';
1820
-
1821
- // Verify flag - check if model is available on proxy
1822
- let verifyStatus = '';
1823
- if (verifyFlag) {
1824
- if (proxyModels) {
1825
- const isAvailable = proxyModels.includes(effectiveModel.model);
1826
- verifyStatus = isAvailable ? '✓' : '✗';
1827
- } else {
1828
- verifyStatus = '?';
1829
- }
1830
- console.log(
1831
- `${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${verifyStatus.padEnd(7)} ${status}`
1832
- );
1833
- } else {
1834
- console.log(
1835
- `${casteDisplay.padEnd(14)} ${modelDisplay.padEnd(14)} ${provider.padEnd(10)} ${contextWindow.padEnd(8)} ${status}`
1836
- );
1837
- }
1838
- }
1839
-
1840
- // Show overrides summary if any exist
1841
- const overrideCount = Object.keys(overrides).length;
1842
- if (overrideCount > 0) {
1843
- console.log('');
1844
- console.log(c.info(`Active overrides: ${overrideCount}`));
1845
- for (const [caste, model] of Object.entries(overrides)) {
1846
- console.log(` ${caste}: ${model}`);
1847
- }
1848
- }
1849
- }));
1850
-
1851
- // set subcommand
1852
- casteModelsCmd
1853
- .command('set')
1854
- .description('Set model override for a caste')
1855
- .argument('<assignment>', 'caste=model (e.g., builder=glm-5)')
1856
- .action(wrapCommand(async (assignment) => {
1857
- // Parse caste=model format
1858
- const match = assignment.match(/^([^=]+)=(.+)$/);
1859
- if (!match) {
1860
- const error = new ValidationError(
1861
- `Invalid assignment format: '${assignment}'`,
1862
- { received: assignment },
1863
- 'Use format: caste=model (e.g., builder=glm-5)'
1864
- );
1865
- throw error;
1866
- }
1867
-
1868
- const [, caste, model] = match;
1869
-
1870
- const repoPath = process.cwd();
1871
-
1872
- // Validate and set
1873
- try {
1874
- const result = setModelOverride(repoPath, caste, model);
1875
-
1876
- if (result.previous) {
1877
- console.log(c.success(`Updated ${caste}: ${result.previous} → ${model}`));
1878
- } else {
1879
- console.log(c.success(`Set ${caste} to ${model}`));
1880
- }
1881
- } catch (error) {
1882
- if (error.name === 'ValidationError') {
1883
- // Add helpful suggestions
1884
- if (error.details?.validCastes) {
1885
- console.error(c.error(`Error: ${error.message}`));
1886
- console.error('\nValid castes:');
1887
- for (const casteName of error.details.validCastes) {
1888
- const emoji = CASTE_EMOJIS[casteName] || '•';
1889
- console.error(` ${emoji} ${casteName}`);
1890
- }
1891
- } else if (error.details?.validModels) {
1892
- console.error(c.error(`Error: ${error.message}`));
1893
- console.error('\nValid models:');
1894
- for (const modelName of error.details.validModels) {
1895
- console.error(` • ${modelName}`);
1896
- }
1897
- }
1898
- process.exit(1);
1899
- }
1900
- throw error;
1901
- }
1902
- }));
1903
-
1904
- // reset subcommand
1905
- casteModelsCmd
1906
- .command('reset')
1907
- .description('Reset caste to default model (remove override)')
1908
- .argument('<caste>', 'caste name (e.g., builder)')
1909
- .action(wrapCommand(async (caste) => {
1910
- const repoPath = process.cwd();
1911
-
1912
- try {
1913
- const result = resetModelOverride(repoPath, caste);
1914
-
1915
- if (result.hadOverride) {
1916
- console.log(c.success(`Reset ${caste} to default model`));
1917
- } else {
1918
- console.log(c.info(`${caste} was already using default model`));
1919
- }
1920
- } catch (error) {
1921
- if (error.name === 'ValidationError' && error.details?.validCastes) {
1922
- console.error(c.error(`Error: ${error.message}`));
1923
- console.error('\nValid castes:');
1924
- for (const casteName of error.details.validCastes) {
1925
- const emoji = CASTE_EMOJIS[casteName] || '•';
1926
- console.error(` ${emoji} ${casteName}`);
1927
- }
1928
- process.exit(1);
1929
- }
1930
- throw error;
1931
- }
1932
- }));
1933
-
1934
- // Verify-models command - Verify model routing configuration
1935
- program
1936
- .command('verify-models')
1937
- .description('Verify model routing configuration is active')
1938
- .action(wrapCommand(async () => {
1939
- const repoPath = process.cwd();
1940
- const report = await createVerificationReport(repoPath);
1941
-
1942
- console.log('=== Model Routing Verification ===\n');
1943
-
1944
- // Proxy status
1945
- console.log(`LiteLLM Proxy: ${report.proxy.running ? '✓ Running' : '✗ Not running'}`);
1946
- if (report.proxy.running) {
1947
- console.log(` Latency: ${report.proxy.latency}ms`);
1948
- }
1949
-
1950
- // Environment
1951
- console.log(`\nEnvironment:`);
1952
- console.log(` ANTHROPIC_MODEL: ${report.env.model || '(not set)'}`);
1953
- console.log(` ANTHROPIC_BASE_URL: ${report.env.baseUrl || '(not set)'}`);
1954
-
1955
- // Caste assignments
1956
- console.log(`\nCaste Model Assignments:`);
1957
- for (const [caste, info] of Object.entries(report.castes)) {
1958
- const status = info.assigned ? '✓' : '✗';
1959
- console.log(` ${status} ${caste}: ${info.model || 'default'}`);
1960
- }
1961
-
1962
- // Model profiles file
1963
- console.log(`\nModel Profiles File:`);
1964
- if (report.profilesFile.exists) {
1965
- console.log(` ✓ Found: ${report.profilesFile.path}`);
1966
- const profileCount = Object.keys(report.profilesFile.profiles).length;
1967
- console.log(` Profiles: ${profileCount} castes configured`);
1968
- } else {
1969
- console.log(` ✗ Not found: ${report.profilesFile.path}`);
1970
- }
1971
-
1972
- // Issues
1973
- if (report.issues.length > 0) {
1974
- console.log(`\nIssues Found:`);
1975
- report.issues.forEach(issue => console.log(` ⚠ ${issue}`));
1976
- }
1977
-
1978
- // Recommendation
1979
- console.log(`\n${report.recommendation}`);
1980
- }));
1981
-
1982
1925
  // Spawn-log command - Log a worker spawn event
1983
1926
  program
1984
1927
  .command('spawn-log')
@@ -2108,141 +2051,6 @@ program
2108
2051
  console.log(formatNestmates(nestmates));
2109
2052
  }));
2110
2053
 
2111
- // Telemetry command - View model performance telemetry
2112
- const telemetryCmd = program
2113
- .command('telemetry')
2114
- .description('View model performance telemetry')
2115
- .action(wrapCommand(async () => {
2116
- // Default action: show summary
2117
- const repoPath = process.cwd();
2118
- const summary = getTelemetrySummary(repoPath);
2119
-
2120
- console.log(c.header('Model Performance Telemetry\n'));
2121
- console.log(`Total Spawns: ${summary.total_spawns}`);
2122
- console.log(`Models Used: ${summary.total_models}\n`);
2123
-
2124
- if (summary.total_spawns === 0) {
2125
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
2126
- return;
2127
- }
2128
-
2129
- console.log('Model Performance:');
2130
- console.log('─'.repeat(60));
2131
- for (const [model, stats] of Object.entries(summary.models)) {
2132
- const rate = (stats.success_rate * 100).toFixed(1);
2133
- const rateColor = stats.success_rate >= 0.9 ? c.success :
2134
- stats.success_rate >= 0.7 ? c.warning : c.error;
2135
- console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
2136
- }
2137
-
2138
- if (summary.recent_decisions.length > 0) {
2139
- console.log('\nRecent Routing Decisions:');
2140
- console.log('─'.repeat(60));
2141
- for (const decision of summary.recent_decisions.slice(-5)) {
2142
- console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
2143
- }
2144
- }
2145
- }));
2146
-
2147
- // summary subcommand (explicit)
2148
- telemetryCmd
2149
- .command('summary')
2150
- .description('Show overall telemetry summary')
2151
- .action(wrapCommand(async () => {
2152
- const repoPath = process.cwd();
2153
- const summary = getTelemetrySummary(repoPath);
2154
-
2155
- console.log(c.header('Model Performance Telemetry\n'));
2156
- console.log(`Total Spawns: ${summary.total_spawns}`);
2157
- console.log(`Models Used: ${summary.total_models}\n`);
2158
-
2159
- if (summary.total_spawns === 0) {
2160
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
2161
- return;
2162
- }
2163
-
2164
- console.log('Model Performance:');
2165
- console.log('─'.repeat(60));
2166
- for (const [model, stats] of Object.entries(summary.models)) {
2167
- const rate = (stats.success_rate * 100).toFixed(1);
2168
- const rateColor = stats.success_rate >= 0.9 ? c.success :
2169
- stats.success_rate >= 0.7 ? c.warning : c.error;
2170
- console.log(` ${model.padEnd(15)} ${String(stats.total_spawns).padStart(4)} spawns ${rateColor(rate + '%')} success`);
2171
- }
2172
-
2173
- if (summary.recent_decisions.length > 0) {
2174
- console.log('\nRecent Routing Decisions:');
2175
- console.log('─'.repeat(60));
2176
- for (const decision of summary.recent_decisions.slice(-5)) {
2177
- console.log(` ${decision.caste.padEnd(10)} → ${decision.selected_model.padEnd(12)} (${decision.source})`);
2178
- }
2179
- }
2180
- }));
2181
-
2182
- // model subcommand
2183
- telemetryCmd
2184
- .command('model <model-name>')
2185
- .description('Show detailed performance for a specific model')
2186
- .action(wrapCommand(async (modelName) => {
2187
- const repoPath = process.cwd();
2188
- const performance = getModelPerformance(repoPath, modelName);
2189
-
2190
- if (!performance) {
2191
- console.log(c.warning(`No data for model: ${modelName}`));
2192
- return;
2193
- }
2194
-
2195
- console.log(c.header(`Model Performance: ${modelName}\n`));
2196
- console.log(`Total Spawns: ${performance.total_spawns}`);
2197
- console.log(`Success Rate: ${(performance.success_rate * 100).toFixed(1)}%`);
2198
- console.log(` ✓ Completed: ${performance.successful_completions}`);
2199
- console.log(` ✗ Failed: ${performance.failed_completions}`);
2200
- console.log(` 🚫 Blocked: ${performance.blocked}`);
2201
-
2202
- if (Object.keys(performance.by_caste).length > 0) {
2203
- console.log('\nPerformance by Caste:');
2204
- console.log('─'.repeat(50));
2205
- for (const [caste, stats] of Object.entries(performance.by_caste)) {
2206
- const casteRate = stats.spawns > 0 ? (stats.success / stats.spawns * 100).toFixed(1) : '0.0';
2207
- console.log(` ${caste.padEnd(12)} ${String(stats.spawns).padStart(4)} spawns ${casteRate}% success`);
2208
- }
2209
- }
2210
- }));
2211
-
2212
- // performance subcommand
2213
- telemetryCmd
2214
- .command('performance')
2215
- .description('Show models ranked by performance')
2216
- .action(wrapCommand(async () => {
2217
- const repoPath = process.cwd();
2218
- const summary = getTelemetrySummary(repoPath);
2219
-
2220
- console.log(c.header('Model Performance Ranking\n'));
2221
-
2222
- if (summary.total_spawns === 0) {
2223
- console.log(c.info('No telemetry data yet. Run some builds to collect data.'));
2224
- return;
2225
- }
2226
-
2227
- // Sort models by success rate
2228
- const ranked = Object.entries(summary.models)
2229
- .map(([model, stats]) => ({ model, ...stats }))
2230
- .sort((a, b) => b.success_rate - a.success_rate);
2231
-
2232
- console.log(`${'Rank'.padEnd(6)} ${'Model'.padEnd(15)} ${'Spawns'.padStart(6)} ${'Success'.padStart(8)} ${'Rate'.padStart(6)}`);
2233
- console.log('─'.repeat(60));
2234
-
2235
- ranked.forEach((m, i) => {
2236
- const rank = `${i + 1}.`.padEnd(6);
2237
- const rate = (m.success_rate * 100).toFixed(1);
2238
- const rateColor = m.success_rate >= 0.9 ? c.success :
2239
- m.success_rate >= 0.7 ? c.warning : c.error;
2240
- console.log(`${rank} ${m.model.padEnd(15)} ${String(m.total_spawns).padStart(6)} ${String(m.successful_completions || 0).padStart(8)} ${rateColor(rate.padStart(5) + '%')}`);
2241
- });
2242
-
2243
- console.log('\n' + c.dim('Tip: Use "aether telemetry model <name>" for detailed stats'));
2244
- }));
2245
-
2246
2054
  // Context command - Show auto-loaded context
2247
2055
  program
2248
2056
  .command('context')
@@ -2313,8 +2121,7 @@ program
2313
2121
  console.log('Next steps:');
2314
2122
  console.log(' 1. Define your colony goal in .aether/data/COLONY_STATE.json');
2315
2123
  console.log(' 2. Run: aether sync-state');
2316
- console.log(' 3. Run: aether verify-models');
2317
- console.log(' 4. Start building: /ant:init');
2124
+ console.log(' 3. Start building: /ant:init');
2318
2125
  }
2319
2126
  }));
2320
2127
 
@@ -2326,7 +2133,6 @@ program.on('--help', () => {
2326
2133
  console.log(' install Install slash-commands and set up distribution hub');
2327
2134
  console.log(' update Update current repo from hub');
2328
2135
  console.log(' sync-state Synchronize COLONY_STATE.json with .planning/STATE.md');
2329
- console.log(' verify-models Verify model routing configuration');
2330
2136
  console.log(' version Show installed version');
2331
2137
  console.log(' uninstall Remove slash-commands (preserves project state and hub)');
2332
2138
  console.log('');
@@ -2364,6 +2170,7 @@ module.exports = {
2364
2170
  saveCheckpointMetadata,
2365
2171
  isUserData,
2366
2172
  syncDirWithCleanup,
2173
+ syncSkillsToHub,
2367
2174
  listFilesRecursive,
2368
2175
  cleanEmptyDirs
2369
2176
  };