aether-colony 5.0.0 → 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 +3150 -3349
- 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 +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 +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/.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/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 +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 +10 -8
- package/.aether/utils/session.sh +552 -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 +199 -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 +1 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +9 -16
- 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 +316 -191
- package/.claude/commands/ant/insert-phase.md +101 -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 +175 -52
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +34 -20
- package/.claude/commands/ant/resume.md +51 -7
- package/.claude/commands/ant/run.md +195 -0
- package/.claude/commands/ant/seal.md +497 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +127 -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 +8 -17
- package/.opencode/commands/ant/continue.md +595 -66
- 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 +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 +365 -156
- 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 +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 +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 +71 -20
- package/.opencode/commands/ant/run.md +201 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +124 -31
- 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 +278 -1
- package/README.md +188 -340
- 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 +7 -3
- 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
|
|
@@ -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
|
|
@@ -87,8 +87,8 @@ json_err() {
|
|
|
87
87
|
"$code" "$escaped_message" "$details_json" "$recovery" "$timestamp" >&2
|
|
88
88
|
|
|
89
89
|
# Log to activity.log (best effort)
|
|
90
|
-
if [[ -n "${
|
|
91
|
-
echo "[$timestamp] ERROR $code: $escaped_message" >> "$
|
|
90
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
91
|
+
echo "[$timestamp] ERROR $code: $escaped_message" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
92
92
|
fi
|
|
93
93
|
|
|
94
94
|
exit 1
|
|
@@ -111,8 +111,22 @@ json_warn() {
|
|
|
111
111
|
"$code" "$escaped_message" "$timestamp"
|
|
112
112
|
|
|
113
113
|
# Log to activity.log (best effort)
|
|
114
|
-
if [[ -n "${
|
|
115
|
-
echo "[$timestamp] WARN $code: $escaped_message" >> "$
|
|
114
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
115
|
+
echo "[$timestamp] WARN $code: $escaped_message" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# --- _aether_log_error function for surfaced errors ---
|
|
120
|
+
# Dual output: [error] prefix to stderr (screen) + timestamped entry to errors.log (file)
|
|
121
|
+
# Distinct from: json_err (structured JSON), json_warn (non-fatal JSON), ⚠ (recovery), [trimmed] (budget)
|
|
122
|
+
_aether_log_error() {
|
|
123
|
+
local message="$1"
|
|
124
|
+
local timestamp
|
|
125
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
126
|
+
echo "[error] $message" >&2
|
|
127
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
128
|
+
mkdir -p "$DATA_DIR" 2>/dev/null # SUPPRESS:OK -- idempotent: ensure dir exists
|
|
129
|
+
echo "[$timestamp] $message" >> "$COLONY_DATA_DIR/errors.log" 2>/dev/null # SUPPRESS:OK -- cleanup: log write is best-effort
|
|
116
130
|
fi
|
|
117
131
|
}
|
|
118
132
|
|
|
@@ -139,8 +153,8 @@ error_handler() {
|
|
|
139
153
|
"$E_BASH_ERROR" "$details" "$(_recovery_default)" "$timestamp" >&2
|
|
140
154
|
|
|
141
155
|
# Log to activity.log (best effort)
|
|
142
|
-
if [[ -n "${
|
|
143
|
-
echo "[$timestamp] ERROR $E_BASH_ERROR: Command failed at line $line_num (exit $exit_code)" >> "$
|
|
156
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
157
|
+
echo "[$timestamp] ERROR $E_BASH_ERROR: Command failed at line $line_num (exit $exit_code)" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
144
158
|
fi
|
|
145
159
|
|
|
146
160
|
exit 1
|
|
@@ -198,7 +212,7 @@ feature_log_degradation() {
|
|
|
198
212
|
}
|
|
199
213
|
|
|
200
214
|
# --- Export all functions and variables ---
|
|
201
|
-
export -f json_err json_warn error_handler
|
|
215
|
+
export -f json_err json_warn _aether_log_error error_handler
|
|
202
216
|
export -f feature_enable feature_disable feature_enabled feature_log_degradation
|
|
203
217
|
export -f _get_recovery _recovery_hub_not_found _recovery_repo_not_init
|
|
204
218
|
export -f _recovery_file_not_found _recovery_json_invalid _recovery_lock_failed
|