aether-colony 5.0.0 → 5.2.1
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 +3226 -3345
- 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 +442 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +509 -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 +502 -0
- package/.aether/commands/insert-phase.yaml +102 -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 +1364 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +375 -0
- package/.aether/commands/resume.yaml +407 -0
- package/.aether/commands/run.yaml +229 -0
- package/.aether/commands/seal.yaml +1214 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +408 -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 +11 -11
- package/.aether/docs/README.md +32 -2
- 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 +1683 -0
- package/.aether/docs/command-playbooks/build-prep.md +284 -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 +1725 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +407 -0
- package/.aether/docs/context-continuity.md +84 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
- package/.aether/docs/error-codes.md +1 -1
- package/.aether/docs/known-issues.md +34 -173
- package/.aether/docs/pheromones.md +86 -6
- package/.aether/docs/plans/pheromone-display-plan.md +257 -0
- package/.aether/docs/queen-commands.md +10 -9
- package/.aether/docs/source-of-truth-map.md +132 -0
- package/.aether/docs/xml-utilities.md +47 -0
- package/.aether/rules/aether-colony.md +23 -13
- 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 +23 -41
- package/.aether/templates/colony-state-reset.jq.template +1 -0
- package/.aether/templates/colony-state.template.json +4 -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/.aether/utils/atomic-write.sh +63 -17
- package/.aether/utils/chamber-utils.sh +145 -2
- package/.aether/utils/council.sh +425 -0
- package/.aether/utils/emoji-audit.sh +166 -0
- package/.aether/utils/error-handler.sh +21 -7
- package/.aether/utils/file-lock.sh +182 -27
- package/.aether/utils/flag.sh +278 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +520 -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 +1710 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +816 -0
- package/.aether/utils/skills.sh +509 -0
- package/.aether/utils/spawn-tree.sh +103 -271
- package/.aether/utils/spawn.sh +260 -0
- package/.aether/utils/state-api.sh +389 -0
- package/.aether/utils/state-loader.sh +8 -6
- package/.aether/utils/suggest.sh +611 -0
- package/.aether/utils/swarm-display.sh +10 -1
- package/.aether/utils/swarm.sh +1004 -0
- package/.aether/utils/watch-spawn-tree.sh +11 -2
- package/.aether/utils/xml-compose.sh +2 -2
- package/.aether/utils/xml-convert.sh +9 -5
- package/.aether/utils/xml-core.sh +5 -9
- package/.aether/utils/xml-query.sh +4 -4
- package/.aether/workers.md +86 -67
- package/.claude/agents/ant/aether-ambassador.md +2 -1
- package/.claude/agents/ant/aether-archaeologist.md +6 -1
- package/.claude/agents/ant/aether-architect.md +236 -0
- package/.claude/agents/ant/aether-auditor.md +6 -1
- package/.claude/agents/ant/aether-builder.md +38 -1
- package/.claude/agents/ant/aether-chaos.md +2 -1
- package/.claude/agents/ant/aether-chronicler.md +1 -0
- package/.claude/agents/ant/aether-gatekeeper.md +6 -1
- package/.claude/agents/ant/aether-includer.md +1 -0
- package/.claude/agents/ant/aether-keeper.md +1 -0
- package/.claude/agents/ant/aether-measurer.md +6 -1
- package/.claude/agents/ant/aether-oracle.md +237 -0
- package/.claude/agents/ant/aether-probe.md +2 -1
- package/.claude/agents/ant/aether-queen.md +6 -1
- package/.claude/agents/ant/aether-route-setter.md +6 -1
- package/.claude/agents/ant/aether-sage.md +68 -3
- package/.claude/agents/ant/aether-scout.md +38 -1
- package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
- package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
- package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
- package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
- package/.claude/agents/ant/aether-tracker.md +6 -1
- package/.claude/agents/ant/aether-watcher.md +37 -1
- package/.claude/agents/ant/aether-weaver.md +2 -1
- package/.claude/commands/ant/archaeology.md +1 -8
- package/.claude/commands/ant/build.md +43 -1159
- package/.claude/commands/ant/chaos.md +1 -14
- package/.claude/commands/ant/colonize.md +3 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +213 -15
- package/.claude/commands/ant/data-clean.md +81 -0
- package/.claude/commands/ant/dream.md +12 -9
- package/.claude/commands/ant/entomb.md +62 -87
- package/.claude/commands/ant/export-signals.md +57 -0
- package/.claude/commands/ant/feedback.md +18 -0
- package/.claude/commands/ant/flag.md +12 -0
- package/.claude/commands/ant/flags.md +22 -8
- package/.claude/commands/ant/focus.md +18 -0
- package/.claude/commands/ant/help.md +40 -8
- package/.claude/commands/ant/history.md +3 -0
- package/.claude/commands/ant/import-signals.md +71 -0
- package/.claude/commands/ant/init.md +349 -191
- package/.claude/commands/ant/insert-phase.md +105 -0
- package/.claude/commands/ant/interpret.md +11 -0
- package/.claude/commands/ant/lay-eggs.md +167 -158
- package/.claude/commands/ant/maturity.md +22 -11
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +6 -0
- package/.claude/commands/ant/oracle.md +317 -62
- package/.claude/commands/ant/organize.md +10 -5
- package/.claude/commands/ant/patrol.md +620 -0
- package/.claude/commands/ant/pause-colony.md +8 -22
- package/.claude/commands/ant/phase.md +26 -37
- package/.claude/commands/ant/pheromones.md +156 -0
- package/.claude/commands/ant/plan.md +199 -50
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +37 -22
- package/.claude/commands/ant/resume.md +60 -7
- package/.claude/commands/ant/run.md +231 -0
- package/.claude/commands/ant/seal.md +506 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +171 -1
- package/.claude/commands/ant/swarm.md +11 -23
- package/.claude/commands/ant/tunnels.md +1 -0
- package/.claude/commands/ant/update.md +58 -135
- package/.claude/commands/ant/verify-castes.md +90 -42
- package/.claude/commands/ant/watch.md +1 -0
- package/.opencode/agents/aether-ambassador.md +1 -1
- package/.opencode/agents/aether-architect.md +133 -0
- package/.opencode/agents/aether-builder.md +3 -3
- package/.opencode/agents/aether-oracle.md +137 -0
- package/.opencode/agents/aether-queen.md +1 -1
- package/.opencode/agents/aether-route-setter.md +1 -1
- package/.opencode/agents/aether-scout.md +1 -1
- package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
- package/.opencode/agents/aether-surveyor-nest.md +6 -1
- package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
- package/.opencode/agents/aether-surveyor-provisions.md +6 -1
- package/.opencode/agents/aether-tracker.md +1 -1
- package/.opencode/agents/aether-watcher.md +1 -1
- package/.opencode/agents/aether-weaver.md +1 -1
- package/.opencode/commands/ant/archaeology.md +7 -14
- package/.opencode/commands/ant/build.md +54 -88
- package/.opencode/commands/ant/chaos.md +7 -24
- package/.opencode/commands/ant/colonize.md +10 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +150 -18
- package/.opencode/commands/ant/data-clean.md +77 -0
- package/.opencode/commands/ant/dream.md +15 -17
- package/.opencode/commands/ant/entomb.md +28 -18
- 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 +396 -154
- package/.opencode/commands/ant/insert-phase.md +111 -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 +14 -12
- 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 +210 -57
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +41 -29
- package/.opencode/commands/ant/resume.md +80 -20
- package/.opencode/commands/ant/run.md +237 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +125 -30
- package/.opencode/commands/ant/swarm.md +3 -345
- package/.opencode/commands/ant/tunnels.md +3 -9
- package/.opencode/commands/ant/update.md +63 -127
- package/.opencode/commands/ant/verify-castes.md +96 -42
- package/.opencode/commands/ant/watch.md +7 -0
- package/CHANGELOG.md +368 -1
- package/README.md +195 -324
- package/bin/cli.js +236 -429
- package/bin/generate-commands.js +186 -0
- package/bin/generate-commands.sh +128 -89
- package/bin/lib/spawn-logger.js +0 -15
- package/bin/lib/update-transaction.js +285 -35
- package/bin/npx-install.js +178 -0
- package/bin/validate-package.sh +85 -3
- package/package.json +16 -4
- package/.aether/CONTEXT.md +0 -160
- package/.aether/docs/QUEEN.md +0 -84
- package/.aether/exchange/colony-registry.xml +0 -11
- package/.aether/exchange/pheromones.xml +0 -87
- package/.aether/exchange/queen-wisdom.xml +0 -14
- package/.aether/model-profiles.yaml +0 -100
- package/.aether/utils/spawn-with-model.sh +0 -56
- 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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_template": "midden",
|
|
3
|
+
"_version": "1.0",
|
|
4
|
+
"_instructions": "Write to .aether/data/midden/midden.json. No substitution needed. Remove underscore-prefixed keys.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"signals": [],
|
|
7
|
+
"spawn_metrics": {
|
|
8
|
+
"total_spawned": 0,
|
|
9
|
+
"completed": 0,
|
|
10
|
+
"failed": 0,
|
|
11
|
+
"efficiency_pct": 0
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_template": "session",
|
|
3
|
+
"_version": "1.0",
|
|
4
|
+
"_instructions": "Write to .aether/data/session.json. Replace __SESSION_ID__, __GOAL__, __TIMESTAMP__. Remove underscore-prefixed keys.",
|
|
5
|
+
"session_id": "__SESSION_ID__",
|
|
6
|
+
"colony_goal": "__GOAL__",
|
|
7
|
+
"started_at": "__TIMESTAMP__",
|
|
8
|
+
"last_activity": "__TIMESTAMP__"
|
|
9
|
+
}
|
|
@@ -25,11 +25,13 @@ if [ ! -f "$_AETHER_UTILS_DIR/file-lock.sh" ]; then
|
|
|
25
25
|
fi
|
|
26
26
|
source "$_AETHER_UTILS_DIR/file-lock.sh"
|
|
27
27
|
|
|
28
|
-
# Aether root detection - use git root
|
|
29
|
-
if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
# Aether root detection - respect existing AETHER_ROOT, or use git root, or use current directory
|
|
29
|
+
if [[ -z "${AETHER_ROOT:-}" ]]; then
|
|
30
|
+
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
31
|
+
AETHER_ROOT="$(git rev-parse --show-toplevel)"
|
|
32
|
+
else
|
|
33
|
+
AETHER_ROOT="$(pwd)"
|
|
34
|
+
fi
|
|
33
35
|
fi
|
|
34
36
|
|
|
35
37
|
TEMP_DIR="$AETHER_ROOT/.aether/temp"
|
|
@@ -41,9 +43,44 @@ mkdir -p "$TEMP_DIR" "$BACKUP_DIR"
|
|
|
41
43
|
# Number of backups to keep
|
|
42
44
|
MAX_BACKUPS=3
|
|
43
45
|
|
|
46
|
+
# Safety stats file for tracking data safety events (best-effort, never fails operations)
|
|
47
|
+
SAFETY_STATS_FILE="${AETHER_ROOT}/.aether/data/safety-stats.json"
|
|
48
|
+
|
|
49
|
+
# Increment a safety stats counter (best-effort, never fails the calling operation)
|
|
50
|
+
# Arguments: counter_name (e.g., "stale_locks_cleaned", "json_validation_rejects")
|
|
51
|
+
_safety_stats_increment() {
|
|
52
|
+
local counter_name="$1"
|
|
53
|
+
local stats_file="$SAFETY_STATS_FILE"
|
|
54
|
+
local now_iso
|
|
55
|
+
now_iso=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")
|
|
56
|
+
|
|
57
|
+
# Ensure data directory exists
|
|
58
|
+
mkdir -p "$(dirname "$stats_file")" 2>/dev/null || return 0
|
|
59
|
+
|
|
60
|
+
# Initialize if missing
|
|
61
|
+
if [[ ! -f "$stats_file" ]]; then
|
|
62
|
+
printf '{"stale_locks_cleaned":0,"json_validation_rejects":0,"last_updated":"%s"}\n' "$now_iso" > "$stats_file" 2>/dev/null || return 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Increment counter (best-effort, don't fail on jq errors)
|
|
66
|
+
local updated
|
|
67
|
+
updated=$(jq --arg key "$counter_name" --arg ts "$now_iso" '
|
|
68
|
+
.[$key] = ((.[$key] // 0) + 1) |
|
|
69
|
+
.last_updated = $ts
|
|
70
|
+
' "$stats_file" 2>/dev/null) || return 0
|
|
71
|
+
|
|
72
|
+
if [[ -n "$updated" ]]; then
|
|
73
|
+
printf '%s\n' "$updated" > "$stats_file" 2>/dev/null || return 0
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
|
|
44
77
|
# Atomic write: write content to file via temporary file
|
|
45
78
|
# Arguments: target_file, content
|
|
46
79
|
# Returns: 0 on success, 1 on failure
|
|
80
|
+
# NOTE: atomic_write does NOT interact with file locks. Lock management
|
|
81
|
+
# (acquire_lock/release_lock) is the CALLER's responsibility. If you need
|
|
82
|
+
# exclusive access, acquire the lock before calling atomic_write, and release
|
|
83
|
+
# it after (including on error paths).
|
|
47
84
|
atomic_write() {
|
|
48
85
|
local target_file="$1"
|
|
49
86
|
local content="$2"
|
|
@@ -53,10 +90,10 @@ atomic_write() {
|
|
|
53
90
|
mkdir -p "$target_dir"
|
|
54
91
|
|
|
55
92
|
# Create unique temp file
|
|
56
|
-
local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$(date +%s
|
|
93
|
+
local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$( date +%s )_${RANDOM}.tmp"
|
|
57
94
|
|
|
58
95
|
# Write content to temp file
|
|
59
|
-
if !
|
|
96
|
+
if ! printf '%s\n' "$content" > "$temp_file"; then
|
|
60
97
|
echo "Failed to write to temp file: $temp_file"
|
|
61
98
|
rm -f "$temp_file"
|
|
62
99
|
return 1
|
|
@@ -67,11 +104,12 @@ atomic_write() {
|
|
|
67
104
|
create_backup "$target_file"
|
|
68
105
|
fi
|
|
69
106
|
|
|
70
|
-
# Validate JSON if it's a JSON file
|
|
107
|
+
# Validate JSON if it's a JSON file (lock management is caller's responsibility)
|
|
71
108
|
if [[ "$target_file" == *.json ]]; then
|
|
72
|
-
if !
|
|
109
|
+
if ! jq empty "$temp_file" 2>/dev/null; then
|
|
73
110
|
echo "Invalid JSON in temp file: $temp_file"
|
|
74
111
|
rm -f "$temp_file"
|
|
112
|
+
_safety_stats_increment "json_validation_rejects" 2>/dev/null || true
|
|
75
113
|
return 1
|
|
76
114
|
fi
|
|
77
115
|
fi
|
|
@@ -108,7 +146,7 @@ atomic_write_from_file() {
|
|
|
108
146
|
mkdir -p "$target_dir"
|
|
109
147
|
|
|
110
148
|
# Create unique temp file
|
|
111
|
-
local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$(date +%s
|
|
149
|
+
local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$( date +%s )_${RANDOM}.tmp"
|
|
112
150
|
|
|
113
151
|
# Copy source to temp
|
|
114
152
|
if ! cp "$source_file" "$temp_file"; then
|
|
@@ -122,11 +160,12 @@ atomic_write_from_file() {
|
|
|
122
160
|
create_backup "$target_file"
|
|
123
161
|
fi
|
|
124
162
|
|
|
125
|
-
# Validate JSON if it's a JSON file
|
|
163
|
+
# Validate JSON if it's a JSON file (lock management is caller's responsibility)
|
|
126
164
|
if [[ "$target_file" == *.json ]]; then
|
|
127
|
-
if !
|
|
165
|
+
if ! jq empty "$temp_file" 2>/dev/null; then
|
|
128
166
|
echo "Invalid JSON in temp file: $temp_file"
|
|
129
167
|
rm -f "$temp_file"
|
|
168
|
+
_safety_stats_increment "json_validation_rejects" 2>/dev/null || true
|
|
130
169
|
return 1
|
|
131
170
|
fi
|
|
132
171
|
fi
|
|
@@ -166,10 +205,17 @@ create_backup() {
|
|
|
166
205
|
# Arguments: base_name
|
|
167
206
|
rotate_backups() {
|
|
168
207
|
local base_name="$1"
|
|
169
|
-
local backups=$(ls -t "${BACKUP_DIR}/${base_name}".*.backup 2>/dev/null | wc -l)
|
|
170
208
|
|
|
171
|
-
|
|
172
|
-
|
|
209
|
+
# Use find with -print0 for safe handling of paths with spaces
|
|
210
|
+
local backup_count
|
|
211
|
+
backup_count=$(find "$BACKUP_DIR" -maxdepth 1 -name "${base_name}.*.backup" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
212
|
+
|
|
213
|
+
if [ "$backup_count" -gt "$MAX_BACKUPS" ]; then
|
|
214
|
+
# Delete oldest backups beyond MAX_BACKUPS using find for space-safe handling
|
|
215
|
+
find "$BACKUP_DIR" -maxdepth 1 -name "${base_name}.*.backup" -type f -print0 2>/dev/null \
|
|
216
|
+
| xargs -0 ls -t 2>/dev/null \
|
|
217
|
+
| tail -n +$((MAX_BACKUPS + 1)) \
|
|
218
|
+
| while IFS= read -r file; do rm -f "$file" 2>/dev/null || true; done
|
|
173
219
|
fi
|
|
174
220
|
}
|
|
175
221
|
|
|
@@ -209,9 +255,9 @@ list_backups() {
|
|
|
209
255
|
|
|
210
256
|
# Cleanup temp files older than 1 hour
|
|
211
257
|
cleanup_temp_files() {
|
|
212
|
-
find "$TEMP_DIR" -name "*.tmp" -
|
|
258
|
+
find "$TEMP_DIR" -name "*.tmp" -mmin +60 -delete 2>/dev/null || true
|
|
213
259
|
}
|
|
214
260
|
|
|
215
261
|
# Export functions
|
|
216
262
|
export -f atomic_write atomic_write_from_file create_backup rotate_backups
|
|
217
|
-
export -f restore_backup list_backups cleanup_temp_files
|
|
263
|
+
export -f restore_backup list_backups cleanup_temp_files _safety_stats_increment
|
|
@@ -17,7 +17,10 @@ CURRENT_LOCK=${CURRENT_LOCK:-""}
|
|
|
17
17
|
|
|
18
18
|
# Get script directory for sourcing (preserve parent SCRIPT_DIR if set)
|
|
19
19
|
__chamber_utils_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
-
|
|
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
|
|
21
24
|
|
|
22
25
|
# Use parent SCRIPT_DIR if available, otherwise use local
|
|
23
26
|
SCRIPT_DIR="${SCRIPT_DIR:-$__chamber_utils_dir}"
|
|
@@ -44,6 +47,7 @@ fi
|
|
|
44
47
|
: "${E_FILE_NOT_FOUND:=E_FILE_NOT_FOUND}"
|
|
45
48
|
: "${E_BASH_ERROR:=E_BASH_ERROR}"
|
|
46
49
|
: "${E_JSON_INVALID:=E_JSON_INVALID}"
|
|
50
|
+
: "${E_FEATURE_UNAVAILABLE:=E_FEATURE_UNAVAILABLE}"
|
|
47
51
|
|
|
48
52
|
# --- Chamber Functions ---
|
|
49
53
|
|
|
@@ -293,5 +297,144 @@ EOF
|
|
|
293
297
|
json_ok "$sorted"
|
|
294
298
|
}
|
|
295
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
|
+
|
|
296
439
|
# Export functions for use in other scripts
|
|
297
|
-
export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list
|
|
440
|
+
export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list _colony_archive_xml
|