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,575 @@
1
+ #!/bin/bash
2
+ # Pheromone Exchange Module
3
+ # JSON/XML bidirectional conversion for pheromone signals
4
+ #
5
+ # Usage: source .aether/exchange/pheromone-xml.sh
6
+ # xml-pheromone-export <pheromone_json> [output_xml]
7
+ # xml-pheromone-import <pheromone_xml> [output_json]
8
+ # xml-pheromone-validate <pheromone_xml>
9
+ # xml-pheromone-merge <colony_prefix> <xml_files...> [output_xml]
10
+
11
+ # Don't use set -e for library-style scripts - let callers handle errors
12
+ # set -euo pipefail
13
+
14
+ # Source dependencies - handle being sourced vs executed
15
+ if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
17
+ else
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
19
+ fi
20
+ source "$SCRIPT_DIR/utils/xml-core.sh"
21
+
22
+ # Ensure tool availability variables are available
23
+ if [[ -z "${XMLLINT_AVAILABLE:-}" ]]; then
24
+ XMLLINT_AVAILABLE=false
25
+ command -v xmllint >/dev/null 2>&1 && XMLLINT_AVAILABLE=true
26
+ fi
27
+ if [[ -z "${XMLSTARLET_AVAILABLE:-}" ]]; then
28
+ XMLSTARLET_AVAILABLE=false
29
+ command -v xmlstarlet >/dev/null 2>&1 && XMLSTARLET_AVAILABLE=true
30
+ fi
31
+
32
+ # ============================================================================
33
+ # Pheromone Export (JSON to XML)
34
+ # ============================================================================
35
+
36
+ # xml-pheromone-export: Convert pheromone JSON to XML format
37
+ # Usage: xml-pheromone-export <pheromone_json_file> [output_xml_file]
38
+ # Returns: {"ok":true,"result":{"xml":"...","path":"..."}}
39
+ xml-pheromone-export() {
40
+ local json_file="${1:-}"
41
+ local output_xml="${2:-}"
42
+ local xsd_file="${3:-.aether/schemas/pheromone.xsd}"
43
+
44
+ [[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
45
+ [[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
46
+
47
+ # Validate JSON
48
+ if ! jq empty "$json_file" 2>/dev/null; then
49
+ xml_json_err "PARSE_ERROR" "Invalid JSON file: $json_file"
50
+ return 1
51
+ fi
52
+
53
+ # Generate ISO timestamp
54
+ local generated_at
55
+ generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
56
+
57
+ # Extract metadata
58
+ local version colony_id
59
+ version=$(jq -r '.version // "1.0.0"' "$json_file")
60
+ colony_id=$(jq -r '.colony_id // "unknown"' "$json_file")
61
+
62
+ # Build XML header
63
+ local xml_output
64
+ xml_output="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
65
+ <pheromones xmlns=\"http://aether.colony/schemas/pheromones\"
66
+ xmlns:ph=\"http://aether.colony/schemas/pheromones\"
67
+ version=\"$version\"
68
+ generated_at=\"$generated_at\"
69
+ colony_id=\"$colony_id\">"
70
+
71
+ # Add metadata
72
+ local source_type context
73
+ source_type=$(jq -r '.metadata.source.type // "system"' "$json_file" 2>/dev/null || echo "system")
74
+ context=$(jq -r '.metadata.context // "Colony pheromone signals"' "$json_file" 2>/dev/null || echo "Colony pheromone signals")
75
+
76
+ xml_output="$xml_output
77
+ <metadata>
78
+ <source type=\"$source_type\">aether-pheromone-converter</source>
79
+ <context>$(echo "$context" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')</context>
80
+ </metadata>"
81
+
82
+ # Process signals
83
+ local sig_array_length
84
+ sig_array_length=$(jq '.signals | length' "$json_file" 2>/dev/null || echo "0")
85
+
86
+ local sig_idx=0
87
+ while [[ $sig_idx -lt $sig_array_length ]]; do
88
+ local signal
89
+ signal=$(jq -c ".signals[$sig_idx]" "$json_file" 2>/dev/null)
90
+ [[ -n "$signal" ]] || { ((sig_idx++)); continue; }
91
+
92
+ # Extract signal fields
93
+ local sig_id sig_type priority source created_at expires_at active
94
+ sig_id=$(echo "$signal" | jq -r '.id // "sig_'"$(date +%s)"'_'"$sig_idx"'"')
95
+ sig_type=$(echo "$signal" | jq -r '.type // "FOCUS"' | tr '[:lower:]' '[:upper:]')
96
+ priority=$(echo "$signal" | jq -r '.priority // "normal"' | tr '[:upper:]' '[:lower:]')
97
+ source=$(echo "$signal" | jq -r '.source // "system"')
98
+ created_at=$(echo "$signal" | jq -r '.created_at // "'"$generated_at"'"')
99
+ expires_at=$(echo "$signal" | jq -r '.expires_at // empty')
100
+ active=$(echo "$signal" | jq -r '.active // true')
101
+
102
+ # Validate signal type
103
+ case "$sig_type" in
104
+ FOCUS|REDIRECT|FEEDBACK) ;;
105
+ *) sig_type="FOCUS" ;;
106
+ esac
107
+
108
+ # Validate priority
109
+ case "$priority" in
110
+ critical|high|normal|low) ;;
111
+ *) priority="normal" ;;
112
+ esac
113
+
114
+ # Build signal element
115
+ xml_output="$xml_output
116
+ <signal id=\"$(echo "$sig_id" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g')\"
117
+ type=\"$sig_type\"
118
+ priority=\"$priority\"
119
+ source=\"$(echo "$source" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g')\"
120
+ created_at=\"$created_at\""
121
+
122
+ [[ -n "$expires_at" && "$expires_at" != "null" ]] && xml_output="$xml_output
123
+ expires_at=\"$expires_at\""
124
+
125
+ xml_output="$xml_output
126
+ active=\"$active\">"
127
+
128
+ # Content section
129
+ # Handle both string content (.content = "text") and object content (.content.text = "text")
130
+ local content_text
131
+ content_text=$(echo "$signal" | jq -r 'if (.content | type) == "string" then .content elif .content.text then .content.text else .message // "" end')
132
+ if [[ -n "$content_text" ]]; then
133
+ xml_output="$xml_output
134
+ <content>
135
+ <text>$(echo "$content_text" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')</text>
136
+ </content>"
137
+ fi
138
+
139
+ xml_output="$xml_output
140
+ </signal>"
141
+
142
+ ((sig_idx++))
143
+ done
144
+
145
+ xml_output="$xml_output
146
+ </pheromones>"
147
+
148
+ # Validate against schema if available
149
+ local validated=false
150
+ if [[ -f "$xsd_file" && "$XMLLINT_AVAILABLE" == "true" ]]; then
151
+ local temp_xml
152
+ temp_xml=$(mktemp)
153
+ echo "$xml_output" > "$temp_xml"
154
+ if xmllint --nonet --noent --noout --schema "$xsd_file" "$temp_xml" 2>/dev/null; then
155
+ validated=true
156
+ fi
157
+ rm -f "$temp_xml"
158
+ fi
159
+
160
+ # Output result
161
+ if [[ -n "$output_xml" ]]; then
162
+ echo "$xml_output" > "$output_xml"
163
+ xml_json_ok "{\"path\":\"$output_xml\",\"validated\":$validated}"
164
+ else
165
+ local escaped_xml
166
+ escaped_xml=$(echo "$xml_output" | jq -Rs '.')
167
+ xml_json_ok "{\"xml\":$escaped_xml,\"validated\":$validated}"
168
+ fi
169
+ }
170
+
171
+ # ============================================================================
172
+ # Pheromone Import (XML to JSON)
173
+ # ============================================================================
174
+
175
+ # xml-pheromone-import: Convert pheromone XML back to JSON
176
+ # Usage: xml-pheromone-import <pheromone_xml_file> [output_json_file]
177
+ # Returns: {"ok":true,"result":{"json":"...","signals":N,"path":"..."}}
178
+ xml-pheromone-import() {
179
+ local xml_file="${1:-}"
180
+ local output_json="${2:-}"
181
+ local preserve_prefixes="${3:-false}"
182
+
183
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
184
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
185
+
186
+ # Check well-formedness
187
+ xmllint --nonet --noent --noout "$xml_file" 2>/dev/null || {
188
+ xml_json_err "PARSE_ERROR" "XML is not well-formed"
189
+ return 1
190
+ }
191
+
192
+ # Extract metadata using XPath
193
+ local version colony_id generated_at
194
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
195
+ version=$(xmllint --nonet --noent --xpath "string(/*/@version)" "$xml_file" 2>/dev/null || echo "1.0.0")
196
+ colony_id=$(xmllint --nonet --noent --xpath "string(/*/@colony_id)" "$xml_file" 2>/dev/null || echo "unknown")
197
+ generated_at=$(xmllint --nonet --noent --xpath "string(/*/@generated_at)" "$xml_file" 2>/dev/null || echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")")
198
+ else
199
+ xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for XML import"
200
+ return 1
201
+ fi
202
+
203
+ # Build JSON structure
204
+ local json_output
205
+ json_output=$(jq -n \
206
+ --arg version "$version" \
207
+ --arg colony_id "$colony_id" \
208
+ --arg generated_at "$generated_at" \
209
+ '{
210
+ version: $version,
211
+ colony_id: $colony_id,
212
+ generated_at: $generated_at,
213
+ signals: []
214
+ }')
215
+
216
+ # Extract signals - use xmlstarlet if available, fallback to grep/awk
217
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
218
+ # Use xmlstarlet for proper namespace handling
219
+ local signals_json
220
+ signals_json=$(xmlstarlet sel -N ph="http://aether.colony/schemas/pheromones" \
221
+ -t -m "//ph:signal" \
222
+ -o '{"id":"' -v "@id" -o '","type":"' -v "@type" -o '","priority":"' -v "@priority" -o '","source":"' -v "@source" -o '","created_at":"' -v "@created_at" -o '","active":' -v "@active" -o '}' \
223
+ -n "$xml_file" 2>/dev/null | jq -s '.')
224
+
225
+ # Extract content text for each signal
226
+ local idx=0
227
+ local enriched_signals="[]"
228
+ while true; do
229
+ local content_text
230
+ content_text=$(xmlstarlet sel -N ph="http://aether.colony/schemas/pheromones" \
231
+ -t -v "//ph:signal[$idx + 1]/ph:content/ph:text" "$xml_file" 2>/dev/null || echo "")
232
+
233
+ if [[ -z "$content_text" && $idx -gt 0 ]]; then
234
+ break
235
+ fi
236
+
237
+ # Add content to signal
238
+ enriched_signals=$(echo "$signals_json" | jq --arg idx "$idx" --arg text "$content_text" '
239
+ .[$idx | tonumber] |= . + {content: {text: $text}}'
240
+ )
241
+
242
+ ((idx++))
243
+ [[ $idx -gt 100 ]] && break # Safety limit
244
+ done
245
+
246
+ # Merge signals into output
247
+ if [[ "$enriched_signals" != "[]" ]]; then
248
+ json_output=$(echo "$json_output" | jq --argjson signals "$enriched_signals" '.signals = $signals')
249
+ else
250
+ json_output=$(echo "$json_output" | jq --argjson signals "$signals_json" '.signals = $signals')
251
+ fi
252
+ else
253
+ # Fallback: basic extraction with grep/sed
254
+ local fallback_signals="[]"
255
+ while IFS= read -r line; do
256
+ if [[ "$line" =~ id=\"([^\"]+)\" ]]; then
257
+ local sid="${BASH_REMATCH[1]}"
258
+ local stype="FOCUS"
259
+ local spriority="normal"
260
+
261
+ # Try to extract type and priority
262
+ if [[ "$line" =~ type=\"([^\"]+)\" ]]; then
263
+ stype="${BASH_REMATCH[1]}"
264
+ fi
265
+ if [[ "$line" =~ priority=\"([^\"]+)\" ]]; then
266
+ spriority="${BASH_REMATCH[1]}"
267
+ fi
268
+
269
+ # Remove namespace prefix if not preserving
270
+ if [[ "$preserve_prefixes" != "true" ]]; then
271
+ sid=$(echo "$sid" | sed 's/^[^:]*://')
272
+ fi
273
+
274
+ fallback_signals=$(echo "$fallback_signals" | jq \
275
+ --arg id "$sid" \
276
+ --arg type "$stype" \
277
+ --arg priority "$spriority" \
278
+ '. + [{id: $id, type: $type, priority: $priority, source: "xml-import", created_at: "'"$generated_at"'", active: true}]')
279
+ fi
280
+ done < <(grep '<signal' "$xml_file")
281
+
282
+ json_output=$(echo "$json_output" | jq --argjson signals "$fallback_signals" '.signals = $signals')
283
+ fi
284
+
285
+ local signal_count
286
+ signal_count=$(echo "$json_output" | jq '.signals | length')
287
+
288
+ # Output result
289
+ if [[ -n "$output_json" ]]; then
290
+ echo "$json_output" > "$output_json"
291
+ xml_json_ok "{\"path\":\"$output_json\",\"signals\":$signal_count}"
292
+ else
293
+ local escaped_json
294
+ escaped_json=$(echo "$json_output" | jq -Rs '.')
295
+ xml_json_ok "{\"json\":$escaped_json,\"signals\":$signal_count}"
296
+ fi
297
+ }
298
+
299
+ # ============================================================================
300
+ # Pheromone Merge (Multiple Colonies)
301
+ # ============================================================================
302
+
303
+ # xml-pheromone-merge: Merge pheromone XML from multiple colonies
304
+ # Usage: xml-pheromone-merge <output_xml> <input_xml_files...>
305
+ # Options:
306
+ # --namespace <prefix> - Add colony prefix to signal IDs (default: auto-generate from colony_id)
307
+ # --deduplicate - Remove duplicate signals by ID (default: true)
308
+ # --target <path> - Target output file (default: ~/.aether/eternal/pheromones.xml)
309
+ # Returns: {"ok":true,"result":{"path":"...","signals":N,"colonies":M}}
310
+ xml-pheromone-merge() {
311
+ local output_file=""
312
+ local namespace_prefix=""
313
+ local deduplicate=true
314
+ local input_files=()
315
+ local arg_idx=0
316
+
317
+ # Parse arguments
318
+ while [[ $arg_idx -lt $# ]]; do
319
+ local arg="${*:$((arg_idx + 1)):1}"
320
+ case "$arg" in
321
+ --namespace)
322
+ namespace_prefix="${*:$((arg_idx + 2)):1}"
323
+ ((arg_idx += 2))
324
+ ;;
325
+ --no-deduplicate)
326
+ deduplicate=false
327
+ ((arg_idx++))
328
+ ;;
329
+ --deduplicate)
330
+ deduplicate=true
331
+ ((arg_idx++))
332
+ ;;
333
+ --target)
334
+ output_file="${*:$((arg_idx + 2)):1}"
335
+ ((arg_idx += 2))
336
+ ;;
337
+ -*)
338
+ xml_json_err "INVALID_ARG" "Unknown option: $arg"
339
+ return 1
340
+ ;;
341
+ *)
342
+ if [[ -z "$output_file" ]]; then
343
+ output_file="$arg"
344
+ else
345
+ input_files+=("$arg")
346
+ fi
347
+ ((arg_idx++))
348
+ ;;
349
+ esac
350
+ done
351
+
352
+ # Default output path
353
+ [[ -z "$output_file" ]] && output_file="${HOME}/.aether/eternal/pheromones.xml"
354
+
355
+ # Validate input files
356
+ if [[ ${#input_files[@]} -eq 0 ]]; then
357
+ xml_json_err "MISSING_ARG" "No input XML files specified"
358
+ return 1
359
+ fi
360
+
361
+ for file in "${input_files[@]}"; do
362
+ [[ -f "$file" ]] || { xml_json_err "FILE_NOT_FOUND" "Input file not found: $file"; return 1; }
363
+ done
364
+
365
+ # Ensure output directory exists
366
+ mkdir -p "$(dirname "$output_file")"
367
+
368
+ # Generate merged XML
369
+ local generated_at
370
+ generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
371
+
372
+ local merged_xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>
373
+ <pheromones xmlns=\"http://aether.colony/schemas/pheromones\"
374
+ xmlns:ph=\"http://aether.colony/schemas/pheromones\"
375
+ version=\"1.0.0\"
376
+ generated_at=\"$generated_at\"
377
+ colony_id=\"merged\">"
378
+
379
+ merged_xml="$merged_xml
380
+ <metadata>
381
+ <source type=\"system\">aether-pheromone-merge</source>
382
+ <context>Merged pheromones from ${#input_files[@]} colonies</context>
383
+ </metadata>"
384
+
385
+ # Track unique signal IDs for deduplication
386
+ declare -A seen_ids
387
+ local total_signals=0
388
+ local unique_signals=0
389
+ local colonies_merged=0
390
+
391
+ for input_file in "${input_files[@]}"; do
392
+ # Get colony ID from source file
393
+ local source_colony_id
394
+ if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
395
+ source_colony_id=$(xmllint --nonet --noent --xpath "string(/*/@colony_id)" "$input_file" 2>/dev/null || echo "colony_$colonies_merged")
396
+ else
397
+ source_colony_id=$(grep -o 'colony_id="[^"]*"' "$input_file" | head -1 | cut -d'"' -f2 || echo "colony_$colonies_merged")
398
+ fi
399
+
400
+ # Determine prefix for this colony
401
+ local colony_prefix
402
+ if [[ -n "$namespace_prefix" ]]; then
403
+ colony_prefix="$namespace_prefix"
404
+ else
405
+ colony_prefix="$source_colony_id"
406
+ fi
407
+
408
+ # Extract signals from this file using xmlstarlet
409
+ if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
410
+ # Get signal count
411
+ local sig_count
412
+ sig_count=$(xmlstarlet sel -N ph="http://aether.colony/schemas/pheromones" \
413
+ -t -v "count(//ph:signal)" "$input_file" 2>/dev/null || echo "0")
414
+
415
+ local idx=1
416
+ while [[ $idx -le $sig_count ]]; do
417
+ # Extract full signal element
418
+ local sig_xml
419
+ sig_xml=$(xmlstarlet sel -N ph="http://aether.colony/schemas/pheromones" \
420
+ -t -c "//ph:signal[$idx]" "$input_file" 2>/dev/null)
421
+
422
+ if [[ -n "$sig_xml" ]]; then
423
+ # Extract original ID
424
+ local orig_id
425
+ orig_id=$(echo "$sig_xml" | grep -oE 'id="[^"]+"' | head -1 | cut -d'"' -f2)
426
+ local new_id="${colony_prefix}:${orig_id}"
427
+
428
+ ((total_signals++))
429
+
430
+ # Check for duplicates
431
+ if [[ "$deduplicate" == "true" ]]; then
432
+ if [[ -n "${seen_ids[$new_id]:-}" ]]; then
433
+ ((idx++))
434
+ continue
435
+ fi
436
+ seen_ids[$new_id]=1
437
+ fi
438
+
439
+ # Replace ID with prefixed version
440
+ sig_xml=$(echo "$sig_xml" | sed "s/id=\"$orig_id\"/id=\"$new_id\"/")
441
+
442
+ # Add signal to merged XML
443
+ merged_xml="$merged_xml
444
+ $sig_xml"
445
+ ((unique_signals++))
446
+ fi
447
+ ((idx++))
448
+ done
449
+ else
450
+ # Fallback: extract with awk and sed
451
+ local sig_block=""
452
+ local in_signal=false
453
+ while IFS= read -r line; do
454
+ if echo "$line" | grep -qE '^[[:space:]]*<signal[[:space:]]'; then
455
+ in_signal=true
456
+ sig_block="$line"
457
+ # Extract and prefix ID
458
+ if [[ "$line" =~ id=\"([^\"]+)\" ]]; then
459
+ local orig_id="${BASH_REMATCH[1]}"
460
+ local new_id="${colony_prefix}:${orig_id}"
461
+ line=$(echo "$line" | sed "s/id=\"$orig_id\"/id=\"$new_id\"/")
462
+ fi
463
+ elif [[ "$in_signal" == true ]]; then
464
+ sig_block="$sig_block
465
+ $line"
466
+ if echo "$line" | grep -qE '</signal>'; then
467
+ # Process complete signal
468
+ local signal_id
469
+ if [[ "$sig_block" =~ id=\"([^\"]+)\" ]]; then
470
+ signal_id="${BASH_REMATCH[1]}"
471
+ ((total_signals++))
472
+
473
+ # Check for duplicates
474
+ if [[ "$deduplicate" == "true" ]]; then
475
+ if [[ -n "${seen_ids[$signal_id]:-}" ]]; then
476
+ in_signal=false
477
+ sig_block=""
478
+ continue
479
+ fi
480
+ seen_ids[$signal_id]=1
481
+ fi
482
+
483
+ # Add signal to merged XML
484
+ merged_xml="$merged_xml
485
+ $sig_block"
486
+ ((unique_signals++))
487
+ fi
488
+ in_signal=false
489
+ sig_block=""
490
+ fi
491
+ fi
492
+ done < "$input_file"
493
+ fi
494
+
495
+ ((colonies_merged++))
496
+ done
497
+
498
+ # Close root element
499
+ merged_xml="$merged_xml
500
+ </pheromones>"
501
+
502
+ # Write output
503
+ echo "$merged_xml" > "$output_file"
504
+
505
+ xml_json_ok "{\"path\":\"$output_file\",\"signals\":$unique_signals,\"colonies\":$colonies_merged,\"duplicates_removed\":$((total_signals - unique_signals))}"
506
+ }
507
+
508
+ # ============================================================================
509
+ # Pheromone Validation
510
+ # ============================================================================
511
+
512
+ # xml-pheromone-validate: Validate pheromone XML against schema
513
+ # Usage: xml-pheromone-validate <pheromone_xml> [xsd_schema]
514
+ # Returns: {"ok":true,"result":{"valid":true,"errors":[]}}
515
+ xml-pheromone-validate() {
516
+ local xml_file="${1:-}"
517
+ local xsd_file="${2:-.aether/schemas/pheromone.xsd}"
518
+
519
+ [[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
520
+ [[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
521
+
522
+ if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
523
+ xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for validation"
524
+ return 1
525
+ fi
526
+
527
+ if [[ ! -f "$xsd_file" ]]; then
528
+ xml_json_err "SCHEMA_NOT_FOUND" "XSD schema not found: $xsd_file"
529
+ return 1
530
+ fi
531
+
532
+ local errors
533
+ errors=$(xmllint --nonet --noent --noout --schema "$xsd_file" "$xml_file" 2>&1) && {
534
+ xml_json_ok '{"valid":true,"errors":[]}'
535
+ return 0
536
+ } || {
537
+ local escaped_errors
538
+ escaped_errors=$(echo "$errors" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g' | tr '\n' ' ')
539
+ xml_json_ok "{\"valid\":false,\"errors\":[\"$escaped_errors\"]}"
540
+ return 0
541
+ }
542
+ }
543
+
544
+ # ============================================================================
545
+ # Namespace Utilities
546
+ # ============================================================================
547
+
548
+ # xml-pheromone-prefix-id: Add namespace prefix to signal ID
549
+ # Usage: xml-pheromone-prefix-id <signal_id> <colony_prefix>
550
+ # Returns: Prefixed ID (direct output, not JSON)
551
+ xml-pheromone-prefix-id() {
552
+ local signal_id="${1:-}"
553
+ local colony_prefix="${2:-}"
554
+
555
+ [[ -z "$signal_id" ]] && { echo ""; return 1; }
556
+ [[ -z "$colony_prefix" ]] && { echo "$signal_id"; return 0; }
557
+
558
+ echo "${colony_prefix}:${signal_id}"
559
+ }
560
+
561
+ # xml-pheromone-deprefix-id: Remove namespace prefix from signal ID
562
+ # Usage: xml-pheromone-deprefix-id <prefixed_id>
563
+ # Returns: Original ID (direct output, not JSON)
564
+ xml-pheromone-deprefix-id() {
565
+ local prefixed_id="${1:-}"
566
+
567
+ [[ -z "$prefixed_id" ]] && { echo ""; return 1; }
568
+
569
+ # Extract ID after colon
570
+ echo "$prefixed_id" | sed 's/^[^:]*://'
571
+ }
572
+
573
+ # Export functions
574
+ export -f xml-pheromone-export xml-pheromone-import xml-pheromone-validate xml-pheromone-merge
575
+ export -f xml-pheromone-prefix-id xml-pheromone-deprefix-id
@@ -0,0 +1,87 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <pheromones xmlns="http://aether.colony/schemas/pheromones"
3
+ xmlns:ph="http://aether.colony/schemas/pheromones"
4
+ version="1.0.0"
5
+ generated_at="2026-02-17T23:51:44Z"
6
+ colony_id="aether-dev">
7
+ <metadata>
8
+ <source type="system">aether-pheromone-converter</source>
9
+ <context>Colony pheromone signals</context>
10
+ </metadata>
11
+ <signal id="sig_focus_001"
12
+ type="FOCUS"
13
+ priority="normal"
14
+ source="user"
15
+ created_at="2026-02-16T10:00:00Z"
16
+ expires_at="2026-02-17T10:00:00Z"
17
+ active="true">
18
+ <content>
19
+ <text>XML migration and pheromone system implementation</text>
20
+ </content>
21
+ </signal>
22
+ <signal id="sig_redirect_001"
23
+ type="REDIRECT"
24
+ priority="high"
25
+ source="system"
26
+ created_at="2026-02-16T08:00:00Z"
27
+ expires_at="2026-03-16T08:00:00Z"
28
+ active="true">
29
+ <content>
30
+ <text>Avoid editing runtime/ directly - edit .aether/ instead</text>
31
+ </content>
32
+ </signal>
33
+ <signal id="sig_feedback_001"
34
+ type="FEEDBACK"
35
+ priority="low"
36
+ source="worker_builder"
37
+ created_at="2026-02-16T12:00:00Z"
38
+ active="true">
39
+ <content>
40
+ <text>Test coverage is good, continue maintaining 80%+ coverage</text>
41
+ </content>
42
+ </signal>
43
+ <signal id="sig_focus_1771366307000"
44
+ type="FOCUS"
45
+ priority="normal"
46
+ source="user"
47
+ created_at="2026-02-17T22:11:47Z"
48
+ expires_at="phase_end"
49
+ active="true">
50
+ <content>
51
+ <text>test area for pheromone unification</text>
52
+ </content>
53
+ </signal>
54
+ <signal id="sig_focus_1771366386000"
55
+ type="FOCUS"
56
+ priority="normal"
57
+ source="user"
58
+ created_at="2026-02-17T22:13:06Z"
59
+ expires_at="phase_end"
60
+ active="true">
61
+ <content>
62
+ <text>test area</text>
63
+ </content>
64
+ </signal>
65
+ <signal id="sig_redirect_1771366398000"
66
+ type="REDIRECT"
67
+ priority="high"
68
+ source="user"
69
+ created_at="2026-02-17T22:13:18Z"
70
+ expires_at="phase_end"
71
+ active="true">
72
+ <content>
73
+ <text>avoid direct runtime edits</text>
74
+ </content>
75
+ </signal>
76
+ <signal id="sig_feedback_1771366398000"
77
+ type="FEEDBACK"
78
+ priority="low"
79
+ source="user"
80
+ created_at="2026-02-17T22:13:18Z"
81
+ expires_at="phase_end"
82
+ active="true">
83
+ <content>
84
+ <text>keep commits small</text>
85
+ </content>
86
+ </signal>
87
+ </pheromones>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <queen-wisdom xmlns="http://aether.colony/schemas/queen-wisdom/1.0"
3
+ xmlns:qw="http://aether.colony/schemas/queen-wisdom/1.0">
4
+ <metadata>
5
+ <version>1.0.0</version>
6
+ <created>2026-02-18T01:48:47Z</created>
7
+ <modified>2026-02-18T01:48:47Z</modified>
8
+ <colony_id></colony_id>
9
+ </metadata>
10
+ <philosophies>
11
+ </philosophies>
12
+ <patterns>
13
+ </patterns>
14
+ </queen-wisdom>