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.
- package/.aether/CONTEXT.md +160 -0
- package/.aether/QUEEN.md +84 -0
- package/.aether/aether-utils.sh +7749 -0
- package/.aether/docs/QUEEN-SYSTEM.md +211 -0
- package/.aether/docs/README.md +68 -0
- package/.aether/docs/caste-system.md +48 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +93 -0
- package/.aether/docs/disciplines/coding-standards.md +197 -0
- package/.aether/docs/disciplines/debugging.md +207 -0
- package/.aether/docs/disciplines/learning.md +254 -0
- package/.aether/docs/disciplines/tdd.md +257 -0
- package/.aether/docs/disciplines/verification-loop.md +167 -0
- package/.aether/docs/disciplines/verification.md +116 -0
- package/.aether/docs/error-codes.md +268 -0
- package/.aether/docs/known-issues.md +233 -0
- package/.aether/docs/pheromones.md +205 -0
- package/.aether/docs/queen-commands.md +97 -0
- package/.aether/exchange/colony-registry.xml +11 -0
- package/.aether/exchange/pheromone-xml.sh +575 -0
- package/.aether/exchange/pheromones.xml +87 -0
- package/.aether/exchange/queen-wisdom.xml +14 -0
- package/.aether/exchange/registry-xml.sh +273 -0
- package/.aether/exchange/wisdom-xml.sh +319 -0
- package/.aether/midden/approach-changes.md +5 -0
- package/.aether/midden/build-failures.md +5 -0
- package/.aether/midden/test-failures.md +5 -0
- package/.aether/model-profiles.yaml +100 -0
- package/.aether/rules/aether-colony.md +134 -0
- package/.aether/schemas/aether-types.xsd +255 -0
- package/.aether/schemas/colony-registry.xsd +309 -0
- package/.aether/schemas/example-prompt-builder.xml +234 -0
- package/.aether/schemas/pheromone.xsd +163 -0
- package/.aether/schemas/prompt.xsd +416 -0
- package/.aether/schemas/queen-wisdom.xsd +325 -0
- package/.aether/schemas/worker-priming.xsd +276 -0
- package/.aether/templates/QUEEN.md.template +79 -0
- package/.aether/templates/colony-state-reset.jq.template +22 -0
- package/.aether/templates/colony-state.template.json +35 -0
- package/.aether/templates/constraints.template.json +9 -0
- package/.aether/templates/crowned-anthill.template.md +36 -0
- package/.aether/templates/handoff-build-error.template.md +30 -0
- package/.aether/templates/handoff-build-success.template.md +39 -0
- package/.aether/templates/handoff.template.md +40 -0
- package/.aether/templates/learning-observations.template.json +6 -0
- package/.aether/templates/midden.template.json +7 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/.aether/utils/atomic-write.sh +219 -0
- package/.aether/utils/chamber-compare.sh +193 -0
- package/.aether/utils/chamber-utils.sh +297 -0
- package/.aether/utils/colorize-log.sh +132 -0
- package/.aether/utils/error-handler.sh +212 -0
- package/.aether/utils/file-lock.sh +158 -0
- package/.aether/utils/queen-to-md.xsl +395 -0
- package/.aether/utils/semantic-cli.sh +413 -0
- package/.aether/utils/spawn-tree.sh +428 -0
- package/.aether/utils/spawn-with-model.sh +56 -0
- package/.aether/utils/state-loader.sh +215 -0
- package/.aether/utils/swarm-display.sh +268 -0
- package/.aether/utils/watch-spawn-tree.sh +253 -0
- package/.aether/utils/xml-compose.sh +253 -0
- package/.aether/utils/xml-convert.sh +273 -0
- package/.aether/utils/xml-core.sh +186 -0
- package/.aether/utils/xml-query.sh +201 -0
- package/.aether/utils/xml-utils.sh +110 -0
- package/.aether/workers.md +765 -0
- package/.claude/agents/ant/aether-ambassador.md +264 -0
- package/.claude/agents/ant/aether-archaeologist.md +322 -0
- package/.claude/agents/ant/aether-auditor.md +266 -0
- package/.claude/agents/ant/aether-builder.md +187 -0
- package/.claude/agents/ant/aether-chaos.md +268 -0
- package/.claude/agents/ant/aether-chronicler.md +304 -0
- package/.claude/agents/ant/aether-gatekeeper.md +325 -0
- package/.claude/agents/ant/aether-includer.md +373 -0
- package/.claude/agents/ant/aether-keeper.md +271 -0
- package/.claude/agents/ant/aether-measurer.md +317 -0
- package/.claude/agents/ant/aether-probe.md +210 -0
- package/.claude/agents/ant/aether-queen.md +325 -0
- package/.claude/agents/ant/aether-route-setter.md +173 -0
- package/.claude/agents/ant/aether-sage.md +353 -0
- package/.claude/agents/ant/aether-scout.md +142 -0
- package/.claude/agents/ant/aether-surveyor-disciplines.md +416 -0
- package/.claude/agents/ant/aether-surveyor-nest.md +354 -0
- package/.claude/agents/ant/aether-surveyor-pathogens.md +288 -0
- package/.claude/agents/ant/aether-surveyor-provisions.md +359 -0
- package/.claude/agents/ant/aether-tracker.md +265 -0
- package/.claude/agents/ant/aether-watcher.md +244 -0
- package/.claude/agents/ant/aether-weaver.md +247 -0
- package/.claude/commands/ant/archaeology.md +341 -0
- package/.claude/commands/ant/build.md +1160 -0
- package/.claude/commands/ant/chaos.md +349 -0
- package/.claude/commands/ant/colonize.md +270 -0
- package/.claude/commands/ant/continue.md +1070 -0
- package/.claude/commands/ant/council.md +309 -0
- package/.claude/commands/ant/dream.md +265 -0
- package/.claude/commands/ant/entomb.md +487 -0
- package/.claude/commands/ant/feedback.md +78 -0
- package/.claude/commands/ant/flag.md +139 -0
- package/.claude/commands/ant/flags.md +155 -0
- package/.claude/commands/ant/focus.md +58 -0
- package/.claude/commands/ant/help.md +122 -0
- package/.claude/commands/ant/history.md +137 -0
- package/.claude/commands/ant/init.md +409 -0
- package/.claude/commands/ant/interpret.md +267 -0
- package/.claude/commands/ant/lay-eggs.md +201 -0
- package/.claude/commands/ant/maturity.md +102 -0
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +165 -0
- package/.claude/commands/ant/oracle.md +387 -0
- package/.claude/commands/ant/organize.md +227 -0
- package/.claude/commands/ant/pause-colony.md +247 -0
- package/.claude/commands/ant/phase.md +126 -0
- package/.claude/commands/ant/plan.md +544 -0
- package/.claude/commands/ant/redirect.md +58 -0
- package/.claude/commands/ant/resume-colony.md +182 -0
- package/.claude/commands/ant/resume.md +363 -0
- package/.claude/commands/ant/seal.md +306 -0
- package/.claude/commands/ant/status.md +272 -0
- package/.claude/commands/ant/swarm.md +361 -0
- package/.claude/commands/ant/tunnels.md +425 -0
- package/.claude/commands/ant/update.md +209 -0
- package/.claude/commands/ant/verify-castes.md +95 -0
- package/.claude/commands/ant/watch.md +238 -0
- package/.opencode/agents/aether-ambassador.md +140 -0
- package/.opencode/agents/aether-archaeologist.md +108 -0
- package/.opencode/agents/aether-auditor.md +144 -0
- package/.opencode/agents/aether-builder.md +184 -0
- package/.opencode/agents/aether-chaos.md +115 -0
- package/.opencode/agents/aether-chronicler.md +122 -0
- package/.opencode/agents/aether-gatekeeper.md +116 -0
- package/.opencode/agents/aether-includer.md +117 -0
- package/.opencode/agents/aether-keeper.md +177 -0
- package/.opencode/agents/aether-measurer.md +128 -0
- package/.opencode/agents/aether-probe.md +133 -0
- package/.opencode/agents/aether-queen.md +286 -0
- package/.opencode/agents/aether-route-setter.md +130 -0
- package/.opencode/agents/aether-sage.md +106 -0
- package/.opencode/agents/aether-scout.md +101 -0
- package/.opencode/agents/aether-surveyor-disciplines.md +386 -0
- package/.opencode/agents/aether-surveyor-nest.md +324 -0
- package/.opencode/agents/aether-surveyor-pathogens.md +259 -0
- package/.opencode/agents/aether-surveyor-provisions.md +329 -0
- package/.opencode/agents/aether-tracker.md +137 -0
- package/.opencode/agents/aether-watcher.md +174 -0
- package/.opencode/agents/aether-weaver.md +130 -0
- package/.opencode/commands/ant/archaeology.md +338 -0
- package/.opencode/commands/ant/build.md +1200 -0
- package/.opencode/commands/ant/chaos.md +346 -0
- package/.opencode/commands/ant/colonize.md +202 -0
- package/.opencode/commands/ant/continue.md +938 -0
- package/.opencode/commands/ant/council.md +305 -0
- package/.opencode/commands/ant/dream.md +262 -0
- package/.opencode/commands/ant/entomb.md +367 -0
- package/.opencode/commands/ant/feedback.md +80 -0
- package/.opencode/commands/ant/flag.md +137 -0
- package/.opencode/commands/ant/flags.md +153 -0
- package/.opencode/commands/ant/focus.md +56 -0
- package/.opencode/commands/ant/help.md +124 -0
- package/.opencode/commands/ant/history.md +127 -0
- package/.opencode/commands/ant/init.md +337 -0
- package/.opencode/commands/ant/interpret.md +256 -0
- package/.opencode/commands/ant/lay-eggs.md +141 -0
- package/.opencode/commands/ant/maturity.md +92 -0
- package/.opencode/commands/ant/memory-details.md +77 -0
- package/.opencode/commands/ant/migrate-state.md +153 -0
- package/.opencode/commands/ant/oracle.md +338 -0
- package/.opencode/commands/ant/organize.md +224 -0
- package/.opencode/commands/ant/pause-colony.md +220 -0
- package/.opencode/commands/ant/phase.md +123 -0
- package/.opencode/commands/ant/plan.md +531 -0
- package/.opencode/commands/ant/redirect.md +67 -0
- package/.opencode/commands/ant/resume-colony.md +178 -0
- package/.opencode/commands/ant/resume.md +363 -0
- package/.opencode/commands/ant/seal.md +247 -0
- package/.opencode/commands/ant/status.md +272 -0
- package/.opencode/commands/ant/swarm.md +357 -0
- package/.opencode/commands/ant/tunnels.md +406 -0
- package/.opencode/commands/ant/update.md +191 -0
- package/.opencode/commands/ant/verify-castes.md +85 -0
- package/.opencode/commands/ant/watch.md +220 -0
- package/.opencode/opencode.json +3 -0
- package/CHANGELOG.md +325 -0
- package/DISCLAIMER.md +74 -0
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/bin/cli.js +2436 -0
- package/bin/generate-commands.sh +291 -0
- package/bin/lib/caste-colors.js +57 -0
- package/bin/lib/colors.js +76 -0
- package/bin/lib/errors.js +255 -0
- package/bin/lib/event-types.js +190 -0
- package/bin/lib/file-lock.js +695 -0
- package/bin/lib/init.js +454 -0
- package/bin/lib/logger.js +242 -0
- package/bin/lib/model-profiles.js +445 -0
- package/bin/lib/model-verify.js +288 -0
- package/bin/lib/nestmate-loader.js +130 -0
- package/bin/lib/proxy-health.js +253 -0
- package/bin/lib/spawn-logger.js +266 -0
- package/bin/lib/state-guard.js +602 -0
- package/bin/lib/state-sync.js +516 -0
- package/bin/lib/telemetry.js +441 -0
- package/bin/lib/update-transaction.js +1454 -0
- package/bin/npx-install.js +178 -0
- package/bin/sync-to-runtime.sh +6 -0
- package/bin/validate-package.sh +88 -0
- package/package.json +70 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# XML Core Utilities
|
|
3
|
+
# Fundamental XML operations: validation, formatting, escaping
|
|
4
|
+
#
|
|
5
|
+
# Usage: source .aether/utils/xml-core.sh
|
|
6
|
+
# xml-validate <xml_file> <xsd_file>
|
|
7
|
+
# xml-well-formed <xml_file>
|
|
8
|
+
# xml-format <xml_file>
|
|
9
|
+
# xml-escape <text>
|
|
10
|
+
# xml-unescape <text>
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# Feature Detection
|
|
14
|
+
# ============================================================================
|
|
15
|
+
|
|
16
|
+
# Check for required XML tools (only if not already set)
|
|
17
|
+
if [[ -z "${XMLLINT_AVAILABLE:-}" ]]; then
|
|
18
|
+
XMLLINT_AVAILABLE=false
|
|
19
|
+
if command -v xmllint >/dev/null 2>&1; then
|
|
20
|
+
XMLLINT_AVAILABLE=true
|
|
21
|
+
fi
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [[ -z "${XMLSTARLET_AVAILABLE:-}" ]]; then
|
|
25
|
+
XMLSTARLET_AVAILABLE=false
|
|
26
|
+
if command -v xmlstarlet >/dev/null 2>&1; then
|
|
27
|
+
XMLSTARLET_AVAILABLE=true
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [[ -z "${XSLTPROC_AVAILABLE:-}" ]]; then
|
|
32
|
+
XSLTPROC_AVAILABLE=false
|
|
33
|
+
if command -v xsltproc >/dev/null 2>&1; then
|
|
34
|
+
XSLTPROC_AVAILABLE=true
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# ============================================================================
|
|
39
|
+
# JSON Output Helpers
|
|
40
|
+
# ============================================================================
|
|
41
|
+
|
|
42
|
+
xml_json_ok() { printf '{"ok":true,"result":%s}\n' "$1"; }
|
|
43
|
+
|
|
44
|
+
xml_json_err() {
|
|
45
|
+
local code="${1:-UNKNOWN_ERROR}"
|
|
46
|
+
local message="${2:-$1}"
|
|
47
|
+
local details="${3:-}"
|
|
48
|
+
if [[ -n "$details" ]]; then
|
|
49
|
+
printf '{"ok":false,"error":"%s","code":"%s","details":"%s"}\n' "$message" "$code" "$details" >&2
|
|
50
|
+
else
|
|
51
|
+
printf '{"ok":false,"error":"%s","code":"%s"}\n' "$message" "$code" >&2
|
|
52
|
+
fi
|
|
53
|
+
return 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# ============================================================================
|
|
57
|
+
# Core XML Functions
|
|
58
|
+
# ============================================================================
|
|
59
|
+
|
|
60
|
+
# xml-detect-tools: Detect available XML tools
|
|
61
|
+
# Usage: xml-detect-tools
|
|
62
|
+
# Returns: {"ok":true,"result":{"xmllint":true,"xmlstarlet":false,...}}
|
|
63
|
+
xml-detect-tools() {
|
|
64
|
+
xml_json_ok "{\"xmllint\":$XMLLINT_AVAILABLE,\"xmlstarlet\":$XMLSTARLET_AVAILABLE,\"xsltproc\":$XSLTPROC_AVAILABLE}"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# xml-validate: Validate XML against XSD schema using xmllint
|
|
68
|
+
# Usage: xml-validate <xml_file> <xsd_file>
|
|
69
|
+
# Returns: {"ok":true,"result":{"valid":true,"errors":[]}} or error
|
|
70
|
+
xml-validate() {
|
|
71
|
+
local xml_file="${1:-}"
|
|
72
|
+
local xsd_file="${2:-}"
|
|
73
|
+
|
|
74
|
+
# Validate arguments
|
|
75
|
+
[[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
|
|
76
|
+
[[ -z "$xsd_file" ]] && { xml_json_err "MISSING_ARG" "Missing XSD schema file argument"; return 1; }
|
|
77
|
+
[[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
|
|
78
|
+
[[ -f "$xsd_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XSD schema file not found: $xsd_file"; return 1; }
|
|
79
|
+
|
|
80
|
+
# Check for xmllint
|
|
81
|
+
if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
|
|
82
|
+
xml_json_err "TOOL_NOT_AVAILABLE" "xmllint not available. Install libxml2 utilities."
|
|
83
|
+
return 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Validate XML against XSD (with XXE protection)
|
|
87
|
+
local errors
|
|
88
|
+
errors=$(xmllint --nonet --noent --noout --schema "$xsd_file" "$xml_file" 2>&1) && {
|
|
89
|
+
xml_json_ok '{"valid":true,"errors":[]}'
|
|
90
|
+
return 0
|
|
91
|
+
} || {
|
|
92
|
+
# Escape errors for JSON
|
|
93
|
+
local escaped_errors
|
|
94
|
+
escaped_errors=$(echo "$errors" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g' | tr '\n' ' ')
|
|
95
|
+
xml_json_ok "{\"valid\":false,\"errors\":[\"$escaped_errors\"]}"
|
|
96
|
+
return 0
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# xml-well-formed: Check if XML is well-formed (no schema validation)
|
|
101
|
+
# Usage: xml-well-formed <xml_file>
|
|
102
|
+
# Returns: {"ok":true,"result":{"well_formed":true}} or error
|
|
103
|
+
xml-well-formed() {
|
|
104
|
+
local xml_file="${1:-}"
|
|
105
|
+
|
|
106
|
+
[[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
|
|
107
|
+
[[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
|
|
108
|
+
|
|
109
|
+
if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
|
|
110
|
+
xml_json_err "TOOL_NOT_AVAILABLE" "xmllint not available. Install libxml2 utilities."
|
|
111
|
+
return 1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Check well-formedness with XXE protection
|
|
115
|
+
if xmllint --nonet --noent --noout "$xml_file" 2>/dev/null; then
|
|
116
|
+
xml_json_ok '{"well_formed":true}'
|
|
117
|
+
return 0
|
|
118
|
+
else
|
|
119
|
+
xml_json_ok '{"well_formed":false}'
|
|
120
|
+
return 0
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# xml-format: Pretty-print XML with proper indentation
|
|
125
|
+
# Usage: xml-format <xml_file> [output_file]
|
|
126
|
+
# Returns: {"ok":true,"result":{"formatted":true,"output":"..."}} or writes to file
|
|
127
|
+
xml-format() {
|
|
128
|
+
local xml_file="${1:-}"
|
|
129
|
+
local output_file="${2:-}"
|
|
130
|
+
|
|
131
|
+
[[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
|
|
132
|
+
[[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
|
|
133
|
+
|
|
134
|
+
if [[ "$XMLLINT_AVAILABLE" != "true" ]]; then
|
|
135
|
+
xml_json_err "TOOL_NOT_AVAILABLE" "xmllint not available. Install libxml2 utilities."
|
|
136
|
+
return 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
local formatted
|
|
140
|
+
formatted=$(xmllint --nonet --noent --format "$xml_file" 2>/dev/null) || {
|
|
141
|
+
xml_json_err "PARSE_ERROR" "Failed to parse XML file"
|
|
142
|
+
return 1
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if [[ -n "$output_file" ]]; then
|
|
146
|
+
echo "$formatted" > "$output_file"
|
|
147
|
+
xml_json_ok "{\"formatted\":true,\"path\":\"$output_file\"}"
|
|
148
|
+
else
|
|
149
|
+
# Escape for JSON
|
|
150
|
+
local escaped
|
|
151
|
+
escaped=$(echo "$formatted" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
|
|
152
|
+
xml_json_ok "{\"formatted\":true,\"output\":\"$escaped\"}"
|
|
153
|
+
fi
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# xml-escape: Escape special XML characters
|
|
157
|
+
# Usage: xml-escape <text>
|
|
158
|
+
# Returns: Escaped text (not JSON - direct output)
|
|
159
|
+
xml-escape() {
|
|
160
|
+
local text="${1:-}"
|
|
161
|
+
# Escape &, <, >, ", '
|
|
162
|
+
echo "$text" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g; s/'"'"'/\'/g'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# xml-unescape: Unescape XML entities
|
|
166
|
+
# Usage: xml-unescape <text>
|
|
167
|
+
# Returns: Unescaped text (not JSON - direct output)
|
|
168
|
+
xml-unescape() {
|
|
169
|
+
local text="${1:-}"
|
|
170
|
+
# Unescape XML entities
|
|
171
|
+
echo "$text" | sed 's/&/\&/g; s/</</g; s/>/>/g; s/"/"/g; s/'/'"'"'/g'
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# xml-escape-content: Escape content for XML CDATA or text nodes
|
|
175
|
+
# Internal helper for consistent escaping
|
|
176
|
+
_xml_escape_content() {
|
|
177
|
+
local content="${1:-}"
|
|
178
|
+
# Basic XML escaping for text content
|
|
179
|
+
echo "$content" | sed 's/&/\&/g; s/</\</g; s/>/\>/g'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Export functions for use by other modules
|
|
183
|
+
export -f xml_json_ok xml_json_err
|
|
184
|
+
export -f xml-detect-tools xml-validate xml-well-formed xml-format
|
|
185
|
+
export -f xml-escape xml-unescape _xml_escape_content
|
|
186
|
+
export XMLLINT_AVAILABLE XMLSTARLET_AVAILABLE XSLTPROC_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
|
+
|