aether-colony 1.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 (207) hide show
  1. package/.aether/CONTEXT.md +160 -0
  2. package/.aether/QUEEN.md +84 -0
  3. package/.aether/aether-utils.sh +7749 -0
  4. package/.aether/docs/QUEEN-SYSTEM.md +211 -0
  5. package/.aether/docs/README.md +68 -0
  6. package/.aether/docs/caste-system.md +48 -0
  7. package/.aether/docs/disciplines/DISCIPLINES.md +93 -0
  8. package/.aether/docs/disciplines/coding-standards.md +197 -0
  9. package/.aether/docs/disciplines/debugging.md +207 -0
  10. package/.aether/docs/disciplines/learning.md +254 -0
  11. package/.aether/docs/disciplines/tdd.md +257 -0
  12. package/.aether/docs/disciplines/verification-loop.md +167 -0
  13. package/.aether/docs/disciplines/verification.md +116 -0
  14. package/.aether/docs/error-codes.md +268 -0
  15. package/.aether/docs/known-issues.md +233 -0
  16. package/.aether/docs/pheromones.md +205 -0
  17. package/.aether/docs/queen-commands.md +97 -0
  18. package/.aether/exchange/colony-registry.xml +11 -0
  19. package/.aether/exchange/pheromone-xml.sh +575 -0
  20. package/.aether/exchange/pheromones.xml +87 -0
  21. package/.aether/exchange/queen-wisdom.xml +14 -0
  22. package/.aether/exchange/registry-xml.sh +273 -0
  23. package/.aether/exchange/wisdom-xml.sh +319 -0
  24. package/.aether/midden/approach-changes.md +5 -0
  25. package/.aether/midden/build-failures.md +5 -0
  26. package/.aether/midden/test-failures.md +5 -0
  27. package/.aether/model-profiles.yaml +100 -0
  28. package/.aether/rules/aether-colony.md +134 -0
  29. package/.aether/schemas/aether-types.xsd +255 -0
  30. package/.aether/schemas/colony-registry.xsd +309 -0
  31. package/.aether/schemas/example-prompt-builder.xml +234 -0
  32. package/.aether/schemas/pheromone.xsd +163 -0
  33. package/.aether/schemas/prompt.xsd +416 -0
  34. package/.aether/schemas/queen-wisdom.xsd +325 -0
  35. package/.aether/schemas/worker-priming.xsd +276 -0
  36. package/.aether/templates/QUEEN.md.template +79 -0
  37. package/.aether/templates/colony-state-reset.jq.template +22 -0
  38. package/.aether/templates/colony-state.template.json +35 -0
  39. package/.aether/templates/constraints.template.json +9 -0
  40. package/.aether/templates/crowned-anthill.template.md +36 -0
  41. package/.aether/templates/handoff-build-error.template.md +30 -0
  42. package/.aether/templates/handoff-build-success.template.md +39 -0
  43. package/.aether/templates/handoff.template.md +40 -0
  44. package/.aether/templates/learning-observations.template.json +6 -0
  45. package/.aether/templates/midden.template.json +7 -0
  46. package/.aether/templates/pheromones.template.json +6 -0
  47. package/.aether/templates/session.template.json +9 -0
  48. package/.aether/utils/atomic-write.sh +219 -0
  49. package/.aether/utils/chamber-compare.sh +193 -0
  50. package/.aether/utils/chamber-utils.sh +297 -0
  51. package/.aether/utils/colorize-log.sh +132 -0
  52. package/.aether/utils/error-handler.sh +212 -0
  53. package/.aether/utils/file-lock.sh +158 -0
  54. package/.aether/utils/queen-to-md.xsl +395 -0
  55. package/.aether/utils/semantic-cli.sh +413 -0
  56. package/.aether/utils/spawn-tree.sh +428 -0
  57. package/.aether/utils/spawn-with-model.sh +56 -0
  58. package/.aether/utils/state-loader.sh +215 -0
  59. package/.aether/utils/swarm-display.sh +268 -0
  60. package/.aether/utils/watch-spawn-tree.sh +253 -0
  61. package/.aether/utils/xml-compose.sh +253 -0
  62. package/.aether/utils/xml-convert.sh +273 -0
  63. package/.aether/utils/xml-core.sh +186 -0
  64. package/.aether/utils/xml-query.sh +201 -0
  65. package/.aether/utils/xml-utils.sh +110 -0
  66. package/.aether/workers.md +765 -0
  67. package/.claude/agents/ant/aether-ambassador.md +264 -0
  68. package/.claude/agents/ant/aether-archaeologist.md +322 -0
  69. package/.claude/agents/ant/aether-auditor.md +266 -0
  70. package/.claude/agents/ant/aether-builder.md +187 -0
  71. package/.claude/agents/ant/aether-chaos.md +268 -0
  72. package/.claude/agents/ant/aether-chronicler.md +304 -0
  73. package/.claude/agents/ant/aether-gatekeeper.md +325 -0
  74. package/.claude/agents/ant/aether-includer.md +373 -0
  75. package/.claude/agents/ant/aether-keeper.md +271 -0
  76. package/.claude/agents/ant/aether-measurer.md +317 -0
  77. package/.claude/agents/ant/aether-probe.md +210 -0
  78. package/.claude/agents/ant/aether-queen.md +325 -0
  79. package/.claude/agents/ant/aether-route-setter.md +173 -0
  80. package/.claude/agents/ant/aether-sage.md +353 -0
  81. package/.claude/agents/ant/aether-scout.md +142 -0
  82. package/.claude/agents/ant/aether-surveyor-disciplines.md +416 -0
  83. package/.claude/agents/ant/aether-surveyor-nest.md +354 -0
  84. package/.claude/agents/ant/aether-surveyor-pathogens.md +288 -0
  85. package/.claude/agents/ant/aether-surveyor-provisions.md +359 -0
  86. package/.claude/agents/ant/aether-tracker.md +265 -0
  87. package/.claude/agents/ant/aether-watcher.md +244 -0
  88. package/.claude/agents/ant/aether-weaver.md +247 -0
  89. package/.claude/commands/ant/archaeology.md +341 -0
  90. package/.claude/commands/ant/build.md +1160 -0
  91. package/.claude/commands/ant/chaos.md +349 -0
  92. package/.claude/commands/ant/colonize.md +270 -0
  93. package/.claude/commands/ant/continue.md +1070 -0
  94. package/.claude/commands/ant/council.md +309 -0
  95. package/.claude/commands/ant/dream.md +265 -0
  96. package/.claude/commands/ant/entomb.md +487 -0
  97. package/.claude/commands/ant/feedback.md +78 -0
  98. package/.claude/commands/ant/flag.md +139 -0
  99. package/.claude/commands/ant/flags.md +155 -0
  100. package/.claude/commands/ant/focus.md +58 -0
  101. package/.claude/commands/ant/help.md +122 -0
  102. package/.claude/commands/ant/history.md +137 -0
  103. package/.claude/commands/ant/init.md +409 -0
  104. package/.claude/commands/ant/interpret.md +267 -0
  105. package/.claude/commands/ant/lay-eggs.md +201 -0
  106. package/.claude/commands/ant/maturity.md +102 -0
  107. package/.claude/commands/ant/memory-details.md +77 -0
  108. package/.claude/commands/ant/migrate-state.md +165 -0
  109. package/.claude/commands/ant/oracle.md +387 -0
  110. package/.claude/commands/ant/organize.md +227 -0
  111. package/.claude/commands/ant/pause-colony.md +247 -0
  112. package/.claude/commands/ant/phase.md +126 -0
  113. package/.claude/commands/ant/plan.md +544 -0
  114. package/.claude/commands/ant/redirect.md +58 -0
  115. package/.claude/commands/ant/resume-colony.md +182 -0
  116. package/.claude/commands/ant/resume.md +363 -0
  117. package/.claude/commands/ant/seal.md +306 -0
  118. package/.claude/commands/ant/status.md +272 -0
  119. package/.claude/commands/ant/swarm.md +361 -0
  120. package/.claude/commands/ant/tunnels.md +425 -0
  121. package/.claude/commands/ant/update.md +209 -0
  122. package/.claude/commands/ant/verify-castes.md +95 -0
  123. package/.claude/commands/ant/watch.md +238 -0
  124. package/.opencode/agents/aether-ambassador.md +140 -0
  125. package/.opencode/agents/aether-archaeologist.md +108 -0
  126. package/.opencode/agents/aether-auditor.md +144 -0
  127. package/.opencode/agents/aether-builder.md +184 -0
  128. package/.opencode/agents/aether-chaos.md +115 -0
  129. package/.opencode/agents/aether-chronicler.md +122 -0
  130. package/.opencode/agents/aether-gatekeeper.md +116 -0
  131. package/.opencode/agents/aether-includer.md +117 -0
  132. package/.opencode/agents/aether-keeper.md +177 -0
  133. package/.opencode/agents/aether-measurer.md +128 -0
  134. package/.opencode/agents/aether-probe.md +133 -0
  135. package/.opencode/agents/aether-queen.md +286 -0
  136. package/.opencode/agents/aether-route-setter.md +130 -0
  137. package/.opencode/agents/aether-sage.md +106 -0
  138. package/.opencode/agents/aether-scout.md +101 -0
  139. package/.opencode/agents/aether-surveyor-disciplines.md +386 -0
  140. package/.opencode/agents/aether-surveyor-nest.md +324 -0
  141. package/.opencode/agents/aether-surveyor-pathogens.md +259 -0
  142. package/.opencode/agents/aether-surveyor-provisions.md +329 -0
  143. package/.opencode/agents/aether-tracker.md +137 -0
  144. package/.opencode/agents/aether-watcher.md +174 -0
  145. package/.opencode/agents/aether-weaver.md +130 -0
  146. package/.opencode/commands/ant/archaeology.md +338 -0
  147. package/.opencode/commands/ant/build.md +1200 -0
  148. package/.opencode/commands/ant/chaos.md +346 -0
  149. package/.opencode/commands/ant/colonize.md +202 -0
  150. package/.opencode/commands/ant/continue.md +938 -0
  151. package/.opencode/commands/ant/council.md +305 -0
  152. package/.opencode/commands/ant/dream.md +262 -0
  153. package/.opencode/commands/ant/entomb.md +367 -0
  154. package/.opencode/commands/ant/feedback.md +80 -0
  155. package/.opencode/commands/ant/flag.md +137 -0
  156. package/.opencode/commands/ant/flags.md +153 -0
  157. package/.opencode/commands/ant/focus.md +56 -0
  158. package/.opencode/commands/ant/help.md +124 -0
  159. package/.opencode/commands/ant/history.md +127 -0
  160. package/.opencode/commands/ant/init.md +337 -0
  161. package/.opencode/commands/ant/interpret.md +256 -0
  162. package/.opencode/commands/ant/lay-eggs.md +141 -0
  163. package/.opencode/commands/ant/maturity.md +92 -0
  164. package/.opencode/commands/ant/memory-details.md +77 -0
  165. package/.opencode/commands/ant/migrate-state.md +153 -0
  166. package/.opencode/commands/ant/oracle.md +338 -0
  167. package/.opencode/commands/ant/organize.md +224 -0
  168. package/.opencode/commands/ant/pause-colony.md +220 -0
  169. package/.opencode/commands/ant/phase.md +123 -0
  170. package/.opencode/commands/ant/plan.md +531 -0
  171. package/.opencode/commands/ant/redirect.md +67 -0
  172. package/.opencode/commands/ant/resume-colony.md +178 -0
  173. package/.opencode/commands/ant/resume.md +363 -0
  174. package/.opencode/commands/ant/seal.md +247 -0
  175. package/.opencode/commands/ant/status.md +272 -0
  176. package/.opencode/commands/ant/swarm.md +357 -0
  177. package/.opencode/commands/ant/tunnels.md +406 -0
  178. package/.opencode/commands/ant/update.md +191 -0
  179. package/.opencode/commands/ant/verify-castes.md +85 -0
  180. package/.opencode/commands/ant/watch.md +220 -0
  181. package/.opencode/opencode.json +3 -0
  182. package/CHANGELOG.md +325 -0
  183. package/DISCLAIMER.md +74 -0
  184. package/LICENSE +21 -0
  185. package/README.md +258 -0
  186. package/bin/cli.js +2436 -0
  187. package/bin/generate-commands.sh +291 -0
  188. package/bin/lib/caste-colors.js +57 -0
  189. package/bin/lib/colors.js +76 -0
  190. package/bin/lib/errors.js +255 -0
  191. package/bin/lib/event-types.js +190 -0
  192. package/bin/lib/file-lock.js +695 -0
  193. package/bin/lib/init.js +454 -0
  194. package/bin/lib/logger.js +242 -0
  195. package/bin/lib/model-profiles.js +445 -0
  196. package/bin/lib/model-verify.js +288 -0
  197. package/bin/lib/nestmate-loader.js +130 -0
  198. package/bin/lib/proxy-health.js +253 -0
  199. package/bin/lib/spawn-logger.js +266 -0
  200. package/bin/lib/state-guard.js +602 -0
  201. package/bin/lib/state-sync.js +516 -0
  202. package/bin/lib/telemetry.js +441 -0
  203. package/bin/lib/update-transaction.js +1454 -0
  204. package/bin/npx-install.js +178 -0
  205. package/bin/sync-to-runtime.sh +6 -0
  206. package/bin/validate-package.sh +88 -0
  207. package/package.json +70 -0
@@ -0,0 +1,291 @@
1
+ #!/bin/bash
2
+ # generate-commands.sh - Sync commands between Claude Code and OpenCode
3
+ #
4
+ # This script helps keep commands in sync between the two platforms.
5
+ # Currently it provides a simple diff-based sync, with plans for full
6
+ # YAML-based generation in the future.
7
+ #
8
+ # Usage:
9
+ # ./bin/generate-commands.sh [check|sync|diff]
10
+ #
11
+ # Commands:
12
+ # check - Check if commands are in sync (exit 1 if not)
13
+ # sync - Copy Claude Code commands to OpenCode (with tool name translation)
14
+ # diff - Show differences between command sets
15
+
16
+ set -e
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
20
+
21
+ CLAUDE_DIR="$PROJECT_DIR/.claude/commands/ant"
22
+ OPENCODE_DIR="$PROJECT_DIR/.opencode/commands/ant"
23
+
24
+ # Colors for output
25
+ RED='\033[0;31m'
26
+ GREEN='\033[0;32m'
27
+ YELLOW='\033[1;33m'
28
+ NC='\033[0m' # No Color
29
+
30
+ log_info() {
31
+ echo -e "${GREEN}[INFO]${NC} $1"
32
+ }
33
+
34
+ log_warn() {
35
+ echo -e "${YELLOW}[WARN]${NC} $1"
36
+ }
37
+
38
+ log_error() {
39
+ echo -e "${RED}[ERROR]${NC} $1"
40
+ }
41
+
42
+ # Compute SHA hash with error handling
43
+ # Returns 0 on success, 1 on failure
44
+ # Echoes hash on success, error message on failure
45
+ compute_hash() {
46
+ local file="$1"
47
+
48
+ if [[ ! -r "$file" ]]; then
49
+ echo "NOT_READABLE"
50
+ return 1
51
+ fi
52
+
53
+ local hash
54
+ hash=$(shasum "$file" 2>/dev/null | cut -d' ' -f1)
55
+ if [[ -z "$hash" ]]; then
56
+ echo "HASH_FAILED"
57
+ return 1
58
+ fi
59
+
60
+ echo "$hash"
61
+ return 0
62
+ }
63
+
64
+ # Count commands in each directory
65
+ count_commands() {
66
+ local dir="$1"
67
+ if [[ -d "$dir" ]]; then
68
+ find "$dir" -name "*.md" | wc -l | tr -d ' '
69
+ else
70
+ echo "0"
71
+ fi
72
+ }
73
+
74
+ # List command files (PLAN-006 fix #13 - warn about non-.md files)
75
+ list_commands() {
76
+ local dir="$1"
77
+ if [[ -d "$dir" ]]; then
78
+ # Check for non-.md files and warn
79
+ local non_md_count
80
+ non_md_count=$(find "$dir" -type f ! -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
81
+ if [[ "$non_md_count" -gt 0 ]]; then
82
+ log_warn "$non_md_count non-.md file(s) found in $dir (ignored)"
83
+ fi
84
+
85
+ find "$dir" -name "*.md" -exec basename {} \; | sort
86
+ fi
87
+ }
88
+
89
+ # Check if directories are in sync (by file count and names)
90
+ check_sync() {
91
+ log_info "Checking command sync status..."
92
+
93
+ local claude_count=$(count_commands "$CLAUDE_DIR")
94
+ local opencode_count=$(count_commands "$OPENCODE_DIR")
95
+
96
+ echo "Claude Code commands: $claude_count"
97
+ echo "OpenCode commands: $opencode_count"
98
+
99
+ # PLAN-006 fix #10 - warn about empty directories
100
+ if [[ "$claude_count" -eq 0 ]] && [[ "$opencode_count" -eq 0 ]]; then
101
+ log_warn "Both command directories are empty"
102
+ echo "This may indicate a misconfiguration"
103
+ fi
104
+
105
+ # PLAN-006 fix #11 - warn about large command counts
106
+ local max_commands=500
107
+ if [[ "$claude_count" -gt "$max_commands" ]] || [[ "$opencode_count" -gt "$max_commands" ]]; then
108
+ log_warn "Large number of commands ($claude_count/$opencode_count)"
109
+ echo "This may cause performance issues during sync checks"
110
+ fi
111
+
112
+ if [[ "$claude_count" != "$opencode_count" ]]; then
113
+ log_error "Command counts don't match!"
114
+ return 1
115
+ fi
116
+
117
+ # Check file names match
118
+ local claude_files=$(list_commands "$CLAUDE_DIR")
119
+ local opencode_files=$(list_commands "$OPENCODE_DIR")
120
+
121
+ if [[ "$claude_files" != "$opencode_files" ]]; then
122
+ log_error "Command file names don't match!"
123
+ echo ""
124
+ echo "Only in Claude Code:"
125
+ comm -23 <(echo "$claude_files") <(echo "$opencode_files") | sed 's/^/ /'
126
+ echo ""
127
+ echo "Only in OpenCode:"
128
+ comm -13 <(echo "$claude_files") <(echo "$opencode_files") | sed 's/^/ /'
129
+ return 1
130
+ fi
131
+
132
+ log_info "Commands are in sync ($claude_count commands)"
133
+ return 0
134
+ }
135
+
136
+ # Check content-level sync using checksums (Pass 2)
137
+ # Compares each matching file pair by SHA-1 hash and reports diffs
138
+ check_content() {
139
+ log_info "Checking content-level sync (checksums)..."
140
+
141
+ local drift_count=0
142
+ local error_count=0
143
+ local match_count=0
144
+ local drift_files=""
145
+ local error_files=""
146
+
147
+ # Use null delimiter for safe iteration (handles filenames with spaces)
148
+ while IFS= read -r -d '' claude_file; do
149
+ local file
150
+ file=$(basename "$claude_file")
151
+ local opencode_file="$OPENCODE_DIR/$file"
152
+
153
+ # Skip if OpenCode file doesn't exist (already caught by Pass 1)
154
+ if [[ ! -f "$opencode_file" ]]; then
155
+ continue
156
+ fi
157
+
158
+ # Compute hashes with error handling
159
+ local claude_hash opencode_hash
160
+ claude_hash=$(compute_hash "$claude_file")
161
+ if [[ $? -ne 0 ]]; then
162
+ log_error "Cannot hash $claude_file ($claude_hash)"
163
+ error_files="${error_files} ${file} (${claude_hash})\n"
164
+ error_count=$((error_count + 1))
165
+ continue
166
+ fi
167
+
168
+ opencode_hash=$(compute_hash "$opencode_file")
169
+ if [[ $? -ne 0 ]]; then
170
+ log_error "Cannot hash $opencode_file ($opencode_hash)"
171
+ error_files="${error_files} ${file} (${opencode_hash})\n"
172
+ error_count=$((error_count + 1))
173
+ continue
174
+ fi
175
+
176
+ if [[ "$claude_hash" != "$opencode_hash" ]]; then
177
+ drift_count=$((drift_count + 1))
178
+ drift_files="${drift_files} ${file}\n"
179
+
180
+ log_warn "Content drift: $file"
181
+ echo " Claude: $claude_hash"
182
+ echo " OpenCode: $opencode_hash"
183
+
184
+ # PLAN-006 fix #12 - improved diff error handling
185
+ echo " ---"
186
+ local diff_output
187
+ if diff_output=$(diff -u "$claude_file" "$opencode_file" 2>&1); then
188
+ # Files are same (shouldn't happen if hashes differ, but handle it)
189
+ echo "$diff_output" | head -20
190
+ else
191
+ local diff_exit=$?
192
+ if [[ "$diff_output" == *"diff:"* && "$diff_output" == *"No such file"* ]]; then
193
+ log_error "diff failed: $diff_output"
194
+ else
195
+ # Normal diff output (exit 1 means files differ)
196
+ echo "$diff_output" | head -20
197
+ fi
198
+ fi
199
+ echo " ---"
200
+ echo ""
201
+ else
202
+ match_count=$((match_count + 1))
203
+ fi
204
+ done < <(find "$CLAUDE_DIR" -name "*.md" -type f -print0 2>/dev/null | sort -z)
205
+
206
+ # Report results
207
+ if [[ "$error_count" -gt 0 ]]; then
208
+ echo ""
209
+ log_error "Hash errors in $error_count file(s):"
210
+ echo -e "$error_files"
211
+ fi
212
+
213
+ if [[ "$drift_count" -gt 0 ]]; then
214
+ echo ""
215
+ log_warn "Content drift detected in $drift_count file(s) (non-blocking):"
216
+ echo -e "$drift_files"
217
+ # Content drift is advisory — structural sync is what matters
218
+ fi
219
+
220
+ if [[ "$error_count" -gt 0 ]]; then
221
+ return 1
222
+ fi
223
+
224
+ log_info "All file contents match (checksums verified: $match_count files)"
225
+ return 0
226
+ }
227
+
228
+ # Show diff between command sets
229
+ show_diff() {
230
+ log_info "Comparing command sets..."
231
+
232
+ # Use null delimiter for safe iteration (handles filenames with spaces)
233
+ while IFS= read -r -d '' claude_file; do
234
+ local file
235
+ file=$(basename "$claude_file")
236
+ local opencode_file="$OPENCODE_DIR/$file"
237
+
238
+ if [[ ! -f "$opencode_file" ]]; then
239
+ log_warn "$file exists only in Claude Code"
240
+ continue
241
+ fi
242
+
243
+ # Compare file sizes as a quick check
244
+ local claude_size=$(wc -l < "$claude_file" | tr -d ' ')
245
+ local opencode_size=$(wc -l < "$opencode_file" | tr -d ' ')
246
+
247
+ if [[ "$claude_size" != "$opencode_size" ]]; then
248
+ echo "$file: $claude_size lines (Claude) vs $opencode_size lines (OpenCode)"
249
+ fi
250
+ done < <(find "$CLAUDE_DIR" -name "*.md" -type f -print0 2>/dev/null | sort -z)
251
+ }
252
+
253
+ # Display help
254
+ show_help() {
255
+ echo "Aether Command Sync Tool"
256
+ echo ""
257
+ echo "Usage: $0 [command]"
258
+ echo ""
259
+ echo "Commands:"
260
+ echo " check Check if commands are in sync"
261
+ echo " diff Show differences between command sets"
262
+ echo " help Show this help message"
263
+ echo ""
264
+ echo "Directories:"
265
+ echo " Claude Code: $CLAUDE_DIR"
266
+ echo " OpenCode: $OPENCODE_DIR"
267
+ echo ""
268
+ echo "Note: Commands are maintained manually in both directories."
269
+ echo "Use this tool to verify they stay in sync."
270
+ }
271
+
272
+ # Main
273
+ case "${1:-check}" in
274
+ check)
275
+ # Pass 1: file count + name check
276
+ check_sync
277
+ # Pass 2: content-level checksum comparison
278
+ check_content
279
+ ;;
280
+ diff)
281
+ show_diff
282
+ ;;
283
+ help|--help|-h)
284
+ show_help
285
+ ;;
286
+ *)
287
+ log_error "Unknown command: $1"
288
+ show_help
289
+ exit 1
290
+ ;;
291
+ esac
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Caste Color Definitions Module
4
+ *
5
+ * Centralized caste styling with colors and emojis for consistent
6
+ * theming across the Aether colony system.
7
+ *
8
+ * Caste Colors (ANSI + picocolors):
9
+ * - builder: blue (🔨) - Construction, implementation
10
+ * - watcher: green (👁️) - Observation, monitoring
11
+ * - scout: yellow (🔍) - Exploration, investigation
12
+ * - chaos: red (🎲) - Testing, disruption
13
+ * - prime: magenta (👑) - Coordination, leadership
14
+ */
15
+
16
+ const pc = require('picocolors');
17
+
18
+ // Caste definitions with colors and emojis (per CONTEXT.md decisions)
19
+ const CASTE_STYLES = {
20
+ builder: { color: 'blue', emoji: '🔨🐜', ansi: '\033[34m', pc: pc.blue },
21
+ watcher: { color: 'green', emoji: '👁️🐜', ansi: '\033[32m', pc: pc.green },
22
+ scout: { color: 'yellow', emoji: '🔍🐜', ansi: '\033[33m', pc: pc.yellow },
23
+ chaos: { color: 'red', emoji: '🎲🐜', ansi: '\033[31m', pc: pc.red },
24
+ prime: { color: 'magenta',emoji: '👑🐜', ansi: '\033[35m', pc: pc.magenta }
25
+ };
26
+
27
+ // Get style for a caste (case-insensitive)
28
+ function getCasteStyle(caste) {
29
+ const key = caste.toLowerCase();
30
+ return CASTE_STYLES[key] || { color: 'reset', emoji: '🐜', ansi: '\033[0m', pc: (s) => s };
31
+ }
32
+
33
+ // Format ant name with color and emoji: "🔨 Builder" (both colored)
34
+ function formatAnt(name, caste) {
35
+ const style = getCasteStyle(caste);
36
+ return `${style.emoji} ${style.pc(name)}`;
37
+ }
38
+
39
+ // Format with ANSI codes (for bash scripts)
40
+ function formatAntAnsi(name, caste) {
41
+ const style = getCasteStyle(caste);
42
+ const reset = '\x1b[0m';
43
+ return `${style.ansi}${style.emoji} ${name}${reset}`;
44
+ }
45
+
46
+ // Get all castes for iteration
47
+ function getCastes() {
48
+ return Object.keys(CASTE_STYLES);
49
+ }
50
+
51
+ module.exports = {
52
+ CASTE_STYLES,
53
+ getCasteStyle,
54
+ formatAnt,
55
+ formatAntAnsi,
56
+ getCastes
57
+ };
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Aether Color Palette Module
4
+ *
5
+ * Centralized color definitions for consistent CLI theming.
6
+ * Uses picocolors for lightweight, NO_COLOR-friendly terminal colors.
7
+ *
8
+ * Aether Brand Colors:
9
+ * - queen: magenta (Queen ant)
10
+ * - colony: cyan (Colony/nest)
11
+ * - worker: yellow (Workers)
12
+ * - success: green
13
+ * - warning: yellow
14
+ * - error: red
15
+ * - info: blue
16
+ */
17
+
18
+ const pc = require('picocolors');
19
+
20
+ /**
21
+ * Detect if color output is enabled
22
+ * Checks --no-color flag and NO_COLOR environment variable
23
+ * @returns {boolean} True if colors should be enabled
24
+ */
25
+ function isColorEnabled() {
26
+ // Check explicit --no-color flag
27
+ if (process.argv.includes('--no-color')) {
28
+ return false;
29
+ }
30
+ // Check environment variable (NO_COLOR set to any non-empty value disables colors)
31
+ if (process.env.NO_COLOR && process.env.NO_COLOR !== '') {
32
+ return false;
33
+ }
34
+ // Check if stdout is TTY (disable colors when piped)
35
+ if (!process.stdout.isTTY) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+
41
+ const enabled = isColorEnabled();
42
+ const p = enabled ? pc : pc.createColors(false);
43
+
44
+ /**
45
+ * Aether brand color palette
46
+ * Semantic naming based on ant colony hierarchy and message types
47
+ */
48
+ module.exports = {
49
+ // Aether brand - ant colony hierarchy
50
+ queen: p.magenta, // Queen ant - magenta
51
+ colony: p.cyan, // Colony/nest - cyan
52
+ worker: p.yellow, // Workers - yellow
53
+
54
+ // Semantic message colors
55
+ success: p.green,
56
+ warning: p.yellow,
57
+ error: p.red,
58
+ info: p.blue,
59
+
60
+ // Text styles
61
+ bold: p.bold,
62
+ dim: p.dim,
63
+ italic: p.italic,
64
+ underline: p.underline,
65
+ strikethrough: p.strikethrough,
66
+
67
+ // Headers (combined styles)
68
+ header: (text) => p.bold(p.cyan(text)),
69
+ subheader: (text) => p.bold(text),
70
+
71
+ // Utility
72
+ isEnabled: () => enabled,
73
+
74
+ // Raw picocolors access for advanced usage
75
+ raw: p
76
+ };
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Aether Error Class Hierarchy
4
+ *
5
+ * Centralized error handling for the Aether Colony CLI.
6
+ * Provides structured JSON output, error codes, and recovery suggestions.
7
+ */
8
+
9
+ /**
10
+ * Error codes enum - categorized by error type
11
+ */
12
+ const ErrorCodes = {
13
+ // System errors (1-99)
14
+ E_HUB_NOT_FOUND: 'E_HUB_NOT_FOUND',
15
+ E_REPO_NOT_INITIALIZED: 'E_REPO_NOT_INITIALIZED',
16
+ E_FILE_SYSTEM: 'E_FILE_SYSTEM',
17
+ E_GIT_ERROR: 'E_GIT_ERROR',
18
+
19
+ // Validation errors (100-199)
20
+ E_INVALID_STATE: 'E_INVALID_STATE',
21
+ E_MANIFEST_INVALID: 'E_MANIFEST_INVALID',
22
+ E_JSON_PARSE: 'E_JSON_PARSE',
23
+
24
+ // Runtime errors (200-299)
25
+ E_UPDATE_FAILED: 'E_UPDATE_FAILED',
26
+ E_LOCK_TIMEOUT: 'E_LOCK_TIMEOUT',
27
+ E_ATOMIC_WRITE_FAILED: 'E_ATOMIC_WRITE_FAILED',
28
+
29
+ // Unexpected errors (300-399)
30
+ E_UNEXPECTED: 'E_UNEXPECTED',
31
+ E_UNCAUGHT_EXCEPTION: 'E_UNCAUGHT_EXCEPTION',
32
+ E_UNHANDLED_REJECTION: 'E_UNHANDLED_REJECTION',
33
+
34
+ // Configuration errors (400-499)
35
+ E_CONFIG: 'E_CONFIG',
36
+ };
37
+
38
+ /**
39
+ * Base AetherError class
40
+ * All application errors extend this class for consistent handling
41
+ */
42
+ class AetherError extends Error {
43
+ /**
44
+ * @param {string} code - Error code from ErrorCodes
45
+ * @param {string} message - Human-readable error message
46
+ * @param {object} details - Additional error context
47
+ * @param {string|null} recovery - Recovery suggestion for user
48
+ */
49
+ constructor(code, message, details = {}, recovery = null) {
50
+ super(message);
51
+ this.name = 'AetherError';
52
+ this.code = code;
53
+ this.details = details;
54
+ this.recovery = recovery;
55
+ this.timestamp = new Date().toISOString();
56
+
57
+ // Maintain proper stack trace in V8 environments
58
+ if (Error.captureStackTrace) {
59
+ Error.captureStackTrace(this, AetherError);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Convert error to structured JSON object
65
+ * @returns {object} Structured error representation
66
+ */
67
+ toJSON() {
68
+ return {
69
+ error: {
70
+ code: this.code,
71
+ message: this.message,
72
+ details: this.details,
73
+ recovery: this.recovery,
74
+ timestamp: this.timestamp,
75
+ },
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Convert error to string for console output
81
+ * @returns {string} Formatted error string
82
+ */
83
+ toString() {
84
+ let str = `${this.code}: ${this.message}`;
85
+ if (this.recovery) {
86
+ str += `\n Recovery: ${this.recovery}`;
87
+ }
88
+ return str;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * HubError - Hub-related errors (distribution hub not found, corrupted, etc.)
94
+ */
95
+ class HubError extends AetherError {
96
+ constructor(message, details = {}) {
97
+ super(
98
+ ErrorCodes.E_HUB_NOT_FOUND,
99
+ message,
100
+ details,
101
+ 'Run: aether install'
102
+ );
103
+ this.name = 'HubError';
104
+ }
105
+ }
106
+
107
+ /**
108
+ * RepoError - Repository initialization errors
109
+ */
110
+ class RepoError extends AetherError {
111
+ constructor(message, details = {}) {
112
+ super(
113
+ ErrorCodes.E_REPO_NOT_INITIALIZED,
114
+ message,
115
+ details,
116
+ 'Run /ant:init in this repo first'
117
+ );
118
+ this.name = 'RepoError';
119
+ }
120
+ }
121
+
122
+ /**
123
+ * GitError - Git operation errors
124
+ */
125
+ class GitError extends AetherError {
126
+ constructor(message, details = {}) {
127
+ super(
128
+ ErrorCodes.E_GIT_ERROR,
129
+ message,
130
+ details,
131
+ 'Check git status and resolve conflicts'
132
+ );
133
+ this.name = 'GitError';
134
+ }
135
+ }
136
+
137
+ /**
138
+ * ValidationError - State validation errors
139
+ */
140
+ class ValidationError extends AetherError {
141
+ constructor(message, details = {}) {
142
+ super(
143
+ ErrorCodes.E_INVALID_STATE,
144
+ message,
145
+ details,
146
+ 'Check the state file and fix validation errors'
147
+ );
148
+ this.name = 'ValidationError';
149
+ }
150
+ }
151
+
152
+ /**
153
+ * FileSystemError - File operation errors
154
+ */
155
+ class FileSystemError extends AetherError {
156
+ constructor(message, details = {}) {
157
+ super(
158
+ ErrorCodes.E_FILE_SYSTEM,
159
+ message,
160
+ details,
161
+ 'Check file permissions and available disk space'
162
+ );
163
+ this.name = 'FileSystemError';
164
+ }
165
+ }
166
+
167
+ /**
168
+ * ConfigurationError - Environment/configuration errors
169
+ */
170
+ class ConfigurationError extends AetherError {
171
+ constructor(message, details = {}) {
172
+ super(
173
+ ErrorCodes.E_CONFIG,
174
+ message,
175
+ details,
176
+ 'Check environment variables and configuration'
177
+ );
178
+ this.name = 'ConfigurationError';
179
+ }
180
+ }
181
+
182
+ /**
183
+ * StateSchemaError - State file schema validation errors (PLAN-007 Fix 3)
184
+ */
185
+ class StateSchemaError extends AetherError {
186
+ constructor(message, details = {}) {
187
+ super(
188
+ ErrorCodes.E_INVALID_STATE,
189
+ message,
190
+ details,
191
+ 'Check state file structure and fix schema errors, or restore from backup'
192
+ );
193
+ this.name = 'StateSchemaError';
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Map error codes to sysexits.h exit codes
199
+ * @param {string} code - Error code
200
+ * @returns {number} Exit code (0-255)
201
+ */
202
+ function getExitCode(code) {
203
+ switch (code) {
204
+ case ErrorCodes.E_HUB_NOT_FOUND:
205
+ return 69; // EX_UNAVAILABLE - service unavailable
206
+ case ErrorCodes.E_REPO_NOT_INITIALIZED:
207
+ return 78; // EX_CONFIG - configuration error
208
+ case ErrorCodes.E_INVALID_STATE:
209
+ case ErrorCodes.E_MANIFEST_INVALID:
210
+ case ErrorCodes.E_JSON_PARSE:
211
+ return 65; // EX_DATAERR - data format error
212
+ case ErrorCodes.E_FILE_SYSTEM:
213
+ case ErrorCodes.E_ATOMIC_WRITE_FAILED:
214
+ return 74; // EX_IOERR - I/O error
215
+ case ErrorCodes.E_GIT_ERROR:
216
+ return 70; // EX_SOFTWARE - internal software error
217
+ case ErrorCodes.E_LOCK_TIMEOUT:
218
+ return 73; // EX_CANTCREAT - can't create (lock) file
219
+ case ErrorCodes.E_CONFIG:
220
+ return 78; // EX_CONFIG - configuration error
221
+ default:
222
+ return 1; // Generic error
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Wrap a plain Error in an AetherError
228
+ * @param {Error} error - Plain error to wrap
229
+ * @returns {AetherError} Wrapped error
230
+ */
231
+ function wrapError(error) {
232
+ if (error instanceof AetherError) {
233
+ return error;
234
+ }
235
+ return new AetherError(
236
+ ErrorCodes.E_UNEXPECTED,
237
+ error.message,
238
+ { stack: error.stack, name: error.name },
239
+ 'Please report this issue with the error details'
240
+ );
241
+ }
242
+
243
+ module.exports = {
244
+ AetherError,
245
+ HubError,
246
+ RepoError,
247
+ GitError,
248
+ ValidationError,
249
+ FileSystemError,
250
+ ConfigurationError,
251
+ StateSchemaError,
252
+ ErrorCodes,
253
+ getExitCode,
254
+ wrapError,
255
+ };