aether-colony 3.1.17 → 5.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/aether-utils.sh +5354 -0
- package/.aether/agents-claude/aether-ambassador.md +265 -0
- package/.aether/agents-claude/aether-archaeologist.md +327 -0
- package/.aether/agents-claude/aether-architect.md +236 -0
- package/.aether/agents-claude/aether-auditor.md +271 -0
- package/.aether/agents-claude/aether-builder.md +224 -0
- package/.aether/agents-claude/aether-chaos.md +269 -0
- package/.aether/agents-claude/aether-chronicler.md +305 -0
- package/.aether/agents-claude/aether-gatekeeper.md +330 -0
- package/.aether/agents-claude/aether-includer.md +374 -0
- package/.aether/agents-claude/aether-keeper.md +272 -0
- package/.aether/agents-claude/aether-measurer.md +322 -0
- package/.aether/agents-claude/aether-oracle.md +237 -0
- package/.aether/agents-claude/aether-probe.md +211 -0
- package/.aether/agents-claude/aether-queen.md +330 -0
- package/.aether/agents-claude/aether-route-setter.md +178 -0
- package/.aether/agents-claude/aether-sage.md +418 -0
- package/.aether/agents-claude/aether-scout.md +179 -0
- package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
- package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
- package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
- package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
- package/.aether/agents-claude/aether-tracker.md +270 -0
- package/.aether/agents-claude/aether-watcher.md +280 -0
- package/.aether/agents-claude/aether-weaver.md +248 -0
- package/.aether/commands/archaeology.yaml +653 -0
- package/.aether/commands/build.yaml +1221 -0
- package/.aether/commands/chaos.yaml +653 -0
- package/.aether/commands/colonize.yaml +438 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +304 -0
- package/.aether/commands/data-clean.yaml +80 -0
- package/.aether/commands/dream.yaml +275 -0
- package/.aether/commands/entomb.yaml +863 -0
- package/.aether/commands/export-signals.yaml +64 -0
- package/.aether/commands/feedback.yaml +158 -0
- package/.aether/commands/flag.yaml +160 -0
- package/.aether/commands/flags.yaml +177 -0
- package/.aether/commands/focus.yaml +112 -0
- package/.aether/commands/help.yaml +167 -0
- package/.aether/commands/history.yaml +137 -0
- package/.aether/commands/import-signals.yaml +79 -0
- package/.aether/commands/init.yaml +469 -0
- package/.aether/commands/insert-phase.yaml +98 -0
- package/.aether/commands/interpret.yaml +285 -0
- package/.aether/commands/lay-eggs.yaml +224 -0
- package/.aether/commands/maturity.yaml +122 -0
- package/.aether/commands/memory-details.yaml +74 -0
- package/.aether/commands/migrate-state.yaml +174 -0
- package/.aether/commands/oracle.yaml +1224 -0
- package/.aether/commands/organize.yaml +446 -0
- package/.aether/commands/patrol.yaml +621 -0
- package/.aether/commands/pause-colony.yaml +424 -0
- package/.aether/commands/phase.yaml +124 -0
- package/.aether/commands/pheromones.yaml +153 -0
- package/.aether/commands/plan.yaml +1313 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +373 -0
- package/.aether/commands/resume.yaml +398 -0
- package/.aether/commands/run.yaml +193 -0
- package/.aether/commands/seal.yaml +1205 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +364 -0
- package/.aether/commands/swarm.yaml +352 -0
- package/.aether/commands/tunnels.yaml +814 -0
- package/.aether/commands/update.yaml +131 -0
- package/.aether/commands/verify-castes.yaml +159 -0
- package/.aether/commands/watch.yaml +454 -0
- package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
- package/.aether/docs/QUEEN-SYSTEM.md +211 -0
- package/.aether/docs/README.md +98 -0
- package/.aether/docs/caste-system.md +48 -0
- package/.aether/docs/command-playbooks/README.md +23 -0
- package/.aether/docs/command-playbooks/build-complete.md +349 -0
- package/.aether/docs/command-playbooks/build-context.md +282 -0
- package/.aether/docs/command-playbooks/build-full.md +1682 -0
- package/.aether/docs/command-playbooks/build-prep.md +283 -0
- package/.aether/docs/command-playbooks/build-verify.md +405 -0
- package/.aether/docs/command-playbooks/build-wave.md +749 -0
- package/.aether/docs/command-playbooks/continue-advance.md +524 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
- package/.aether/docs/command-playbooks/continue-full.md +1724 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +406 -0
- package/.aether/docs/context-continuity.md +84 -0
- package/{runtime → .aether/docs/disciplines}/DISCIPLINES.md +13 -11
- package/.aether/docs/error-codes.md +268 -0
- package/.aether/docs/known-issues.md +94 -0
- package/{runtime → .aether}/docs/pheromones.md +86 -6
- package/.aether/docs/plans/pheromone-display-plan.md +257 -0
- package/.aether/docs/queen-commands.md +98 -0
- package/.aether/docs/source-of-truth-map.md +132 -0
- package/.aether/docs/xml-utilities.md +47 -0
- package/{runtime → .aether}/exchange/pheromone-xml.sh +2 -1
- package/{runtime → .aether}/exchange/registry-xml.sh +7 -3
- package/{runtime → .aether}/exchange/wisdom-xml.sh +11 -4
- package/.aether/rules/aether-colony.md +144 -0
- package/.aether/schemas/example-prompt-builder.xml +234 -0
- package/.aether/scripts/incident-test-add.sh +47 -0
- package/.aether/scripts/weekly-audit.sh +79 -0
- package/.aether/skills/.index.json +649 -0
- package/.aether/skills/colony/.manifest.json +16 -0
- package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
- package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
- package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
- package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
- package/.aether/skills/colony/context-management/SKILL.md +80 -0
- package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
- package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
- package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
- package/.aether/skills/colony/state-safety/SKILL.md +84 -0
- package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
- package/.aether/skills/domain/.manifest.json +24 -0
- package/.aether/skills/domain/README.md +33 -0
- package/.aether/skills/domain/django/SKILL.md +49 -0
- package/.aether/skills/domain/docker/SKILL.md +52 -0
- package/.aether/skills/domain/golang/SKILL.md +52 -0
- package/.aether/skills/domain/graphql/SKILL.md +51 -0
- package/.aether/skills/domain/html-css/SKILL.md +48 -0
- package/.aether/skills/domain/nextjs/SKILL.md +45 -0
- package/.aether/skills/domain/nodejs/SKILL.md +53 -0
- package/.aether/skills/domain/postgresql/SKILL.md +53 -0
- package/.aether/skills/domain/prisma/SKILL.md +59 -0
- package/.aether/skills/domain/python/SKILL.md +50 -0
- package/.aether/skills/domain/rails/SKILL.md +52 -0
- package/.aether/skills/domain/react/SKILL.md +45 -0
- package/.aether/skills/domain/rest-api/SKILL.md +58 -0
- package/.aether/skills/domain/svelte/SKILL.md +47 -0
- package/.aether/skills/domain/tailwind/SKILL.md +45 -0
- package/.aether/skills/domain/testing/SKILL.md +53 -0
- package/.aether/skills/domain/typescript/SKILL.md +58 -0
- package/.aether/skills/domain/vue/SKILL.md +49 -0
- package/.aether/templates/QUEEN.md.template +61 -0
- package/.aether/templates/colony-state-reset.jq.template +23 -0
- package/.aether/templates/colony-state.template.json +39 -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 +13 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/{runtime → .aether}/utils/atomic-write.sh +68 -22
- package/{runtime → .aether}/utils/chamber-compare.sh +23 -10
- package/.aether/utils/chamber-utils.sh +440 -0
- package/.aether/utils/emoji-audit.sh +166 -0
- package/{runtime → .aether}/utils/error-handler.sh +34 -8
- package/.aether/utils/file-lock.sh +313 -0
- package/.aether/utils/flag.sh +267 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +342 -0
- package/.aether/utils/oracle/oracle.md +168 -0
- package/.aether/utils/oracle/oracle.sh +1023 -0
- package/.aether/utils/pheromone.sh +2029 -0
- package/.aether/utils/queen.sh +1698 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +415 -0
- package/.aether/utils/session.sh +552 -0
- package/.aether/utils/skills.sh +509 -0
- package/.aether/utils/spawn-tree.sh +260 -0
- package/.aether/utils/spawn.sh +260 -0
- package/.aether/utils/state-api.sh +199 -0
- package/{runtime → .aether}/utils/state-loader.sh +8 -6
- package/.aether/utils/suggest.sh +611 -0
- package/{runtime → .aether}/utils/swarm-display.sh +10 -1
- package/.aether/utils/swarm.sh +1004 -0
- package/{runtime → .aether}/utils/watch-spawn-tree.sh +11 -2
- package/{runtime → .aether}/utils/xml-compose.sh +9 -3
- package/.aether/utils/xml-convert.sh +277 -0
- package/{runtime → .aether}/utils/xml-core.sh +5 -9
- package/.aether/utils/xml-query.sh +201 -0
- package/.aether/utils/xml-utils.sh +110 -0
- package/{runtime → .aether}/workers.md +97 -81
- package/.claude/agents/ant/aether-ambassador.md +265 -0
- package/.claude/agents/ant/aether-archaeologist.md +327 -0
- package/.claude/agents/ant/aether-architect.md +236 -0
- package/.claude/agents/ant/aether-auditor.md +271 -0
- package/.claude/agents/ant/aether-builder.md +224 -0
- package/.claude/agents/ant/aether-chaos.md +269 -0
- package/.claude/agents/ant/aether-chronicler.md +305 -0
- package/.claude/agents/ant/aether-gatekeeper.md +330 -0
- package/.claude/agents/ant/aether-includer.md +374 -0
- package/.claude/agents/ant/aether-keeper.md +272 -0
- package/.claude/agents/ant/aether-measurer.md +322 -0
- package/.claude/agents/ant/aether-oracle.md +237 -0
- package/.claude/agents/ant/aether-probe.md +211 -0
- package/.claude/agents/ant/aether-queen.md +330 -0
- package/.claude/agents/ant/aether-route-setter.md +178 -0
- package/.claude/agents/ant/aether-sage.md +418 -0
- package/.claude/agents/ant/aether-scout.md +179 -0
- package/.claude/agents/ant/aether-surveyor-disciplines.md +417 -0
- package/.claude/agents/ant/aether-surveyor-nest.md +355 -0
- package/.claude/agents/ant/aether-surveyor-pathogens.md +289 -0
- package/.claude/agents/ant/aether-surveyor-provisions.md +360 -0
- package/.claude/agents/ant/aether-tracker.md +270 -0
- package/.claude/agents/ant/aether-watcher.md +280 -0
- package/.claude/agents/ant/aether-weaver.md +248 -0
- package/.claude/commands/ant/archaeology.md +16 -14
- package/.claude/commands/ant/build.md +43 -1028
- package/.claude/commands/ant/chaos.md +19 -23
- package/.claude/commands/ant/colonize.md +52 -31
- package/.claude/commands/ant/continue.md +40 -1016
- package/.claude/commands/ant/council.md +21 -18
- package/.claude/commands/ant/data-clean.md +81 -0
- package/.claude/commands/ant/dream.md +27 -15
- package/.claude/commands/ant/entomb.md +317 -225
- package/.claude/commands/ant/export-signals.md +57 -0
- package/.claude/commands/ant/feedback.md +48 -26
- package/.claude/commands/ant/flag.md +30 -10
- package/.claude/commands/ant/flags.md +34 -12
- package/.claude/commands/ant/focus.md +45 -19
- package/.claude/commands/ant/help.md +50 -8
- package/.claude/commands/ant/history.md +13 -0
- package/.claude/commands/ant/import-signals.md +71 -0
- package/.claude/commands/ant/init.md +345 -194
- package/.claude/commands/ant/insert-phase.md +101 -0
- package/.claude/commands/ant/interpret.md +26 -4
- package/.claude/commands/ant/lay-eggs.md +184 -127
- package/.claude/commands/ant/maturity.md +32 -11
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +20 -2
- package/.claude/commands/ant/oracle.md +337 -74
- package/.claude/commands/ant/organize.md +39 -25
- package/.claude/commands/ant/patrol.md +620 -0
- package/.claude/commands/ant/pause-colony.md +23 -27
- package/.claude/commands/ant/phase.md +40 -42
- package/.claude/commands/ant/pheromones.md +156 -0
- package/.claude/commands/ant/plan.md +185 -51
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/redirect.md +45 -30
- package/.claude/commands/ant/resume-colony.md +51 -27
- package/.claude/commands/ant/resume.md +314 -94
- package/.claude/commands/ant/run.md +195 -0
- package/.claude/commands/ant/seal.md +650 -221
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +196 -31
- package/.claude/commands/ant/swarm.md +16 -46
- package/.claude/commands/ant/tunnels.md +280 -105
- package/.claude/commands/ant/update.md +73 -89
- package/.claude/commands/ant/verify-castes.md +100 -42
- package/.claude/commands/ant/watch.md +14 -12
- package/.opencode/agents/aether-ambassador.md +63 -20
- package/.opencode/agents/aether-archaeologist.md +29 -12
- package/.opencode/agents/aether-architect.md +103 -36
- package/.opencode/agents/aether-auditor.md +51 -18
- package/.opencode/agents/aether-builder.md +70 -20
- package/.opencode/agents/aether-chaos.md +29 -12
- package/.opencode/agents/aether-chronicler.md +60 -18
- package/.opencode/agents/aether-gatekeeper.md +27 -18
- package/.opencode/agents/aether-includer.md +27 -18
- package/.opencode/agents/aether-keeper.md +89 -18
- package/.opencode/agents/aether-measurer.md +27 -18
- package/.opencode/agents/aether-oracle.md +137 -0
- package/.opencode/agents/aether-probe.md +60 -18
- package/.opencode/agents/aether-queen.md +172 -24
- package/.opencode/agents/aether-route-setter.md +57 -12
- package/.opencode/agents/aether-sage.md +26 -18
- package/.opencode/agents/aether-scout.md +28 -20
- package/.opencode/agents/aether-surveyor-disciplines.md +59 -2
- package/.opencode/agents/aether-surveyor-nest.md +59 -2
- package/.opencode/agents/aether-surveyor-pathogens.md +57 -2
- package/.opencode/agents/aether-surveyor-provisions.md +59 -2
- package/.opencode/agents/aether-tracker.md +64 -18
- package/.opencode/agents/aether-watcher.md +66 -19
- package/.opencode/agents/aether-weaver.md +61 -18
- package/.opencode/commands/ant/archaeology.md +7 -14
- package/.opencode/commands/ant/build.md +437 -257
- package/.opencode/commands/ant/chaos.md +7 -24
- package/.opencode/commands/ant/colonize.md +8 -17
- package/.opencode/commands/ant/continue.md +661 -142
- package/.opencode/commands/ant/council.md +11 -22
- package/.opencode/commands/ant/data-clean.md +77 -0
- package/.opencode/commands/ant/dream.md +15 -17
- package/.opencode/commands/ant/entomb.md +133 -62
- package/.opencode/commands/ant/export-signals.md +54 -0
- package/.opencode/commands/ant/feedback.md +24 -5
- package/.opencode/commands/ant/flag.md +16 -4
- package/.opencode/commands/ant/flags.md +24 -10
- package/.opencode/commands/ant/focus.md +22 -5
- package/.opencode/commands/ant/help.md +41 -8
- package/.opencode/commands/ant/history.md +9 -0
- package/.opencode/commands/ant/import-signals.md +68 -0
- package/.opencode/commands/ant/init.md +374 -167
- package/.opencode/commands/ant/insert-phase.md +107 -0
- package/.opencode/commands/ant/interpret.md +16 -0
- package/.opencode/commands/ant/lay-eggs.md +184 -112
- package/.opencode/commands/ant/maturity.md +18 -2
- package/.opencode/commands/ant/memory-details.md +83 -0
- package/.opencode/commands/ant/migrate-state.md +12 -0
- package/.opencode/commands/ant/oracle.md +322 -67
- package/.opencode/commands/ant/organize.md +18 -16
- package/.opencode/commands/ant/patrol.md +626 -0
- package/.opencode/commands/ant/pause-colony.md +12 -29
- package/.opencode/commands/ant/phase.md +30 -40
- package/.opencode/commands/ant/pheromones.md +162 -0
- package/.opencode/commands/ant/plan.md +184 -56
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +38 -27
- package/.opencode/commands/ant/resume.md +385 -0
- package/.opencode/commands/ant/run.md +201 -0
- package/.opencode/commands/ant/seal.md +259 -45
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +135 -31
- package/.opencode/commands/ant/swarm.md +3 -345
- package/.opencode/commands/ant/tunnels.md +152 -9
- package/.opencode/commands/ant/update.md +70 -91
- package/.opencode/commands/ant/verify-castes.md +96 -42
- package/.opencode/commands/ant/watch.md +7 -0
- package/CHANGELOG.md +356 -0
- package/README.md +203 -573
- package/bin/cli.js +455 -527
- package/bin/generate-commands.js +186 -0
- package/bin/generate-commands.sh +127 -88
- package/bin/lib/init.js +13 -3
- package/bin/lib/spawn-logger.js +0 -15
- package/bin/lib/update-transaction.js +392 -140
- package/bin/npx-install.js +178 -0
- package/bin/sync-to-runtime.sh +5 -137
- package/bin/validate-package.sh +166 -0
- package/package.json +14 -7
- package/.opencode/agents/aether-guardian.md +0 -107
- package/.opencode/agents/workers.md +0 -1034
- package/bin/lib/model-profiles.js +0 -445
- package/bin/lib/model-verify.js +0 -288
- package/bin/lib/proxy-health.js +0 -253
- package/bin/lib/telemetry.js +0 -441
- package/runtime/CONTEXT.md +0 -160
- package/runtime/QUEEN_ANT_ARCHITECTURE.md +0 -402
- package/runtime/aether-utils.sh +0 -3879
- package/runtime/data/signatures.json +0 -41
- package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +0 -1343
- package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +0 -2642
- package/runtime/docs/PHEROMONE-INJECTION.md +0 -240
- package/runtime/docs/PHEROMONE-INTEGRATION.md +0 -192
- package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +0 -426
- package/runtime/docs/README.md +0 -94
- package/runtime/docs/VISUAL-OUTPUT-SPEC.md +0 -219
- package/runtime/docs/biological-reference.md +0 -272
- package/runtime/docs/codebase-review.md +0 -399
- package/runtime/docs/command-sync.md +0 -164
- package/runtime/docs/constraints.md +0 -116
- package/runtime/docs/implementation-learnings.md +0 -89
- package/runtime/docs/known-issues.md +0 -217
- package/runtime/docs/namespace.md +0 -148
- package/runtime/docs/pathogen-schema-example.json +0 -36
- package/runtime/docs/pathogen-schema.md +0 -111
- package/runtime/docs/planning-discipline.md +0 -159
- package/runtime/docs/progressive-disclosure.md +0 -184
- package/runtime/lib/queen-utils.sh +0 -729
- package/runtime/model-profiles.yaml +0 -100
- package/runtime/planning.md +0 -159
- package/runtime/recover.sh +0 -136
- package/runtime/templates/QUEEN.md.template +0 -79
- package/runtime/utils/chamber-utils.sh +0 -285
- package/runtime/utils/file-lock.sh +0 -122
- package/runtime/utils/spawn-tree.sh +0 -428
- package/runtime/utils/spawn-with-model.sh +0 -56
- package/runtime/utils/xml-utils.sh +0 -2196
- package/runtime/workers-new-castes.md +0 -516
- /package/{runtime → .aether/docs/disciplines}/coding-standards.md +0 -0
- /package/{runtime → .aether/docs/disciplines}/debugging.md +0 -0
- /package/{runtime → .aether/docs/disciplines}/learning.md +0 -0
- /package/{runtime → .aether/docs/disciplines}/tdd.md +0 -0
- /package/{runtime → .aether/docs/disciplines}/verification-loop.md +0 -0
- /package/{runtime → .aether/docs/disciplines}/verification.md +0 -0
- /package/{runtime → .aether}/schemas/aether-types.xsd +0 -0
- /package/{runtime → .aether}/schemas/colony-registry.xsd +0 -0
- /package/{runtime → .aether}/schemas/pheromone.xsd +0 -0
- /package/{runtime → .aether}/schemas/prompt.xsd +0 -0
- /package/{runtime → .aether}/schemas/queen-wisdom.xsd +0 -0
- /package/{runtime → .aether}/schemas/worker-priming.xsd +0 -0
- /package/{runtime → .aether}/utils/colorize-log.sh +0 -0
- /package/{runtime → .aether}/utils/queen-to-md.xsl +0 -0
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
# Usage: bash watch-spawn-tree.sh [data_dir]
|
|
4
4
|
|
|
5
5
|
DATA_DIR="${1:-.aether/data}"
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# Resolve COLONY_DATA_DIR for per-colony files (standalone script)
|
|
7
|
+
COLONY_DATA_DIR="${COLONY_DATA_DIR:-$DATA_DIR}"
|
|
8
|
+
if [[ -f "$DATA_DIR/COLONY_STATE.json" ]]; then
|
|
9
|
+
_cn=$(jq -r '.colony_name // empty' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null)
|
|
10
|
+
if [[ -n "$_cn" ]]; then
|
|
11
|
+
_cn_safe=$(echo "$_cn" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
|
|
12
|
+
[[ -n "$_cn_safe" ]] && COLONY_DATA_DIR="$DATA_DIR/colonies/$_cn_safe"
|
|
13
|
+
fi
|
|
14
|
+
fi
|
|
15
|
+
SPAWN_FILE="$COLONY_DATA_DIR/spawn-tree.txt"
|
|
16
|
+
VIEW_STATE_FILE="$COLONY_DATA_DIR/view-state.json"
|
|
8
17
|
|
|
9
18
|
# ANSI colors
|
|
10
19
|
YELLOW='\033[33m'
|
|
@@ -5,14 +5,20 @@
|
|
|
5
5
|
# Usage: source .aether/utils/xml-compose.sh
|
|
6
6
|
# xml-compose <input_xml> [output_xml]
|
|
7
7
|
# xml-compose-worker-priming <priming_xml> [output_xml]
|
|
8
|
+
# xml-list-includes <xml_file>
|
|
8
9
|
#
|
|
9
10
|
# These functions enable declarative composition of worker configurations
|
|
10
11
|
# using XInclude directives to merge queen-wisdom, active-trails, and stack-profiles.
|
|
11
12
|
|
|
12
13
|
set -euo pipefail
|
|
13
14
|
|
|
14
|
-
# Note: This file should be sourced AFTER xml-utils.sh
|
|
15
|
+
# Note: This file should be sourced AFTER xml-utils.sh or xml-core.sh
|
|
15
16
|
# It relies on xml_json_ok, xml_json_err, and XMLLINT_AVAILABLE variables
|
|
17
|
+
# Source xml-core.sh for JSON helpers and tool detection if not already loaded
|
|
18
|
+
if ! type xml_json_ok &>/dev/null; then
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
source "$SCRIPT_DIR/xml-core.sh"
|
|
21
|
+
fi
|
|
16
22
|
|
|
17
23
|
# ============================================================================
|
|
18
24
|
# Path Validation (Security)
|
|
@@ -107,12 +113,12 @@ xml-compose() {
|
|
|
107
113
|
echo "$composed" > "$output_xml"
|
|
108
114
|
local escaped_output
|
|
109
115
|
escaped_output=$(echo "$output_xml" | jq -Rs '.[:-1]')
|
|
110
|
-
xml_json_ok "{
|
|
116
|
+
xml_json_ok "$(jq -n --argjson output "$escaped_output" '{composed: true, output: $output, sources_resolved: "auto"}')"
|
|
111
117
|
else
|
|
112
118
|
# Output to stdout wrapped in JSON
|
|
113
119
|
local escaped_composed
|
|
114
120
|
escaped_composed=$(echo "$composed" | jq -Rs '.')
|
|
115
|
-
xml_json_ok "{
|
|
121
|
+
xml_json_ok "$(jq -n --argjson xml "$escaped_composed" '{composed: true, xml: $xml, sources_resolved: "auto"}')"
|
|
116
122
|
fi
|
|
117
123
|
return 0
|
|
118
124
|
fi
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# XML Conversion Utilities
|
|
3
|
+
# Bidirectional JSON/XML conversion and document merging
|
|
4
|
+
#
|
|
5
|
+
# Usage: source .aether/utils/xml-convert.sh
|
|
6
|
+
# xml-to-json <xml_file> [--pretty]
|
|
7
|
+
# json-to-xml <json_file> [root_element]
|
|
8
|
+
# xml-merge <output_file> <main_xml_file>
|
|
9
|
+
# xml-convert-detect-format <file>
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Source xml-core.sh for JSON helpers and tool detection
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
source "$SCRIPT_DIR/xml-core.sh"
|
|
16
|
+
|
|
17
|
+
# Additional tool detection for conversion
|
|
18
|
+
XML2JSON_AVAILABLE=false
|
|
19
|
+
if command -v xml2json >/dev/null 2>&1; then
|
|
20
|
+
XML2JSON_AVAILABLE=true
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# ============================================================================
|
|
24
|
+
# Format Detection
|
|
25
|
+
# ============================================================================
|
|
26
|
+
|
|
27
|
+
# xml-convert-detect-format: Detect if file is XML or JSON
|
|
28
|
+
# Usage: xml-convert-detect-format <file>
|
|
29
|
+
# Returns: {"ok":true,"result":{"format":"xml|json|unknown","confidence":"high|medium|low"}}
|
|
30
|
+
xml-convert-detect-format() {
|
|
31
|
+
local file="${1:-}"
|
|
32
|
+
|
|
33
|
+
[[ -z "$file" ]] && { xml_json_err "MISSING_ARG" "Missing file argument"; return 1; }
|
|
34
|
+
[[ -f "$file" ]] || { xml_json_err "FILE_NOT_FOUND" "File not found: $file"; return 1; }
|
|
35
|
+
|
|
36
|
+
# Read first 1KB for analysis
|
|
37
|
+
local header
|
|
38
|
+
header=$(head -c 1024 "$file" 2>/dev/null || head -c 1024 < "$file")
|
|
39
|
+
|
|
40
|
+
# Check for XML signatures
|
|
41
|
+
if echo "$header" | grep -qE '^\s*<\?xml\s+version'; then
|
|
42
|
+
xml_json_ok '{"format":"xml","confidence":"high","signature":"xml_declaration"}'
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if echo "$header" | grep -qE '^\s*<[a-zA-Z_][a-zA-Z0-9_]*[\s>]'; then
|
|
47
|
+
xml_json_ok '{"format":"xml","confidence":"medium","signature":"root_element"}'
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Check for JSON signatures
|
|
52
|
+
if echo "$header" | grep -qE '^\s*(\{|\[)'; then
|
|
53
|
+
# Verify it's valid JSON
|
|
54
|
+
if jq empty "$file" 2>/dev/null; then
|
|
55
|
+
xml_json_ok '{"format":"json","confidence":"high","signature":"valid_json"}'
|
|
56
|
+
return 0
|
|
57
|
+
else
|
|
58
|
+
xml_json_ok '{"format":"json","confidence":"low","signature":"json_like","note":"Invalid JSON syntax"}'
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
xml_json_ok '{"format":"unknown","confidence":"low","signature":"none"}'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# ============================================================================
|
|
67
|
+
# XML to JSON Conversion
|
|
68
|
+
# ============================================================================
|
|
69
|
+
|
|
70
|
+
# xml-to-json: Convert XML to JSON format
|
|
71
|
+
# Usage: xml-to-json <xml_file> [--pretty]
|
|
72
|
+
# Returns: {"ok":true,"result":{"json":"...","format":"object"}}
|
|
73
|
+
xml-to-json() {
|
|
74
|
+
local xml_file="${1:-}"
|
|
75
|
+
local pretty=false
|
|
76
|
+
|
|
77
|
+
# Parse optional arguments
|
|
78
|
+
shift || true
|
|
79
|
+
while [[ $# -gt 0 ]]; do
|
|
80
|
+
case "$1" in
|
|
81
|
+
--pretty) pretty=true; shift ;;
|
|
82
|
+
*) shift ;;
|
|
83
|
+
esac
|
|
84
|
+
done
|
|
85
|
+
|
|
86
|
+
[[ -z "$xml_file" ]] && { xml_json_err "MISSING_ARG" "Missing XML file argument"; return 1; }
|
|
87
|
+
[[ -f "$xml_file" ]] || { xml_json_err "FILE_NOT_FOUND" "XML file not found: $xml_file"; return 1; }
|
|
88
|
+
|
|
89
|
+
# Check well-formedness first
|
|
90
|
+
local well_formed_result
|
|
91
|
+
well_formed_result=$(xml-well-formed "$xml_file" 2>/dev/null)
|
|
92
|
+
if ! echo "$well_formed_result" | jq -e '.result.well_formed' >/dev/null 2>&1; then
|
|
93
|
+
xml_json_err "PARSE_ERROR" "XML is not well-formed"
|
|
94
|
+
return 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Try xml2json if available (npm package)
|
|
98
|
+
if [[ "$XML2JSON_AVAILABLE" == "true" ]]; then
|
|
99
|
+
local json_output
|
|
100
|
+
if json_output=$(xml2json "$xml_file" 2>/dev/null); then
|
|
101
|
+
if [[ "$pretty" == "true" ]]; then
|
|
102
|
+
json_output=$(echo "$json_output" | jq '.')
|
|
103
|
+
fi
|
|
104
|
+
local escaped_json
|
|
105
|
+
escaped_json=$(echo "$json_output" | jq -Rs '.')
|
|
106
|
+
xml_json_ok "$(jq -n --argjson json "$escaped_json" '{format: "object", json: $json}')"
|
|
107
|
+
return 0
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Fallback: Use xsltproc with built-in XSLT
|
|
112
|
+
if [[ "$XSLTPROC_AVAILABLE" == "true" ]]; then
|
|
113
|
+
local xslt_script
|
|
114
|
+
xslt_script=$(cat << 'XSLT'
|
|
115
|
+
<?xml version="1.0"?>
|
|
116
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
117
|
+
<xsl:output method="text"/>
|
|
118
|
+
<xsl:template match="/">
|
|
119
|
+
<xsl:text>{"root":</xsl:text>
|
|
120
|
+
<xsl:apply-templates select="*"/>
|
|
121
|
+
<xsl:text>}</xsl:text>
|
|
122
|
+
</xsl:template>
|
|
123
|
+
<xsl:template match="*">
|
|
124
|
+
<xsl:text>{"</xsl:text>
|
|
125
|
+
<xsl:value-of select="name()"/>
|
|
126
|
+
<xsl:text>":</xsl:text>
|
|
127
|
+
<xsl:choose>
|
|
128
|
+
<xsl:when test="count(*) > 0">
|
|
129
|
+
<xsl:text>[</xsl:text>
|
|
130
|
+
<xsl:apply-templates select="*"/>
|
|
131
|
+
<xsl:text>]</xsl:text>
|
|
132
|
+
</xsl:when>
|
|
133
|
+
<xsl:otherwise>
|
|
134
|
+
<xsl:text>"</xsl:text>
|
|
135
|
+
<xsl:value-of select="."/>
|
|
136
|
+
<xsl:text>"</xsl:text>
|
|
137
|
+
</xsl:otherwise>
|
|
138
|
+
</xsl:choose>
|
|
139
|
+
<xsl:text>}</xsl:text>
|
|
140
|
+
<xsl:if test="position() != last()">,</xsl:if>
|
|
141
|
+
</xsl:template>
|
|
142
|
+
</xsl:stylesheet>
|
|
143
|
+
XSLT
|
|
144
|
+
)
|
|
145
|
+
local json_result
|
|
146
|
+
json_result=$(echo "$xslt_script" | xsltproc - "$xml_file" 2>/dev/null) || {
|
|
147
|
+
xml_json_err "CONVERSION_ERROR" "XSLT conversion failed"
|
|
148
|
+
return 1
|
|
149
|
+
}
|
|
150
|
+
local xslt_escaped_json
|
|
151
|
+
xslt_escaped_json=$(echo "$json_result" | jq -Rs '.')
|
|
152
|
+
xml_json_ok "$(jq -n --argjson json "$xslt_escaped_json" '{format: "object", json: $json}')"
|
|
153
|
+
return 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Last resort: Use xmlstarlet if available
|
|
157
|
+
if [[ "$XMLSTARLET_AVAILABLE" == "true" ]]; then
|
|
158
|
+
local json_result
|
|
159
|
+
json_result=$(xmlstarlet sel -t -m "/" -o '{"root":{' -m "*" -v "name()" -o ':"' -v "." -o '"' -b -o '}}' "$xml_file" 2>/dev/null) || {
|
|
160
|
+
xml_json_err "CONVERSION_ERROR" "xmlstarlet conversion failed"
|
|
161
|
+
return 1
|
|
162
|
+
}
|
|
163
|
+
local xmlstar_escaped_json
|
|
164
|
+
xmlstar_escaped_json=$(echo "$json_result" | jq -Rs '.')
|
|
165
|
+
xml_json_ok "$(jq -n --argjson json "$xmlstar_escaped_json" '{format: "object", json: $json}')"
|
|
166
|
+
return 0
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
xml_json_err "TOOL_NOT_AVAILABLE" "No XML to JSON conversion tool available. Install xml2json, xsltproc, or xmlstarlet."
|
|
170
|
+
return 1
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# ============================================================================
|
|
174
|
+
# JSON to XML Conversion
|
|
175
|
+
# ============================================================================
|
|
176
|
+
|
|
177
|
+
# json-to-xml: Convert JSON to XML
|
|
178
|
+
# Usage: json-to-xml <json_file> [root_element]
|
|
179
|
+
# Returns: {"ok":true,"result":{"xml":"<root>...</root>"}}
|
|
180
|
+
json-to-xml() {
|
|
181
|
+
local json_file="${1:-}"
|
|
182
|
+
local root_element="${2:-root}"
|
|
183
|
+
|
|
184
|
+
[[ -z "$json_file" ]] && { xml_json_err "MISSING_ARG" "Missing JSON file argument"; return 1; }
|
|
185
|
+
[[ -f "$json_file" ]] || { xml_json_err "FILE_NOT_FOUND" "JSON file not found: $json_file"; return 1; }
|
|
186
|
+
|
|
187
|
+
# Validate JSON first
|
|
188
|
+
if ! jq empty "$json_file" 2>/dev/null; then
|
|
189
|
+
xml_json_err "PARSE_ERROR" "Invalid JSON file: $json_file"
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# Build XML using jq to generate structure
|
|
194
|
+
local xml_output
|
|
195
|
+
xml_output=$(jq -r --arg root "$root_element" '
|
|
196
|
+
def to_xml:
|
|
197
|
+
if type == "object" then
|
|
198
|
+
to_entries | map(
|
|
199
|
+
"<\(.key)>\(.value | to_xml)</\(.key)>"
|
|
200
|
+
) | join("")
|
|
201
|
+
elif type == "array" then
|
|
202
|
+
map("<item>\(. | to_xml)</item>") | join("")
|
|
203
|
+
elif type == "string" then
|
|
204
|
+
.
|
|
205
|
+
elif type == "number" then
|
|
206
|
+
tostring
|
|
207
|
+
elif type == "boolean" then
|
|
208
|
+
tostring
|
|
209
|
+
elif type == "null" then
|
|
210
|
+
""
|
|
211
|
+
else
|
|
212
|
+
tostring
|
|
213
|
+
end;
|
|
214
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<\($root)>\n" + (to_xml) + "\n</\($root)>"
|
|
215
|
+
' "$json_file" 2>/dev/null) || {
|
|
216
|
+
xml_json_err "CONVERSION_ERROR" "JSON to XML conversion failed"
|
|
217
|
+
return 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Escape the XML for JSON output
|
|
221
|
+
local escaped_xml
|
|
222
|
+
escaped_xml=$(echo "$xml_output" | jq -Rs '.')
|
|
223
|
+
xml_json_ok "$(jq -n --argjson xml "$escaped_xml" '{xml: $xml}')"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# ============================================================================
|
|
227
|
+
# XML Document Merging
|
|
228
|
+
# ============================================================================
|
|
229
|
+
|
|
230
|
+
# xml-merge: XInclude document merging
|
|
231
|
+
# Usage: xml-merge <output_file> <main_xml_file>
|
|
232
|
+
# Returns: {"ok":true,"result":{"merged":true,"output":"<path>","sources_resolved":N}}
|
|
233
|
+
xml-merge() {
|
|
234
|
+
local output_file="${1:-}"
|
|
235
|
+
local main_xml="${2:-}"
|
|
236
|
+
|
|
237
|
+
[[ -z "$output_file" ]] && { xml_json_err "MISSING_ARG" "Missing output file argument"; return 1; }
|
|
238
|
+
[[ -z "$main_xml" ]] && { xml_json_err "MISSING_ARG" "Missing main XML file argument"; return 1; }
|
|
239
|
+
[[ -f "$main_xml" ]] || { xml_json_err "FILE_NOT_FOUND" "Main XML file not found: $main_xml"; return 1; }
|
|
240
|
+
|
|
241
|
+
# Check well-formedness of main file
|
|
242
|
+
local well_formed_result
|
|
243
|
+
well_formed_result=$(xml-well-formed "$main_xml" 2>/dev/null)
|
|
244
|
+
if ! echo "$well_formed_result" | jq -e '.result.well_formed' >/dev/null 2>&1; then
|
|
245
|
+
xml_json_err "PARSE_ERROR" "Main XML file is not well-formed"
|
|
246
|
+
return 1
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# Use xmllint for XInclude processing with security flags
|
|
250
|
+
if [[ "$XMLLINT_AVAILABLE" == "true" ]]; then
|
|
251
|
+
local merged
|
|
252
|
+
merged=$(xmllint --nonet --noent --xinclude "$main_xml" 2>/dev/null) || {
|
|
253
|
+
xml_json_err "MERGE_ERROR" "XInclude merge failed"
|
|
254
|
+
return 1
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# Write output
|
|
258
|
+
echo "$merged" > "$output_file"
|
|
259
|
+
|
|
260
|
+
# Count resolved includes (approximate)
|
|
261
|
+
local resolved_count
|
|
262
|
+
resolved_count=$(echo "$merged" | grep -c '<xi:include' 2>/dev/null || echo "0")
|
|
263
|
+
|
|
264
|
+
local escaped_output
|
|
265
|
+
escaped_output=$(echo "$output_file" | jq -Rs '.[:-1]')
|
|
266
|
+
xml_json_ok "$(jq -n --argjson output "$escaped_output" --argjson sources_resolved "$resolved_count" '{merged: true, output: $output, sources_resolved: $sources_resolved}')"
|
|
267
|
+
return 0
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
# Without xmllint, we cannot safely process XInclude
|
|
271
|
+
xml_json_err "TOOL_NOT_AVAILABLE" "xmllint required for XInclude merging. Install libxml2 utilities."
|
|
272
|
+
return 1
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# Export functions
|
|
276
|
+
export -f xml-convert-detect-format xml-to-json json-to-xml xml-merge
|
|
277
|
+
export XML2JSON_AVAILABLE
|
|
@@ -89,10 +89,8 @@ xml-validate() {
|
|
|
89
89
|
xml_json_ok '{"valid":true,"errors":[]}'
|
|
90
90
|
return 0
|
|
91
91
|
} || {
|
|
92
|
-
# Escape errors for JSON
|
|
93
|
-
|
|
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\"]}"
|
|
92
|
+
# Escape errors for JSON using jq
|
|
93
|
+
xml_json_ok "$(jq -n --arg errors "$errors" '{valid: false, errors: [$errors]}')"
|
|
96
94
|
return 0
|
|
97
95
|
}
|
|
98
96
|
}
|
|
@@ -144,12 +142,10 @@ xml-format() {
|
|
|
144
142
|
|
|
145
143
|
if [[ -n "$output_file" ]]; then
|
|
146
144
|
echo "$formatted" > "$output_file"
|
|
147
|
-
xml_json_ok "{
|
|
145
|
+
xml_json_ok "$(jq -n --arg path "$output_file" '{formatted: true, path: $path}')"
|
|
148
146
|
else
|
|
149
|
-
# Escape for JSON
|
|
150
|
-
|
|
151
|
-
escaped=$(echo "$formatted" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
|
|
152
|
-
xml_json_ok "{\"formatted\":true,\"output\":\"$escaped\"}"
|
|
147
|
+
# Escape for JSON using jq
|
|
148
|
+
xml_json_ok "$(jq -n --arg output "$formatted" '{formatted: true, output: $output}')"
|
|
153
149
|
fi
|
|
154
150
|
}
|
|
155
151
|
|
|
@@ -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 "$(jq -n --arg attribute "$attr_name" --argjson values "$json_array" '{attribute: $attribute, values: $values}')"
|
|
116
|
+
else
|
|
117
|
+
xml_json_ok "$(jq -n --arg attribute "$attr_name" '{attribute: $attribute, 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 "$(jq -n --arg element "$element_name" --argjson text "$json_array" '{element: $element, text: $text}')"
|
|
164
|
+
else
|
|
165
|
+
xml_json_ok "$(jq -n --arg element "$element_name" '{element: $element, 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
|
+
|