aether-colony 3.1.17 → 5.0.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 (183) hide show
  1. package/{runtime → .aether}/CONTEXT.md +1 -1
  2. package/{runtime → .aether}/aether-utils.sh +1772 -98
  3. package/.aether/docs/QUEEN-SYSTEM.md +211 -0
  4. package/.aether/docs/QUEEN.md +84 -0
  5. package/.aether/docs/README.md +68 -0
  6. package/.aether/docs/caste-system.md +48 -0
  7. package/{runtime → .aether/docs/disciplines}/DISCIPLINES.md +8 -8
  8. package/.aether/docs/error-codes.md +268 -0
  9. package/{runtime → .aether}/docs/known-issues.md +42 -26
  10. package/.aether/docs/queen-commands.md +97 -0
  11. package/.aether/exchange/colony-registry.xml +11 -0
  12. package/{runtime → .aether}/exchange/pheromone-xml.sh +2 -1
  13. package/.aether/exchange/pheromones.xml +87 -0
  14. package/.aether/exchange/queen-wisdom.xml +14 -0
  15. package/{runtime → .aether}/exchange/registry-xml.sh +7 -3
  16. package/{runtime → .aether}/exchange/wisdom-xml.sh +11 -4
  17. package/.aether/rules/aether-colony.md +134 -0
  18. package/.aether/schemas/example-prompt-builder.xml +234 -0
  19. package/.aether/templates/colony-state-reset.jq.template +22 -0
  20. package/.aether/templates/colony-state.template.json +35 -0
  21. package/.aether/templates/constraints.template.json +9 -0
  22. package/.aether/templates/crowned-anthill.template.md +36 -0
  23. package/.aether/templates/handoff-build-error.template.md +30 -0
  24. package/.aether/templates/handoff-build-success.template.md +39 -0
  25. package/.aether/templates/handoff.template.md +40 -0
  26. package/{runtime → .aether}/utils/atomic-write.sh +5 -5
  27. package/{runtime → .aether}/utils/chamber-compare.sh +23 -10
  28. package/{runtime → .aether}/utils/chamber-utils.sh +32 -20
  29. package/{runtime → .aether}/utils/error-handler.sh +13 -1
  30. package/{runtime → .aether}/utils/file-lock.sh +49 -13
  31. package/.aether/utils/semantic-cli.sh +413 -0
  32. package/{runtime → .aether}/utils/xml-compose.sh +7 -1
  33. package/.aether/utils/xml-convert.sh +273 -0
  34. package/.aether/utils/xml-query.sh +201 -0
  35. package/.aether/utils/xml-utils.sh +110 -0
  36. package/{runtime → .aether}/workers.md +14 -17
  37. package/.claude/agents/ant/aether-ambassador.md +264 -0
  38. package/.claude/agents/ant/aether-archaeologist.md +322 -0
  39. package/.claude/agents/ant/aether-auditor.md +266 -0
  40. package/.claude/agents/ant/aether-builder.md +187 -0
  41. package/.claude/agents/ant/aether-chaos.md +268 -0
  42. package/.claude/agents/ant/aether-chronicler.md +304 -0
  43. package/.claude/agents/ant/aether-gatekeeper.md +325 -0
  44. package/.claude/agents/ant/aether-includer.md +373 -0
  45. package/.claude/agents/ant/aether-keeper.md +271 -0
  46. package/.claude/agents/ant/aether-measurer.md +317 -0
  47. package/.claude/agents/ant/aether-probe.md +210 -0
  48. package/.claude/agents/ant/aether-queen.md +325 -0
  49. package/.claude/agents/ant/aether-route-setter.md +173 -0
  50. package/.claude/agents/ant/aether-sage.md +353 -0
  51. package/.claude/agents/ant/aether-scout.md +142 -0
  52. package/.claude/agents/ant/aether-surveyor-disciplines.md +416 -0
  53. package/.claude/agents/ant/aether-surveyor-nest.md +354 -0
  54. package/.claude/agents/ant/aether-surveyor-pathogens.md +288 -0
  55. package/.claude/agents/ant/aether-surveyor-provisions.md +359 -0
  56. package/.claude/agents/ant/aether-tracker.md +265 -0
  57. package/.claude/agents/ant/aether-watcher.md +244 -0
  58. package/.claude/agents/ant/aether-weaver.md +247 -0
  59. package/.claude/commands/ant/archaeology.md +16 -7
  60. package/.claude/commands/ant/build.md +415 -284
  61. package/.claude/commands/ant/chaos.md +19 -10
  62. package/.claude/commands/ant/colonize.md +58 -24
  63. package/.claude/commands/ant/continue.md +155 -145
  64. package/.claude/commands/ant/council.md +15 -5
  65. package/.claude/commands/ant/dream.md +16 -7
  66. package/.claude/commands/ant/entomb.md +274 -157
  67. package/.claude/commands/ant/feedback.md +33 -29
  68. package/.claude/commands/ant/flag.md +18 -10
  69. package/.claude/commands/ant/flags.md +14 -6
  70. package/.claude/commands/ant/focus.md +29 -21
  71. package/.claude/commands/ant/help.md +11 -1
  72. package/.claude/commands/ant/history.md +10 -0
  73. package/.claude/commands/ant/init.md +91 -65
  74. package/.claude/commands/ant/interpret.md +15 -4
  75. package/.claude/commands/ant/lay-eggs.md +55 -7
  76. package/.claude/commands/ant/maturity.md +11 -1
  77. package/.claude/commands/ant/migrate-state.md +14 -2
  78. package/.claude/commands/ant/oracle.md +23 -15
  79. package/.claude/commands/ant/organize.md +29 -20
  80. package/.claude/commands/ant/pause-colony.md +17 -7
  81. package/.claude/commands/ant/phase.md +17 -8
  82. package/.claude/commands/ant/plan.md +20 -9
  83. package/.claude/commands/ant/redirect.md +29 -32
  84. package/.claude/commands/ant/resume-colony.md +19 -9
  85. package/.claude/commands/ant/resume.md +272 -96
  86. package/.claude/commands/ant/seal.md +201 -191
  87. package/.claude/commands/ant/status.md +71 -32
  88. package/.claude/commands/ant/swarm.md +26 -44
  89. package/.claude/commands/ant/tunnels.md +279 -105
  90. package/.claude/commands/ant/update.md +81 -20
  91. package/.claude/commands/ant/verify-castes.md +14 -4
  92. package/.claude/commands/ant/watch.md +13 -12
  93. package/.opencode/agents/aether-ambassador.md +63 -20
  94. package/.opencode/agents/aether-archaeologist.md +29 -12
  95. package/.opencode/agents/aether-auditor.md +51 -18
  96. package/.opencode/agents/aether-builder.md +69 -19
  97. package/.opencode/agents/aether-chaos.md +29 -12
  98. package/.opencode/agents/aether-chronicler.md +60 -18
  99. package/.opencode/agents/aether-gatekeeper.md +27 -18
  100. package/.opencode/agents/aether-includer.md +27 -18
  101. package/.opencode/agents/aether-keeper.md +89 -18
  102. package/.opencode/agents/aether-measurer.md +27 -18
  103. package/.opencode/agents/aether-probe.md +60 -18
  104. package/.opencode/agents/aether-queen.md +172 -24
  105. package/.opencode/agents/aether-route-setter.md +57 -12
  106. package/.opencode/agents/aether-sage.md +26 -18
  107. package/.opencode/agents/aether-scout.md +27 -19
  108. package/.opencode/agents/aether-surveyor-disciplines.md +53 -1
  109. package/.opencode/agents/aether-surveyor-nest.md +53 -1
  110. package/.opencode/agents/aether-surveyor-pathogens.md +51 -1
  111. package/.opencode/agents/aether-surveyor-provisions.md +53 -1
  112. package/.opencode/agents/aether-tracker.md +64 -18
  113. package/.opencode/agents/aether-watcher.md +66 -19
  114. package/.opencode/agents/aether-weaver.md +61 -18
  115. package/.opencode/commands/ant/build.md +406 -192
  116. package/.opencode/commands/ant/continue.md +66 -76
  117. package/.opencode/commands/ant/entomb.md +106 -45
  118. package/.opencode/commands/ant/init.md +46 -48
  119. package/.opencode/commands/ant/organize.md +5 -5
  120. package/.opencode/commands/ant/resume.md +334 -0
  121. package/.opencode/commands/ant/seal.md +33 -24
  122. package/.opencode/commands/ant/status.md +11 -0
  123. package/.opencode/commands/ant/tunnels.md +149 -0
  124. package/.opencode/commands/ant/update.md +59 -16
  125. package/CHANGELOG.md +79 -0
  126. package/README.md +135 -353
  127. package/bin/cli.js +243 -122
  128. package/bin/generate-commands.sh +2 -2
  129. package/bin/lib/init.js +13 -3
  130. package/bin/lib/update-transaction.js +119 -117
  131. package/bin/sync-to-runtime.sh +5 -137
  132. package/bin/validate-package.sh +84 -0
  133. package/package.json +9 -6
  134. package/.opencode/agents/aether-architect.md +0 -66
  135. package/.opencode/agents/aether-guardian.md +0 -107
  136. package/.opencode/agents/workers.md +0 -1034
  137. package/runtime/QUEEN_ANT_ARCHITECTURE.md +0 -402
  138. package/runtime/data/signatures.json +0 -41
  139. package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +0 -1343
  140. package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +0 -2642
  141. package/runtime/docs/PHEROMONE-INJECTION.md +0 -240
  142. package/runtime/docs/PHEROMONE-INTEGRATION.md +0 -192
  143. package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +0 -426
  144. package/runtime/docs/README.md +0 -94
  145. package/runtime/docs/VISUAL-OUTPUT-SPEC.md +0 -219
  146. package/runtime/docs/biological-reference.md +0 -272
  147. package/runtime/docs/codebase-review.md +0 -399
  148. package/runtime/docs/command-sync.md +0 -164
  149. package/runtime/docs/constraints.md +0 -116
  150. package/runtime/docs/implementation-learnings.md +0 -89
  151. package/runtime/docs/namespace.md +0 -148
  152. package/runtime/docs/pathogen-schema-example.json +0 -36
  153. package/runtime/docs/pathogen-schema.md +0 -111
  154. package/runtime/docs/planning-discipline.md +0 -159
  155. package/runtime/docs/progressive-disclosure.md +0 -184
  156. package/runtime/lib/queen-utils.sh +0 -729
  157. package/runtime/planning.md +0 -159
  158. package/runtime/recover.sh +0 -136
  159. package/runtime/utils/xml-utils.sh +0 -2196
  160. package/runtime/workers-new-castes.md +0 -516
  161. /package/{runtime → .aether/docs/disciplines}/coding-standards.md +0 -0
  162. /package/{runtime → .aether/docs/disciplines}/debugging.md +0 -0
  163. /package/{runtime → .aether/docs/disciplines}/learning.md +0 -0
  164. /package/{runtime → .aether/docs/disciplines}/tdd.md +0 -0
  165. /package/{runtime → .aether/docs/disciplines}/verification-loop.md +0 -0
  166. /package/{runtime → .aether/docs/disciplines}/verification.md +0 -0
  167. /package/{runtime → .aether}/docs/pheromones.md +0 -0
  168. /package/{runtime → .aether}/model-profiles.yaml +0 -0
  169. /package/{runtime → .aether}/schemas/aether-types.xsd +0 -0
  170. /package/{runtime → .aether}/schemas/colony-registry.xsd +0 -0
  171. /package/{runtime → .aether}/schemas/pheromone.xsd +0 -0
  172. /package/{runtime → .aether}/schemas/prompt.xsd +0 -0
  173. /package/{runtime → .aether}/schemas/queen-wisdom.xsd +0 -0
  174. /package/{runtime → .aether}/schemas/worker-priming.xsd +0 -0
  175. /package/{runtime → .aether}/templates/QUEEN.md.template +0 -0
  176. /package/{runtime → .aether}/utils/colorize-log.sh +0 -0
  177. /package/{runtime → .aether}/utils/queen-to-md.xsl +0 -0
  178. /package/{runtime → .aether}/utils/spawn-tree.sh +0 -0
  179. /package/{runtime → .aether}/utils/spawn-with-model.sh +0 -0
  180. /package/{runtime → .aether}/utils/state-loader.sh +0 -0
  181. /package/{runtime → .aether}/utils/swarm-display.sh +0 -0
  182. /package/{runtime → .aether}/utils/watch-spawn-tree.sh +0 -0
  183. /package/{runtime → .aether}/utils/xml-core.sh +0 -0
package/bin/cli.js CHANGED
@@ -67,12 +67,16 @@ if (!HOME) {
67
67
  // Claude Code paths (global)
68
68
  const COMMANDS_SRC = path.join(PACKAGE_DIR, 'commands', 'ant');
69
69
  const COMMANDS_DEST = path.join(HOME, '.claude', 'commands', 'ant');
70
+ const AGENTS_DEST = path.join(HOME, '.claude', 'agents', 'ant');
70
71
 
71
72
  // Hub paths
72
73
  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');
74
+ const HUB_SYSTEM_DIR = path.join(HUB_DIR, 'system');
75
+ const HUB_COMMANDS_CLAUDE = path.join(HUB_SYSTEM_DIR, 'commands', 'claude');
76
+ const HUB_COMMANDS_OPENCODE = path.join(HUB_SYSTEM_DIR, 'commands', 'opencode');
77
+ const HUB_AGENTS = path.join(HUB_SYSTEM_DIR, 'agents');
78
+ const HUB_AGENTS_CLAUDE = path.join(HUB_SYSTEM_DIR, 'agents-claude');
79
+ const HUB_RULES = path.join(HUB_SYSTEM_DIR, 'rules');
76
80
  const HUB_REGISTRY = path.join(HUB_DIR, 'registry.json');
77
81
  const HUB_VERSION = path.join(HUB_DIR, 'version.json');
78
82
 
@@ -374,47 +378,6 @@ function removeDirSync(dir) {
374
378
  return count;
375
379
  }
376
380
 
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) {
402
- let count = 0;
403
- for (const file of SYSTEM_FILES) {
404
- const srcPath = path.join(srcDir, file);
405
- 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
- }
412
- count++;
413
- }
414
- }
415
- return count;
416
- }
417
-
418
381
  function readJsonSafe(filePath) {
419
382
  try {
420
383
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
@@ -585,65 +548,15 @@ function computeFileHash(filePath) {
585
548
  }
586
549
  }
587
550
 
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
551
  // Checkpoint allowlist - only these files are captured in checkpoints
640
552
  // NEVER include: data/, dreams/, oracle/, TO-DOs.md (user data)
553
+ // Note: runtime/ was removed in v4.0 — .aether/ is published directly
641
554
  const CHECKPOINT_ALLOWLIST = [
642
555
  '.aether/*.md', // All .md files directly in .aether/
643
556
  '.claude/commands/ant/**', // All files in .claude/commands/ant/ recursively
557
+ '.claude/agents/ant/**', // All files in .claude/agents/ant/ recursively
644
558
  '.opencode/commands/ant/**', // All files in .opencode/commands/ant/ recursively
645
559
  '.opencode/agents/**', // All files in .opencode/agents/ recursively
646
- 'runtime/**', // All files in runtime/ recursively
647
560
  'bin/cli.js', // Specific file: bin/cli.js
648
561
  ];
649
562
 
@@ -835,7 +748,8 @@ function gitStashFiles(repoPath, files) {
835
748
  }
836
749
 
837
750
  // Directories to exclude from hub sync (user data, local state)
838
- const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp'];
751
+ // 'rules' is excluded here because it is synced via a dedicated step (rulesSrc below)
752
+ const HUB_EXCLUDE_DIRS = ['data', 'dreams', 'checkpoints', 'locks', 'temp', 'rules'];
839
753
 
840
754
  /**
841
755
  * Check if a path should be excluded from hub sync
@@ -955,6 +869,47 @@ function setupHub() {
955
869
  try {
956
870
  fs.mkdirSync(HUB_DIR, { recursive: true });
957
871
 
872
+ // MIGRATION: Check for old structure and migrate to system/
873
+ const oldStructureFiles = [
874
+ path.join(HUB_DIR, 'aether-utils.sh'),
875
+ path.join(HUB_DIR, 'workers.md'),
876
+ ];
877
+ const hasOldStructure = oldStructureFiles.some(f => fs.existsSync(f));
878
+ const hasNewStructure = fs.existsSync(HUB_SYSTEM_DIR);
879
+
880
+ if (hasOldStructure && !hasNewStructure) {
881
+ log(' Migrating hub to new structure...');
882
+ fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
883
+
884
+ // Move system files to system/
885
+ const systemFiles = ['aether-utils.sh', 'workers.md', 'CONTEXT.md', 'model-profiles.yaml'];
886
+ const systemDirs = ['docs', 'utils', 'commands', 'agents', 'schemas', 'exchange', 'templates', 'lib'];
887
+
888
+ for (const file of systemFiles) {
889
+ const oldPath = path.join(HUB_DIR, file);
890
+ if (fs.existsSync(oldPath)) {
891
+ fs.renameSync(oldPath, path.join(HUB_SYSTEM_DIR, file));
892
+ }
893
+ }
894
+
895
+ for (const dir of systemDirs) {
896
+ const oldPath = path.join(HUB_DIR, dir);
897
+ if (fs.existsSync(oldPath)) {
898
+ fs.renameSync(oldPath, path.join(HUB_SYSTEM_DIR, dir));
899
+ }
900
+ }
901
+
902
+ log(' Migration complete: system files moved to ~/.aether/system/');
903
+ }
904
+
905
+ // Create system/ directory structure
906
+ fs.mkdirSync(HUB_SYSTEM_DIR, { recursive: true });
907
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'commands', 'claude'), { recursive: true });
908
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'commands', 'opencode'), { recursive: true });
909
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'agents'), { recursive: true });
910
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'agents-claude'), { recursive: true });
911
+ fs.mkdirSync(path.join(HUB_SYSTEM_DIR, 'rules'), { recursive: true });
912
+
958
913
  // Read previous manifest for delta reporting
959
914
  const prevManifestRaw = readJsonSafe(path.join(HUB_DIR, 'manifest.json'));
960
915
  const prevManifest = prevManifestRaw && validateManifest(prevManifestRaw).valid ? prevManifestRaw : null;
@@ -962,21 +917,32 @@ function setupHub() {
962
917
  log(` Warning: previous manifest is invalid, regenerating`);
963
918
  }
964
919
 
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}`);
920
+ // Sync .aether/ -> ~/.aether/system/ (direct packaging, no staging)
921
+ // v4.0: .aether/ is published directly runtime/ staging removed
922
+ const aetherSrc = path.join(PACKAGE_DIR, '.aether');
923
+ if (fs.existsSync(aetherSrc)) {
924
+ const result = syncAetherToHub(aetherSrc, HUB_SYSTEM_DIR);
925
+ log(` Hub system: ${result.copied} files, ${result.skipped} unchanged -> ${HUB_SYSTEM_DIR}`);
971
926
  if (result.removed.length > 0) {
972
927
  log(` Hub system: removed ${result.removed.length} stale files`);
973
928
  for (const f of result.removed) log(` - ${f}`);
974
929
  }
975
930
  }
976
931
 
977
- // Clean up legacy directories from old hub structure
932
+ // Migration message for users upgrading from pre-4.0 (runtime/ era)
933
+ const prevManifestForMigration = readJsonSafe(path.join(HUB_DIR, 'manifest.json'));
934
+ if (prevManifestForMigration && prevManifestForMigration.version && prevManifestForMigration.version.startsWith('3.')) {
935
+ log('');
936
+ log(' Distribution pipeline simplified (v4.0 change):');
937
+ log(' - runtime/ staging directory has been removed');
938
+ log(' - .aether/ is now published directly (private dirs excluded)');
939
+ log(' - Your colony state and data are unaffected');
940
+ log(' - See CHANGELOG.md for details');
941
+ log('');
942
+ }
943
+
944
+ // Clean up legacy directories from very old hub structure (pre-system/)
978
945
  const legacyDirs = [
979
- path.join(HUB_DIR, 'system'),
980
946
  path.join(HUB_DIR, '.aether'),
981
947
  path.join(HUB_DIR, 'visualizations'),
982
948
  ];
@@ -991,7 +957,7 @@ function setupHub() {
991
957
  }
992
958
  }
993
959
 
994
- // Sync .claude/commands/ant/ -> ~/.aether/commands/claude/
960
+ // Sync .claude/commands/ant/ -> ~/.aether/system/commands/claude/
995
961
  const claudeCmdSrc = fs.existsSync(COMMANDS_SRC)
996
962
  ? COMMANDS_SRC
997
963
  : path.join(PACKAGE_DIR, '.claude', 'commands', 'ant');
@@ -1004,7 +970,7 @@ function setupHub() {
1004
970
  }
1005
971
  }
1006
972
 
1007
- // Sync .opencode/commands/ant/ -> ~/.aether/commands/opencode/
973
+ // Sync .opencode/commands/ant/ -> ~/.aether/system/commands/opencode/
1008
974
  const opencodeCmdSrc = path.join(PACKAGE_DIR, '.opencode', 'commands', 'ant');
1009
975
  if (fs.existsSync(opencodeCmdSrc)) {
1010
976
  const result = syncDirWithCleanup(opencodeCmdSrc, HUB_COMMANDS_OPENCODE);
@@ -1015,7 +981,7 @@ function setupHub() {
1015
981
  }
1016
982
  }
1017
983
 
1018
- // Sync .opencode/agents/ -> ~/.aether/agents/
984
+ // Sync .opencode/agents/ -> ~/.aether/system/agents/
1019
985
  const agentsSrc = path.join(PACKAGE_DIR, '.opencode', 'agents');
1020
986
  if (fs.existsSync(agentsSrc)) {
1021
987
  const result = syncDirWithCleanup(agentsSrc, HUB_AGENTS);
@@ -1026,7 +992,30 @@ function setupHub() {
1026
992
  }
1027
993
  }
1028
994
 
1029
- // Create/preserve registry.json
995
+ // Sync .claude/agents/ant/ -> ~/.aether/system/agents-claude/
996
+ const claudeAgentsSrc = path.join(PACKAGE_DIR, '.claude', 'agents', 'ant');
997
+ if (fs.existsSync(claudeAgentsSrc)) {
998
+ const result = syncDirWithCleanup(claudeAgentsSrc, HUB_AGENTS_CLAUDE);
999
+ log(` Hub agents (claude): ${result.copied} files, ${result.skipped} unchanged -> ${HUB_AGENTS_CLAUDE}`);
1000
+ if (result.removed.length > 0) {
1001
+ log(` Hub agents (claude): removed ${result.removed.length} stale files`);
1002
+ for (const f of result.removed) log(` - ${f}`);
1003
+ }
1004
+ }
1005
+
1006
+ // Sync rules/ from .aether/ -> ~/.aether/system/rules/
1007
+ // v4.0: source is .aether/rules/ directly (no runtime/ staging)
1008
+ const rulesSrc = path.join(PACKAGE_DIR, '.aether', 'rules');
1009
+ if (fs.existsSync(rulesSrc)) {
1010
+ const result = syncDirWithCleanup(rulesSrc, HUB_RULES);
1011
+ log(` Hub rules: ${result.copied} files -> ${HUB_RULES}`);
1012
+ if (result.removed.length > 0) {
1013
+ log(` Hub rules: removed ${result.removed.length} stale files`);
1014
+ for (const f of result.removed) log(` - ${f}`);
1015
+ }
1016
+ }
1017
+
1018
+ // Create/preserve registry.json (at root, not in system/)
1030
1019
  if (!fs.existsSync(HUB_REGISTRY)) {
1031
1020
  writeJsonSync(HUB_REGISTRY, { schema_version: 1, repos: [] });
1032
1021
  log(` Registry: initialized ${HUB_REGISTRY}`);
@@ -1034,7 +1023,7 @@ function setupHub() {
1034
1023
  log(` Registry: preserved existing ${HUB_REGISTRY}`);
1035
1024
  }
1036
1025
 
1037
- // Generate and write manifest
1026
+ // Generate and write manifest (at root, tracks everything)
1038
1027
  const manifest = generateManifest(HUB_DIR);
1039
1028
  const manifestPath = path.join(HUB_DIR, 'manifest.json');
1040
1029
  writeJsonSync(manifestPath, manifest);
@@ -1053,7 +1042,7 @@ function setupHub() {
1053
1042
  }
1054
1043
  }
1055
1044
 
1056
- // Write version.json
1045
+ // Write version.json (at root)
1057
1046
  writeJsonSync(HUB_VERSION, { version: VERSION, updated_at: new Date().toISOString() });
1058
1047
  log(` Hub version: ${VERSION}`);
1059
1048
  } catch (err) {
@@ -1079,7 +1068,7 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1079
1068
  const currentVer = currentVersion ? currentVersion.version : 'unknown';
1080
1069
 
1081
1070
  // Target directories for git safety checks
1082
- const targetDirs = ['.aether', '.claude/commands/ant', '.opencode/commands/ant', '.opencode/agents'];
1071
+ const targetDirs = ['.aether', '.claude/commands/ant', '.claude/agents/ant', '.claude/rules', '.opencode/commands/ant', '.opencode/agents'];
1083
1072
 
1084
1073
  // Git safety: check for dirty files in target directories (skip in dry-run mode)
1085
1074
  let dirtyFiles = [];
@@ -1101,17 +1090,25 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1101
1090
  const systemCopied = result.sync_result?.system?.copied || 0;
1102
1091
  const commandsCopied = (result.sync_result?.commands?.copied || 0);
1103
1092
  const agentsCopied = result.sync_result?.agents?.copied || 0;
1093
+ const rulesCopied = result.sync_result?.rules?.copied || 0;
1094
+ const agentsClaudeCopied = result.sync_result?.agents_claude?.copied || 0;
1104
1095
 
1105
1096
  const systemRemoved = result.sync_result?.system?.removed?.length || 0;
1106
1097
  const commandsRemoved = result.sync_result?.commands?.removed?.length || 0;
1107
1098
  const agentsRemoved = result.sync_result?.agents?.removed?.length || 0;
1099
+ const rulesRemoved = result.sync_result?.rules?.removed?.length || 0;
1100
+ const agentsClaudeRemoved = result.sync_result?.agents_claude?.removed?.length || 0;
1108
1101
 
1109
1102
  const allRemovedFiles = [
1110
1103
  ...(result.sync_result?.system?.removed || []),
1111
1104
  ...(result.sync_result?.commands?.removed || []).map(f => `.claude/commands/ant/${f}`),
1112
1105
  ...(result.sync_result?.agents?.removed || []).map(f => `.opencode/agents/${f}`),
1106
+ ...(result.sync_result?.rules?.removed || []).map(f => `.claude/rules/${f}`),
1107
+ ...(result.sync_result?.agents_claude?.removed || []).map(f => `.claude/agents/ant/${f}`),
1113
1108
  ];
1114
1109
 
1110
+ const cleanupResult = result.cleanup_result || { cleaned: [], failed: [] };
1111
+
1115
1112
  return {
1116
1113
  status: result.status,
1117
1114
  from: currentVer,
@@ -1119,10 +1116,13 @@ async function updateRepo(repoPath, sourceVersion, opts) {
1119
1116
  system: systemCopied,
1120
1117
  commands: commandsCopied,
1121
1118
  agents: agentsCopied,
1122
- removed: systemRemoved + commandsRemoved + agentsRemoved,
1119
+ rules: rulesCopied,
1120
+ agentsClaude: agentsClaudeCopied,
1121
+ removed: systemRemoved + commandsRemoved + agentsRemoved + rulesRemoved + agentsClaudeRemoved,
1123
1122
  removedFiles: allRemovedFiles,
1124
1123
  stashCreated: !!transaction.checkpoint?.stashRef,
1125
1124
  checkpoint_id: result.checkpoint_id,
1125
+ cleanup: cleanupResult,
1126
1126
  };
1127
1127
  } catch (error) {
1128
1128
  // Handle UpdateError with recovery commands
@@ -1161,7 +1161,7 @@ program.on('option:quiet', () => {
1161
1161
  // Install command
1162
1162
  program
1163
1163
  .command('install')
1164
- .description('Install slash-commands to ~/.claude/commands/ant/ and set up distribution hub')
1164
+ .description('Install commands and agents to ~/.claude/ and set up distribution hub')
1165
1165
  .action(wrapCommand(async () => {
1166
1166
  log(c.header(`aether-colony v${VERSION} — installing...`));
1167
1167
 
@@ -1188,6 +1188,17 @@ program
1188
1188
  }
1189
1189
  }
1190
1190
 
1191
+ // Sync agents to ~/.claude/agents/ant/ (with orphan cleanup)
1192
+ const repoAgents = path.join(PACKAGE_DIR, '.claude', 'agents', 'ant');
1193
+ if (fs.existsSync(repoAgents)) {
1194
+ const result = syncDirWithCleanup(repoAgents, AGENTS_DEST);
1195
+ log(` Agents (claude): ${result.copied} files -> ${AGENTS_DEST}`);
1196
+ if (result.removed.length > 0) {
1197
+ log(` Agents (claude): removed ${result.removed.length} stale files`);
1198
+ for (const f of result.removed) log(` - ${f}`);
1199
+ }
1200
+ }
1201
+
1191
1202
  // Set up distribution hub at ~/.aether/
1192
1203
  log('');
1193
1204
  log(c.colony('Setting up distribution hub...'));
@@ -1288,19 +1299,31 @@ program
1288
1299
  console.error(` Skipping. Use --force to stash and update.`);
1289
1300
  dirty++;
1290
1301
  } 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]`);
1302
+ log(` Would update: ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents, ${result.agentsClaude} claude agents]`);
1292
1303
  if (result.removed > 0) {
1293
1304
  log(` Would remove ${result.removed} stale files:`);
1294
1305
  for (const f of result.removedFiles) log(` - ${f}`);
1295
1306
  }
1296
1307
  updated++;
1297
1308
  } 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]`);
1309
+ log(` ${c.success('Updated:')} ${repo.path} (${result.from} -> ${result.to}) [${result.system} system, ${result.commands} commands, ${result.agents} agents, ${result.agentsClaude} claude agents]`);
1299
1310
  if (result.removed > 0) {
1300
1311
  log(` Removed ${result.removed} stale files:`);
1301
1312
  for (const f of result.removedFiles) log(` - ${f}`);
1302
1313
  totalRemoved += result.removed;
1303
1314
  }
1315
+ // Distribution chain cleanup reporting
1316
+ if (result.cleanup && result.cleanup.cleaned.length > 0) {
1317
+ for (const label of result.cleanup.cleaned) {
1318
+ log(` ${c.success('\u2713')} Removed ${label}`);
1319
+ }
1320
+ }
1321
+ for (const failure of (result.cleanup?.failed || [])) {
1322
+ log(` ${c.error('\u2717')} Failed to remove ${failure.label}: ${failure.error}`);
1323
+ }
1324
+ if (result.cleanup && result.cleanup.cleaned.length === 0 && result.cleanup.failed.length === 0) {
1325
+ log(` Distribution chain: ${c.success('\u2713')} clean`);
1326
+ }
1304
1327
  if (result.stashCreated) {
1305
1328
  log(` Stash created. Recover with: cd ${repo.path} && git stash pop`);
1306
1329
  }
@@ -1324,7 +1347,7 @@ program
1324
1347
  }
1325
1348
 
1326
1349
  const label = dryRun ? 'would update' : 'updated';
1327
- let summary = `\nSummary: ${updated} ${label}, ${upToDate} up-to-date, ${pruned} pruned`;
1350
+ let summary = `\nSummary: ${updated} ${label}, ${upToDate} up to date, ${pruned} pruned`;
1328
1351
  if (dirty > 0) summary += `, ${dirty} dirty (skipped)`;
1329
1352
  if (totalRemoved > 0) summary += `, ${totalRemoved} stale files removed`;
1330
1353
  console.log(summary);
@@ -1343,11 +1366,19 @@ program
1343
1366
  process.exit(getExitCode(error.code));
1344
1367
  }
1345
1368
 
1369
+ const pendingPath = path.join(repoAether, '.update-pending');
1370
+ const hasPending = fs.existsSync(pendingPath);
1371
+
1372
+ if (hasPending) {
1373
+ console.log('Detected incomplete update, re-syncing...');
1374
+ try { fs.unlinkSync(pendingPath); } catch { /* ignore */ }
1375
+ }
1376
+
1346
1377
  const currentVersion = readJsonSafe(path.join(repoAether, 'version.json'));
1347
1378
  const currentVer = currentVersion ? currentVersion.version : 'unknown';
1348
1379
 
1349
- if (!forceFlag && !dryRun && currentVer === sourceVersion) {
1350
- console.log(c.info(`Already up-to-date (v${sourceVersion}).`));
1380
+ if (!hasPending && !forceFlag && !dryRun && currentVer === sourceVersion) {
1381
+ console.log(c.info(`Already up to date (v${sourceVersion}).`));
1351
1382
  return;
1352
1383
  }
1353
1384
 
@@ -1371,7 +1402,7 @@ program
1371
1402
 
1372
1403
  if (result.status === 'dry-run') {
1373
1404
  console.log(`Would update: ${result.from} -> ${result.to}`);
1374
- console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files`);
1405
+ console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files, ${result.agentsClaude} claude agent files`);
1375
1406
  if (result.removed > 0) {
1376
1407
  console.log(` Would remove ${result.removed} stale files:`);
1377
1408
  for (const f of result.removedFiles) console.log(` - ${f}`);
@@ -1381,11 +1412,23 @@ program
1381
1412
  }
1382
1413
 
1383
1414
  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`);
1415
+ console.log(` ${result.system} system files, ${result.commands} command files, ${result.agents} agent files, ${result.agentsClaude} claude agent files`);
1385
1416
  if (result.removed > 0) {
1386
1417
  console.log(` Removed ${result.removed} stale files:`);
1387
1418
  for (const f of result.removedFiles) console.log(` - ${f}`);
1388
1419
  }
1420
+ // Distribution chain cleanup reporting
1421
+ if (result.cleanup && result.cleanup.cleaned.length > 0) {
1422
+ for (const label of result.cleanup.cleaned) {
1423
+ console.log(` ${c.success('\u2713')} Removed ${label}`);
1424
+ }
1425
+ }
1426
+ for (const failure of (result.cleanup?.failed || [])) {
1427
+ console.log(` ${c.error('\u2717')} Failed to remove ${failure.label}: ${failure.error}`);
1428
+ }
1429
+ if (result.cleanup && result.cleanup.cleaned.length === 0 && result.cleanup.failed.length === 0) {
1430
+ console.log(` Distribution chain: ${c.success('\u2713')} clean`);
1431
+ }
1389
1432
  if (result.stashCreated) {
1390
1433
  console.log(' Git stash created. Recover with: git stash pop');
1391
1434
  }
@@ -1969,6 +2012,85 @@ program
1969
2012
  console.log(tree);
1970
2013
  }));
1971
2014
 
2015
+ // Status command - Show colony status
2016
+ program
2017
+ .command('status')
2018
+ .description('Show colony status')
2019
+ .option('-j, --json', 'Output as JSON')
2020
+ .action(wrapCommand(async (options) => {
2021
+ const repoPath = process.cwd();
2022
+ const colonyStatePath = path.join(repoPath, '.aether', 'data', 'COLONY_STATE.json');
2023
+
2024
+ // Check if colony exists
2025
+ if (!fs.existsSync(colonyStatePath)) {
2026
+ console.log(c.warning('No colony found in current directory.'));
2027
+ console.log(c.dim('Run /ant:init to create a new colony, or cd to a project with an existing colony.'));
2028
+ return;
2029
+ }
2030
+
2031
+ // Load colony state
2032
+ let state;
2033
+ try {
2034
+ state = JSON.parse(fs.readFileSync(colonyStatePath, 'utf8'));
2035
+ } catch (err) {
2036
+ console.log(c.error('Could not read colony state.'));
2037
+ console.log(c.dim(`Error: ${err.message}`));
2038
+ console.log(c.dim('The colony state file may be corrupted. Consider running /ant:init to reinitialize.'));
2039
+ return;
2040
+ }
2041
+
2042
+ // JSON output
2043
+ if (options.json) {
2044
+ console.log(JSON.stringify(state, null, 2));
2045
+ return;
2046
+ }
2047
+
2048
+ // Dashboard output
2049
+ console.log(c.header('Colony Status\n'));
2050
+
2051
+ // Goal
2052
+ if (state.goal) {
2053
+ console.log(`${c.queen('Goal:')} ${state.goal}`);
2054
+ }
2055
+
2056
+ // State
2057
+ if (state.state) {
2058
+ const stateDisplay = state.state === 'BUILDING' ? c.success(state.state) :
2059
+ state.state === 'PLANNING' ? c.warning(state.state) :
2060
+ state.state === 'COMPLETED' ? c.success(state.state) :
2061
+ state.state;
2062
+ console.log(`${c.colony('State:')} ${stateDisplay}`);
2063
+ }
2064
+
2065
+ // Phase
2066
+ if (state.current_phase !== undefined) {
2067
+ console.log(`${c.info('Phase:')} ${state.current_phase}`);
2068
+ }
2069
+
2070
+ // Version
2071
+ if (state.version) {
2072
+ console.log(`${c.dim('Version:')} ${state.version}`);
2073
+ }
2074
+
2075
+ // Last updated
2076
+ if (state.last_updated) {
2077
+ const lastUpdated = new Date(state.last_updated);
2078
+ const now = new Date();
2079
+ const hoursAgo = Math.round((now - lastUpdated) / (1000 * 60 * 60));
2080
+ const timeDisplay = hoursAgo < 1 ? 'just now' :
2081
+ hoursAgo < 24 ? `${hoursAgo} hours ago` :
2082
+ `${Math.round(hoursAgo / 24)} days ago`;
2083
+ console.log(`${c.dim('Last updated:')} ${timeDisplay}`);
2084
+ }
2085
+
2086
+ // Event count
2087
+ if (state.events && state.events.length > 0) {
2088
+ console.log(`${c.dim('Events:')} ${state.events.length}`);
2089
+ }
2090
+
2091
+ console.log('');
2092
+ }));
2093
+
1972
2094
  // Nestmates command - List sibling colonies
1973
2095
  program
1974
2096
  .command('nestmates')
@@ -2242,7 +2364,6 @@ module.exports = {
2242
2364
  saveCheckpointMetadata,
2243
2365
  isUserData,
2244
2366
  syncDirWithCleanup,
2245
- syncSystemFilesWithCleanup,
2246
2367
  listFilesRecursive,
2247
2368
  cleanEmptyDirs
2248
2369
  };
@@ -212,9 +212,9 @@ check_content() {
212
212
 
213
213
  if [[ "$drift_count" -gt 0 ]]; then
214
214
  echo ""
215
- log_error "Content drift detected in $drift_count file(s):"
215
+ log_warn "Content drift detected in $drift_count file(s) (non-blocking):"
216
216
  echo -e "$drift_files"
217
- return 1
217
+ # Content drift is advisory — structural sync is what matters
218
218
  fi
219
219
 
220
220
  if [[ "$error_count" -gt 0 ]]; then
package/bin/lib/init.js CHANGED
@@ -15,9 +15,10 @@ const path = require('path');
15
15
  const HOME = process.env.HOME || process.env.USERPROFILE;
16
16
  const HUB_DIR = HOME ? path.join(HOME, '.aether') : null;
17
17
  const HUB_SYSTEM = HUB_DIR ? path.join(HUB_DIR, 'system') : null;
18
- const HUB_COMMANDS_CLAUDE = HUB_DIR ? path.join(HUB_DIR, 'commands', 'claude') : null;
19
- const HUB_COMMANDS_OPENCODE = HUB_DIR ? path.join(HUB_DIR, 'commands', 'opencode') : null;
20
- const HUB_AGENTS = HUB_DIR ? path.join(HUB_DIR, 'agents') : null;
18
+ const HUB_COMMANDS_CLAUDE = HUB_SYSTEM ? path.join(HUB_SYSTEM, 'commands', 'claude') : null;
19
+ const HUB_COMMANDS_OPENCODE = HUB_SYSTEM ? path.join(HUB_SYSTEM, 'commands', 'opencode') : null;
20
+ const HUB_AGENTS = HUB_SYSTEM ? path.join(HUB_SYSTEM, 'agents') : null;
21
+ const HUB_AGENTS_CLAUDE = HUB_SYSTEM ? path.join(HUB_SYSTEM, 'agents-claude') : null;
21
22
  const HUB_REGISTRY = HUB_DIR ? path.join(HUB_DIR, 'registry.json') : null;
22
23
  const HUB_VERSION = HUB_DIR ? path.join(HUB_DIR, 'version.json') : null;
23
24
 
@@ -381,6 +382,15 @@ async function initializeRepo(repoPath, options = {}) {
381
382
  }
382
383
  }
383
384
 
385
+ // Sync claude agents
386
+ if (HUB_AGENTS_CLAUDE && fs.existsSync(HUB_AGENTS_CLAUDE)) {
387
+ const destClaudeAgents = path.join(repoPath, '.claude', 'agents', 'ant');
388
+ const claudeAgentsResult = syncFiles(HUB_AGENTS_CLAUDE, destClaudeAgents);
389
+ if (!quiet && claudeAgentsResult.copied > 0) {
390
+ console.log(` Agents (claude): ${claudeAgentsResult.copied} copied, ${claudeAgentsResult.skipped} skipped`);
391
+ }
392
+ }
393
+
384
394
  // Create directory structure (in case some weren't created by sync)
385
395
  const dirs = [
386
396
  path.join(repoPath, '.aether', 'data'),