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,297 @@
1
+ #!/bin/bash
2
+ # Aether Chamber Utilities
3
+ # Manages entombed colonies — directory management, manifest generation, integrity verification
4
+ #
5
+ # Usage:
6
+ # source .aether/utils/chamber-utils.sh
7
+ # chamber_create <chamber_dir> <state_file> <goal> <phases_completed> <total_phases> <milestone> <version> <decisions_json> <learnings_json>
8
+ # chamber_verify <chamber_dir>
9
+ # chamber_list <chambers_root>
10
+ # chamber_sanitize_goal <goal>
11
+
12
+ set -euo pipefail
13
+
14
+ # Initialize lock state before sourcing (file-lock.sh trap needs these)
15
+ LOCK_ACQUIRED=${LOCK_ACQUIRED:-false}
16
+ CURRENT_LOCK=${CURRENT_LOCK:-""}
17
+
18
+ # Get script directory for sourcing (preserve parent SCRIPT_DIR if set)
19
+ __chamber_utils_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ AETHER_ROOT="$(cd "$__chamber_utils_dir/../.." && pwd 2>/dev/null || echo "$__chamber_utils_dir/../..")"
21
+
22
+ # Use parent SCRIPT_DIR if available, otherwise use local
23
+ SCRIPT_DIR="${SCRIPT_DIR:-$__chamber_utils_dir}"
24
+
25
+ # Source atomic-write for safe file operations
26
+ [[ -f "$SCRIPT_DIR/atomic-write.sh" ]] && source "$SCRIPT_DIR/atomic-write.sh"
27
+
28
+ # --- JSON output helpers ---
29
+ json_ok() { printf '{"ok":true,"result":%s}\n' "$1"; }
30
+
31
+ # Guard: yield to error-handler.sh's enhanced json_err when already loaded
32
+ if ! type json_err &>/dev/null; then
33
+ json_err() {
34
+ local code="${1:-E_UNKNOWN}"
35
+ local message="${2:-An unknown error occurred}"
36
+ printf '{"ok":false,"error":{"code":"%s","message":"%s"}}\n' "$code" "$message" >&2
37
+ exit 1
38
+ }
39
+ fi
40
+
41
+ # Fallback E_* constants (no-ops when error-handler.sh is already loaded)
42
+ : "${E_UNKNOWN:=E_UNKNOWN}"
43
+ : "${E_VALIDATION_FAILED:=E_VALIDATION_FAILED}"
44
+ : "${E_FILE_NOT_FOUND:=E_FILE_NOT_FOUND}"
45
+ : "${E_BASH_ERROR:=E_BASH_ERROR}"
46
+ : "${E_JSON_INVALID:=E_JSON_INVALID}"
47
+
48
+ # --- Chamber Functions ---
49
+
50
+ # Sanitize goal string for use in directory names
51
+ # Converts to lowercase, replaces spaces/special chars with hyphens, removes non-alphanumeric
52
+ chamber_sanitize_goal() {
53
+ local goal="$1"
54
+ # Convert to lowercase, replace spaces and special chars with hyphens
55
+ local sanitized=$(echo "$goal" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-')
56
+ # Remove leading/trailing hyphens
57
+ sanitized=$(echo "$sanitized" | sed 's/^-//;s/-$//')
58
+ # Limit length to avoid overly long directory names
59
+ if [[ ${#sanitized} -gt 50 ]]; then
60
+ sanitized="${sanitized:0:50}"
61
+ fi
62
+ echo "$sanitized"
63
+ }
64
+
65
+ # Compute SHA256 hash of a file
66
+ # Returns hash string or empty on error
67
+ chamber_compute_hash() {
68
+ local file_path="$1"
69
+ if [[ ! -f "$file_path" ]]; then
70
+ echo ""
71
+ return 1
72
+ fi
73
+
74
+ # Try sha256sum first (Linux), then shasum -a 256 (macOS)
75
+ if command -v sha256sum >/dev/null 2>&1; then
76
+ sha256sum "$file_path" | cut -d' ' -f1
77
+ elif command -v shasum >/dev/null 2>&1; then
78
+ shasum -a 256 "$file_path" | cut -d' ' -f1
79
+ else
80
+ echo ""
81
+ return 1
82
+ fi
83
+ }
84
+
85
+ # Create a new chamber (entomb a colony)
86
+ # Arguments:
87
+ # chamber_dir: Directory to create for this chamber
88
+ # state_file: Path to COLONY_STATE.json to archive
89
+ # goal: Colony goal string
90
+ # phases_completed: Number of completed phases
91
+ # total_phases: Total number of phases
92
+ # milestone: Milestone name
93
+ # version: Version string
94
+ # decisions_json: JSON array of decisions
95
+ # learnings_json: JSON array of learnings
96
+ chamber_create() {
97
+ local chamber_dir="$1"
98
+ local state_file="$2"
99
+ local goal="$3"
100
+ local phases_completed="$4"
101
+ local total_phases="$5"
102
+ local milestone="$6"
103
+ local version="$7"
104
+ local decisions_json="$8"
105
+ local learnings_json="$9"
106
+
107
+ # Validate inputs
108
+ [[ -z "$chamber_dir" ]] && json_err "$E_VALIDATION_FAILED" "chamber_dir argument is required. Try: pass the chamber directory path."
109
+ [[ -z "$state_file" ]] && json_err "$E_VALIDATION_FAILED" "state_file argument is required. Try: pass the state file path."
110
+ [[ ! -f "$state_file" ]] && json_err "$E_FILE_NOT_FOUND" "State file not found: $state_file. Try: check the file path."
111
+
112
+ # Create chamber directory
113
+ mkdir -p "$chamber_dir" || json_err "$E_BASH_ERROR" "Couldn't create chamber directory: $chamber_dir. Try: check disk space and permissions."
114
+
115
+ # Copy state file to chamber
116
+ local target_state="$chamber_dir/COLONY_STATE.json"
117
+ cp "$state_file" "$target_state" || json_err "$E_BASH_ERROR" "Couldn't copy the state file. Try: check disk space and permissions."
118
+
119
+ # Compute hash of the copied state file
120
+ local state_hash=$(chamber_compute_hash "$target_state")
121
+ [[ -z "$state_hash" ]] && json_err "$E_BASH_ERROR" "Couldn't compute state file hash. Try: check that shasum is available."
122
+
123
+ # Generate timestamp
124
+ local entombed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
125
+
126
+ # Create manifest.json
127
+ local manifest_file="$chamber_dir/manifest.json"
128
+ local manifest_content=$(cat <<EOF
129
+ {
130
+ "entombed_at": "$entombed_at",
131
+ "goal": $(echo "$goal" | jq -Rs '.[:-1]'),
132
+ "phases_completed": $phases_completed,
133
+ "total_phases": $total_phases,
134
+ "milestone": $(echo "$milestone" | jq -Rs '.[:-1]'),
135
+ "version": $(echo "$version" | jq -Rs '.[:-1]'),
136
+ "decisions": $decisions_json,
137
+ "learnings": $learnings_json,
138
+ "files": {
139
+ "COLONY_STATE.json": "$state_hash"
140
+ }
141
+ }
142
+ EOF
143
+ )
144
+
145
+ # Write manifest atomically if atomic_write is available, otherwise direct
146
+ if type atomic_write &>/dev/null; then
147
+ atomic_write "$manifest_file" "$manifest_content" || json_err "$E_BASH_ERROR" "Couldn't write chamber manifest. Try: check disk space."
148
+ else
149
+ echo "$manifest_content" > "$manifest_file" || json_err "$E_BASH_ERROR" "Couldn't write chamber manifest. Try: check disk space."
150
+ fi
151
+
152
+ # Verify the manifest was written correctly
153
+ if [[ ! -f "$manifest_file" ]]; then
154
+ json_err "$E_FILE_NOT_FOUND" "Chamber manifest wasn't created. Try: check disk space and permissions."
155
+ fi
156
+
157
+ # Return success with chamber info
158
+ local result=$(cat <<EOF
159
+ {
160
+ "chamber_dir": "$chamber_dir",
161
+ "manifest": {
162
+ "entombed_at": "$entombed_at",
163
+ "goal": $(echo "$goal" | jq -Rs '.[:-1]'),
164
+ "phases_completed": $phases_completed,
165
+ "total_phases": $total_phases,
166
+ "milestone": $(echo "$milestone" | jq -Rs '.[:-1]'),
167
+ "version": $(echo "$version" | jq -Rs '.[:-1]')
168
+ }
169
+ }
170
+ EOF
171
+ )
172
+
173
+ json_ok "$result"
174
+ }
175
+
176
+ # Verify chamber integrity
177
+ # Arguments:
178
+ # chamber_dir: Directory containing the chamber
179
+ chamber_verify() {
180
+ local chamber_dir="$1"
181
+
182
+ # Validate inputs
183
+ [[ -z "$chamber_dir" ]] && json_err "$E_VALIDATION_FAILED" "chamber_dir argument is required. Try: pass the chamber directory path."
184
+ [[ ! -d "$chamber_dir" ]] && json_err "$E_FILE_NOT_FOUND" "Chamber directory not found: $chamber_dir. Try: check the path."
185
+
186
+ local manifest_file="$chamber_dir/manifest.json"
187
+ local state_file="$chamber_dir/COLONY_STATE.json"
188
+
189
+ # Check required files exist
190
+ [[ ! -f "$manifest_file" ]] && json_err "$E_FILE_NOT_FOUND" "Manifest not found in chamber. Try: verify the chamber was created correctly."
191
+ [[ ! -f "$state_file" ]] && json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found in chamber. Try: re-entomb the colony."
192
+
193
+ # Read stored hash from manifest
194
+ local stored_hash=$(jq -r '.files["COLONY_STATE.json"] // empty' "$manifest_file" 2>/dev/null)
195
+ [[ -z "$stored_hash" ]] && json_err "$E_JSON_INVALID" "No hash found in manifest. Try: re-entomb the colony."
196
+
197
+ # Compute current hash
198
+ local current_hash=$(chamber_compute_hash "$state_file")
199
+ [[ -z "$current_hash" ]] && json_err "$E_BASH_ERROR" "Couldn't compute state file hash. Try: check that shasum is available."
200
+
201
+ # Compare hashes
202
+ if [[ "$stored_hash" != "$current_hash" ]]; then
203
+ local result=$(cat <<EOF
204
+ {
205
+ "verified": false,
206
+ "chamber_dir": "$chamber_dir",
207
+ "error": "hash mismatch",
208
+ "stored_hash": "$stored_hash",
209
+ "current_hash": "$current_hash"
210
+ }
211
+ EOF
212
+ )
213
+ json_ok "$result"
214
+ return 0
215
+ fi
216
+
217
+ # Verification passed
218
+ local result=$(cat <<EOF
219
+ {
220
+ "verified": true,
221
+ "chamber_dir": "$chamber_dir",
222
+ "hash": "$current_hash"
223
+ }
224
+ EOF
225
+ )
226
+
227
+ json_ok "$result"
228
+ }
229
+
230
+ # List all chambers
231
+ # Arguments:
232
+ # chambers_root: Root directory containing chambers (default: .aether/chambers/)
233
+ chamber_list() {
234
+ local chambers_root="${1:-$AETHER_ROOT/.aether/chambers}"
235
+
236
+ # Default to current directory's chambers if AETHER_ROOT not set
237
+ if [[ -z "$chambers_root" || "$chambers_root" == "/.aether/chambers" ]]; then
238
+ chambers_root="$(pwd)/.aether/chambers"
239
+ fi
240
+
241
+ # Check if chambers directory exists
242
+ if [[ ! -d "$chambers_root" ]]; then
243
+ json_ok "[]"
244
+ return 0
245
+ fi
246
+
247
+ # Build array of chamber summaries
248
+ local chambers="["
249
+ local first=true
250
+
251
+ # Find all directories in chambers_root
252
+ while IFS= read -r -d '' chamber_dir; do
253
+ local chamber_name=$(basename "$chamber_dir")
254
+ local manifest_file="$chamber_dir/manifest.json"
255
+
256
+ # Skip if no manifest
257
+ [[ ! -f "$manifest_file" ]] && continue
258
+
259
+ # Read manifest fields
260
+ local goal=$(jq -r '.goal // "unknown"' "$manifest_file" 2>/dev/null)
261
+ local milestone=$(jq -r '.milestone // "unknown"' "$manifest_file" 2>/dev/null)
262
+ local phases_completed=$(jq -r '.phases_completed // 0' "$manifest_file" 2>/dev/null)
263
+ local entombed_at=$(jq -r '.entombed_at // ""' "$manifest_file" 2>/dev/null)
264
+
265
+ # Escape for JSON
266
+ goal=$(echo "$goal" | jq -Rs '.[:-1]')
267
+ milestone=$(echo "$milestone" | jq -Rs '.[:-1]')
268
+
269
+ # Add comma if not first
270
+ if [[ "$first" == "true" ]]; then
271
+ first=false
272
+ else
273
+ chambers+=","
274
+ fi
275
+
276
+ chambers+=$(cat <<EOF
277
+ {
278
+ "name": $(echo "$chamber_name" | jq -Rs '.[:-1]'),
279
+ "goal": $goal,
280
+ "milestone": $milestone,
281
+ "phases_completed": $phases_completed,
282
+ "entombed_at": $(echo "$entombed_at" | jq -Rs '.[:-1]')
283
+ }
284
+ EOF
285
+ )
286
+ done < <(find "$chambers_root" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null || true)
287
+
288
+ chambers+="]"
289
+
290
+ # Sort by entombed_at descending using jq
291
+ local sorted=$(echo "$chambers" | jq 'sort_by(.entombed_at) | reverse')
292
+
293
+ json_ok "$sorted"
294
+ }
295
+
296
+ # Export functions for use in other scripts
297
+ export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list
@@ -0,0 +1,132 @@
1
+ #!/bin/bash
2
+ # Colorized activity log stream for tmux watch pane
3
+ # Usage: bash colorize-log.sh [log_file]
4
+
5
+ LOG_FILE="${1:-.aether/data/activity.log}"
6
+
7
+ # ANSI color codes by caste (as per V2 improvement plan)
8
+ QUEEN='\033[35m' # Magenta
9
+ BUILDER='\033[33m' # Yellow
10
+ WATCHER='\033[36m' # Cyan
11
+ SCOUT='\033[32m' # Green
12
+ COLONIZER='\033[34m' # Blue
13
+ ARCHITECT='\033[37m' # White
14
+
15
+ # Action colors (bright variants)
16
+ SPAWN='\033[93m' # Bright Yellow
17
+ COMPLETE='\033[92m' # Bright Green
18
+ ERROR='\033[91m' # Bright Red
19
+ CREATED='\033[96m' # Bright Cyan
20
+ MODIFIED='\033[94m' # Bright Blue
21
+
22
+ # Base colors
23
+ YELLOW='\033[33m'
24
+ GREEN='\033[32m'
25
+ RED='\033[31m'
26
+ CYAN='\033[36m'
27
+ MAGENTA='\033[35m'
28
+ BLUE='\033[34m'
29
+ BOLD='\033[1m'
30
+ DIM='\033[2m'
31
+ RESET='\033[0m'
32
+
33
+ # Get caste color from ant name patterns
34
+ get_caste_color() {
35
+ case "$1" in
36
+ *Queen*|*QUEEN*)
37
+ echo "$QUEEN"
38
+ ;;
39
+ *Builder*|*Bolt*|*Hammer*|*Forge*|*Mason*|*Brick*|*Anvil*|*Weld*)
40
+ echo "$BUILDER"
41
+ ;;
42
+ *Watcher*|*Vigil*|*Sentinel*|*Guard*|*Keen*|*Sharp*|*Hawk*|*Watch*|*Alert*)
43
+ echo "$WATCHER"
44
+ ;;
45
+ *Scout*|*Swift*|*Dash*|*Ranger*|*Track*|*Seek*|*Path*|*Roam*|*Quest*)
46
+ echo "$SCOUT"
47
+ ;;
48
+ *Colonizer*|*Pioneer*|*Map*|*Chart*|*Venture*|*Explore*|*Compass*|*Atlas*|*Trek*)
49
+ echo "$COLONIZER"
50
+ ;;
51
+ *Architect*|*Blueprint*|*Draft*|*Design*|*Plan*|*Schema*|*Frame*|*Sketch*|*Model*)
52
+ echo "$ARCHITECT"
53
+ ;;
54
+ *Prime*|*Alpha*|*Lead*|*Chief*|*First*|*Core*|*Apex*|*Crown*)
55
+ echo "$MAGENTA"
56
+ ;;
57
+ *)
58
+ echo "$RESET"
59
+ ;;
60
+ esac
61
+ }
62
+
63
+ # Get emoji for caste
64
+ get_emoji() {
65
+ case "$1" in
66
+ *Queen*|*QUEEN*) echo "👑" ;;
67
+ *Builder*|*Bolt*|*Hammer*|*Forge*|*Mason*|*Brick*|*Anvil*|*Weld*) echo "🔨" ;;
68
+ *Watcher*|*Vigil*|*Sentinel*|*Guard*|*Keen*|*Sharp*|*Hawk*|*Watch*|*Alert*) echo "👁️" ;;
69
+ *Scout*|*Swift*|*Dash*|*Ranger*|*Track*|*Seek*|*Path*|*Roam*|*Quest*) echo "🔍" ;;
70
+ *Colonizer*|*Pioneer*|*Map*|*Chart*|*Venture*|*Explore*|*Compass*|*Atlas*|*Trek*) echo "🗺️" ;;
71
+ *Architect*|*Blueprint*|*Draft*|*Design*|*Plan*|*Schema*|*Frame*|*Sketch*|*Model*) echo "🏛️" ;;
72
+ *Prime*|*Alpha*|*Lead*|*Chief*|*First*|*Core*|*Apex*|*Crown*) echo "👑" ;;
73
+ *) echo "🐜" ;;
74
+ esac
75
+ }
76
+
77
+ # Colony header
78
+ echo -e "${BOLD}${MAGENTA}"
79
+ cat << 'EOF'
80
+ .-.
81
+ (o o) AETHER COLONY
82
+ | O | Activity Stream
83
+ `-`
84
+ EOF
85
+ echo -e "${RESET}"
86
+ echo -e "${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
87
+ echo ""
88
+
89
+ # Stream and colorize log entries
90
+ tail -f "$LOG_FILE" 2>/dev/null | while IFS= read -r line; do
91
+ # Extract caste color if ant name is in the line
92
+ caste_color=$(get_caste_color "$line")
93
+ emoji=$(get_emoji "$line")
94
+
95
+ case "$line" in
96
+ *SPAWN*)
97
+ printf "${SPAWN}⚡ %s${RESET}\n" "$line"
98
+ ;;
99
+ *COMPLETE*|*completed*)
100
+ printf "${COMPLETE}✅ %s${RESET}\n" "$line"
101
+ ;;
102
+ *ERROR*|*FAILED*|*failed*)
103
+ printf "${ERROR}${BOLD}❌ %s${RESET}\n" "$line"
104
+ ;;
105
+ *CREATED*)
106
+ printf "${caste_color}✨ %s${RESET}\n" "$line"
107
+ ;;
108
+ *MODIFIED*)
109
+ printf "${caste_color}📝 %s${RESET}\n" "$line"
110
+ ;;
111
+ *RESEARCH*|*EXPLORING*)
112
+ printf "${caste_color}🔬 %s${RESET}\n" "$line"
113
+ ;;
114
+ *EXECUTING*|*BUILDING*)
115
+ printf "${caste_color}⚙️ %s${RESET}\n" "$line"
116
+ ;;
117
+ "# Phase"*)
118
+ # Phase headers get special treatment
119
+ printf "\n${BOLD}${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n"
120
+ printf "${BOLD}${MAGENTA}🐜 %s${RESET}\n" "$line"
121
+ printf "${BOLD}${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}\n"
122
+ ;;
123
+ *)
124
+ # Apply caste color if detected, otherwise default
125
+ if [[ "$caste_color" != "$RESET" ]]; then
126
+ printf "${caste_color}%s %s${RESET}\n" "$emoji" "$line"
127
+ else
128
+ echo "$line"
129
+ fi
130
+ ;;
131
+ esac
132
+ done
@@ -0,0 +1,212 @@
1
+ #!/bin/bash
2
+ # Aether Colony Error Handler Module
3
+ # Structured JSON error handling for bash utilities
4
+ #
5
+ # Usage: source "$SCRIPT_DIR/utils/error-handler.sh"
6
+ #
7
+ # Provides consistent error format between Node.js CLI and bash utilities
8
+
9
+ # --- Error Code Constants (matching Node.js error codes) ---
10
+ E_UNKNOWN="E_UNKNOWN"
11
+ E_HUB_NOT_FOUND="E_HUB_NOT_FOUND"
12
+ E_REPO_NOT_INITIALIZED="E_REPO_NOT_INITIALIZED"
13
+ E_FILE_NOT_FOUND="E_FILE_NOT_FOUND"
14
+ E_JSON_INVALID="E_JSON_INVALID"
15
+ E_LOCK_FAILED="E_LOCK_FAILED"
16
+ E_LOCK_STALE="E_LOCK_STALE"
17
+ E_GIT_ERROR="E_GIT_ERROR"
18
+ E_VALIDATION_FAILED="E_VALIDATION_FAILED"
19
+ E_FEATURE_UNAVAILABLE="E_FEATURE_UNAVAILABLE"
20
+ E_BASH_ERROR="E_BASH_ERROR"
21
+ E_DEPENDENCY_MISSING="E_DEPENDENCY_MISSING"
22
+ E_RESOURCE_NOT_FOUND="E_RESOURCE_NOT_FOUND"
23
+
24
+ # --- Recovery Suggestion Functions (internal, prefixed with _) ---
25
+ _recovery_hub_not_found() { echo '"Run: aether install"'; }
26
+ _recovery_repo_not_init() { echo '"Run /ant:init in this repo first"'; }
27
+ _recovery_file_not_found() { echo '"Check file path and permissions"'; }
28
+ _recovery_json_invalid() { echo '"Validate JSON syntax"'; }
29
+ _recovery_lock_failed() { echo '"Wait for other operations to complete"'; }
30
+ _recovery_lock_stale() { echo '"Remove the stale lock file manually or run: aether force-unlock"'; }
31
+ _recovery_git_error() { echo '"Check git status and resolve conflicts"'; }
32
+ _recovery_default() { echo 'null'; }
33
+ _recovery_dependency_missing() { echo '"Install the required dependency"'; }
34
+ _recovery_resource_not_found() { echo '"Check that the resource exists and try again"'; }
35
+
36
+ # Get recovery suggestion based on error code
37
+ _get_recovery() {
38
+ local code="$1"
39
+ case "$code" in
40
+ "$E_HUB_NOT_FOUND") _recovery_hub_not_found ;;
41
+ "$E_REPO_NOT_INITIALIZED") _recovery_repo_not_init ;;
42
+ "$E_FILE_NOT_FOUND") _recovery_file_not_found ;;
43
+ "$E_JSON_INVALID") _recovery_json_invalid ;;
44
+ "$E_LOCK_FAILED") _recovery_lock_failed ;;
45
+ "$E_LOCK_STALE") _recovery_lock_stale ;;
46
+ "$E_GIT_ERROR") _recovery_git_error ;;
47
+ "$E_DEPENDENCY_MISSING") _recovery_dependency_missing ;;
48
+ "$E_RESOURCE_NOT_FOUND") _recovery_resource_not_found ;;
49
+ *) _recovery_default ;;
50
+ esac
51
+ }
52
+
53
+ # --- Enhanced json_err function ---
54
+ # Signature: json_err [code] [message] [details] [recovery]
55
+ # All parameters optional with sensible defaults
56
+ json_err() {
57
+ local code="${1:-$E_UNKNOWN}"
58
+ local message="${2:-An unknown error occurred}"
59
+ local details="${3:-null}"
60
+ local recovery="${4:-}"
61
+ local timestamp
62
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
63
+
64
+ # Get recovery suggestion if not provided
65
+ if [[ -z "$recovery" ]]; then
66
+ recovery=$(_get_recovery "$code")
67
+ else
68
+ # Escape and quote the recovery string
69
+ recovery=$(echo "$recovery" | sed 's/"/\\"/g' | tr '\n' ' ')
70
+ recovery="\"$recovery\""
71
+ fi
72
+
73
+ # Escape message for JSON
74
+ local escaped_message
75
+ escaped_message=$(echo "$message" | sed 's/"/\\"/g' | tr '\n' ' ')
76
+
77
+ # Build details JSON
78
+ local details_json
79
+ if [[ "$details" == "null" || -z "$details" ]]; then
80
+ details_json="null"
81
+ else
82
+ details_json="$details"
83
+ fi
84
+
85
+ # Output structured JSON to stderr
86
+ printf '{"ok":false,"error":{"code":"%s","message":"%s","details":%s,"recovery":%s,"timestamp":"%s"}}\n' \
87
+ "$code" "$escaped_message" "$details_json" "$recovery" "$timestamp" >&2
88
+
89
+ # Log to activity.log (best effort)
90
+ if [[ -n "${DATA_DIR:-}" ]]; then
91
+ echo "[$timestamp] ERROR $code: $escaped_message" >> "$DATA_DIR/activity.log" 2>/dev/null || true
92
+ fi
93
+
94
+ exit 1
95
+ }
96
+
97
+ # --- json_warn function for non-fatal warnings ---
98
+ # Signature: json_warn [code] [message]
99
+ json_warn() {
100
+ local code="${1:-W_UNKNOWN}"
101
+ local message="${2:-Warning}"
102
+ local timestamp
103
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
104
+
105
+ # Escape message for JSON
106
+ local escaped_message
107
+ escaped_message=$(echo "$message" | sed 's/"/\\"/g' | tr '\n' ' ')
108
+
109
+ # Output warning JSON to stdout (not stderr - this is non-fatal)
110
+ printf '{"ok":true,"warning":{"code":"%s","message":"%s","timestamp":"%s"}}\n' \
111
+ "$code" "$escaped_message" "$timestamp"
112
+
113
+ # Log to activity.log (best effort)
114
+ if [[ -n "${DATA_DIR:-}" ]]; then
115
+ echo "[$timestamp] WARN $code: $escaped_message" >> "$DATA_DIR/activity.log" 2>/dev/null || true
116
+ fi
117
+ }
118
+
119
+ # --- error_handler function for trap ERR ---
120
+ # Captures: line number, command, exit code
121
+ # Usage: trap 'error_handler ${LINENO} "$BASH_COMMAND" $?' ERR
122
+ error_handler() {
123
+ local line_num="${1:-unknown}"
124
+ local command="${2:-unknown}"
125
+ local exit_code="${3:-1}"
126
+ local timestamp
127
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
128
+
129
+ # Escape command for JSON
130
+ local escaped_command
131
+ escaped_command=$(echo "$command" | sed 's/"/\\"/g' | tr '\n' ' ')
132
+
133
+ # Build details JSON
134
+ local details
135
+ details="{\"line\":$line_num,\"command\":\"$escaped_command\",\"exit_code\":$exit_code}"
136
+
137
+ # Output structured JSON to stderr
138
+ printf '{"ok":false,"error":{"code":"%s","message":"Bash command failed","details":%s,"recovery":%s,"timestamp":"%s"}}\n' \
139
+ "$E_BASH_ERROR" "$details" "$(_recovery_default)" "$timestamp" >&2
140
+
141
+ # Log to activity.log (best effort)
142
+ if [[ -n "${DATA_DIR:-}" ]]; then
143
+ echo "[$timestamp] ERROR $E_BASH_ERROR: Command failed at line $line_num (exit $exit_code)" >> "$DATA_DIR/activity.log" 2>/dev/null || true
144
+ fi
145
+
146
+ exit 1
147
+ }
148
+
149
+ # --- Feature flag functions for graceful degradation ---
150
+ # Using simple variables for bash 3.2+ compatibility (no associative arrays)
151
+
152
+ # Track disabled features as colon-separated list: "feature1:reason1|feature2:reason2"
153
+ _FEATURES_DISABLED=""
154
+
155
+ # Enable a feature (remove from disabled list if present)
156
+ feature_enable() {
157
+ local name="$1"
158
+ # Remove from disabled list if present
159
+ _FEATURES_DISABLED=$(echo "$_FEATURES_DISABLED" | sed "s/:$name:[^|]*//g" | sed 's/^|//;s/|$//')
160
+ }
161
+
162
+ # Disable a feature with reason
163
+ feature_disable() {
164
+ local name="$1"
165
+ local reason="${2:-disabled}"
166
+ # Remove existing entry if present, then add new
167
+ _FEATURES_DISABLED=$(echo "$_FEATURES_DISABLED" | sed "s/:$name:[^|]*//g")
168
+ if [[ -z "$_FEATURES_DISABLED" ]]; then
169
+ _FEATURES_DISABLED=":$name:$reason"
170
+ else
171
+ _FEATURES_DISABLED="${_FEATURES_DISABLED}|:$name:$reason"
172
+ fi
173
+ }
174
+
175
+ # Check if feature is enabled (returns 0 if enabled, 1 if disabled)
176
+ feature_enabled() {
177
+ local name="$1"
178
+ if echo "$_FEATURES_DISABLED" | grep -q ":$name:"; then
179
+ return 1
180
+ fi
181
+ return 0
182
+ }
183
+
184
+ # Get reason for feature being disabled
185
+ _feature_reason() {
186
+ local name="$1"
187
+ echo "$_FEATURES_DISABLED" | grep -o ":$name:[^|]*" | sed "s/:$name://" || echo "unknown"
188
+ }
189
+
190
+ # Log degradation warning
191
+ feature_log_degradation() {
192
+ local name="$1"
193
+ local reason="${2:-}"
194
+ if [[ -z "$reason" ]]; then
195
+ reason=$(_feature_reason "$name")
196
+ fi
197
+ json_warn "W_DEGRADED" "Feature '$name' is disabled: $reason"
198
+ }
199
+
200
+ # --- Export all functions and variables ---
201
+ export -f json_err json_warn error_handler
202
+ export -f feature_enable feature_disable feature_enabled feature_log_degradation
203
+ export -f _get_recovery _recovery_hub_not_found _recovery_repo_not_init
204
+ export -f _recovery_file_not_found _recovery_json_invalid _recovery_lock_failed
205
+ export -f _recovery_lock_stale
206
+ export -f _recovery_git_error _recovery_default _feature_reason
207
+ export -f _recovery_dependency_missing _recovery_resource_not_found
208
+ export E_UNKNOWN E_HUB_NOT_FOUND E_REPO_NOT_INITIALIZED E_FILE_NOT_FOUND
209
+ export E_JSON_INVALID E_LOCK_FAILED E_LOCK_STALE E_GIT_ERROR E_VALIDATION_FAILED
210
+ export E_FEATURE_UNAVAILABLE E_BASH_ERROR
211
+ export E_DEPENDENCY_MISSING E_RESOURCE_NOT_FOUND
212
+ export _FEATURES_DISABLED