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