aether-colony 3.1.17 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/{runtime → .aether}/CONTEXT.md +1 -1
  2. package/{runtime → .aether}/aether-utils.sh +1772 -98
  3. package/.aether/docs/QUEEN-SYSTEM.md +211 -0
  4. package/.aether/docs/QUEEN.md +84 -0
  5. package/.aether/docs/README.md +68 -0
  6. package/.aether/docs/caste-system.md +48 -0
  7. package/{runtime → .aether/docs/disciplines}/DISCIPLINES.md +8 -8
  8. package/.aether/docs/error-codes.md +268 -0
  9. package/{runtime → .aether}/docs/known-issues.md +42 -26
  10. package/.aether/docs/queen-commands.md +97 -0
  11. package/.aether/exchange/colony-registry.xml +11 -0
  12. package/{runtime → .aether}/exchange/pheromone-xml.sh +2 -1
  13. package/.aether/exchange/pheromones.xml +87 -0
  14. package/.aether/exchange/queen-wisdom.xml +14 -0
  15. package/{runtime → .aether}/exchange/registry-xml.sh +7 -3
  16. package/{runtime → .aether}/exchange/wisdom-xml.sh +11 -4
  17. package/.aether/rules/aether-colony.md +134 -0
  18. package/.aether/schemas/example-prompt-builder.xml +234 -0
  19. package/.aether/templates/colony-state-reset.jq.template +22 -0
  20. package/.aether/templates/colony-state.template.json +35 -0
  21. package/.aether/templates/constraints.template.json +9 -0
  22. package/.aether/templates/crowned-anthill.template.md +36 -0
  23. package/.aether/templates/handoff-build-error.template.md +30 -0
  24. package/.aether/templates/handoff-build-success.template.md +39 -0
  25. package/.aether/templates/handoff.template.md +40 -0
  26. package/{runtime → .aether}/utils/atomic-write.sh +5 -5
  27. package/{runtime → .aether}/utils/chamber-compare.sh +23 -10
  28. package/{runtime → .aether}/utils/chamber-utils.sh +32 -20
  29. package/{runtime → .aether}/utils/error-handler.sh +13 -1
  30. package/{runtime → .aether}/utils/file-lock.sh +49 -13
  31. package/.aether/utils/semantic-cli.sh +413 -0
  32. package/{runtime → .aether}/utils/xml-compose.sh +7 -1
  33. package/.aether/utils/xml-convert.sh +273 -0
  34. package/.aether/utils/xml-query.sh +201 -0
  35. package/.aether/utils/xml-utils.sh +110 -0
  36. package/{runtime → .aether}/workers.md +14 -17
  37. package/.claude/agents/ant/aether-ambassador.md +264 -0
  38. package/.claude/agents/ant/aether-archaeologist.md +322 -0
  39. package/.claude/agents/ant/aether-auditor.md +266 -0
  40. package/.claude/agents/ant/aether-builder.md +187 -0
  41. package/.claude/agents/ant/aether-chaos.md +268 -0
  42. package/.claude/agents/ant/aether-chronicler.md +304 -0
  43. package/.claude/agents/ant/aether-gatekeeper.md +325 -0
  44. package/.claude/agents/ant/aether-includer.md +373 -0
  45. package/.claude/agents/ant/aether-keeper.md +271 -0
  46. package/.claude/agents/ant/aether-measurer.md +317 -0
  47. package/.claude/agents/ant/aether-probe.md +210 -0
  48. package/.claude/agents/ant/aether-queen.md +325 -0
  49. package/.claude/agents/ant/aether-route-setter.md +173 -0
  50. package/.claude/agents/ant/aether-sage.md +353 -0
  51. package/.claude/agents/ant/aether-scout.md +142 -0
  52. package/.claude/agents/ant/aether-surveyor-disciplines.md +416 -0
  53. package/.claude/agents/ant/aether-surveyor-nest.md +354 -0
  54. package/.claude/agents/ant/aether-surveyor-pathogens.md +288 -0
  55. package/.claude/agents/ant/aether-surveyor-provisions.md +359 -0
  56. package/.claude/agents/ant/aether-tracker.md +265 -0
  57. package/.claude/agents/ant/aether-watcher.md +244 -0
  58. package/.claude/agents/ant/aether-weaver.md +247 -0
  59. package/.claude/commands/ant/archaeology.md +16 -7
  60. package/.claude/commands/ant/build.md +415 -284
  61. package/.claude/commands/ant/chaos.md +19 -10
  62. package/.claude/commands/ant/colonize.md +58 -24
  63. package/.claude/commands/ant/continue.md +155 -145
  64. package/.claude/commands/ant/council.md +15 -5
  65. package/.claude/commands/ant/dream.md +16 -7
  66. package/.claude/commands/ant/entomb.md +274 -157
  67. package/.claude/commands/ant/feedback.md +33 -29
  68. package/.claude/commands/ant/flag.md +18 -10
  69. package/.claude/commands/ant/flags.md +14 -6
  70. package/.claude/commands/ant/focus.md +29 -21
  71. package/.claude/commands/ant/help.md +11 -1
  72. package/.claude/commands/ant/history.md +10 -0
  73. package/.claude/commands/ant/init.md +91 -65
  74. package/.claude/commands/ant/interpret.md +15 -4
  75. package/.claude/commands/ant/lay-eggs.md +55 -7
  76. package/.claude/commands/ant/maturity.md +11 -1
  77. package/.claude/commands/ant/migrate-state.md +14 -2
  78. package/.claude/commands/ant/oracle.md +23 -15
  79. package/.claude/commands/ant/organize.md +29 -20
  80. package/.claude/commands/ant/pause-colony.md +17 -7
  81. package/.claude/commands/ant/phase.md +17 -8
  82. package/.claude/commands/ant/plan.md +20 -9
  83. package/.claude/commands/ant/redirect.md +29 -32
  84. package/.claude/commands/ant/resume-colony.md +19 -9
  85. package/.claude/commands/ant/resume.md +272 -96
  86. package/.claude/commands/ant/seal.md +201 -191
  87. package/.claude/commands/ant/status.md +71 -32
  88. package/.claude/commands/ant/swarm.md +26 -44
  89. package/.claude/commands/ant/tunnels.md +279 -105
  90. package/.claude/commands/ant/update.md +81 -20
  91. package/.claude/commands/ant/verify-castes.md +14 -4
  92. package/.claude/commands/ant/watch.md +13 -12
  93. package/.opencode/agents/aether-ambassador.md +63 -20
  94. package/.opencode/agents/aether-archaeologist.md +29 -12
  95. package/.opencode/agents/aether-auditor.md +51 -18
  96. package/.opencode/agents/aether-builder.md +69 -19
  97. package/.opencode/agents/aether-chaos.md +29 -12
  98. package/.opencode/agents/aether-chronicler.md +60 -18
  99. package/.opencode/agents/aether-gatekeeper.md +27 -18
  100. package/.opencode/agents/aether-includer.md +27 -18
  101. package/.opencode/agents/aether-keeper.md +89 -18
  102. package/.opencode/agents/aether-measurer.md +27 -18
  103. package/.opencode/agents/aether-probe.md +60 -18
  104. package/.opencode/agents/aether-queen.md +172 -24
  105. package/.opencode/agents/aether-route-setter.md +57 -12
  106. package/.opencode/agents/aether-sage.md +26 -18
  107. package/.opencode/agents/aether-scout.md +27 -19
  108. package/.opencode/agents/aether-surveyor-disciplines.md +53 -1
  109. package/.opencode/agents/aether-surveyor-nest.md +53 -1
  110. package/.opencode/agents/aether-surveyor-pathogens.md +51 -1
  111. package/.opencode/agents/aether-surveyor-provisions.md +53 -1
  112. package/.opencode/agents/aether-tracker.md +64 -18
  113. package/.opencode/agents/aether-watcher.md +66 -19
  114. package/.opencode/agents/aether-weaver.md +61 -18
  115. package/.opencode/commands/ant/build.md +406 -192
  116. package/.opencode/commands/ant/continue.md +66 -76
  117. package/.opencode/commands/ant/entomb.md +106 -45
  118. package/.opencode/commands/ant/init.md +46 -48
  119. package/.opencode/commands/ant/organize.md +5 -5
  120. package/.opencode/commands/ant/resume.md +334 -0
  121. package/.opencode/commands/ant/seal.md +33 -24
  122. package/.opencode/commands/ant/status.md +11 -0
  123. package/.opencode/commands/ant/tunnels.md +149 -0
  124. package/.opencode/commands/ant/update.md +59 -16
  125. package/CHANGELOG.md +79 -0
  126. package/README.md +135 -353
  127. package/bin/cli.js +243 -122
  128. package/bin/generate-commands.sh +2 -2
  129. package/bin/lib/init.js +13 -3
  130. package/bin/lib/update-transaction.js +119 -117
  131. package/bin/sync-to-runtime.sh +5 -137
  132. package/bin/validate-package.sh +84 -0
  133. package/package.json +9 -6
  134. package/.opencode/agents/aether-architect.md +0 -66
  135. package/.opencode/agents/aether-guardian.md +0 -107
  136. package/.opencode/agents/workers.md +0 -1034
  137. package/runtime/QUEEN_ANT_ARCHITECTURE.md +0 -402
  138. package/runtime/data/signatures.json +0 -41
  139. package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +0 -1343
  140. package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +0 -2642
  141. package/runtime/docs/PHEROMONE-INJECTION.md +0 -240
  142. package/runtime/docs/PHEROMONE-INTEGRATION.md +0 -192
  143. package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +0 -426
  144. package/runtime/docs/README.md +0 -94
  145. package/runtime/docs/VISUAL-OUTPUT-SPEC.md +0 -219
  146. package/runtime/docs/biological-reference.md +0 -272
  147. package/runtime/docs/codebase-review.md +0 -399
  148. package/runtime/docs/command-sync.md +0 -164
  149. package/runtime/docs/constraints.md +0 -116
  150. package/runtime/docs/implementation-learnings.md +0 -89
  151. package/runtime/docs/namespace.md +0 -148
  152. package/runtime/docs/pathogen-schema-example.json +0 -36
  153. package/runtime/docs/pathogen-schema.md +0 -111
  154. package/runtime/docs/planning-discipline.md +0 -159
  155. package/runtime/docs/progressive-disclosure.md +0 -184
  156. package/runtime/lib/queen-utils.sh +0 -729
  157. package/runtime/planning.md +0 -159
  158. package/runtime/recover.sh +0 -136
  159. package/runtime/utils/xml-utils.sh +0 -2196
  160. package/runtime/workers-new-castes.md +0 -516
  161. /package/{runtime → .aether/docs/disciplines}/coding-standards.md +0 -0
  162. /package/{runtime → .aether/docs/disciplines}/debugging.md +0 -0
  163. /package/{runtime → .aether/docs/disciplines}/learning.md +0 -0
  164. /package/{runtime → .aether/docs/disciplines}/tdd.md +0 -0
  165. /package/{runtime → .aether/docs/disciplines}/verification-loop.md +0 -0
  166. /package/{runtime → .aether/docs/disciplines}/verification.md +0 -0
  167. /package/{runtime → .aether}/docs/pheromones.md +0 -0
  168. /package/{runtime → .aether}/model-profiles.yaml +0 -0
  169. /package/{runtime → .aether}/schemas/aether-types.xsd +0 -0
  170. /package/{runtime → .aether}/schemas/colony-registry.xsd +0 -0
  171. /package/{runtime → .aether}/schemas/pheromone.xsd +0 -0
  172. /package/{runtime → .aether}/schemas/prompt.xsd +0 -0
  173. /package/{runtime → .aether}/schemas/queen-wisdom.xsd +0 -0
  174. /package/{runtime → .aether}/schemas/worker-priming.xsd +0 -0
  175. /package/{runtime → .aether}/templates/QUEEN.md.template +0 -0
  176. /package/{runtime → .aether}/utils/colorize-log.sh +0 -0
  177. /package/{runtime → .aether}/utils/queen-to-md.xsl +0 -0
  178. /package/{runtime → .aether}/utils/spawn-tree.sh +0 -0
  179. /package/{runtime → .aether}/utils/spawn-with-model.sh +0 -0
  180. /package/{runtime → .aether}/utils/state-loader.sh +0 -0
  181. /package/{runtime → .aether}/utils/swarm-display.sh +0 -0
  182. /package/{runtime → .aether}/utils/watch-spawn-tree.sh +0 -0
  183. /package/{runtime → .aether}/utils/xml-core.sh +0 -0
@@ -0,0 +1,273 @@
1
+ #!/bin/bash
2
+ # XML Conversion Utilities
3
+ # Bidirectional JSON/XML conversion and document merging
4
+ #
5
+ # Usage: source .aether/utils/xml-convert.sh
6
+ # xml-to-json <xml_file> [--pretty]
7
+ # json-to-xml <json_file> [root_element]
8
+ # xml-merge <output_file> <main_xml_file>
9
+ # xml-convert-detect-format <file>
10
+
11
+ set -euo pipefail
12
+
13
+ # Source xml-core.sh for JSON helpers and tool detection
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ source "$SCRIPT_DIR/xml-core.sh"
16
+
17
+ # Additional tool detection for conversion
18
+ XML2JSON_AVAILABLE=false
19
+ if command -v xml2json >/dev/null 2>&1; then
20
+ XML2JSON_AVAILABLE=true
21
+ fi
22
+
23
+ # ============================================================================
24
+ # Format Detection
25
+ # ============================================================================
26
+
27
+ # xml-convert-detect-format: Detect if file is XML or JSON
28
+ # Usage: xml-convert-detect-format <file>
29
+ # Returns: {"ok":true,"result":{"format":"xml|json|unknown","confidence":"high|medium|low"}}
30
+ xml-convert-detect-format() {
31
+ local file="${1:-}"
32
+
33
+ [[ -z "$file" ]] && { xml_json_err "MISSING_ARG" "Missing file argument"; return 1; }
34
+ [[ -f "$file" ]] || { xml_json_err "FILE_NOT_FOUND" "File not found: $file"; return 1; }
35
+
36
+ # Read first 1KB for analysis
37
+ local header
38
+ header=$(head -c 1024 "$file" 2>/dev/null || head -c 1024 < "$file")
39
+
40
+ # Check for XML signatures
41
+ if echo "$header" | grep -qE '^\s*<\?xml\s+version'; then
42
+ xml_json_ok '{"format":"xml","confidence":"high","signature":"xml_declaration"}'
43
+ return 0
44
+ fi
45
+
46
+ if echo "$header" | grep -qE '^\s*<[a-zA-Z_][a-zA-Z0-9_]*[\s>]'; then
47
+ xml_json_ok '{"format":"xml","confidence":"medium","signature":"root_element"}'
48
+ return 0
49
+ fi
50
+
51
+ # Check for JSON signatures
52
+ if echo "$header" | grep -qE '^\s*(\{|\[)'; then
53
+ # Verify it's valid JSON
54
+ if jq empty "$file" 2>/dev/null; then
55
+ xml_json_ok '{"format":"json","confidence":"high","signature":"valid_json"}'
56
+ return 0
57
+ else
58
+ xml_json_ok '{"format":"json","confidence":"low","signature":"json_like","note":"Invalid JSON syntax"}'
59
+ return 0
60
+ fi
61
+ fi
62
+
63
+ xml_json_ok '{"format":"unknown","confidence":"low","signature":"none"}'
64
+ }
65
+
66
+ # ============================================================================
67
+ # XML to JSON Conversion
68
+ # ============================================================================
69
+
70
+ # xml-to-json: Convert XML to JSON format
71
+ # Usage: xml-to-json <xml_file> [--pretty]
72
+ # Returns: {"ok":true,"result":{"json":"...","format":"object"}}
73
+ xml-to-json() {
74
+ local xml_file="${1:-}"
75
+ local pretty=false
76
+
77
+ # Parse optional arguments
78
+ shift || true
79
+ while [[ $# -gt 0 ]]; do
80
+ case "$1" in
81
+ --pretty) pretty=true; shift ;;
82
+ *) shift ;;
83
+ esac
84
+ done
85
+
86
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
87
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
88
+
89
+ # Check well-formedness first
90
+ local well_formed_result
91
+ well_formed_result=$(xml-well-formed "$xml_file" 2>/dev/null)
92
+ if ! echo "$well_formed_result" | jq -e '.result.well_formed' >/dev/null 2>&1; then
93
+ xml_json_err "PARSE_ERROR" "XML is not well-formed"
94
+ return 1
95
+ fi
96
+
97
+ # Try xml2json if available (npm package)
98
+ if [[ "$XML2JSON_AVAILABLE" == "true" ]]; then
99
+ local json_output
100
+ if json_output=$(xml2json "$xml_file" 2>/dev/null); then
101
+ if [[ "$pretty" == "true" ]]; then
102
+ json_output=$(echo "$json_output" | jq '.')
103
+ fi
104
+ local escaped_json
105
+ escaped_json=$(echo "$json_output" | jq -Rs '.')
106
+ xml_json_ok "{\"format\":\"object\",\"json\":$escaped_json}"
107
+ return 0
108
+ fi
109
+ fi
110
+
111
+ # Fallback: Use xsltproc with built-in XSLT
112
+ if [[ "$XSLTPROC_AVAILABLE" == "true" ]]; then
113
+ local xslt_script
114
+ xslt_script=$(cat << 'XSLT'
115
+ <?xml version="1.0"?>
116
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
117
+ <xsl:output method="text"/>
118
+ <xsl:template match="/">
119
+ <xsl:text>{"root":</xsl:text>
120
+ <xsl:apply-templates select="*"/>
121
+ <xsl:text>}</xsl:text>
122
+ </xsl:template>
123
+ <xsl:template match="*">
124
+ <xsl:text>{"</xsl:text>
125
+ <xsl:value-of select="name()"/>
126
+ <xsl:text>":</xsl:text>
127
+ <xsl:choose>
128
+ <xsl:when test="count(*) > 0">
129
+ <xsl:text>[</xsl:text>
130
+ <xsl:apply-templates select="*"/>
131
+ <xsl:text>]</xsl:text>
132
+ </xsl:when>
133
+ <xsl:otherwise>
134
+ <xsl:text>"</xsl:text>
135
+ <xsl:value-of select="."/>
136
+ <xsl:text>"</xsl:text>
137
+ </xsl:otherwise>
138
+ </xsl:choose>
139
+ <xsl:text>}</xsl:text>
140
+ <xsl:if test="position() != last()">,</xsl:if>
141
+ </xsl:template>
142
+ </xsl:stylesheet>
143
+ XSLT
144
+ )
145
+ local json_result
146
+ json_result=$(echo "$xslt_script" | xsltproc - "$xml_file" 2>/dev/null) || {
147
+ xml_json_err "CONVERSION_ERROR" "XSLT conversion failed"
148
+ return 1
149
+ }
150
+ xml_json_ok "{\"format\":\"object\",\"json\":$(echo "$json_result" | jq -Rs '.')}"
151
+ return 0
152
+ fi
153
+
154
+ # Last resort: Use xmlstarlet if available
155
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
156
+ local json_result
157
+ json_result=$(xmlstarlet sel -t -m "/" -o '{"root":{' -m "*" -v "name()" -o ':"' -v "." -o '"' -b -o '}}' "$xml_file" 2>/dev/null) || {
158
+ xml_json_err "CONVERSION_ERROR" "xmlstarlet conversion failed"
159
+ return 1
160
+ }
161
+ xml_json_ok "{\"format\":\"object\",\"json\":$(echo "$json_result" | jq -Rs '.')}"
162
+ return 0
163
+ fi
164
+
165
+ xml_json_err "TOOL_NOT_AVAILABLE" "No XML to JSON conversion tool available. Install xml2json, xsltproc, or xmlstarlet."
166
+ return 1
167
+ }
168
+
169
+ # ============================================================================
170
+ # JSON to XML Conversion
171
+ # ============================================================================
172
+
173
+ # json-to-xml: Convert JSON to XML
174
+ # Usage: json-to-xml <json_file> [root_element]
175
+ # Returns: {"ok":true,"result":{"xml":"<root>...</root>"}}
176
+ json-to-xml() {
177
+ local json_file="${1:-}"
178
+ local root_element="${2:-root}"
179
+
180
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
181
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
182
+
183
+ # Validate JSON first
184
+ if ! jq empty "$json_file" 2>/dev/null; then
185
+ xml_json_err "PARSE_ERROR" "Invalid JSON file: $json_file"
186
+ return 1
187
+ fi
188
+
189
+ # Build XML using jq to generate structure
190
+ local xml_output
191
+ xml_output=$(jq -r --arg root "$root_element" '
192
+ def to_xml:
193
+ if type == "object" then
194
+ to_entries | map(
195
+ "<\(.key)>\(.value | to_xml)</\(.key)>"
196
+ ) | join("")
197
+ elif type == "array" then
198
+ map("<item>\(. | to_xml)</item>") | join("")
199
+ elif type == "string" then
200
+ .
201
+ elif type == "number" then
202
+ tostring
203
+ elif type == "boolean" then
204
+ tostring
205
+ elif type == "null" then
206
+ ""
207
+ else
208
+ tostring
209
+ end;
210
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<\($root)>\n" + (to_xml) + "\n</\($root)>"
211
+ ' "$json_file" 2>/dev/null) || {
212
+ xml_json_err "CONVERSION_ERROR" "JSON to XML conversion failed"
213
+ return 1
214
+ }
215
+
216
+ # Escape the XML for JSON output
217
+ local escaped_xml
218
+ escaped_xml=$(echo "$xml_output" | jq -Rs '.')
219
+ xml_json_ok "{\"xml\":$escaped_xml}"
220
+ }
221
+
222
+ # ============================================================================
223
+ # XML Document Merging
224
+ # ============================================================================
225
+
226
+ # xml-merge: XInclude document merging
227
+ # Usage: xml-merge <output_file> <main_xml_file>
228
+ # Returns: {"ok":true,"result":{"merged":true,"output":"<path>","sources_resolved":N}}
229
+ xml-merge() {
230
+ local output_file="${1:-}"
231
+ local main_xml="${2:-}"
232
+
233
+ [[ -z "$output_file" ]] && { xml_json_err "MISSING_ARG" "Missing output file argument"; return 1; }
234
+ [[ -z "$main_xml" ]] && { xml_json_err "MISSING_ARG" "Missing main XML file argument"; return 1; }
235
+ [[ -f "$main_xml" ]] || { xml_json_err "FILE_NOT_FOUND" "Main XML file not found: $main_xml"; return 1; }
236
+
237
+ # Check well-formedness of main file
238
+ local well_formed_result
239
+ well_formed_result=$(xml-well-formed "$main_xml" 2>/dev/null)
240
+ if ! echo "$well_formed_result" | jq -e '.result.well_formed' >/dev/null 2>&1; then
241
+ xml_json_err "PARSE_ERROR" "Main XML file is not well-formed"
242
+ return 1
243
+ fi
244
+
245
+ # Use xmllint for XInclude processing with security flags
246
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
247
+ local merged
248
+ merged=$(xmllint --nonet --noent --xinclude "$main_xml" 2>/dev/null) || {
249
+ xml_json_err "MERGE_ERROR" "XInclude merge failed"
250
+ return 1
251
+ }
252
+
253
+ # Write output
254
+ echo "$merged" > "$output_file"
255
+
256
+ # Count resolved includes (approximate)
257
+ local resolved_count
258
+ resolved_count=$(echo "$merged" | grep -c '<xi:include' 2>/dev/null || echo "0")
259
+
260
+ local escaped_output
261
+ escaped_output=$(echo "$output_file" | jq -Rs '.[:-1]')
262
+ xml_json_ok "{\"merged\":true,\"output\":$escaped_output,\"sources_resolved\":$resolved_count}"
263
+ return 0
264
+ fi
265
+
266
+ # Without xmllint, we cannot safely process XInclude
267
+ xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for XInclude merging. Install libxml2 utilities."
268
+ return 1
269
+ }
270
+
271
+ # Export functions
272
+ export -f xml-convert-detect-format xml-to-json json-to-xml xml-merge
273
+ export XML2JSON_AVAILABLE
@@ -0,0 +1,201 @@
1
+ #!/bin/bash
2
+ # XML Query Utilities
3
+ # XPath queries with xmlstarlet and xmllint fallback
4
+ #
5
+ # Usage: source .aether/utils/xml-query.sh
6
+ # xml-query <xml_file> <xpath_expression>
7
+ # xml-query-attr <xml_file> <xpath_expression>
8
+ # xml-query-text <xml_file> <element_name>
9
+ # xml-query-count <xml_file> <xpath_expression>
10
+
11
+ set -euo pipefail
12
+
13
+ # Source xml-core.sh for JSON helpers and tool detection
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ source "$SCRIPT_DIR/xml-core.sh"
16
+
17
+ # ============================================================================
18
+ # XPath Query Functions
19
+ # ============================================================================
20
+
21
+ # xml-query: Execute XPath query against XML file
22
+ # Usage: xml-query <xml_file> <xpath_expression>
23
+ # Returns: {"ok":true,"result":{"matches":["value1","value2",...]}}
24
+ xml-query() {
25
+ local xml_file="${1:-}"
26
+ local xpath="${2:-}"
27
+
28
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
29
+ [[ -z "$xpath" ]] && { xml_json_err "MISSING_ARG" "Missing XPath expression"; return 1; }
30
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
31
+
32
+ local results=""
33
+
34
+ # Prefer xmlstarlet for full XPath support
35
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
36
+ results=$(xmlstarlet sel -t -v "$xpath" "$xml_file" 2>/dev/null | tr '\n' '|')
37
+ # Remove trailing pipe
38
+ results="${results%|}"
39
+ elif [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
40
+ # xmllint has limited XPath but works for basic queries
41
+ # Note: xmllint --xpath returns the text content of matched nodes
42
+ results=$(xmllint --nonet --noent --xpath "$xpath" "$xml_file" 2>/dev/null | \
43
+ sed 's/<[^>]*>//g' | tr '\n' '|' | sed 's/|$//')
44
+ else
45
+ xml_json_err "TOOL_NOT_AVAILABLE" "No XPath-capable tool available (install xmlstarlet or libxml2)"
46
+ return 1
47
+ fi
48
+
49
+ # Build JSON array from pipe-separated results
50
+ if [[ -n "$results" ]]; then
51
+ local json_array="["
52
+ local first=true
53
+ IFS='|' read -ra matches <<< "$results"
54
+ for match in "${matches[@]}"; do
55
+ # Trim whitespace and escape for JSON
56
+ match=$(echo "$match" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
57
+ match=$(echo "$match" | sed 's/\\/\\\\/g; s/"/\\"/g')
58
+ if [[ "$first" == "true" ]]; then
59
+ first=false
60
+ json_array="$json_array\"$match\""
61
+ else
62
+ json_array="$json_array,\"$match\""
63
+ fi
64
+ done
65
+ json_array="$json_array]"
66
+ xml_json_ok "{\"matches\":$json_array}"
67
+ else
68
+ xml_json_ok '{"matches":[]}'
69
+ fi
70
+ }
71
+
72
+ # xml-query-attr: Extract attribute values using XPath
73
+ # Usage: xml-query-attr <xml_file> <element_xpath> <attribute_name>
74
+ # Returns: {"ok":true,"result":{"attribute":"name","values":["val1","val2"]}}
75
+ xml-query-attr() {
76
+ local xml_file="${1:-}"
77
+ local element_xpath="${2:-}"
78
+ local attr_name="${3:-}"
79
+
80
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
81
+ [[ -z "$element_xpath" ]] && { xml_json_err "MISSING_ARG" "Missing element XPath"; return 1; }
82
+ [[ -z "$attr_name" ]] && { xml_json_err "MISSING_ARG" "Missing attribute name"; return 1; }
83
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
84
+
85
+ local full_xpath="${element_xpath}/@${attr_name}"
86
+ local results=""
87
+
88
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
89
+ results=$(xmlstarlet sel -t -v "$full_xpath" "$xml_file" 2>/dev/null | tr '\n' '|')
90
+ results="${results%|}"
91
+ elif [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
92
+ # xmllint can extract attributes with //@attrname syntax
93
+ results=$(xmllint --nonet --noent --xpath "$full_xpath" "$xml_file" 2>/dev/null | tr '\n' '|')
94
+ results="${results%|}"
95
+ else
96
+ xml_json_err "TOOL_NOT_AVAILABLE" "No XPath-capable tool available"
97
+ return 1
98
+ fi
99
+
100
+ # Build JSON array
101
+ if [[ -n "$results" ]]; then
102
+ local json_array="["
103
+ local first=true
104
+ IFS='|' read -ra values <<< "$results"
105
+ for val in "${values[@]}"; do
106
+ val=$(echo "$val" | sed 's/\\/\\\\/g; s/"/\\"/g')
107
+ if [[ "$first" == "true" ]]; then
108
+ first=false
109
+ json_array="$json_array\"$val\""
110
+ else
111
+ json_array="$json_array,\"$val\""
112
+ fi
113
+ done
114
+ json_array="$json_array]"
115
+ xml_json_ok "{\"attribute\":\"$attr_name\",\"values\":$json_array}"
116
+ else
117
+ xml_json_ok "{\"attribute\":\"$attr_name\",\"values\":[]}"
118
+ fi
119
+ }
120
+
121
+ # xml-query-text: Extract text content of elements
122
+ # Usage: xml-query-text <xml_file> <element_name>
123
+ # Returns: {"ok":true,"result":{"element":"name","text":["text1","text2"]}}
124
+ xml-query-text() {
125
+ local xml_file="${1:-}"
126
+ local element_name="${2:-}"
127
+
128
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
129
+ [[ -z "$element_name" ]] && { xml_json_err "MISSING_ARG" "Missing element name"; return 1; }
130
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
131
+
132
+ local xpath="//$element_name"
133
+ local results=""
134
+
135
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
136
+ # Use -m to match nodes and extract text
137
+ results=$(xmlstarlet sel -t -m "$xpath" -v "." -n "$xml_file" 2>/dev/null | tr '\n' '|')
138
+ results="${results%|}"
139
+ elif [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
140
+ # xmllint --xpath returns text content directly for simple paths
141
+ results=$(xmllint --nonet --noent --xpath "$xpath" "$xml_file" 2>/dev/null | \
142
+ sed 's/<[^>]*>//g' | tr '\n' '|' | sed 's/|$//')
143
+ else
144
+ xml_json_err "TOOL_NOT_AVAILABLE" "No XPath-capable tool available"
145
+ return 1
146
+ fi
147
+
148
+ if [[ -n "$results" ]]; then
149
+ local json_array="["
150
+ local first=true
151
+ IFS='|' read -ra texts <<< "$results"
152
+ for text in "${texts[@]}"; do
153
+ text=$(echo "$text" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
154
+ text=$(echo "$text" | sed 's/\\/\\\\/g; s/"/\\"/g')
155
+ if [[ "$first" == "true" ]]; then
156
+ first=false
157
+ json_array="$json_array\"$text\""
158
+ else
159
+ json_array="$json_array,\"$text\""
160
+ fi
161
+ done
162
+ json_array="$json_array]"
163
+ xml_json_ok "{\"element\":\"$element_name\",\"text\":$json_array}"
164
+ else
165
+ xml_json_ok "{\"element\":\"$element_name\",\"text\":[]}"
166
+ fi
167
+ }
168
+
169
+ # xml-query-count: Count nodes matching XPath expression
170
+ # Usage: xml-query-count <xml_file> <xpath_expression>
171
+ # Returns: {"ok":true,"result":{"count":5}}
172
+ xml-query-count() {
173
+ local xml_file="${1:-}"
174
+ local xpath="${2:-}"
175
+
176
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
177
+ [[ -z "$xpath" ]] && { xml_json_err "MISSING_ARG" "Missing XPath expression"; return 1; }
178
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
179
+
180
+ local count=0
181
+
182
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
183
+ # Use count() XPath function
184
+ local count_result
185
+ count_result=$(xmlstarlet sel -t -v "count($xpath)" "$xml_file" 2>/dev/null)
186
+ count="${count_result:-0}"
187
+ elif [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
188
+ # Count matching lines (approximate for simple cases)
189
+ local matches
190
+ matches=$(xmllint --nonet --noent --xpath "$xpath" "$xml_file" 2>/dev/null | grep -c '<' || true)
191
+ count="${matches:-0}"
192
+ else
193
+ xml_json_err "TOOL_NOT_AVAILABLE" "No XPath-capable tool available"
194
+ return 1
195
+ fi
196
+
197
+ xml_json_ok "{\"count\":$count}"
198
+ }
199
+
200
+ # Export functions
201
+ export -f xml-query xml-query-attr xml-query-text xml-query-count
@@ -0,0 +1,110 @@
1
+ #!/bin/bash
2
+ # XML Utilities Loader
3
+ # Sources all XML modules for backward compatibility
4
+ #
5
+ # IMPORTANT: This file is now a loader only. New code should source
6
+ # individual modules directly from .aether/utils/ or .aether/exchange/
7
+ #
8
+ # Usage: source .aether/utils/xml-utils.sh
9
+ #
10
+ # Modules loaded:
11
+ # - xml-core.sh : Core operations (validate, format, escape)
12
+ # - xml-query.sh : XPath queries
13
+ # - xml-convert.sh : JSON/XML conversion
14
+ # - xml-compose.sh : XInclude composition
15
+ # - pheromone-xml.sh : Pheromone exchange
16
+ # - wisdom-xml.sh : Queen wisdom exchange
17
+ # - registry-xml.sh : Colony registry exchange
18
+ #
19
+ # Deprecated functions (maintained for compatibility):
20
+ # - pheromone-to-xml() -> Use xml-pheromone-export()
21
+ # - pheromone-from-xml() -> Use xml-pheromone-import()
22
+ # - queen-wisdom-to-xml() -> Use xml-wisdom-export()
23
+ # - queen-wisdom-from-xml() -> Use xml-wisdom-import()
24
+
25
+ set -euo pipefail
26
+
27
+ # Determine script directory for relative sourcing
28
+ # Handle case when sourced interactively (BASH_SOURCE[0] may be empty)
29
+ if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ else
32
+ # Fallback: derive from the sourced script's location
33
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
34
+ fi
35
+ EXCHANGE_DIR="$(cd "$SCRIPT_DIR/../exchange" && pwd)"
36
+
37
+ # ============================================================================
38
+ # Load Core Modules
39
+ # ============================================================================
40
+
41
+ # Core utilities (required by other modules)
42
+ source "$SCRIPT_DIR/xml-core.sh"
43
+
44
+ # Query functions
45
+ source "$SCRIPT_DIR/xml-query.sh"
46
+
47
+ # Conversion functions
48
+ source "$SCRIPT_DIR/xml-convert.sh"
49
+
50
+ # Composition functions
51
+ source "$SCRIPT_DIR/xml-compose.sh"
52
+
53
+ # ============================================================================
54
+ # Load Exchange Modules
55
+ # ============================================================================
56
+
57
+ # Pheromone exchange (export/import)
58
+ source "$EXCHANGE_DIR/pheromone-xml.sh"
59
+
60
+ # Queen wisdom exchange
61
+ source "$EXCHANGE_DIR/wisdom-xml.sh"
62
+
63
+ # Colony registry exchange
64
+ source "$EXCHANGE_DIR/registry-xml.sh"
65
+
66
+ # ============================================================================
67
+ # Backward Compatibility Aliases
68
+ # ============================================================================
69
+
70
+ # Map old function names to new ones for compatibility
71
+ pheromone-to-xml() { xml-pheromone-export "$@"; }
72
+ pheromone-from-xml() { xml-pheromone-import "$@"; }
73
+ queen-wisdom-to-xml() { xml-wisdom-export "$@"; }
74
+ queen-wisdom-from-xml() { xml-wisdom-import "$@"; }
75
+ registry-to-xml() { xml-registry-export "$@"; }
76
+ registry-from-xml() { xml-registry-import "$@"; }
77
+
78
+ # Export compatibility aliases
79
+ export -f pheromone-to-xml pheromone-from-xml
80
+ export -f queen-wisdom-to-xml queen-wisdom-from-xml
81
+ export -f registry-to-xml registry-from-xml
82
+
83
+ # ============================================================================
84
+ # Module Information
85
+ # ============================================================================
86
+
87
+ # xml-utils-info: Display module information
88
+ # Usage: xml-utils-info
89
+ xml-utils-info() {
90
+ xml_json_ok '{
91
+ "modules": [
92
+ "xml-core.sh",
93
+ "xml-query.sh",
94
+ "xml-convert.sh",
95
+ "xml-compose.sh",
96
+ "pheromone-xml.sh",
97
+ "wisdom-xml.sh",
98
+ "registry-xml.sh"
99
+ ],
100
+ "note": "This loader provides backward compatibility. New code should source individual modules.",
101
+ "tools": {
102
+ "xmllint": '"$XMLLINT_AVAILABLE"',
103
+ "xmlstarlet": '"$XMLSTARLET_AVAILABLE"',
104
+ "xsltproc": '"$XSLTPROC_AVAILABLE"'
105
+ }
106
+ }'
107
+ }
108
+
109
+ export -f xml-utils-info
110
+
@@ -136,7 +136,7 @@ Before reporting ANY task as complete:
136
136
  - Run relevant tests yourself
137
137
  - Confirm success criteria with evidence
138
138
 
139
- See `.aether/verification.md` for full discipline reference.
139
+ See `.aether/docs/disciplines/verification.md` for full discipline reference.
140
140
 
141
141
  ### Verification Loop Discipline
142
142
 
@@ -163,7 +163,7 @@ Diff: [X files changed]
163
163
  Overall: [READY/NOT READY]
164
164
  ```
165
165
 
166
- See `.aether/verification-loop.md` for full discipline reference.
166
+ See `.aether/docs/disciplines/verification-loop.md` for full discipline reference.
167
167
 
168
168
  ### Debugging Discipline
169
169
 
@@ -187,7 +187,7 @@ When you encounter ANY bug, test failure, or unexpected behavior:
187
187
  - "Just try changing X"
188
188
  - "I don't fully understand but this might work"
189
189
 
190
- See `.aether/debugging.md` for full discipline reference.
190
+ See `.aether/docs/disciplines/debugging.md` for full discipline reference.
191
191
 
192
192
  ### TDD Discipline
193
193
 
@@ -210,7 +210,7 @@ When implementing ANY new code:
210
210
 
211
211
  **Coverage target:** 80%+ for new code.
212
212
 
213
- See `.aether/tdd.md` for full discipline reference.
213
+ See `.aether/docs/disciplines/tdd.md` for full discipline reference.
214
214
 
215
215
  ### Learning Discipline
216
216
 
@@ -228,7 +228,7 @@ The colony learns from every phase. Observe patterns for future improvement.
228
228
 
229
229
  **Report patterns observed** in your output for colony learning.
230
230
 
231
- See `.aether/learning.md` for full discipline reference.
231
+ See `.aether/docs/disciplines/learning.md` for full discipline reference.
232
232
 
233
233
  ### Coding Standards Discipline
234
234
 
@@ -252,7 +252,7 @@ Quick checklist before completing code:
252
252
  - **Error handling** - Try/catch with meaningful messages
253
253
  - **Async** - Parallelize with Promise.all where possible
254
254
 
255
- See `.aether/coding-standards.md` for full discipline reference.
255
+ See `.aether/docs/disciplines/coding-standards.md` for full discipline reference.
256
256
 
257
257
  ### Activity Log
258
258
 
@@ -387,14 +387,9 @@ All spawns are logged to `.aether/data/spawn-tree.txt` and visible in `/ant:watc
387
387
 
388
388
  ### Visual Identity
389
389
 
390
- | Role | Emoji |
391
- |------|-------|
392
- | Builder | 🔨🐜 |
393
- | Watcher | 👁️🐜 |
394
- | Scout | 🔍🐜 |
395
- | Colonizer | 🗺️🐜 |
396
- | Architect | 🏛️🐜 |
397
- | Route-Setter | 📋🐜 |
390
+ Workers display as `{caste_emoji} {worker_name}` (e.g., `🔨🐜 Hammer-42`).
391
+
392
+ For complete caste emoji reference, see `.aether/docs/caste-system.md`.
398
393
 
399
394
  Use your emoji in output headers: `{emoji} {Role} Ant -- {status}`
400
395
 
@@ -448,7 +443,7 @@ All passing: ✓
448
443
 
449
444
  **When Encountering Errors:**
450
445
 
451
- Follow systematic debugging (see `.aether/debugging.md`):
446
+ Follow systematic debugging (see `.aether/docs/disciplines/debugging.md`):
452
447
 
453
448
  1. **STOP** - Do not attempt quick fixes
454
449
  2. **Read error completely** - Stack trace, line numbers, error codes
@@ -562,7 +557,7 @@ Findings:
562
557
 
563
558
  **When Tests Fail:**
564
559
 
565
- Follow systematic debugging (see `.aether/debugging.md`):
560
+ Follow systematic debugging (see `.aether/docs/disciplines/debugging.md`):
566
561
 
567
562
  1. **Read the failure completely** - Full error, stack trace
568
563
  2. **Reproduce** - Run the specific failing test
@@ -626,7 +621,9 @@ Recommendation: {specific fix or investigation needed}
626
621
 
627
622
  ---
628
623
 
629
- ## Architect
624
+ ## Architect (Merged into Keeper)
625
+
626
+ > **Note:** As of Phase 25, Architect capabilities are absorbed by the Keeper agent as "Architecture Mode". Workers named with Architect patterns still resolve to the 🏛️🐜 caste emoji. See `.opencode/agents/aether-keeper.md` for the merged definition.
630
627
 
631
628
  🏛️ **Purpose:** Synthesize knowledge, extract patterns, and coordinate documentation. The colony's wisdom -- when the colony learns, you organize and preserve that knowledge.
632
629