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,273 @@
1
+ #!/bin/bash
2
+ # Colony Registry Exchange Module
3
+ # JSON/XML bidirectional conversion for colony registry
4
+ #
5
+ # Usage: source .aether/exchange/registry-xml.sh
6
+ # xml-registry-export <registry_json> [output_xml]
7
+ # xml-registry-import <registry_xml> [output_json]
8
+ # xml-registry-lineage <registry_json> <colony_id>
9
+
10
+ # Source dependencies - handle being sourced vs executed
11
+ if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13
+ else
14
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
15
+ fi
16
+ source "$SCRIPT_DIR/utils/xml-core.sh"
17
+
18
+ # Ensure tool availability variables are available
19
+ if [[ -z "${XMLLINT_AVAILABLE:-}" ]]; then
20
+ XMLLINT_AVAILABLE=false
21
+ command -v xmllint > /dev/null 2>&1 && XMLLINT_AVAILABLE=true
22
+ fi
23
+ if [[ -z "${XMLSTARLET_AVAILABLE:-}" ]]; then
24
+ XMLSTARLET_AVAILABLE=false
25
+ command -v xmlstarlet > /dev/null 2>&1 && XMLSTARLET_AVAILABLE=true
26
+ fi
27
+
28
+ # ============================================================================
29
+ # Registry Export (JSON to XML)
30
+ # ============================================================================
31
+
32
+ # xml-registry-export: Convert colony registry JSON to XML
33
+ # Usage: xml-registry-export <registry_json_file> [output_xml_file]
34
+ # Returns: {"ok":true,"result":{"path":"...","colonies":N}}
35
+ xml-registry-export() {
36
+ local json_file="${1:-}"
37
+ local output_xml="${2:-}"
38
+
39
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
40
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
41
+
42
+ # Validate JSON
43
+ if ! jq empty "$json_file" 2>/dev/null; then
44
+ xml_json_err "PARSE_ERROR" "Invalid JSON file: $json_file"
45
+ return 1
46
+ fi
47
+
48
+ local version generated_at
49
+ version=$(jq -r '.version // "1.0.0"' "$json_file")
50
+ generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
51
+
52
+ # Build XML
53
+ local xml_output
54
+ xml_output="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
55
+ <colony-registry xmlns=\"http://aether.colony/schemas/registry/1.0\"
56
+ version=\"$version\"
57
+ generated_at=\"$generated_at\">"
58
+
59
+ # Process colonies
60
+ local colony_count
61
+ colony_count=$(jq '.colonies | length' "$json_file" 2>/dev/null || echo "0")
62
+
63
+ local idx=0
64
+ while [[ $idx -lt $colony_count ]]; do
65
+ local colony
66
+ colony=$(jq -c ".colonies[$idx]" "$json_file")
67
+
68
+ local id name created_at status parent_id
69
+ id=$(echo "$colony" | jq -r '.id' | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g')
70
+ name=$(echo "$colony" | jq -r '.name // "Unnamed Colony"')
71
+ created_at=$(echo "$colony" | jq -r '.created_at // "'"$generated_at"'"')
72
+ status=$(echo "$colony" | jq -r '.status // "active"')
73
+ parent_id=$(echo "$colony" | jq -r '.parent_id // empty')
74
+
75
+ xml_output="$xml_output
76
+ <colony id=\"$id\" status=\"$status\" created_at=\"$created_at\">"
77
+
78
+ [[ -n "$parent_id" && "$parent_id" != "null" ]] && \
79
+ xml_output="$xml_output
80
+ <parent_id>$parent_id</parent_id>"
81
+
82
+ xml_output="$xml_output
83
+ <name>$(echo "$name" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')</name>"
84
+
85
+ # Add lineage if present
86
+ local ancestors
87
+ ancestors=$(echo "$colony" | jq -c '.ancestors // []')
88
+ if [[ "$ancestors" != "[]" && -n "$ancestors" ]]; then
89
+ xml_output="$xml_output
90
+ <lineage>"
91
+ local anc_count anc_idx
92
+ anc_count=$(echo "$ancestors" | jq 'length')
93
+ anc_idx=0
94
+ while [[ $anc_idx -lt $anc_count ]]; do
95
+ local ancestor_id
96
+ ancestor_id=$(echo "$ancestors" | jq -r ".[$anc_idx]")
97
+ xml_output="$xml_output
98
+ <ancestor id=\"$ancestor_id\"/>
99
+ "
100
+ ((anc_idx++))
101
+ done
102
+ xml_output="$xml_output
103
+ </lineage>"
104
+ fi
105
+
106
+ xml_output="$xml_output
107
+ </colony>"
108
+ ((idx++))
109
+ done
110
+
111
+ xml_output="$xml_output
112
+ </colony-registry>"
113
+
114
+ # Output
115
+ if [[ -n "$output_xml" ]]; then
116
+ echo "$xml_output" > "$output_xml"
117
+ xml_json_ok "{\"path\":\"$output_xml\",\"colonies\":$colony_count}"
118
+ else
119
+ local escaped
120
+ escaped=$(echo "$xml_output" | jq -Rs '.')
121
+ xml_json_ok "{\"xml\":$escaped,\"colonies\":$colony_count}"
122
+ fi
123
+ }
124
+
125
+ # ============================================================================
126
+ # Registry Import (XML to JSON) - Round-Trip
127
+ # ============================================================================
128
+
129
+ # xml-registry-import: Convert colony registry XML to JSON
130
+ # Usage: xml-registry-import <registry_xml_file> [output_json_file]
131
+ # Returns: {"ok":true,"result":{"colonies":N,"path":"..."}}
132
+ xml-registry-import() {
133
+ local xml_file="${1:-}"
134
+ local output_json="${2:-}"
135
+
136
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
137
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
138
+
139
+ # Check well-formedness
140
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
141
+ xmllint --nonet --noent --noout "$xml_file" 2>/dev/null || {
142
+ xml_json_err "PARSE_ERROR" "XML is not well-formed"
143
+ return 1
144
+ }
145
+ fi
146
+
147
+ # Extract metadata
148
+ local version generated_at
149
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
150
+ version=$(xmllint --nonet --noent --xpath "string(/*/@version)" "$xml_file" 2>/dev/null || echo "1.0.0")
151
+ generated_at=$(xmllint --nonet --noent --xpath "string(/*/@generated_at)" "$xml_file" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
152
+ else
153
+ version="1.0.0"
154
+ generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
155
+ fi
156
+
157
+ # Build JSON structure
158
+ local json_output
159
+ json_output=$(jq -n \
160
+ --arg version "$version" \
161
+ --arg generated_at "$generated_at" \
162
+ '{
163
+ version: $version,
164
+ generated_at: $generated_at,
165
+ colonies: []
166
+ }')
167
+
168
+ local colony_count=0
169
+
170
+ # Extract colonies using xmlstarlet if available
171
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
172
+ local colony_array
173
+ # Note: xmlstarlet sel returns exit 1 when no nodes match.
174
+ # With pipefail, the pipeline exit code reflects xmlstarlet failure even if jq succeeds.
175
+ # Use a subshell with set +e to safely capture the output regardless of xmlstarlet exit code.
176
+ colony_array=$(set +e; xmlstarlet sel -t -m "//colony" \
177
+ -o '{"id":"' -v "@id" -o '","name":"' -v "name" -o '","status":"' -v "@status" -o '","created_at":"' -v "@created_at" -o '"}' \
178
+ -n "$xml_file" 2>/dev/null | jq -s '.' 2>/dev/null; true) || colony_array='[]'
179
+ [[ -z "$colony_array" || "$colony_array" == "null" ]] && colony_array='[]'
180
+
181
+ colony_count=$(echo "$colony_array" | jq 'length')
182
+ if [[ $colony_count -gt 0 ]]; then
183
+ json_output=$(echo "$json_output" | jq --argjson colonies "$colony_array" '.colonies = $colonies')
184
+ fi
185
+ fi
186
+
187
+ # Output
188
+ if [[ -n "$output_json" ]]; then
189
+ echo "$json_output" > "$output_json"
190
+ xml_json_ok "{\"path\":\"$output_json\",\"colonies\":$colony_count}"
191
+ else
192
+ local escaped
193
+ escaped=$(echo "$json_output" | jq -Rs '.')
194
+ xml_json_ok "{\"json\":$escaped,\"colonies\":$colony_count}"
195
+ fi
196
+ }
197
+
198
+ # ============================================================================
199
+ # Registry Validation
200
+ # ============================================================================
201
+
202
+ # xml-registry-validate: Validate registry XML against schema
203
+ # Usage: xml-registry-validate <registry_xml> [xsd_schema]
204
+ xml-registry-validate() {
205
+ local xml_file="${1:-}"
206
+ local xsd_file="${2:-.aether/schemas/colony-registry.xsd}"
207
+
208
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
209
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
210
+
211
+ if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
212
+ xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for validation"
213
+ return 1
214
+ fi
215
+
216
+ if [[ ! -f "$xsd_file" ]]; then
217
+ xml_json_ok '{"valid":true,"warning":"Schema not found, skipping validation","schema":"'$xsd_file'"}'
218
+ return 0
219
+ fi
220
+
221
+ xmllint --nonet --noent --noout --schema "$xsd_file" "$xml_file" 2>/dev/null && {
222
+ xml_json_ok '{"valid":true}'
223
+ } || {
224
+ xml_json_ok '{"valid":false}'
225
+ }
226
+ }
227
+
228
+ # ============================================================================
229
+ # Lineage Queries
230
+ # ============================================================================
231
+
232
+ # xml-registry-lineage: Get ancestry chain for a colony
233
+ # Usage: xml-registry-lineage <registry_json> <colony_id>
234
+ # Returns: {"ok":true,"result":{"colony_id":"...","ancestors":[...],"depth":N}}
235
+ xml-registry-lineage() {
236
+ local json_file="${1:-}"
237
+ local colony_id="${2:-}"
238
+
239
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
240
+ [[ -z "$colony_id" ]] && { xml_json_err "MISSING_ARG" "Missing colony ID argument"; return 1; }
241
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
242
+
243
+ # Find the colony
244
+ local colony
245
+ colony=$(jq --arg id "$colony_id" '.colonies[] | select(.id == $id)' "$json_file")
246
+
247
+ if [[ -z "$colony" || "$colony" == "null" ]]; then
248
+ xml_json_err "COLONY_NOT_FOUND" "Colony not found in registry: $colony_id"
249
+ return 1
250
+ fi
251
+
252
+ # Build ancestry chain
253
+ local ancestors="[]"
254
+ local current_id="$colony_id"
255
+ local depth=0
256
+ local max_depth=10 # Prevent infinite loops
257
+
258
+ while [[ $depth -lt $max_depth ]]; do
259
+ local parent_id
260
+ parent_id=$(jq --arg id "$current_id" -r '.colonies[] | select(.id == $id) | .parent_id // empty' "$json_file")
261
+
262
+ [[ -z "$parent_id" ]] && break
263
+
264
+ ancestors=$(echo "$ancestors" | jq --arg parent "$parent_id" '. + [$parent]')
265
+ current_id="$parent_id"
266
+ ((depth++))
267
+ done
268
+
269
+ xml_json_ok "{\"colony_id\":\"$colony_id\",\"ancestors\":$ancestors,\"depth\":$depth}"
270
+ }
271
+
272
+ # Export functions
273
+ export -f xml-registry-export xml-registry-import xml-registry-validate xml-registry-lineage
@@ -0,0 +1,319 @@
1
+ #!/bin/bash
2
+ # Queen Wisdom Exchange Module
3
+ # JSON/XML bidirectional conversion for wisdom entries
4
+ #
5
+ # Usage: source .aether/exchange/wisdom-xml.sh
6
+ # xml-wisdom-export <wisdom_json> [output_xml]
7
+ # xml-wisdom-import <wisdom_xml> [output_json]
8
+ # xml-wisdom-promote <entry_id>
9
+
10
+ # Source dependencies - handle being sourced vs executed
11
+ if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13
+ else
14
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
15
+ fi
16
+ source "$SCRIPT_DIR/utils/xml-core.sh"
17
+
18
+ # Ensure tool availability variables are available
19
+ if [[ -z "${XMLLINT_AVAILABLE:-}" ]]; then
20
+ XMLLINT_AVAILABLE=false
21
+ command -v xmllint > /dev/null 2>&1 && XMLLINT_AVAILABLE=true
22
+ fi
23
+ if [[ -z "${XMLSTARLET_AVAILABLE:-}" ]]; then
24
+ XMLSTARLET_AVAILABLE=false
25
+ command -v xmlstarlet > /dev/null 2>&1 && XMLSTARLET_AVAILABLE=true
26
+ fi
27
+
28
+ # Default promotion threshold (confidence level required)
29
+ PROMOTION_THRESHOLD=0.8
30
+
31
+ # ============================================================================
32
+ # Wisdom Export (JSON to XML)
33
+ # ============================================================================
34
+
35
+ # xml-wisdom-export: Convert queen-wisdom JSON to XML
36
+ # Usage: xml-wisdom-export <wisdom_json_file> [output_xml_file]
37
+ # Returns: {"ok":true,"result":{"path":"...","entries":N}}
38
+ xml-wisdom-export() {
39
+ local json_file="${1:-}"
40
+ local output_xml="${2:-}"
41
+
42
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
43
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
44
+
45
+ # Validate JSON
46
+ if ! jq empty "$json_file" 2>/dev/null; then
47
+ xml_json_err "PARSE_ERROR" "Invalid JSON file: $json_file"
48
+ return 1
49
+ fi
50
+
51
+ local version created modified colony_id
52
+ version=$(jq -r '.version // "1.0.0"' "$json_file")
53
+ created=$(jq -r '.metadata.created // "'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"' "$json_file" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
54
+ modified=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
55
+ colony_id=$(jq -r '.metadata.colony_id // "unknown"' "$json_file" 2>/dev/null || echo "unknown")
56
+
57
+ # Build XML
58
+ local xml_output
59
+ xml_output="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
60
+ <queen-wisdom xmlns=\"http://aether.colony/schemas/queen-wisdom/1.0\"
61
+ xmlns:qw=\"http://aether.colony/schemas/queen-wisdom/1.0\">
62
+ <metadata>
63
+ <version>$version</version>
64
+ <created>$created</created>
65
+ <modified>$modified</modified>
66
+ <colony_id>$colony_id</colony_id>
67
+ </metadata>"
68
+
69
+ # Process philosophies
70
+ xml_output="$xml_output
71
+ <philosophies>"
72
+
73
+ local phil_count
74
+ phil_count=$(jq '.philosophies | length' "$json_file" 2>/dev/null || echo "0")
75
+ local idx=0
76
+ while [[ $idx -lt $phil_count ]]; do
77
+ local entry
78
+ entry=$(jq -c ".philosophies[$idx]" "$json_file")
79
+ local id confidence domain source content
80
+ id=$(echo "$entry" | jq -r '.id')
81
+ confidence=$(echo "$entry" | jq -r '.confidence // "0.5"')
82
+ domain=$(echo "$entry" | jq -r '.domain // "general"')
83
+ source=$(echo "$entry" | jq -r '.source // "observation"')
84
+ content=$(echo "$entry" | jq -r '.content // ""' | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
85
+
86
+ xml_output="$xml_output
87
+ <philosophy id=\"$id\" confidence=\"$confidence\" domain=\"$domain\" source=\"$source\" created_at=\"$modified\">
88
+ <content>$content</content>
89
+ </philosophy>"
90
+ ((idx++))
91
+ done
92
+
93
+ xml_output="$xml_output
94
+ </philosophies>"
95
+
96
+ # Process patterns
97
+ xml_output="$xml_output
98
+ <patterns>"
99
+
100
+ local pattern_count
101
+ pattern_count=$(jq '.patterns | length' "$json_file" 2>/dev/null || echo "0")
102
+ idx=0
103
+ while [[ $idx -lt $pattern_count ]]; do
104
+ local entry
105
+ entry=$(jq -c ".patterns[$idx]" "$json_file")
106
+ local id confidence domain source
107
+ id=$(echo "$entry" | jq -r '.id')
108
+ confidence=$(echo "$entry" | jq -r '.confidence // "0.5"')
109
+ domain=$(echo "$entry" | jq -r '.domain // "general"')
110
+ source=$(echo "$entry" | jq -r '.source // "observation"')
111
+
112
+ xml_output="$xml_output
113
+ <pattern id=\"$id\" confidence=\"$confidence\" domain=\"$domain\" source=\"$source\" created_at=\"$modified\">
114
+ <content>$(echo "$entry" | jq -r '.content // ""' | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')</content>
115
+ </pattern>"
116
+ ((idx++))
117
+ done
118
+
119
+ xml_output="$xml_output
120
+ </patterns>
121
+ </queen-wisdom>"
122
+
123
+ local entry_count=$((phil_count + pattern_count))
124
+
125
+ # Output
126
+ if [[ -n "$output_xml" ]]; then
127
+ echo "$xml_output" > "$output_xml"
128
+ xml_json_ok "{\"path\":\"$output_xml\",\"entries\":$entry_count}"
129
+ else
130
+ local escaped
131
+ escaped=$(echo "$xml_output" | jq -Rs '.')
132
+ xml_json_ok "{\"xml\":$escaped,\"entries\":$entry_count}"
133
+ fi
134
+ }
135
+
136
+ # ============================================================================
137
+ # Wisdom Import (XML to JSON) - Round-Trip
138
+ # ============================================================================
139
+
140
+ # xml-wisdom-import: Convert queen-wisdom XML to JSON
141
+ # Usage: xml-wisdom-import <wisdom_xml_file> [output_json_file]
142
+ # Returns: {"ok":true,"result":{"entries":N,"path":"..."}}
143
+ xml-wisdom-import() {
144
+ local xml_file="${1:-}"
145
+ local output_json="${2:-}"
146
+
147
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
148
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
149
+
150
+ # Check well-formedness
151
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
152
+ xmllint --nonet --noent --noout "$xml_file" 2>/dev/null || {
153
+ xml_json_err "PARSE_ERROR" "XML is not well-formed"
154
+ return 1
155
+ }
156
+ fi
157
+
158
+ # Extract metadata
159
+ local version created modified colony_id
160
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
161
+ version=$(xmllint --nonet --noent --xpath "string(/*/metadata/version)" "$xml_file" 2>/dev/null || echo "1.0.0")
162
+ created=$(xmllint --nonet --noent --xpath "string(/*/metadata/created)" "$xml_file" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
163
+ modified=$(xmllint --nonet --noent --xpath "string(/*/metadata/modified)" "$xml_file" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ")
164
+ colony_id=$(xmllint --nonet --noent --xpath "string(/*/metadata/colony_id)" "$xml_file" 2>/dev/null || echo "unknown")
165
+ else
166
+ version="1.0.0"
167
+ created=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
168
+ modified="$created"
169
+ colony_id="unknown"
170
+ fi
171
+
172
+ # Build JSON structure
173
+ local json_output
174
+ json_output=$(jq -n \
175
+ --arg version "$version" \
176
+ --arg created "$created" \
177
+ --arg modified "$modified" \
178
+ --arg colony_id "$colony_id" \
179
+ '{
180
+ version: $version,
181
+ metadata: {
182
+ created: $created,
183
+ modified: $modified,
184
+ colony_id: $colony_id
185
+ },
186
+ philosophies: [],
187
+ patterns: [],
188
+ redirects: [],
189
+ stack_wisdom: [],
190
+ decrees: []
191
+ }')
192
+
193
+ local entry_count=0
194
+
195
+ # Extract philosophies using xmlstarlet if available
196
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
197
+ local phil_array
198
+ # Note: xmlstarlet sel returns exit 1 when no nodes match.
199
+ # With pipefail, the pipeline exit code reflects xmlstarlet failure even if jq succeeds.
200
+ # Use set +e in subshell to safely capture output regardless of xmlstarlet exit code.
201
+ phil_array=$(set +e; xmlstarlet sel -t -m "//philosophy" \
202
+ -o '{"id":"' -v "@id" -o '","confidence":' -v "@confidence" -o ',"domain":"' -v "@domain" -o '","source":"' -v "@source" -o '","content":"' -v "content" -o '"}' \
203
+ -n "$xml_file" 2>/dev/null | jq -s '.' 2>/dev/null; true) || phil_array='[]'
204
+ [[ -z "$phil_array" || "$phil_array" == "null" ]] && phil_array='[]'
205
+
206
+ local phil_count
207
+ phil_count=$(echo "$phil_array" | jq 'length')
208
+ if [[ $phil_count -gt 0 ]]; then
209
+ json_output=$(echo "$json_output" | jq --argjson philosophies "$phil_array" '.philosophies = $philosophies')
210
+ entry_count=$((entry_count + phil_count))
211
+ fi
212
+
213
+ # Extract patterns
214
+ local pattern_array
215
+ # Note: xmlstarlet sel returns exit 1 when no nodes match.
216
+ # Use set +e in subshell to safely capture output regardless of xmlstarlet exit code.
217
+ pattern_array=$(set +e; xmlstarlet sel -t -m "//pattern" \
218
+ -o '{"id":"' -v "@id" -o '","confidence":' -v "@confidence" -o ',"domain":"' -v "@domain" -o '"}' \
219
+ -n "$xml_file" 2>/dev/null | jq -s '.' 2>/dev/null; true) || pattern_array='[]'
220
+ [[ -z "$pattern_array" || "$pattern_array" == "null" ]] && pattern_array='[]'
221
+
222
+ local pattern_count
223
+ pattern_count=$(echo "$pattern_array" | jq 'length')
224
+ if [[ $pattern_count -gt 0 ]]; then
225
+ json_output=$(echo "$json_output" | jq --argjson patterns "$pattern_array" '.patterns = $patterns')
226
+ entry_count=$((entry_count + pattern_count))
227
+ fi
228
+ fi
229
+
230
+ # Output
231
+ if [[ -n "$output_json" ]]; then
232
+ echo "$json_output" > "$output_json"
233
+ xml_json_ok "{\"path\":\"$output_json\",\"entries\":$entry_count}"
234
+ else
235
+ local escaped
236
+ escaped=$(echo "$json_output" | jq -Rs '.')
237
+ xml_json_ok "{\"json\":$escaped,\"entries\":$entry_count}"
238
+ fi
239
+ }
240
+
241
+ # ============================================================================
242
+ # Wisdom Validation
243
+ # ============================================================================
244
+
245
+ # xml-wisdom-validate: Validate wisdom XML against schema
246
+ # Usage: xml-wisdom-validate <wisdom_xml> [xsd_schema]
247
+ xml-wisdom-validate() {
248
+ local xml_file="${1:-}"
249
+ local xsd_file="${2:-.aether/schemas/queen-wisdom.xsd}"
250
+
251
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
252
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
253
+
254
+ if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
255
+ xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for validation"
256
+ return 1
257
+ fi
258
+
259
+ if [[ ! -f "$xsd_file" ]]; then
260
+ xml_json_ok '{"valid":true,"warning":"Schema not found, skipping validation","schema":"'$xsd_file'"}'
261
+ return 0
262
+ fi
263
+
264
+ xmllint --nonet --noent --noout --schema "$xsd_file" "$xml_file" 2>/dev/null && {
265
+ xml_json_ok '{"valid":true}'
266
+ } || {
267
+ xml_json_ok '{"valid":false}'
268
+ }
269
+ }
270
+
271
+ # ============================================================================
272
+ # Wisdom Promotion
273
+ # ============================================================================
274
+
275
+ # xml-wisdom-promote: Promote a pattern to philosophy if confidence threshold met
276
+ # Usage: xml-wisdom-promote <wisdom_json> <entry_id>
277
+ # Returns: {"ok":true,"result":{"promoted":true,"new_domain":"philosophy"}}
278
+ xml-wisdom-promote() {
279
+ local json_file="${1:-}"
280
+ local entry_id="${2:-}"
281
+
282
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
283
+ [[ -z "$entry_id" ]] && { xml_json_err "MISSING_ARG" "Missing entry ID argument"; return 1; }
284
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
285
+
286
+ # Find entry in patterns
287
+ local entry
288
+ entry=$(jq --arg id "$entry_id" '.patterns[] | select(.id == $id)' "$json_file")
289
+
290
+ if [[ -z "$entry" || "$entry" == "null" ]]; then
291
+ xml_json_err "ENTRY_NOT_FOUND" "Pattern entry not found: $entry_id"
292
+ return 1
293
+ fi
294
+
295
+ # Check confidence
296
+ local confidence
297
+ confidence=$(echo "$entry" | jq -r '.confidence // 0')
298
+
299
+ if (( $(echo "$confidence < $PROMOTION_THRESHOLD" | bc -l) )); then
300
+ xml_json_ok "{\"promoted\":false,\"reason\":\"confidence_below_threshold\",\"confidence\":$confidence,\"threshold\":$PROMOTION_THRESHOLD}"
301
+ return 0
302
+ fi
303
+
304
+ # Promote: add to philosophies, remove from patterns
305
+ local updated_json
306
+ updated_json=$(jq --arg id "$entry_id" '
307
+ (.patterns[] | select(.id == $id)) as $entry |
308
+ .philosophies += [$entry] |
309
+ .patterns = [.patterns[] | select(.id != $id)]
310
+ ' "$json_file")
311
+
312
+ echo "$updated_json" > "$json_file"
313
+
314
+ xml_json_ok "{\"promoted\":true,\"new_domain\":\"philosophy\",\"confidence\":$confidence}"
315
+ }
316
+
317
+ # Export functions
318
+ export -f xml-wisdom-export xml-wisdom-import xml-wisdom-validate xml-wisdom-promote
319
+ export PROMOTION_THRESHOLD
@@ -0,0 +1,5 @@
1
+ # Approach Changes Log
2
+ # Captures when workers try X, it doesn't work, and switch to Y
3
+ # Format: YAML list entries with standardized fields
4
+
5
+ ---
@@ -0,0 +1,5 @@
1
+ # Build Failures Log
2
+ # Captures worker failures during /ant:build execution
3
+ # Format: YAML list entries with standardized fields
4
+
5
+ ---
@@ -0,0 +1,5 @@
1
+ # Test Failures Log
2
+ # Captures test failures including TDD red-green cycle
3
+ # Format: YAML list entries with standardized fields
4
+
5
+ ---