aether-colony 3.1.4 → 3.1.15

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.
Files changed (124) hide show
  1. package/.claude/commands/ant/archaeology.md +12 -0
  2. package/.claude/commands/ant/build.md +382 -319
  3. package/.claude/commands/ant/chaos.md +23 -1
  4. package/.claude/commands/ant/colonize.md +147 -87
  5. package/.claude/commands/ant/continue.md +213 -23
  6. package/.claude/commands/ant/council.md +22 -0
  7. package/.claude/commands/ant/dream.md +18 -0
  8. package/.claude/commands/ant/entomb.md +178 -6
  9. package/.claude/commands/ant/init.md +87 -13
  10. package/.claude/commands/ant/lay-eggs.md +45 -5
  11. package/.claude/commands/ant/oracle.md +82 -9
  12. package/.claude/commands/ant/organize.md +2 -2
  13. package/.claude/commands/ant/pause-colony.md +86 -28
  14. package/.claude/commands/ant/phase.md +26 -0
  15. package/.claude/commands/ant/plan.md +204 -111
  16. package/.claude/commands/ant/resume-colony.md +23 -1
  17. package/.claude/commands/ant/resume.md +159 -0
  18. package/.claude/commands/ant/seal.md +177 -3
  19. package/.claude/commands/ant/swarm.md +78 -97
  20. package/.claude/commands/ant/verify-castes.md +7 -7
  21. package/.claude/commands/ant/watch.md +17 -0
  22. package/.opencode/agents/aether-ambassador.md +97 -0
  23. package/.opencode/agents/aether-archaeologist.md +91 -0
  24. package/.opencode/agents/aether-architect.md +66 -0
  25. package/.opencode/agents/aether-auditor.md +111 -0
  26. package/.opencode/agents/aether-builder.md +28 -10
  27. package/.opencode/agents/aether-chaos.md +98 -0
  28. package/.opencode/agents/aether-chronicler.md +80 -0
  29. package/.opencode/agents/aether-gatekeeper.md +107 -0
  30. package/.opencode/agents/aether-guardian.md +107 -0
  31. package/.opencode/agents/aether-includer.md +108 -0
  32. package/.opencode/agents/aether-keeper.md +106 -0
  33. package/.opencode/agents/aether-measurer.md +119 -0
  34. package/.opencode/agents/aether-probe.md +91 -0
  35. package/.opencode/agents/aether-queen.md +72 -19
  36. package/.opencode/agents/aether-route-setter.md +85 -0
  37. package/.opencode/agents/aether-sage.md +98 -0
  38. package/.opencode/agents/aether-scout.md +33 -15
  39. package/.opencode/agents/aether-surveyor-disciplines.md +334 -0
  40. package/.opencode/agents/aether-surveyor-nest.md +272 -0
  41. package/.opencode/agents/aether-surveyor-pathogens.md +209 -0
  42. package/.opencode/agents/aether-surveyor-provisions.md +277 -0
  43. package/.opencode/agents/aether-tracker.md +91 -0
  44. package/.opencode/agents/aether-watcher.md +30 -12
  45. package/.opencode/agents/aether-weaver.md +87 -0
  46. package/.opencode/agents/workers.md +1034 -0
  47. package/.opencode/commands/ant/archaeology.md +44 -26
  48. package/.opencode/commands/ant/build.md +327 -295
  49. package/.opencode/commands/ant/chaos.md +32 -4
  50. package/.opencode/commands/ant/colonize.md +119 -93
  51. package/.opencode/commands/ant/continue.md +98 -10
  52. package/.opencode/commands/ant/council.md +28 -0
  53. package/.opencode/commands/ant/dream.md +24 -0
  54. package/.opencode/commands/ant/entomb.md +73 -1
  55. package/.opencode/commands/ant/feedback.md +8 -2
  56. package/.opencode/commands/ant/flag.md +9 -3
  57. package/.opencode/commands/ant/flags.md +8 -2
  58. package/.opencode/commands/ant/focus.md +8 -2
  59. package/.opencode/commands/ant/help.md +12 -0
  60. package/.opencode/commands/ant/init.md +49 -4
  61. package/.opencode/commands/ant/lay-eggs.md +30 -2
  62. package/.opencode/commands/ant/oracle.md +39 -7
  63. package/.opencode/commands/ant/organize.md +9 -3
  64. package/.opencode/commands/ant/pause-colony.md +54 -1
  65. package/.opencode/commands/ant/phase.md +36 -4
  66. package/.opencode/commands/ant/plan.md +225 -117
  67. package/.opencode/commands/ant/redirect.md +8 -2
  68. package/.opencode/commands/ant/resume-colony.md +51 -26
  69. package/.opencode/commands/ant/seal.md +76 -0
  70. package/.opencode/commands/ant/status.md +50 -20
  71. package/.opencode/commands/ant/swarm.md +108 -104
  72. package/.opencode/commands/ant/tunnels.md +107 -2
  73. package/CHANGELOG.md +21 -0
  74. package/README.md +199 -86
  75. package/bin/cli.js +142 -25
  76. package/bin/generate-commands.sh +100 -16
  77. package/bin/lib/caste-colors.js +5 -5
  78. package/bin/lib/errors.js +16 -0
  79. package/bin/lib/file-lock.js +279 -44
  80. package/bin/lib/state-sync.js +206 -23
  81. package/bin/lib/update-transaction.js +206 -24
  82. package/bin/sync-to-runtime.sh +129 -0
  83. package/package.json +2 -2
  84. package/runtime/CONTEXT.md +160 -0
  85. package/runtime/aether-utils.sh +1421 -55
  86. package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +1343 -0
  87. package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +2642 -0
  88. package/runtime/docs/PHEROMONE-INJECTION.md +240 -0
  89. package/runtime/docs/PHEROMONE-INTEGRATION.md +192 -0
  90. package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +426 -0
  91. package/runtime/docs/README.md +94 -0
  92. package/runtime/docs/VISUAL-OUTPUT-SPEC.md +219 -0
  93. package/runtime/docs/biological-reference.md +272 -0
  94. package/runtime/docs/codebase-review.md +399 -0
  95. package/runtime/docs/command-sync.md +164 -0
  96. package/runtime/docs/implementation-learnings.md +89 -0
  97. package/runtime/docs/known-issues.md +217 -0
  98. package/runtime/docs/namespace.md +148 -0
  99. package/runtime/docs/planning-discipline.md +159 -0
  100. package/runtime/lib/queen-utils.sh +729 -0
  101. package/runtime/model-profiles.yaml +100 -0
  102. package/runtime/recover.sh +136 -0
  103. package/runtime/templates/QUEEN.md.template +79 -0
  104. package/runtime/utils/atomic-write.sh +5 -5
  105. package/runtime/utils/chamber-utils.sh +6 -3
  106. package/runtime/utils/error-handler.sh +200 -0
  107. package/runtime/utils/queen-to-md.xsl +395 -0
  108. package/runtime/utils/spawn-tree.sh +428 -0
  109. package/runtime/utils/spawn-with-model.sh +56 -0
  110. package/runtime/utils/state-loader.sh +215 -0
  111. package/runtime/utils/swarm-display.sh +5 -5
  112. package/runtime/utils/watch-spawn-tree.sh +90 -22
  113. package/runtime/utils/xml-compose.sh +247 -0
  114. package/runtime/utils/xml-core.sh +186 -0
  115. package/runtime/utils/xml-utils.sh +2161 -0
  116. package/runtime/verification-loop.md +1 -1
  117. package/runtime/workers-new-castes.md +516 -0
  118. package/runtime/workers.md +20 -8
  119. package/.aether/visualizations/anthill-stages/brood-stable.txt +0 -26
  120. package/.aether/visualizations/anthill-stages/crowned-anthill.txt +0 -30
  121. package/.aether/visualizations/anthill-stages/first-mound.txt +0 -18
  122. package/.aether/visualizations/anthill-stages/open-chambers.txt +0 -24
  123. package/.aether/visualizations/anthill-stages/sealed-chambers.txt +0 -28
  124. package/.aether/visualizations/anthill-stages/ventilated-nest.txt +0 -27
@@ -0,0 +1,100 @@
1
+ version: '1.0'
2
+ description: Default model assignments for Aether colony workers
3
+ profile: default
4
+ worker_models:
5
+ prime: glm-5
6
+ archaeologist: glm-5
7
+ architect: glm-5
8
+ oracle: minimax-2.5
9
+ route_setter: kimi-k2.5
10
+ builder: kimi-k2.5
11
+ watcher: kimi-k2.5
12
+ scout: kimi-k2.5
13
+ chaos: kimi-k2.5
14
+ colonizer: kimi-k2.5
15
+ model_metadata:
16
+ glm-5:
17
+ description: '744B MoE model (40B active), long-horizon agentic engineering'
18
+ provider: z_ai
19
+ capabilities:
20
+ - planning
21
+ - coordination
22
+ - long_context
23
+ - complex_reasoning
24
+ - agentic_workflows
25
+ context_window: 200000
26
+ speed: medium
27
+ cost_tier: high
28
+ best_for:
29
+ - 'Long-horizon task execution (tested: 1 year business simulation)'
30
+ - Strategic coordination and planning
31
+ - Historical pattern analysis across large contexts
32
+ - Multi-step autonomous workflows
33
+ minimax-2.5:
34
+ description: Frontier model for architectural planning and agentic tool use
35
+ provider: minimax
36
+ capabilities:
37
+ - system_design
38
+ - architecture
39
+ - browse
40
+ - search
41
+ - task_decomposition
42
+ context_window: 200000
43
+ speed: fast
44
+ cost_tier: medium
45
+ best_for:
46
+ - Architectural-level planning and system design
47
+ - Research with browse/search (76.3% BrowseComp)
48
+ - Efficient task decomposition (20% fewer search rounds)
49
+ - Full-stack development (80.2% SWE-Bench Verified)
50
+ kimi-k2.5:
51
+ description: '1T MoE model (32B active), multimodal agent with swarm capabilities'
52
+ provider: kimi
53
+ capabilities:
54
+ - coding
55
+ - multimodal
56
+ - agent_swarm
57
+ - visual_coding
58
+ - validation
59
+ context_window: 256000
60
+ speed: fast
61
+ cost_tier: low
62
+ best_for:
63
+ - Code generation and refactoring (76.8% SWE-Bench)
64
+ - Visual coding (screenshots to code)
65
+ - Parallel exploration (up to 100 sub-agents)
66
+ - Validation and testing workflows
67
+ task_routing:
68
+ default_model: kimi-k2.5
69
+ complexity_indicators:
70
+ complex:
71
+ keywords:
72
+ - design
73
+ - architecture
74
+ - plan
75
+ - coordinate
76
+ - synthesize
77
+ - strategize
78
+ - optimize
79
+ model: glm-5
80
+ simple:
81
+ keywords:
82
+ - implement
83
+ - code
84
+ - refactor
85
+ - write
86
+ - create
87
+ model: kimi-k2.5
88
+ validate:
89
+ keywords:
90
+ - test
91
+ - validate
92
+ - verify
93
+ - check
94
+ - review
95
+ - audit
96
+ model: minimax-2.5
97
+ proxy:
98
+ endpoint: 'http://localhost:4000'
99
+ auth_token: ${LITELLM_AUTH_TOKEN:-sk-litellm-local}
100
+ health_check: 'http://localhost:4000/health'
@@ -0,0 +1,136 @@
1
+ #!/bin/bash
2
+ # Aether v3.1 Recovery Script
3
+ # Run this after reading RECOVERY-PLAN.md
4
+
5
+ set -e
6
+
7
+ echo "╔══════════════════════════════════════════════════════════════╗"
8
+ echo "║ AETHER v3.1 RECOVERY - RUNNING SYNC ║"
9
+ echo "╚══════════════════════════════════════════════════════════════╝"
10
+ echo ""
11
+
12
+ # Colors
13
+ RED='\033[0;31m'
14
+ GREEN='\033[0;32m'
15
+ YELLOW='\033[1;33m'
16
+ NC='\033[0m' # No Color
17
+
18
+ # Step 1: Check if we're in the right place
19
+ if [ ! -f "package.json" ] || [ ! -d ".aether" ] || [ ! -d "runtime" ]; then
20
+ echo -e "${RED}ERROR: Must run from Aether repo root${NC}"
21
+ exit 1
22
+ fi
23
+
24
+ # Step 2: Commit any staged changes
25
+ echo "Step 1: Checking git status..."
26
+ if ! git diff --cached --quiet; then
27
+ echo "Found staged changes. Committing..."
28
+ git commit -m "recovery: staged changes before sync"
29
+ fi
30
+
31
+ # Step 3: Sync runtime/ from .aether/
32
+ echo ""
33
+ echo "Step 2: Syncing runtime/ from .aether/..."
34
+ echo "------------------------------------------"
35
+
36
+ # Core files
37
+ echo " → workers.md"
38
+ cp .aether/workers.md runtime/workers.md
39
+
40
+ echo " → aether-utils.sh"
41
+ cp .aether/aether-utils.sh runtime/aether-utils.sh
42
+
43
+ echo " → verification-loop.md"
44
+ cp .aether/verification-loop.md runtime/verification-loop.md 2>/dev/null || echo " (skipped - not in .aether)"
45
+
46
+ # Utils - sync all files
47
+ echo ""
48
+ echo " → utilities/"
49
+ for file in .aether/utils/*.sh; do
50
+ if [ -f "$file" ]; then
51
+ filename=$(basename "$file")
52
+ echo " copying $filename"
53
+ cp "$file" "runtime/utils/$filename"
54
+ fi
55
+ done
56
+
57
+ # Docs - ensure runtime/docs/ exists and has files
58
+ if [ -d ".aether/docs" ]; then
59
+ echo ""
60
+ echo " → docs/"
61
+ mkdir -p runtime/docs
62
+ for file in .aether/docs/*.md .aether/docs/*.json 2>/dev/null; do
63
+ if [ -f "$file" ]; then
64
+ filename=$(basename "$file")
65
+ echo " copying $filename"
66
+ cp "$file" "runtime/docs/$filename"
67
+ fi
68
+ done
69
+ fi
70
+
71
+ # Step 4: Verify the sync
72
+ echo ""
73
+ echo "Step 3: Verifying sync..."
74
+ echo "------------------------------------------"
75
+
76
+ # Check emoji section
77
+ if grep -q "Caste Emoji Mapping:" runtime/workers.md; then
78
+ echo -e "${GREEN} ✓ workers.md has emoji section${NC}"
79
+ else
80
+ echo -e "${RED} ✗ workers.md MISSING emoji section${NC}"
81
+ fi
82
+
83
+ # Check get_caste_emoji
84
+ if grep -q "get_caste_emoji()" runtime/aether-utils.sh; then
85
+ echo -e "${GREEN} ✓ aether-utils.sh has get_caste_emoji${NC}"
86
+ else
87
+ echo -e "${RED} ✗ aether-utils.sh MISSING get_caste_emoji${NC}"
88
+ fi
89
+
90
+ # Count utils files
91
+ RUNTIME_UTILS=$(ls runtime/utils/*.sh 2>/dev/null | wc -l)
92
+ AETHER_UTILS=$(ls .aether/utils/*.sh 2>/dev/null | wc -l)
93
+ echo " → runtime/utils: $RUNTIME_UTILS files"
94
+ echo " → .aether/utils: $AETHER_UTILS files"
95
+
96
+ if [ "$RUNTIME_UTILS" -eq "$AETHER_UTILS" ]; then
97
+ echo -e "${GREEN} ✓ Utils counts match${NC}"
98
+ else
99
+ echo -e "${YELLOW} ⚠ Utils counts differ (may be okay if some are .aether-specific)${NC}"
100
+ fi
101
+
102
+ # Step 5: Stage the changes
103
+ echo ""
104
+ echo "Step 4: Staging changes..."
105
+ git add runtime/
106
+
107
+ # Show status
108
+ echo ""
109
+ echo "Git status after sync:"
110
+ git status --short runtime/
111
+
112
+ # Step 6: Instructions
113
+ echo ""
114
+ echo "╔══════════════════════════════════════════════════════════════╗"
115
+ echo "║ RECOVERY SYNC COMPLETE ║"
116
+ echo "╚══════════════════════════════════════════════════════════════╝"
117
+ echo ""
118
+ echo "Next steps:"
119
+ echo ""
120
+ echo " 1. Review the changes:"
121
+ echo " git diff --cached runtime/"
122
+ echo ""
123
+ echo " 2. Commit the sync:"
124
+ echo " git commit -m 'sync: runtime/ updated from working .aether/'"
125
+ echo ""
126
+ echo " 3. Reinstall to update hub:"
127
+ echo " npm install -g ."
128
+ echo ""
129
+ echo " 4. Verify hub updated:"
130
+ echo " grep 'Caste Emoji Mapping:' ~/.aether/system/workers.md"
131
+ echo ""
132
+ echo " 5. Test in this repo:"
133
+ echo " /ant:init 'Test recovery'"
134
+ echo ""
135
+ echo "Remember: .aether/ is the SOURCE OF TRUTH. runtime/ is auto-populated by bin/sync-to-runtime.sh"
136
+ echo ""
@@ -0,0 +1,79 @@
1
+ # QUEEN.md — Colony Wisdom
2
+
3
+ > Last evolved: {TIMESTAMP}
4
+ > Colonies contributed: 0
5
+ > Wisdom version: 1.0.0
6
+
7
+ ---
8
+
9
+ ## 📜 Philosophies
10
+
11
+ Core beliefs that guide all colony work. These are validated through repeated successful application across multiple colonies.
12
+
13
+ *No philosophies recorded yet. Philosophies are promoted from PHILOSOPHY pheromones after 5+ validations.*
14
+
15
+ ---
16
+
17
+ ## 🧭 Patterns
18
+
19
+ Validated approaches that consistently work. These represent discovered best practices that have proven themselves in the field.
20
+
21
+ *No patterns recorded yet. Patterns are promoted from PATTERN pheromones after 3+ validations.*
22
+
23
+ ---
24
+
25
+ ## ⚠️ Redirects
26
+
27
+ Anti-patterns to avoid. These represent approaches that have caused problems and should be redirected away from.
28
+
29
+ *No redirects recorded yet. Redirects are promoted from REDIRECT pheromones after 2+ triggers.*
30
+
31
+ ---
32
+
33
+ ## 🔧 Stack Wisdom
34
+
35
+ Technology-specific insights and constraints detected through codebase analysis.
36
+
37
+ *No stack wisdom recorded yet. Stack wisdom is auto-populated by `/ant:colonize`.*
38
+
39
+ ---
40
+
41
+ ## 🏛️ Decrees
42
+
43
+ User-mandated rules that override other guidance. These represent explicit directives from the Queen.
44
+
45
+ *No decrees recorded yet. Decrees are added via `/ant:decree "<rule>"`.*
46
+
47
+ ---
48
+
49
+ ## 📊 Evolution Log
50
+
51
+ Track how wisdom has evolved over time.
52
+
53
+ | Date | Colony | Change | Details |
54
+ |------|--------|--------|---------|
55
+ | {TIMESTAMP} | system | initialized | QUEEN.md created from template |
56
+
57
+ ---
58
+
59
+ <!-- METADATA
60
+ {
61
+ "version": "1.0.0",
62
+ "last_evolved": "{TIMESTAMP}",
63
+ "colonies_contributed": [],
64
+ "promotion_thresholds": {
65
+ "philosophy": 5,
66
+ "pattern": 3,
67
+ "redirect": 2,
68
+ "stack": 1,
69
+ "decree": 0
70
+ },
71
+ "stats": {
72
+ "total_philosophies": 0,
73
+ "total_patterns": 0,
74
+ "total_redirects": 0,
75
+ "total_stack_entries": 0,
76
+ "total_decrees": 0
77
+ }
78
+ }
79
+ -->
@@ -62,6 +62,11 @@ atomic_write() {
62
62
  return 1
63
63
  fi
64
64
 
65
+ # Create backup if target exists (do this BEFORE validation to avoid race condition)
66
+ if [ -f "$target_file" ]; then
67
+ create_backup "$target_file"
68
+ fi
69
+
65
70
  # Validate JSON if it's a JSON file
66
71
  if [[ "$target_file" == *.json ]]; then
67
72
  if ! python3 -c "import json; json.load(open('$temp_file'))" 2>/dev/null; then
@@ -71,11 +76,6 @@ atomic_write() {
71
76
  fi
72
77
  fi
73
78
 
74
- # Create backup if target exists
75
- if [ -f "$target_file" ]; then
76
- create_backup "$target_file"
77
- fi
78
-
79
79
  # Atomic rename (overwrites target if exists)
80
80
  if ! mv "$temp_file" "$target_file"; then
81
81
  echo "Failed to rename temp file to target: $target_file"
@@ -15,9 +15,12 @@ set -euo pipefail
15
15
  LOCK_ACQUIRED=${LOCK_ACQUIRED:-false}
16
16
  CURRENT_LOCK=${CURRENT_LOCK:-""}
17
17
 
18
- # Get script directory for sourcing
19
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
- AETHER_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd 2>/dev/null || echo "$SCRIPT_DIR/../..")"
18
+ # Get script directory for sourcing (preserve parent SCRIPT_DIR if set)
19
+ __chamber_utils_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ AETHER_ROOT="$(cd "$__chamber_utils_dir/../.." && pwd 2>/dev/null || echo "$__chamber_utils_dir/../..")"
21
+
22
+ # Use parent SCRIPT_DIR if available, otherwise use local
23
+ SCRIPT_DIR="${SCRIPT_DIR:-$__chamber_utils_dir}"
21
24
 
22
25
  # Source atomic-write for safe file operations
23
26
  [[ -f "$SCRIPT_DIR/atomic-write.sh" ]] && source "$SCRIPT_DIR/atomic-write.sh"
@@ -0,0 +1,200 @@
1
+ #!/bin/bash
2
+ # Aether Colony Error Handler Module
3
+ # Structured JSON error handling for bash utilities
4
+ #
5
+ # Usage: source "$SCRIPT_DIR/utils/error-handler.sh"
6
+ #
7
+ # Provides consistent error format between Node.js CLI and bash utilities
8
+
9
+ # --- Error Code Constants (matching Node.js error codes) ---
10
+ E_UNKNOWN="E_UNKNOWN"
11
+ E_HUB_NOT_FOUND="E_HUB_NOT_FOUND"
12
+ E_REPO_NOT_INITIALIZED="E_REPO_NOT_INITIALIZED"
13
+ E_FILE_NOT_FOUND="E_FILE_NOT_FOUND"
14
+ E_JSON_INVALID="E_JSON_INVALID"
15
+ E_LOCK_FAILED="E_LOCK_FAILED"
16
+ E_GIT_ERROR="E_GIT_ERROR"
17
+ E_VALIDATION_FAILED="E_VALIDATION_FAILED"
18
+ E_FEATURE_UNAVAILABLE="E_FEATURE_UNAVAILABLE"
19
+ E_BASH_ERROR="E_BASH_ERROR"
20
+
21
+ # --- Recovery Suggestion Functions (internal, prefixed with _) ---
22
+ _recovery_hub_not_found() { echo '"Run: aether install"'; }
23
+ _recovery_repo_not_init() { echo '"Run /ant:init in this repo first"'; }
24
+ _recovery_file_not_found() { echo '"Check file path and permissions"'; }
25
+ _recovery_json_invalid() { echo '"Validate JSON syntax"'; }
26
+ _recovery_lock_failed() { echo '"Wait for other operations to complete"'; }
27
+ _recovery_git_error() { echo '"Check git status and resolve conflicts"'; }
28
+ _recovery_default() { echo 'null'; }
29
+
30
+ # Get recovery suggestion based on error code
31
+ _get_recovery() {
32
+ local code="$1"
33
+ case "$code" in
34
+ "$E_HUB_NOT_FOUND") _recovery_hub_not_found ;;
35
+ "$E_REPO_NOT_INITIALIZED") _recovery_repo_not_init ;;
36
+ "$E_FILE_NOT_FOUND") _recovery_file_not_found ;;
37
+ "$E_JSON_INVALID") _recovery_json_invalid ;;
38
+ "$E_LOCK_FAILED") _recovery_lock_failed ;;
39
+ "$E_GIT_ERROR") _recovery_git_error ;;
40
+ *) _recovery_default ;;
41
+ esac
42
+ }
43
+
44
+ # --- Enhanced json_err function ---
45
+ # Signature: json_err [code] [message] [details] [recovery]
46
+ # All parameters optional with sensible defaults
47
+ json_err() {
48
+ local code="${1:-$E_UNKNOWN}"
49
+ local message="${2:-An unknown error occurred}"
50
+ local details="${3:-null}"
51
+ local recovery="${4:-}"
52
+ local timestamp
53
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
54
+
55
+ # Get recovery suggestion if not provided
56
+ if [[ -z "$recovery" ]]; then
57
+ recovery=$(_get_recovery "$code")
58
+ else
59
+ # Escape and quote the recovery string
60
+ recovery=$(echo "$recovery" | sed 's/"/\\"/g' | tr '\n' ' ')
61
+ recovery="\"$recovery\""
62
+ fi
63
+
64
+ # Escape message for JSON
65
+ local escaped_message
66
+ escaped_message=$(echo "$message" | sed 's/"/\\"/g' | tr '\n' ' ')
67
+
68
+ # Build details JSON
69
+ local details_json
70
+ if [[ "$details" == "null" || -z "$details" ]]; then
71
+ details_json="null"
72
+ else
73
+ details_json="$details"
74
+ fi
75
+
76
+ # Output structured JSON to stderr
77
+ printf '{"ok":false,"error":{"code":"%s","message":"%s","details":%s,"recovery":%s,"timestamp":"%s"}}\n' \
78
+ "$code" "$escaped_message" "$details_json" "$recovery" "$timestamp" >&2
79
+
80
+ # Log to activity.log (best effort)
81
+ if [[ -n "${DATA_DIR:-}" ]]; then
82
+ echo "[$timestamp] ERROR $code: $escaped_message" >> "$DATA_DIR/activity.log" 2>/dev/null || true
83
+ fi
84
+
85
+ exit 1
86
+ }
87
+
88
+ # --- json_warn function for non-fatal warnings ---
89
+ # Signature: json_warn [code] [message]
90
+ json_warn() {
91
+ local code="${1:-W_UNKNOWN}"
92
+ local message="${2:-Warning}"
93
+ local timestamp
94
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
95
+
96
+ # Escape message for JSON
97
+ local escaped_message
98
+ escaped_message=$(echo "$message" | sed 's/"/\\"/g' | tr '\n' ' ')
99
+
100
+ # Output warning JSON to stdout (not stderr - this is non-fatal)
101
+ printf '{"ok":true,"warning":{"code":"%s","message":"%s","timestamp":"%s"}}\n' \
102
+ "$code" "$escaped_message" "$timestamp"
103
+
104
+ # Log to activity.log (best effort)
105
+ if [[ -n "${DATA_DIR:-}" ]]; then
106
+ echo "[$timestamp] WARN $code: $escaped_message" >> "$DATA_DIR/activity.log" 2>/dev/null || true
107
+ fi
108
+ }
109
+
110
+ # --- error_handler function for trap ERR ---
111
+ # Captures: line number, command, exit code
112
+ # Usage: trap 'error_handler ${LINENO} "$BASH_COMMAND" $?' ERR
113
+ error_handler() {
114
+ local line_num="${1:-unknown}"
115
+ local command="${2:-unknown}"
116
+ local exit_code="${3:-1}"
117
+ local timestamp
118
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
119
+
120
+ # Escape command for JSON
121
+ local escaped_command
122
+ escaped_command=$(echo "$command" | sed 's/"/\\"/g' | tr '\n' ' ')
123
+
124
+ # Build details JSON
125
+ local details
126
+ details="{\"line\":$line_num,\"command\":\"$escaped_command\",\"exit_code\":$exit_code}"
127
+
128
+ # Output structured JSON to stderr
129
+ printf '{"ok":false,"error":{"code":"%s","message":"Bash command failed","details":%s,"recovery":%s,"timestamp":"%s"}}\n' \
130
+ "$E_BASH_ERROR" "$details" "$(_recovery_default)" "$timestamp" >&2
131
+
132
+ # Log to activity.log (best effort)
133
+ if [[ -n "${DATA_DIR:-}" ]]; then
134
+ echo "[$timestamp] ERROR $E_BASH_ERROR: Command failed at line $line_num (exit $exit_code)" >> "$DATA_DIR/activity.log" 2>/dev/null || true
135
+ fi
136
+
137
+ exit 1
138
+ }
139
+
140
+ # --- Feature flag functions for graceful degradation ---
141
+ # Using simple variables for bash 3.2+ compatibility (no associative arrays)
142
+
143
+ # Track disabled features as colon-separated list: "feature1:reason1|feature2:reason2"
144
+ _FEATURES_DISABLED=""
145
+
146
+ # Enable a feature (remove from disabled list if present)
147
+ feature_enable() {
148
+ local name="$1"
149
+ # Remove from disabled list if present
150
+ _FEATURES_DISABLED=$(echo "$_FEATURES_DISABLED" | sed "s/:$name:[^|]*//g" | sed 's/^|//;s/|$//')
151
+ }
152
+
153
+ # Disable a feature with reason
154
+ feature_disable() {
155
+ local name="$1"
156
+ local reason="${2:-disabled}"
157
+ # Remove existing entry if present, then add new
158
+ _FEATURES_DISABLED=$(echo "$_FEATURES_DISABLED" | sed "s/:$name:[^|]*//g")
159
+ if [[ -z "$_FEATURES_DISABLED" ]]; then
160
+ _FEATURES_DISABLED=":$name:$reason"
161
+ else
162
+ _FEATURES_DISABLED="${_FEATURES_DISABLED}|:$name:$reason"
163
+ fi
164
+ }
165
+
166
+ # Check if feature is enabled (returns 0 if enabled, 1 if disabled)
167
+ feature_enabled() {
168
+ local name="$1"
169
+ if echo "$_FEATURES_DISABLED" | grep -q ":$name:"; then
170
+ return 1
171
+ fi
172
+ return 0
173
+ }
174
+
175
+ # Get reason for feature being disabled
176
+ _feature_reason() {
177
+ local name="$1"
178
+ echo "$_FEATURES_DISABLED" | grep -o ":$name:[^|]*" | sed "s/:$name://" || echo "unknown"
179
+ }
180
+
181
+ # Log degradation warning
182
+ feature_log_degradation() {
183
+ local name="$1"
184
+ local reason="${2:-}"
185
+ if [[ -z "$reason" ]]; then
186
+ reason=$(_feature_reason "$name")
187
+ fi
188
+ json_warn "W_DEGRADED" "Feature '$name' is disabled: $reason"
189
+ }
190
+
191
+ # --- Export all functions and variables ---
192
+ export -f json_err json_warn error_handler
193
+ export -f feature_enable feature_disable feature_enabled feature_log_degradation
194
+ export -f _get_recovery _recovery_hub_not_found _recovery_repo_not_init
195
+ export -f _recovery_file_not_found _recovery_json_invalid _recovery_lock_failed
196
+ export -f _recovery_git_error _recovery_default _feature_reason
197
+ export E_UNKNOWN E_HUB_NOT_FOUND E_REPO_NOT_INITIALIZED E_FILE_NOT_FOUND
198
+ export E_JSON_INVALID E_LOCK_FAILED E_GIT_ERROR E_VALIDATION_FAILED
199
+ export E_FEATURE_UNAVAILABLE E_BASH_ERROR
200
+ export _FEATURES_DISABLED