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,428 @@
1
+ #!/bin/bash
2
+ # Spawn Tree Reconstruction Module
3
+ # Parses spawn-tree.txt and provides tree traversal functions
4
+ #
5
+ # Usage: source .aether/utils/spawn-tree.sh
6
+ # All functions output JSON to stdout
7
+
8
+ # Data directory - can be overridden
9
+ SPAWN_TREE_DATA_DIR="${SPAWN_TREE_DATA_DIR:-.aether/data}"
10
+ SPAWN_TREE_FILE="${SPAWN_TREE_FILE:-$SPAWN_TREE_DATA_DIR/spawn-tree.txt}"
11
+
12
+ # Parse spawn-tree.txt into structured data
13
+ # Usage: parse_spawn_tree [file_path]
14
+ # Outputs: JSON representation of all spawns
15
+ parse_spawn_tree() {
16
+ local file_path="${1:-$SPAWN_TREE_FILE}"
17
+
18
+ # Check if file exists
19
+ if [[ ! -f "$file_path" ]]; then
20
+ echo '{"spawns":[],"metadata":{"total_count":0,"active_count":0,"completed_count":0,"file_exists":false}}'
21
+ return 0
22
+ fi
23
+
24
+ # Temporary files for data storage (Bash 3.2 compatible)
25
+ local tmpdir
26
+ tmpdir=$(mktemp -d)
27
+ local names_file="$tmpdir/names"
28
+ local parents_file="$tmpdir/parents"
29
+ local castes_file="$tmpdir/castes"
30
+ local tasks_file="$tmpdir/tasks"
31
+ local statuses_file="$tmpdir/statuses"
32
+ local timestamps_file="$tmpdir/timestamps"
33
+ local completed_file="$tmpdir/completed"
34
+ local children_file="$tmpdir/children"
35
+
36
+ touch "$names_file" "$parents_file" "$castes_file" "$tasks_file" "$statuses_file" "$timestamps_file" "$completed_file" "$children_file"
37
+
38
+ # Read file line by line
39
+ while IFS= read -r line || [[ -n "$line" ]]; do
40
+ # Skip empty lines
41
+ [[ -z "$line" ]] && continue
42
+
43
+ # Count pipe separators to determine line type
44
+ local pipe_count
45
+ pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
46
+
47
+ if [[ $pipe_count -eq 6 ]]; then
48
+ # Spawn event: timestamp|parent|caste|child_name|task|model|status
49
+ local timestamp parent caste child_name task model spawn_status
50
+ timestamp=$(echo "$line" | cut -d'|' -f1)
51
+ parent=$(echo "$line" | cut -d'|' -f2)
52
+ caste=$(echo "$line" | cut -d'|' -f3)
53
+ child_name=$(echo "$line" | cut -d'|' -f4)
54
+ task=$(echo "$line" | cut -d'|' -f5)
55
+ model=$(echo "$line" | cut -d'|' -f6)
56
+ spawn_status=$(echo "$line" | cut -d'|' -f7)
57
+
58
+ # Add to files
59
+ echo "$child_name" >> "$names_file"
60
+ echo "$parent" >> "$parents_file"
61
+ echo "$caste" >> "$castes_file"
62
+ echo "$task" >> "$tasks_file"
63
+ echo "$spawn_status" >> "$statuses_file"
64
+ echo "$timestamp" >> "$timestamps_file"
65
+ echo "" >> "$completed_file"
66
+ echo "" >> "$children_file"
67
+
68
+ elif [[ $pipe_count -eq 3 ]]; then
69
+ # Completion event: timestamp|ant_name|status|summary
70
+ local timestamp ant_name complete_status summary
71
+ timestamp=$(echo "$line" | cut -d'|' -f1)
72
+ ant_name=$(echo "$line" | cut -d'|' -f2)
73
+ complete_status=$(echo "$line" | cut -d'|' -f3)
74
+
75
+ # Find the ant and update its status
76
+ local idx=0
77
+ local found=0
78
+ while IFS= read -r name; do
79
+ if [[ "$name" == "$ant_name" ]]; then
80
+ found=1
81
+ break
82
+ fi
83
+ ((idx++))
84
+ done < "$names_file"
85
+
86
+ if [[ $found -eq 1 ]]; then
87
+ # Update status at line idx+1 (sed is 1-indexed)
88
+ sed -i.bak "${idx}d" "$statuses_file" 2>/dev/null || sed -i "${idx}d" "$statuses_file"
89
+ sed -i.bak "${idx}i\\
90
+ $complete_status" "$statuses_file" 2>/dev/null || sed -i "${idx}i\\
91
+ $complete_status" "$statuses_file"
92
+ sed -i.bak "${idx}d" "$completed_file" 2>/dev/null || sed -i "${idx}d" "$completed_file"
93
+ sed -i.bak "${idx}i\\
94
+ $timestamp" "$completed_file" 2>/dev/null || sed -i "${idx}i\\
95
+ $timestamp" "$completed_file"
96
+ rm -f "$statuses_file.bak" "$completed_file.bak" 2>/dev/null
97
+ fi
98
+ fi
99
+ done < "$file_path"
100
+
101
+ # Build parent-child relationships
102
+ local total_count
103
+ total_count=$(wc -l < "$names_file" | tr -d ' ')
104
+
105
+ if [[ $total_count -gt 0 ]]; then
106
+ local i
107
+ for ((i = 1; i <= total_count; i++)); do
108
+ local parent
109
+ parent=$(sed -n "${i}p" "$parents_file")
110
+
111
+ # Find parent index
112
+ local parent_idx=0
113
+ local found=0
114
+ while IFS= read -r name; do
115
+ if [[ "$name" == "$parent" ]]; then
116
+ found=1
117
+ break
118
+ fi
119
+ ((parent_idx++))
120
+ done < "$names_file"
121
+
122
+ if [[ $found -eq 1 ]]; then
123
+ # Add child i-1 to parent's children (0-indexed)
124
+ local child_idx=$((i - 1))
125
+ local current_children
126
+ current_children=$(sed -n "$((parent_idx + 1))p" "$children_file")
127
+ if [[ -z "$current_children" ]]; then
128
+ sed -i.bak "$((parent_idx + 1))d" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))d" "$children_file"
129
+ sed -i.bak "$((parent_idx + 1))i\\
130
+ $child_idx" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))i\\
131
+ $child_idx" "$children_file"
132
+ else
133
+ sed -i.bak "$((parent_idx + 1))d" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))d" "$children_file"
134
+ sed -i.bak "$((parent_idx + 1))i\\
135
+ $current_children $child_idx" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))i\\
136
+ $current_children $child_idx" "$children_file"
137
+ fi
138
+ rm -f "$children_file.bak" 2>/dev/null
139
+ fi
140
+ done
141
+ fi
142
+
143
+ # Count statuses
144
+ local active_count=0
145
+ local completed_count=0
146
+ while IFS= read -r status; do
147
+ if [[ "$status" == "active" || "$status" == "spawned" ]]; then
148
+ ((active_count++))
149
+ elif [[ "$status" == "completed" || "$status" == "failed" || "$status" == "blocked" ]]; then
150
+ ((completed_count++))
151
+ fi
152
+ done < "$statuses_file"
153
+
154
+ # Build spawns array
155
+ local spawns_json="["
156
+ local i
157
+ for ((i = 1; i <= total_count; i++)); do
158
+ if [[ $i -gt 1 ]]; then
159
+ spawns_json+=","
160
+ fi
161
+
162
+ local name parent caste task status timestamp completed children
163
+ name=$(sed -n "${i}p" "$names_file")
164
+ parent=$(sed -n "${i}p" "$parents_file")
165
+ caste=$(sed -n "${i}p" "$castes_file")
166
+ task=$(sed -n "${i}p" "$tasks_file")
167
+ status=$(sed -n "${i}p" "$statuses_file")
168
+ timestamp=$(sed -n "${i}p" "$timestamps_file")
169
+ completed=$(sed -n "${i}p" "$completed_file")
170
+ children=$(sed -n "${i}p" "$children_file")
171
+
172
+ # Build children array
173
+ local children_json="["
174
+ if [[ -n "$children" ]]; then
175
+ local first_child=true
176
+ for child_idx in $children; do
177
+ if [[ "$first_child" == "true" ]]; then
178
+ first_child=false
179
+ else
180
+ children_json+=","
181
+ fi
182
+ local child_name
183
+ child_name=$(sed -n "$((child_idx + 1))p" "$names_file")
184
+ children_json+="\"$child_name\""
185
+ done
186
+ fi
187
+ children_json+="]"
188
+
189
+ # Escape task for JSON
190
+ task=$(echo "$task" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
191
+
192
+ spawns_json+="{"
193
+ spawns_json+="\"name\":\"$name\","
194
+ spawns_json+="\"parent\":\"$parent\","
195
+ spawns_json+="\"caste\":\"$caste\","
196
+ spawns_json+="\"task\":\"$task\","
197
+ spawns_json+="\"status\":\"$status\","
198
+ spawns_json+="\"spawned_at\":\"$timestamp\","
199
+ spawns_json+="\"completed_at\":\"$completed\","
200
+ spawns_json+="\"children\":$children_json"
201
+ spawns_json+="}"
202
+ done
203
+ spawns_json+="]"
204
+
205
+ # Output JSON
206
+ echo "{"
207
+ echo " \"spawns\": $spawns_json,"
208
+ echo " \"metadata\": {"
209
+ echo " \"total_count\": $total_count,"
210
+ echo " \"active_count\": $active_count,"
211
+ echo " \"completed_count\": $completed_count,"
212
+ echo " \"file_exists\": true"
213
+ echo " }"
214
+ echo "}"
215
+
216
+ # Cleanup
217
+ rm -rf "$tmpdir"
218
+ }
219
+
220
+ # Get spawn depth for a given ant name
221
+ # Usage: get_spawn_depth <ant_name>
222
+ # Returns: JSON with ant name and depth
223
+ get_spawn_depth() {
224
+ local ant_name="${1:-}"
225
+
226
+ if [[ -z "$ant_name" || "$ant_name" == "Queen" ]]; then
227
+ echo "{\"ant\":\"${ant_name:-Queen}\",\"depth\":0}"
228
+ return 0
229
+ fi
230
+
231
+ local file_path="${SPAWN_TREE_FILE}"
232
+
233
+ if [[ ! -f "$file_path" ]]; then
234
+ echo "{\"ant\":\"$ant_name\",\"depth\":1,\"found\":false}"
235
+ return 0
236
+ fi
237
+
238
+ # Check if ant exists
239
+ if ! grep -q "|$ant_name|" "$file_path" 2>/dev/null; then
240
+ echo "{\"ant\":\"$ant_name\",\"depth\":1,\"found\":false}"
241
+ return 0
242
+ fi
243
+
244
+ # Calculate depth by traversing parent chain
245
+ local depth=1
246
+ local current="$ant_name"
247
+ local safety=0
248
+
249
+ while [[ $safety -lt 5 ]]; do
250
+ # Find who spawned this ant
251
+ local parent
252
+ parent=$(grep "|$current|" "$file_path" 2>/dev/null | grep "|spawned$" | head -1 | cut -d'|' -f2 || echo "")
253
+
254
+ if [[ -z "$parent" || "$parent" == "Queen" ]]; then
255
+ break
256
+ fi
257
+
258
+ ((depth++))
259
+ current="$parent"
260
+ ((safety++))
261
+ done
262
+
263
+ echo "{\"ant\":\"$ant_name\",\"depth\":$depth,\"found\":true}"
264
+ }
265
+
266
+ # Get list of active spawns
267
+ # Usage: get_active_spawns [file_path]
268
+ # Returns: JSON array of active spawns
269
+ get_active_spawns() {
270
+ local file_path="${1:-$SPAWN_TREE_FILE}"
271
+
272
+ if [[ ! -f "$file_path" ]]; then
273
+ echo "[]"
274
+ return 0
275
+ fi
276
+
277
+ local active_json="["
278
+ local first=true
279
+
280
+ # Read spawn events and check if completed
281
+ while IFS= read -r line || [[ -n "$line" ]]; do
282
+ [[ -z "$line" ]] && continue
283
+
284
+ local pipe_count
285
+ pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
286
+
287
+ if [[ $pipe_count -eq 6 ]]; then
288
+ # Spawn event: timestamp|parent|caste|child_name|task|model|status
289
+ local timestamp parent caste child_name task model spawn_status
290
+ timestamp=$(echo "$line" | cut -d'|' -f1)
291
+ parent=$(echo "$line" | cut -d'|' -f2)
292
+ caste=$(echo "$line" | cut -d'|' -f3)
293
+ child_name=$(echo "$line" | cut -d'|' -f4)
294
+ task=$(echo "$line" | cut -d'|' -f5)
295
+ model=$(echo "$line" | cut -d'|' -f6)
296
+
297
+ # Check if this ant has a completion event
298
+ local is_completed=false
299
+ if grep -q "^[^|]*|$child_name|completed\|^[^|]*|$child_name|failed\|^[^|]*|$child_name|blocked" "$file_path" 2>/dev/null; then
300
+ is_completed=true
301
+ fi
302
+
303
+ if [[ "$is_completed" == "false" ]]; then
304
+ if [[ "$first" == "true" ]]; then
305
+ first=false
306
+ else
307
+ active_json+=","
308
+ fi
309
+
310
+ # Escape task for JSON
311
+ task=$(echo "$task" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
312
+
313
+ active_json+="{"
314
+ active_json+="\"name\":\"$child_name\","
315
+ active_json+="\"caste\":\"$caste\","
316
+ active_json+="\"parent\":\"$parent\","
317
+ active_json+="\"task\":\"$task\","
318
+ active_json+="\"spawned_at\":\"$timestamp\""
319
+ active_json+="}"
320
+ fi
321
+ fi
322
+ done < "$file_path"
323
+
324
+ active_json+="]"
325
+ echo "$active_json"
326
+ }
327
+
328
+ # Get direct children of a spawn
329
+ # Usage: get_spawn_children <ant_name> [file_path]
330
+ # Returns: JSON array of child names
331
+ get_spawn_children() {
332
+ local ant_name="${1:-}"
333
+ local file_path="${2:-$SPAWN_TREE_FILE}"
334
+
335
+ if [[ -z "$ant_name" || ! -f "$file_path" ]]; then
336
+ echo "[]"
337
+ return 0
338
+ fi
339
+
340
+ local children_json="["
341
+ local first=true
342
+
343
+ # Find all spawns where parent matches
344
+ while IFS= read -r line || [[ -n "$line" ]]; do
345
+ [[ -z "$line" ]] && continue
346
+
347
+ local pipe_count
348
+ pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
349
+
350
+ if [[ $pipe_count -eq 6 ]]; then
351
+ local parent child_name
352
+ parent=$(echo "$line" | cut -d'|' -f2)
353
+ child_name=$(echo "$line" | cut -d'|' -f4)
354
+
355
+ if [[ "$parent" == "$ant_name" ]]; then
356
+ if [[ "$first" == "true" ]]; then
357
+ first=false
358
+ else
359
+ children_json+=","
360
+ fi
361
+ children_json+="\"$child_name\""
362
+ fi
363
+ fi
364
+ done < "$file_path"
365
+
366
+ children_json+="]"
367
+ echo "$children_json"
368
+ }
369
+
370
+ # Get full lineage from ant up to Queen
371
+ # Usage: get_spawn_lineage <ant_name> [file_path]
372
+ # Returns: JSON array from ant up to Queen (inclusive)
373
+ get_spawn_lineage() {
374
+ local ant_name="${1:-}"
375
+ local file_path="${2:-$SPAWN_TREE_FILE}"
376
+
377
+ if [[ -z "$ant_name" ]]; then
378
+ echo "[]"
379
+ return 0
380
+ fi
381
+
382
+ if [[ ! -f "$file_path" ]]; then
383
+ echo "[\"$ant_name\",\"Queen\"]"
384
+ return 0
385
+ fi
386
+
387
+ # Build lineage array (ant first, then ancestors)
388
+ local lineage=""
389
+ local current="$ant_name"
390
+ local safety=0
391
+
392
+ lineage="\"$current\""
393
+
394
+ while [[ $safety -lt 5 ]]; do
395
+ # Find who spawned this ant
396
+ local parent
397
+ parent=$(grep "|$current|" "$file_path" 2>/dev/null | grep "|spawned$" | head -1 | cut -d'|' -f2 || echo "")
398
+
399
+ if [[ -z "$parent" || "$parent" == "Queen" ]]; then
400
+ lineage+=",\"Queen\""
401
+ break
402
+ fi
403
+
404
+ lineage+=",\"$parent\""
405
+ current="$parent"
406
+ ((safety++))
407
+ done
408
+
409
+ echo "[$lineage]"
410
+ }
411
+
412
+ # Reconstruct full tree as JSON
413
+ # Usage: reconstruct_tree_json [file_path]
414
+ # Returns: Complete spawn tree with metadata
415
+ reconstruct_tree_json() {
416
+ local file_path="${1:-$SPAWN_TREE_FILE}"
417
+ parse_spawn_tree "$file_path"
418
+ }
419
+
420
+ # Export functions if being sourced (Bash 3.2 compatible)
421
+ if [[ "${BASH_SOURCE[0]:-}" != "${0}" ]]; then
422
+ export -f parse_spawn_tree 2>/dev/null || true
423
+ export -f get_spawn_depth 2>/dev/null || true
424
+ export -f get_active_spawns 2>/dev/null || true
425
+ export -f get_spawn_children 2>/dev/null || true
426
+ export -f get_spawn_lineage 2>/dev/null || true
427
+ export -f reconstruct_tree_json 2>/dev/null || true
428
+ fi
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+ # Aether Colony Spawn Helper with Model Assignment
3
+ # Usage: spawn-with-model.sh <caste> <task_description> [project_root]
4
+ #
5
+ # This script sets up the correct environment variables for model routing
6
+ # through the LiteLLM proxy before spawning Claude Code.
7
+
8
+ set -euo pipefail
9
+
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ AETHER_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
12
+
13
+ # Arguments
14
+ CASTE="${1:-}"
15
+ TASK="${2:-}"
16
+ PROJECT_ROOT="${3:-$AETHER_ROOT}"
17
+
18
+ # Validate arguments
19
+ [[ -z "$CASTE" ]] && { echo "Usage: spawn-with-model.sh <caste> <task_description> [project_root]" >&2; exit 1; }
20
+ [[ -z "$TASK" ]] && { echo "Usage: spawn-with-model.sh <caste> <task_description> [project_root]" >&2; exit 1; }
21
+
22
+ # Get model for this caste
23
+ model_info=$(bash "$AETHER_ROOT/.aether/aether-utils.sh" model-profile get "$CASTE" 2>/dev/null || echo '{"ok":true,"result":{"model":"kimi-k2.5"}}')
24
+ model=$(echo "$model_info" | jq -r '.result.model // "kimi-k2.5"')
25
+
26
+ # Log the spawn with model
27
+ ant_name=$(bash "$AETHER_ROOT/.aether/aether-utils.sh" generate-ant-name "$CASTE" 2>/dev/null || echo "${CASTE}-$(date +%s)")
28
+ bash "$AETHER_ROOT/.aether/aether-utils.sh" spawn-log "Queen" "$CASTE" "$ant_name" "$TASK" 2>/dev/null || true
29
+ bash "$AETHER_ROOT/.aether/aether-utils.sh" activity-log "SPAWN" "$ant_name ($CASTE)" "Model: $model - $TASK" 2>/dev/null || true
30
+
31
+ # Export environment for Claude Code
32
+ export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://localhost:4000}"
33
+ export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-sk-litellm-local}"
34
+ export ANTHROPIC_MODEL="$model"
35
+
36
+ echo "[$(date '+%H:%M:%S')] Spawning $ant_name ($CASTE) with model: $model"
37
+ echo " Task: $TASK"
38
+ echo " Project: $PROJECT_ROOT"
39
+
40
+ # Check proxy health
41
+ if curl -s http://localhost:4000/health | grep -q "healthy" 2>/dev/null; then
42
+ echo " Proxy: healthy"
43
+ else
44
+ echo " Proxy: unavailable (will use default routing)"
45
+ fi
46
+
47
+ # Start Claude Code with the environment set
48
+ # Note: This assumes claude is in PATH
49
+ if command -v claude &> /dev/null; then
50
+ claude --cwd "$PROJECT_ROOT"
51
+ else
52
+ echo "Claude Code not found in PATH. Environment prepared:"
53
+ echo " ANTHROPIC_BASE_URL=$ANTHROPIC_BASE_URL"
54
+ echo " ANTHROPIC_MODEL=$ANTHROPIC_MODEL"
55
+ exit 0
56
+ fi
@@ -0,0 +1,215 @@
1
+ #!/bin/bash
2
+ # Aether Colony State Loader
3
+ # Loads and validates COLONY_STATE.json with file lock protection
4
+ #
5
+ # Usage:
6
+ # source .aether/utils/state-loader.sh
7
+ # load_colony_state
8
+ # # ... use state ...
9
+ # unload_colony_state
10
+ #
11
+ # Provides: load_colony_state, unload_colony_state, get_handoff_summary, display_resumption_context
12
+
13
+ # Aether root detection - use git root if available, otherwise use current directory
14
+ if git rev-parse --show-toplevel >/dev/null 2>&1; then
15
+ AETHER_ROOT="$(git rev-parse --show-toplevel)"
16
+ else
17
+ AETHER_ROOT="$(pwd)"
18
+ fi
19
+
20
+ SCRIPT_DIR="${SCRIPT_DIR:-$AETHER_ROOT/.aether}"
21
+ DATA_DIR="$AETHER_ROOT/.aether/data"
22
+
23
+ # Initialize lock state before sourcing (file-lock.sh trap needs these)
24
+ LOCK_ACQUIRED=${LOCK_ACQUIRED:-false}
25
+ CURRENT_LOCK=${CURRENT_LOCK:-""}
26
+
27
+ # Source shared infrastructure if available
28
+ [[ -f "$SCRIPT_DIR/utils/file-lock.sh" ]] && source "$SCRIPT_DIR/utils/file-lock.sh"
29
+ [[ -f "$SCRIPT_DIR/utils/error-handler.sh" ]] && source "$SCRIPT_DIR/utils/error-handler.sh"
30
+
31
+ # State loading globals
32
+ LOADED_STATE=""
33
+ STATE_LOCK_ACQUIRED=false
34
+ HANDOFF_DETECTED=false
35
+ HANDOFF_CONTENT=""
36
+
37
+ # --- load_colony_state ---
38
+ # Main loading function that acquires lock, validates, and loads state
39
+ # Returns: 0 on success, 1 on failure
40
+ # Exports: LOADED_STATE, STATE_LOCK_ACQUIRED, HANDOFF_DETECTED, HANDOFF_CONTENT
41
+ load_colony_state() {
42
+ local state_file="$DATA_DIR/COLONY_STATE.json"
43
+
44
+ # Check file exists
45
+ if [[ ! -f "$state_file" ]]; then
46
+ if type json_err &>/dev/null; then
47
+ json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found" '{"file":"COLONY_STATE.json"}' "Run /ant:init to initialize colony"
48
+ else
49
+ echo '{"ok":false,"error":{"code":"E_FILE_NOT_FOUND","message":"COLONY_STATE.json not found"}}' >&2
50
+ fi
51
+ return 1
52
+ fi
53
+
54
+ # Acquire lock
55
+ if type acquire_lock &>/dev/null; then
56
+ if ! acquire_lock "$state_file"; then
57
+ if type json_err &>/dev/null; then
58
+ json_err "$E_LOCK_FAILED" "Failed to acquire state lock" '{"file":"COLONY_STATE.json"}' "Wait for other operations to complete"
59
+ else
60
+ echo '{"ok":false,"error":{"code":"E_LOCK_FAILED","message":"Failed to acquire state lock"}}' >&2
61
+ fi
62
+ return 1
63
+ fi
64
+ else
65
+ # Locking unavailable - proceed with warning
66
+ if type json_warn &>/dev/null; then
67
+ json_warn "W_DEGRADED" "File locking unavailable - proceeding without lock"
68
+ fi
69
+ fi
70
+
71
+ # Validate state before loading
72
+ local validation
73
+ validation=$(bash "$SCRIPT_DIR/aether-utils.sh" validate-state colony 2>/dev/null)
74
+ if ! echo "$validation" | jq -e '.result.pass' >/dev/null 2>&1; then
75
+ # Validation failed - release lock and report error
76
+ if type release_lock &>/dev/null; then
77
+ release_lock
78
+ fi
79
+
80
+ local validation_error
81
+ validation_error=$(echo "$validation" | jq -r '.result.checks[] | select(. != "pass") | .' 2>/dev/null | head -1 || echo "unknown validation error")
82
+
83
+ if type json_err &>/dev/null; then
84
+ json_err "$E_VALIDATION_FAILED" "State validation failed" "{\"details\":\"$validation_error\"}" "Check COLONY_STATE.json for errors or run /ant:init to reset"
85
+ else
86
+ echo "{\"ok\":false,\"error\":{\"code\":\"E_VALIDATION_FAILED\",\"message\":\"State validation failed: $validation_error\"}}" >&2
87
+ fi
88
+ return 1
89
+ fi
90
+
91
+ # Read state into variable
92
+ local state
93
+ state=$(cat "$state_file")
94
+ if [[ -z "$state" ]]; then
95
+ if type release_lock &>/dev/null; then
96
+ release_lock
97
+ fi
98
+ if type json_err &>/dev/null; then
99
+ json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json is empty" '{"file":"COLONY_STATE.json"}' "Run /ant:init to initialize colony"
100
+ else
101
+ echo '{"ok":false,"error":{"code":"E_FILE_NOT_FOUND","message":"COLONY_STATE.json is empty"}}' >&2
102
+ fi
103
+ return 1
104
+ fi
105
+
106
+ # Check for handoff document
107
+ local handoff_file="$AETHER_ROOT/.aether/HANDOFF.md"
108
+ if [[ -f "$handoff_file" ]]; then
109
+ HANDOFF_DETECTED=true
110
+ HANDOFF_CONTENT=$(cat "$handoff_file")
111
+ else
112
+ HANDOFF_DETECTED=false
113
+ HANDOFF_CONTENT=""
114
+ fi
115
+
116
+ # Export loaded state
117
+ LOADED_STATE="$state"
118
+ STATE_LOCK_ACQUIRED=true
119
+
120
+ return 0
121
+ }
122
+
123
+ # --- unload_colony_state ---
124
+ # Cleanup function that releases lock and unsets state
125
+ # Should be called when done with state
126
+ unload_colony_state() {
127
+ if [[ "$STATE_LOCK_ACQUIRED" == "true" ]]; then
128
+ if type release_lock &>/dev/null; then
129
+ release_lock
130
+ fi
131
+ STATE_LOCK_ACQUIRED=false
132
+ fi
133
+ LOADED_STATE=""
134
+ HANDOFF_DETECTED=false
135
+ HANDOFF_CONTENT=""
136
+ }
137
+
138
+ # --- get_handoff_summary ---
139
+ # Extract brief summary from handoff content
140
+ # Returns: "Phase X - Name" format or empty string
141
+ get_handoff_summary() {
142
+ if [[ "$HANDOFF_DETECTED" != "true" || -z "$HANDOFF_CONTENT" ]]; then
143
+ echo ""
144
+ return 1
145
+ fi
146
+
147
+ # Parse HANDOFF.md for Phase line
148
+ # Format: "Phase: X - Name" or "## Phase X: Name"
149
+ local phase_line
150
+ phase_line=$(echo "$HANDOFF_CONTENT" | grep -E "^(Phase:|## Phase)" | head -1)
151
+
152
+ if [[ -n "$phase_line" ]]; then
153
+ # Extract phase number and name
154
+ local phase_num
155
+ local phase_name
156
+ phase_num=$(echo "$phase_line" | grep -oE "[0-9]+" | head -1)
157
+ phase_name=$(echo "$phase_line" | sed -E 's/.*Phase:?[[:space:]]*[0-9]+[[:space:]-]+//' | sed 's/^[[:space:]]*//')
158
+
159
+ if [[ -n "$phase_num" && -n "$phase_name" ]]; then
160
+ echo "Phase $phase_num - $phase_name"
161
+ elif [[ -n "$phase_num" ]]; then
162
+ echo "Phase $phase_num"
163
+ else
164
+ echo "Resuming colony session"
165
+ fi
166
+ else
167
+ echo "Resuming colony session"
168
+ fi
169
+ }
170
+
171
+ # --- display_resumption_context ---
172
+ # Show brief resume message and clean up handoff
173
+ # Returns: 0 on success
174
+ display_resumption_context() {
175
+ if [[ "$HANDOFF_DETECTED" != "true" ]]; then
176
+ return 0
177
+ fi
178
+
179
+ local summary
180
+ summary=$(get_handoff_summary)
181
+
182
+ if [[ -n "$summary" ]]; then
183
+ echo "Resuming: $summary"
184
+ fi
185
+
186
+ # Remove handoff file after successful display
187
+ local handoff_file="$AETHER_ROOT/.aether/HANDOFF.md"
188
+ if [[ -f "$handoff_file" ]]; then
189
+ rm -f "$handoff_file"
190
+ fi
191
+
192
+ HANDOFF_DETECTED=false
193
+ HANDOFF_CONTENT=""
194
+
195
+ return 0
196
+ }
197
+
198
+ # --- ensure_cleanup ---
199
+ # Internal function to ensure lock is released on script exit
200
+ _ensure_cleanup() {
201
+ if [[ "$STATE_LOCK_ACQUIRED" == "true" ]]; then
202
+ unload_colony_state
203
+ fi
204
+ }
205
+
206
+ # Register cleanup on exit
207
+ # Note: This works alongside file-lock.sh's trap
208
+ _original_trap_exit() {
209
+ _ensure_cleanup
210
+ }
211
+
212
+ # Export functions for use in other scripts
213
+ export -f load_colony_state unload_colony_state get_handoff_summary display_resumption_context
214
+ export -f _ensure_cleanup _original_trap_exit
215
+ export LOADED_STATE STATE_LOCK_ACQUIRED HANDOFF_DETECTED HANDOFF_CONTENT