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
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Aether Chamber Utilities
|
|
3
|
+
# Manages entombed colonies — directory management, manifest generation, integrity verification
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# source .aether/utils/chamber-utils.sh
|
|
7
|
+
# chamber_create <chamber_dir> <state_file> <goal> <phases_completed> <total_phases> <milestone> <version> <decisions_json> <learnings_json>
|
|
8
|
+
# chamber_verify <chamber_dir>
|
|
9
|
+
# chamber_list <chambers_root>
|
|
10
|
+
# chamber_sanitize_goal <goal>
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
# Initialize lock state before sourcing (file-lock.sh trap needs these)
|
|
15
|
+
LOCK_ACQUIRED=${LOCK_ACQUIRED:-false}
|
|
16
|
+
CURRENT_LOCK=${CURRENT_LOCK:-""}
|
|
17
|
+
|
|
18
|
+
# Get script directory for sourcing (preserve parent SCRIPT_DIR if set)
|
|
19
|
+
__chamber_utils_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
# Respect existing AETHER_ROOT if already set
|
|
21
|
+
if [[ -z "${AETHER_ROOT:-}" ]]; then
|
|
22
|
+
AETHER_ROOT="$(cd "$__chamber_utils_dir/../.." && pwd 2>/dev/null || echo "$__chamber_utils_dir/../..")"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Use parent SCRIPT_DIR if available, otherwise use local
|
|
26
|
+
SCRIPT_DIR="${SCRIPT_DIR:-$__chamber_utils_dir}"
|
|
27
|
+
|
|
28
|
+
# Source atomic-write for safe file operations
|
|
29
|
+
[[ -f "$SCRIPT_DIR/atomic-write.sh" ]] && source "$SCRIPT_DIR/atomic-write.sh"
|
|
30
|
+
|
|
31
|
+
# --- JSON output helpers ---
|
|
32
|
+
json_ok() { printf '{"ok":true,"result":%s}\n' "$1"; }
|
|
33
|
+
|
|
34
|
+
# Guard: yield to error-handler.sh's enhanced json_err when already loaded
|
|
35
|
+
if ! type json_err &>/dev/null; then
|
|
36
|
+
json_err() {
|
|
37
|
+
local code="${1:-E_UNKNOWN}"
|
|
38
|
+
local message="${2:-An unknown error occurred}"
|
|
39
|
+
printf '{"ok":false,"error":{"code":"%s","message":"%s"}}\n' "$code" "$message" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
}
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Fallback E_* constants (no-ops when error-handler.sh is already loaded)
|
|
45
|
+
: "${E_UNKNOWN:=E_UNKNOWN}"
|
|
46
|
+
: "${E_VALIDATION_FAILED:=E_VALIDATION_FAILED}"
|
|
47
|
+
: "${E_FILE_NOT_FOUND:=E_FILE_NOT_FOUND}"
|
|
48
|
+
: "${E_BASH_ERROR:=E_BASH_ERROR}"
|
|
49
|
+
: "${E_JSON_INVALID:=E_JSON_INVALID}"
|
|
50
|
+
: "${E_FEATURE_UNAVAILABLE:=E_FEATURE_UNAVAILABLE}"
|
|
51
|
+
|
|
52
|
+
# --- Chamber Functions ---
|
|
53
|
+
|
|
54
|
+
# Sanitize goal string for use in directory names
|
|
55
|
+
# Converts to lowercase, replaces spaces/special chars with hyphens, removes non-alphanumeric
|
|
56
|
+
chamber_sanitize_goal() {
|
|
57
|
+
local goal="$1"
|
|
58
|
+
# Convert to lowercase, replace spaces and special chars with hyphens
|
|
59
|
+
local sanitized=$(echo "$goal" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-')
|
|
60
|
+
# Remove leading/trailing hyphens
|
|
61
|
+
sanitized=$(echo "$sanitized" | sed 's/^-//;s/-$//')
|
|
62
|
+
# Limit length to avoid overly long directory names
|
|
63
|
+
if [[ ${#sanitized} -gt 50 ]]; then
|
|
64
|
+
sanitized="${sanitized:0:50}"
|
|
65
|
+
fi
|
|
66
|
+
echo "$sanitized"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Compute SHA256 hash of a file
|
|
70
|
+
# Returns hash string or empty on error
|
|
71
|
+
chamber_compute_hash() {
|
|
72
|
+
local file_path="$1"
|
|
73
|
+
if [[ ! -f "$file_path" ]]; then
|
|
74
|
+
echo ""
|
|
75
|
+
return 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Try sha256sum first (Linux), then shasum -a 256 (macOS)
|
|
79
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
80
|
+
sha256sum "$file_path" | cut -d' ' -f1
|
|
81
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
82
|
+
shasum -a 256 "$file_path" | cut -d' ' -f1
|
|
83
|
+
else
|
|
84
|
+
echo ""
|
|
85
|
+
return 1
|
|
86
|
+
fi
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Create a new chamber (entomb a colony)
|
|
90
|
+
# Arguments:
|
|
91
|
+
# chamber_dir: Directory to create for this chamber
|
|
92
|
+
# state_file: Path to COLONY_STATE.json to archive
|
|
93
|
+
# goal: Colony goal string
|
|
94
|
+
# phases_completed: Number of completed phases
|
|
95
|
+
# total_phases: Total number of phases
|
|
96
|
+
# milestone: Milestone name
|
|
97
|
+
# version: Version string
|
|
98
|
+
# decisions_json: JSON array of decisions
|
|
99
|
+
# learnings_json: JSON array of learnings
|
|
100
|
+
chamber_create() {
|
|
101
|
+
local chamber_dir="$1"
|
|
102
|
+
local state_file="$2"
|
|
103
|
+
local goal="$3"
|
|
104
|
+
local phases_completed="$4"
|
|
105
|
+
local total_phases="$5"
|
|
106
|
+
local milestone="$6"
|
|
107
|
+
local version="$7"
|
|
108
|
+
local decisions_json="$8"
|
|
109
|
+
local learnings_json="$9"
|
|
110
|
+
|
|
111
|
+
# Validate inputs
|
|
112
|
+
[[ -z "$chamber_dir" ]] && json_err "$E_VALIDATION_FAILED" "chamber_dir argument is required. Try: pass the chamber directory path."
|
|
113
|
+
[[ -z "$state_file" ]] && json_err "$E_VALIDATION_FAILED" "state_file argument is required. Try: pass the state file path."
|
|
114
|
+
[[ ! -f "$state_file" ]] && json_err "$E_FILE_NOT_FOUND" "State file not found: $state_file. Try: check the file path."
|
|
115
|
+
|
|
116
|
+
# Create chamber directory
|
|
117
|
+
mkdir -p "$chamber_dir" || json_err "$E_BASH_ERROR" "Couldn't create chamber directory: $chamber_dir. Try: check disk space and permissions."
|
|
118
|
+
|
|
119
|
+
# Copy state file to chamber
|
|
120
|
+
local target_state="$chamber_dir/COLONY_STATE.json"
|
|
121
|
+
cp "$state_file" "$target_state" || json_err "$E_BASH_ERROR" "Couldn't copy the state file. Try: check disk space and permissions."
|
|
122
|
+
|
|
123
|
+
# Compute hash of the copied state file
|
|
124
|
+
local state_hash=$(chamber_compute_hash "$target_state")
|
|
125
|
+
[[ -z "$state_hash" ]] && json_err "$E_BASH_ERROR" "Couldn't compute state file hash. Try: check that shasum is available."
|
|
126
|
+
|
|
127
|
+
# Generate timestamp
|
|
128
|
+
local entombed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
129
|
+
|
|
130
|
+
# Create manifest.json
|
|
131
|
+
local manifest_file="$chamber_dir/manifest.json"
|
|
132
|
+
local manifest_content=$(cat <<EOF
|
|
133
|
+
{
|
|
134
|
+
"entombed_at": "$entombed_at",
|
|
135
|
+
"goal": $(echo "$goal" | jq -Rs '.[:-1]'),
|
|
136
|
+
"phases_completed": $phases_completed,
|
|
137
|
+
"total_phases": $total_phases,
|
|
138
|
+
"milestone": $(echo "$milestone" | jq -Rs '.[:-1]'),
|
|
139
|
+
"version": $(echo "$version" | jq -Rs '.[:-1]'),
|
|
140
|
+
"decisions": $decisions_json,
|
|
141
|
+
"learnings": $learnings_json,
|
|
142
|
+
"files": {
|
|
143
|
+
"COLONY_STATE.json": "$state_hash"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
EOF
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Write manifest atomically if atomic_write is available, otherwise direct
|
|
150
|
+
if type atomic_write &>/dev/null; then
|
|
151
|
+
atomic_write "$manifest_file" "$manifest_content" || json_err "$E_BASH_ERROR" "Couldn't write chamber manifest. Try: check disk space."
|
|
152
|
+
else
|
|
153
|
+
echo "$manifest_content" > "$manifest_file" || json_err "$E_BASH_ERROR" "Couldn't write chamber manifest. Try: check disk space."
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Verify the manifest was written correctly
|
|
157
|
+
if [[ ! -f "$manifest_file" ]]; then
|
|
158
|
+
json_err "$E_FILE_NOT_FOUND" "Chamber manifest wasn't created. Try: check disk space and permissions."
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Return success with chamber info
|
|
162
|
+
local result=$(cat <<EOF
|
|
163
|
+
{
|
|
164
|
+
"chamber_dir": "$chamber_dir",
|
|
165
|
+
"manifest": {
|
|
166
|
+
"entombed_at": "$entombed_at",
|
|
167
|
+
"goal": $(echo "$goal" | jq -Rs '.[:-1]'),
|
|
168
|
+
"phases_completed": $phases_completed,
|
|
169
|
+
"total_phases": $total_phases,
|
|
170
|
+
"milestone": $(echo "$milestone" | jq -Rs '.[:-1]'),
|
|
171
|
+
"version": $(echo "$version" | jq -Rs '.[:-1]')
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
EOF
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
json_ok "$result"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Verify chamber integrity
|
|
181
|
+
# Arguments:
|
|
182
|
+
# chamber_dir: Directory containing the chamber
|
|
183
|
+
chamber_verify() {
|
|
184
|
+
local chamber_dir="$1"
|
|
185
|
+
|
|
186
|
+
# Validate inputs
|
|
187
|
+
[[ -z "$chamber_dir" ]] && json_err "$E_VALIDATION_FAILED" "chamber_dir argument is required. Try: pass the chamber directory path."
|
|
188
|
+
[[ ! -d "$chamber_dir" ]] && json_err "$E_FILE_NOT_FOUND" "Chamber directory not found: $chamber_dir. Try: check the path."
|
|
189
|
+
|
|
190
|
+
local manifest_file="$chamber_dir/manifest.json"
|
|
191
|
+
local state_file="$chamber_dir/COLONY_STATE.json"
|
|
192
|
+
|
|
193
|
+
# Check required files exist
|
|
194
|
+
[[ ! -f "$manifest_file" ]] && json_err "$E_FILE_NOT_FOUND" "Manifest not found in chamber. Try: verify the chamber was created correctly."
|
|
195
|
+
[[ ! -f "$state_file" ]] && json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found in chamber. Try: re-entomb the colony."
|
|
196
|
+
|
|
197
|
+
# Read stored hash from manifest
|
|
198
|
+
local stored_hash=$(jq -r '.files["COLONY_STATE.json"] // empty' "$manifest_file" 2>/dev/null)
|
|
199
|
+
[[ -z "$stored_hash" ]] && json_err "$E_JSON_INVALID" "No hash found in manifest. Try: re-entomb the colony."
|
|
200
|
+
|
|
201
|
+
# Compute current hash
|
|
202
|
+
local current_hash=$(chamber_compute_hash "$state_file")
|
|
203
|
+
[[ -z "$current_hash" ]] && json_err "$E_BASH_ERROR" "Couldn't compute state file hash. Try: check that shasum is available."
|
|
204
|
+
|
|
205
|
+
# Compare hashes
|
|
206
|
+
if [[ "$stored_hash" != "$current_hash" ]]; then
|
|
207
|
+
local result=$(cat <<EOF
|
|
208
|
+
{
|
|
209
|
+
"verified": false,
|
|
210
|
+
"chamber_dir": "$chamber_dir",
|
|
211
|
+
"error": "hash mismatch",
|
|
212
|
+
"stored_hash": "$stored_hash",
|
|
213
|
+
"current_hash": "$current_hash"
|
|
214
|
+
}
|
|
215
|
+
EOF
|
|
216
|
+
)
|
|
217
|
+
json_ok "$result"
|
|
218
|
+
return 0
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Verification passed
|
|
222
|
+
local result=$(cat <<EOF
|
|
223
|
+
{
|
|
224
|
+
"verified": true,
|
|
225
|
+
"chamber_dir": "$chamber_dir",
|
|
226
|
+
"hash": "$current_hash"
|
|
227
|
+
}
|
|
228
|
+
EOF
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
json_ok "$result"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# List all chambers
|
|
235
|
+
# Arguments:
|
|
236
|
+
# chambers_root: Root directory containing chambers (default: .aether/chambers/)
|
|
237
|
+
chamber_list() {
|
|
238
|
+
local chambers_root="${1:-$AETHER_ROOT/.aether/chambers}"
|
|
239
|
+
|
|
240
|
+
# Default to current directory's chambers if AETHER_ROOT not set
|
|
241
|
+
if [[ -z "$chambers_root" || "$chambers_root" == "/.aether/chambers" ]]; then
|
|
242
|
+
chambers_root="$(pwd)/.aether/chambers"
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Check if chambers directory exists
|
|
246
|
+
if [[ ! -d "$chambers_root" ]]; then
|
|
247
|
+
json_ok "[]"
|
|
248
|
+
return 0
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# Build array of chamber summaries
|
|
252
|
+
local chambers="["
|
|
253
|
+
local first=true
|
|
254
|
+
|
|
255
|
+
# Find all directories in chambers_root
|
|
256
|
+
while IFS= read -r -d '' chamber_dir; do
|
|
257
|
+
local chamber_name=$(basename "$chamber_dir")
|
|
258
|
+
local manifest_file="$chamber_dir/manifest.json"
|
|
259
|
+
|
|
260
|
+
# Skip if no manifest
|
|
261
|
+
[[ ! -f "$manifest_file" ]] && continue
|
|
262
|
+
|
|
263
|
+
# Read manifest fields
|
|
264
|
+
local goal=$(jq -r '.goal // "unknown"' "$manifest_file" 2>/dev/null)
|
|
265
|
+
local milestone=$(jq -r '.milestone // "unknown"' "$manifest_file" 2>/dev/null)
|
|
266
|
+
local phases_completed=$(jq -r '.phases_completed // 0' "$manifest_file" 2>/dev/null)
|
|
267
|
+
local entombed_at=$(jq -r '.entombed_at // ""' "$manifest_file" 2>/dev/null)
|
|
268
|
+
|
|
269
|
+
# Escape for JSON
|
|
270
|
+
goal=$(echo "$goal" | jq -Rs '.[:-1]')
|
|
271
|
+
milestone=$(echo "$milestone" | jq -Rs '.[:-1]')
|
|
272
|
+
|
|
273
|
+
# Add comma if not first
|
|
274
|
+
if [[ "$first" == "true" ]]; then
|
|
275
|
+
first=false
|
|
276
|
+
else
|
|
277
|
+
chambers+=","
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
chambers+=$(cat <<EOF
|
|
281
|
+
{
|
|
282
|
+
"name": $(echo "$chamber_name" | jq -Rs '.[:-1]'),
|
|
283
|
+
"goal": $goal,
|
|
284
|
+
"milestone": $milestone,
|
|
285
|
+
"phases_completed": $phases_completed,
|
|
286
|
+
"entombed_at": $(echo "$entombed_at" | jq -Rs '.[:-1]')
|
|
287
|
+
}
|
|
288
|
+
EOF
|
|
289
|
+
)
|
|
290
|
+
done < <(find "$chambers_root" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null || true)
|
|
291
|
+
|
|
292
|
+
chambers+="]"
|
|
293
|
+
|
|
294
|
+
# Sort by entombed_at descending using jq
|
|
295
|
+
local sorted=$(echo "$chambers" | jq 'sort_by(.entombed_at) | reverse')
|
|
296
|
+
|
|
297
|
+
json_ok "$sorted"
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# --- Colony Archive XML ---
|
|
301
|
+
|
|
302
|
+
# Export combined colony archive XML containing pheromones, wisdom, and registry
|
|
303
|
+
# Usage: _colony_archive_xml [output_file]
|
|
304
|
+
# Default output: .aether/exchange/colony-archive.xml
|
|
305
|
+
# Always filters to active-only pheromone signals
|
|
306
|
+
_colony_archive_xml() {
|
|
307
|
+
# Graceful degradation: check for xmllint
|
|
308
|
+
if ! command -v xmllint >/dev/null 2>&1; then
|
|
309
|
+
json_err "$E_FEATURE_UNAVAILABLE" "xmllint is not installed. Try: xcode-select --install on macOS."
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
cax_output="${1:-$SCRIPT_DIR/exchange/colony-archive.xml}"
|
|
313
|
+
mkdir -p "$(dirname "$cax_output")"
|
|
314
|
+
|
|
315
|
+
# Step 1: Filter active-only pheromone signals to a temp file
|
|
316
|
+
cax_tmp_pheromones=$(mktemp)
|
|
317
|
+
if [[ -f "$COLONY_DATA_DIR/pheromones.json" ]]; then
|
|
318
|
+
jq '{
|
|
319
|
+
version: .version,
|
|
320
|
+
colony_id: .colony_id,
|
|
321
|
+
generated_at: .generated_at,
|
|
322
|
+
signals: [.signals[] | select(.active == true)]
|
|
323
|
+
}' "$COLONY_DATA_DIR/pheromones.json" > "$cax_tmp_pheromones" 2>/dev/null # SUPPRESS:OK -- read-default: file may not exist yet
|
|
324
|
+
else
|
|
325
|
+
printf '%s\n' '{"version":"1.0","colony_id":"unknown","generated_at":"","signals":[]}' > "$cax_tmp_pheromones"
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# Step 2: Export each section to temp XML files
|
|
329
|
+
cax_tmp_dir=$(mktemp -d)
|
|
330
|
+
|
|
331
|
+
# Pheromone section (using filtered active-only)
|
|
332
|
+
source "$SCRIPT_DIR/exchange/pheromone-xml.sh"
|
|
333
|
+
xml-pheromone-export "$cax_tmp_pheromones" "$cax_tmp_dir/pheromones.xml" 2>/dev/null || _aether_log_error "Could not export pheromones to XML"
|
|
334
|
+
|
|
335
|
+
# Wisdom section — reuse wisdom-export-xml fallback logic
|
|
336
|
+
source "$SCRIPT_DIR/exchange/wisdom-xml.sh"
|
|
337
|
+
cax_wisdom_input="$DATA_DIR/queen-wisdom.json"
|
|
338
|
+
if [[ ! -f "$cax_wisdom_input" ]]; then
|
|
339
|
+
# MIGRATE: direct COLONY_STATE.json access -- use _state_read_field instead
|
|
340
|
+
# Try extracting from COLONY_STATE.json memory field
|
|
341
|
+
if [[ -f "$DATA_DIR/COLONY_STATE.json" ]]; then
|
|
342
|
+
cax_wex_memory=$(jq '.memory // {}' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null || echo '{}') # SUPPRESS:OK -- read-default: returns fallback if missing
|
|
343
|
+
if [[ "$cax_wex_memory" != "{}" && "$cax_wex_memory" != "null" ]]; then
|
|
344
|
+
cax_wex_created_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
345
|
+
cax_wisdom_input="$cax_tmp_dir/wisdom-input.json"
|
|
346
|
+
printf '%s\n' "{
|
|
347
|
+
\"version\": \"1.0.0\",
|
|
348
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
349
|
+
\"metadata\": {\"created\": \"$cax_wex_created_at\", \"colony_id\": \"$(jq -r '.goal // \"unknown\"' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null)\"},
|
|
350
|
+
\"philosophies\": [],
|
|
351
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
352
|
+
\"patterns\": $(echo "$cax_wex_memory" | jq '[.instincts // [] | .[] | {"id": (. | @base64), "content": ., "confidence": 0.7, "domain": "general", "source": "colony_memory"}]' 2>/dev/null || echo '[]')
|
|
353
|
+
}" > "$cax_wisdom_input"
|
|
354
|
+
fi
|
|
355
|
+
fi
|
|
356
|
+
fi
|
|
357
|
+
if [[ -f "$cax_wisdom_input" ]]; then
|
|
358
|
+
xml-wisdom-export "$cax_wisdom_input" "$cax_tmp_dir/wisdom.xml" 2>/dev/null || _aether_log_error "Could not export wisdom to XML"
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
# Registry section — reuse registry-export-xml on-demand generation logic
|
|
362
|
+
source "$SCRIPT_DIR/exchange/registry-xml.sh"
|
|
363
|
+
cax_registry_input="$DATA_DIR/colony-registry.json"
|
|
364
|
+
if [[ ! -f "$cax_registry_input" ]]; then
|
|
365
|
+
cax_rex_chambers_dir="$AETHER_ROOT/.aether/chambers"
|
|
366
|
+
cax_rex_generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
367
|
+
cax_rex_colonies="[]"
|
|
368
|
+
if [[ -d "$cax_rex_chambers_dir" ]]; then
|
|
369
|
+
cax_rex_colonies=$(
|
|
370
|
+
for manifest in "$cax_rex_chambers_dir"/*/manifest.json; do
|
|
371
|
+
[[ -f "$manifest" ]] || continue
|
|
372
|
+
jq -c '{
|
|
373
|
+
id: (.colony_id // .goal // "unknown"),
|
|
374
|
+
name: (.goal // "Unnamed Colony"),
|
|
375
|
+
created_at: (.created_at // "unknown"),
|
|
376
|
+
sealed_at: (.sealed_at // null),
|
|
377
|
+
status: (if .sealed_at then "sealed" else "active" end),
|
|
378
|
+
chamber: input_filename
|
|
379
|
+
}' "$manifest" 2>/dev/null || true # SUPPRESS:OK -- cleanup: operation is best-effort
|
|
380
|
+
done | jq -s '.' 2>/dev/null || echo '[]' # SUPPRESS:OK -- read-default: returns fallback if missing
|
|
381
|
+
)
|
|
382
|
+
fi
|
|
383
|
+
cax_registry_input="$cax_tmp_dir/registry-input.json"
|
|
384
|
+
printf '%s\n' "{
|
|
385
|
+
\"version\": \"1.0.0\",
|
|
386
|
+
\"generated_at\": \"$cax_rex_generated_at\",
|
|
387
|
+
\"colonies\": $cax_rex_colonies
|
|
388
|
+
}" > "$cax_registry_input"
|
|
389
|
+
fi
|
|
390
|
+
xml-registry-export "$cax_registry_input" "$cax_tmp_dir/registry.xml" 2>/dev/null || _aether_log_error "Could not export registry to XML"
|
|
391
|
+
|
|
392
|
+
# Step 3: Build combined XML
|
|
393
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
394
|
+
cax_colony_id=$(jq -r '.goal // "unknown"' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//;s/-$//')
|
|
395
|
+
[[ -z "$cax_colony_id" || "$cax_colony_id" == "unknown" ]] && cax_colony_id="unknown"
|
|
396
|
+
cax_sealed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
397
|
+
cax_pheromone_count=$(jq '.signals | length' "$cax_tmp_pheromones" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: file may not exist yet
|
|
398
|
+
|
|
399
|
+
{
|
|
400
|
+
printf '<?xml version="1.0" encoding="UTF-8"?>\n'
|
|
401
|
+
printf '<colony-archive\n'
|
|
402
|
+
printf ' xmlns="http://aether.colony/schemas/archive/1.0"\n'
|
|
403
|
+
printf ' colony_id="%s"\n' "$cax_colony_id"
|
|
404
|
+
printf ' sealed_at="%s"\n' "$cax_sealed_at"
|
|
405
|
+
printf ' version="1.0.0"\n'
|
|
406
|
+
printf ' pheromone_count="%s">\n' "$cax_pheromone_count"
|
|
407
|
+
|
|
408
|
+
# Append pheromone section (strip XML declaration)
|
|
409
|
+
if [[ -f "$cax_tmp_dir/pheromones.xml" ]]; then
|
|
410
|
+
sed '1{/^<?xml/d;}' "$cax_tmp_dir/pheromones.xml"
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
# Append wisdom section (strip XML declaration)
|
|
414
|
+
if [[ -f "$cax_tmp_dir/wisdom.xml" ]]; then
|
|
415
|
+
sed '1{/^<?xml/d;}' "$cax_tmp_dir/wisdom.xml"
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
# Append registry section (strip XML declaration)
|
|
419
|
+
if [[ -f "$cax_tmp_dir/registry.xml" ]]; then
|
|
420
|
+
sed '1{/^<?xml/d;}' "$cax_tmp_dir/registry.xml"
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
printf '</colony-archive>\n'
|
|
424
|
+
} > "$cax_output"
|
|
425
|
+
|
|
426
|
+
# Step 4: Validate well-formedness
|
|
427
|
+
if xmllint --noout "$cax_output" 2>/dev/null; then # SUPPRESS:OK -- validation: testing XML validity
|
|
428
|
+
cax_valid=true
|
|
429
|
+
else
|
|
430
|
+
cax_valid=false
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
# Step 5: Cleanup temp files
|
|
434
|
+
rm -rf "$cax_tmp_dir" "$cax_tmp_pheromones"
|
|
435
|
+
|
|
436
|
+
json_ok "$(jq -n --arg path "$cax_output" --argjson valid "$cax_valid" --arg colony_id "$cax_colony_id" --argjson pheromone_count "$cax_pheromone_count" '{path: $path, valid: $valid, colony_id: $colony_id, pheromone_count: $pheromone_count}')"
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
# Export functions for use in other scripts
|
|
440
|
+
export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list _colony_archive_xml
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# emoji-audit.sh — Audit emoji usage across Aether command files
|
|
3
|
+
# Checks command files against the canonical emoji reference map defined in
|
|
4
|
+
# .aether/skills/colony/colony-visuals/SKILL.md
|
|
5
|
+
#
|
|
6
|
+
# Usage: bash emoji-audit.sh [repo_root]
|
|
7
|
+
#
|
|
8
|
+
# Output: JSON compatible with aether-utils.sh subcommand pattern:
|
|
9
|
+
# {"ok": true, "result": {"files_scanned": N, "total_emojis": N,
|
|
10
|
+
# "unmapped": [...], "unused": [...], "usage": {...}}}
|
|
11
|
+
#
|
|
12
|
+
# Compatible with bash 3.x (macOS system bash).
|
|
13
|
+
# Can be sourced by aether-utils.sh or run standalone.
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# _emoji_audit_main — perform the audit and print JSON result
|
|
17
|
+
# Uses Python3 for emoji extraction and map logic (handles multi-codepoint sequences)
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
_emoji_audit_main() {
|
|
20
|
+
local repo_root="${1:-.}"
|
|
21
|
+
|
|
22
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
23
|
+
printf '{"ok":false,"error":"python3 is required but not found"}\n'
|
|
24
|
+
return 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
python3 - "$repo_root" <<'PYEOF'
|
|
28
|
+
import sys
|
|
29
|
+
import os
|
|
30
|
+
import re
|
|
31
|
+
import json
|
|
32
|
+
import glob
|
|
33
|
+
|
|
34
|
+
repo_root = sys.argv[1] if len(sys.argv) > 1 else "."
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Canonical emoji reference map — must match colony-visuals SKILL.md
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
EMOJI_REF_MAP = {
|
|
40
|
+
"\U0001f528": "Builder ant", # 🔨
|
|
41
|
+
"\U0001f441\ufe0f": "Watcher ant", # 👁️
|
|
42
|
+
"\U0001f3b2": "Chaos ant", # 🎲
|
|
43
|
+
"\U0001f50d": "Scout ant", # 🔍
|
|
44
|
+
"\U0001f3fa": "Archaeologist / Seal", # 🏺
|
|
45
|
+
"\U0001f52e": "Oracle ant", # 🔮
|
|
46
|
+
"\U0001f3db\ufe0f": "Architect ant", # 🏛️
|
|
47
|
+
"\U0001f50c": "Ambassador ant", # 🔌
|
|
48
|
+
"\U0001f4ca": "Measurer ant / Status", # 📊
|
|
49
|
+
"\U0001f9ea": "Probe / Tests", # 🧪
|
|
50
|
+
"\U0001f504": "Weaver / Refresh", # 🔄
|
|
51
|
+
"\U0001f4e6": "Gatekeeper / Package", # 📦
|
|
52
|
+
"\U0001f465": "Auditor", # 👥
|
|
53
|
+
"\U0001f6a9": "Flag / Blocker", # 🚩
|
|
54
|
+
"\U0001f4ad": "Dream", # 💭
|
|
55
|
+
"\U0001f95a": "Queen / Init", # 🥚
|
|
56
|
+
"\U0001f4cb": "Plan / List", # 📋
|
|
57
|
+
"\u2705": "Pass / Success", # ✅
|
|
58
|
+
"\u274c": "Fail / Error", # ❌
|
|
59
|
+
"\u26a0\ufe0f": "Warning", # ⚠️
|
|
60
|
+
"\u26d4": "Hard block", # ⛔
|
|
61
|
+
"\U0001f4be": "Save / Persist", # 💾
|
|
62
|
+
"\U0001f3af": "Focus signal", # 🎯
|
|
63
|
+
"\U0001f6ab": "Redirect signal", # 🚫
|
|
64
|
+
"\U0001f4ac": "Feedback signal", # 💬
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
# Scan command files
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
scan_dirs = [
|
|
71
|
+
os.path.join(repo_root, ".claude", "commands", "ant"),
|
|
72
|
+
os.path.join(repo_root, ".opencode", "commands", "ant"),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
scan_files = []
|
|
76
|
+
for d in scan_dirs:
|
|
77
|
+
if os.path.isdir(d):
|
|
78
|
+
scan_files.extend(glob.glob(os.path.join(d, "*.md")))
|
|
79
|
+
|
|
80
|
+
files_scanned = len(scan_files)
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# Extract emoji sequences from combined content
|
|
84
|
+
# Matches base emoji + optional variation selectors, ZWJ sequences
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
EMOJI_PATTERN = re.compile(
|
|
87
|
+
r'[\U0001F300-\U0001F9FF\U00002600-\U000027BF\U00002702-\U000027B0'
|
|
88
|
+
r'\U0001FA00-\U0001FA9F\U0001FAA0-\U0001FAFF\U00002300-\U000023FF'
|
|
89
|
+
r'\U00002B00-\U00002BFF\U00003000-\U0000303F'
|
|
90
|
+
r'\U0001F600-\U0001F64F\U0001F680-\U0001F6FF'
|
|
91
|
+
r'\u2300-\u27BF\u2B00-\u2BFF\u2600-\u27FF'
|
|
92
|
+
r'\u2702-\u27B0\u2194-\u21AA\u231A-\u231B\u23E9-\u23F3\u23F8-\u23FA'
|
|
93
|
+
r'\u25AA-\u25FE\u2614-\u2615\u2648-\u2653\u267F\u2693\u26A0-\u26A1'
|
|
94
|
+
r'\u26AA-\u26AB\u26BD-\u26BE\u26C4-\u26C5\u26CE\u26D4\u26EA'
|
|
95
|
+
r'\u26F2-\u26F3\u26F5\u26FA\u26FD\u2702\u2705\u2708-\u270D'
|
|
96
|
+
r'\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733-\u2734\u2744'
|
|
97
|
+
r'\u2747\u274C\u274E\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797'
|
|
98
|
+
r'\u27A1\u27B0\u27BF\u2934-\u2935\u2B05-\u2B07\u2B1B-\u2B1C\u2B50'
|
|
99
|
+
r'\u2B55\u3030\u303D\u3297\u3299]'
|
|
100
|
+
r'[\uFE0E\uFE0F\u20D0-\u20FF\u200D\U0001F3FB-\U0001F3FF]*'
|
|
101
|
+
r'(?:\u200D[\U0001F300-\U0001FFFF\u2600-\u27BF][\uFE0E\uFE0F\u20D0-\u20FF]*)*',
|
|
102
|
+
re.UNICODE
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
found_emojis = set()
|
|
106
|
+
for filepath in scan_files:
|
|
107
|
+
try:
|
|
108
|
+
with open(filepath, "r", encoding="utf-8", errors="replace") as fh:
|
|
109
|
+
content = fh.read()
|
|
110
|
+
for m in EMOJI_PATTERN.finditer(content):
|
|
111
|
+
e = m.group(0)
|
|
112
|
+
if e.strip():
|
|
113
|
+
found_emojis.add(e)
|
|
114
|
+
except OSError:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
total_emojis = len(found_emojis)
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
# Compute results
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Normalize ref map keys for lookup (strip variation selectors for comparison)
|
|
123
|
+
def normalize(s):
|
|
124
|
+
return s.replace("\ufe0f", "").replace("\ufe0e", "")
|
|
125
|
+
|
|
126
|
+
ref_normalized = {normalize(k): (k, v) for k, v in EMOJI_REF_MAP.items()}
|
|
127
|
+
found_normalized = {normalize(e): e for e in found_emojis}
|
|
128
|
+
|
|
129
|
+
# unmapped: found in files but not in reference map (by normalized form)
|
|
130
|
+
unmapped = sorted([
|
|
131
|
+
raw for norm, raw in found_normalized.items()
|
|
132
|
+
if norm not in ref_normalized
|
|
133
|
+
])
|
|
134
|
+
|
|
135
|
+
# unused: in reference map but not found in files (by normalized form)
|
|
136
|
+
unused = sorted([
|
|
137
|
+
canonical for norm, (canonical, concept) in ref_normalized.items()
|
|
138
|
+
if norm not in found_normalized
|
|
139
|
+
])
|
|
140
|
+
|
|
141
|
+
# usage: reference map entries found in files -> concept
|
|
142
|
+
usage = {}
|
|
143
|
+
for norm, (canonical, concept) in ref_normalized.items():
|
|
144
|
+
if norm in found_normalized:
|
|
145
|
+
usage[canonical] = concept
|
|
146
|
+
|
|
147
|
+
output = {
|
|
148
|
+
"ok": True,
|
|
149
|
+
"result": {
|
|
150
|
+
"files_scanned": files_scanned,
|
|
151
|
+
"total_emojis": total_emojis,
|
|
152
|
+
"unmapped": unmapped,
|
|
153
|
+
"unused": unused,
|
|
154
|
+
"usage": usage,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
print(json.dumps(output))
|
|
158
|
+
PYEOF
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Entry point when run as a standalone script
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
165
|
+
_emoji_audit_main "${1:-$(pwd)}"
|
|
166
|
+
fi
|