aether-colony 1.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/CONTEXT.md +160 -0
- package/.aether/QUEEN.md +84 -0
- package/.aether/aether-utils.sh +7749 -0
- package/.aether/docs/QUEEN-SYSTEM.md +211 -0
- package/.aether/docs/README.md +68 -0
- package/.aether/docs/caste-system.md +48 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +93 -0
- package/.aether/docs/disciplines/coding-standards.md +197 -0
- package/.aether/docs/disciplines/debugging.md +207 -0
- package/.aether/docs/disciplines/learning.md +254 -0
- package/.aether/docs/disciplines/tdd.md +257 -0
- package/.aether/docs/disciplines/verification-loop.md +167 -0
- package/.aether/docs/disciplines/verification.md +116 -0
- package/.aether/docs/error-codes.md +268 -0
- package/.aether/docs/known-issues.md +233 -0
- package/.aether/docs/pheromones.md +205 -0
- package/.aether/docs/queen-commands.md +97 -0
- package/.aether/exchange/colony-registry.xml +11 -0
- package/.aether/exchange/pheromone-xml.sh +575 -0
- package/.aether/exchange/pheromones.xml +87 -0
- package/.aether/exchange/queen-wisdom.xml +14 -0
- package/.aether/exchange/registry-xml.sh +273 -0
- package/.aether/exchange/wisdom-xml.sh +319 -0
- package/.aether/midden/approach-changes.md +5 -0
- package/.aether/midden/build-failures.md +5 -0
- package/.aether/midden/test-failures.md +5 -0
- package/.aether/model-profiles.yaml +100 -0
- package/.aether/rules/aether-colony.md +134 -0
- package/.aether/schemas/aether-types.xsd +255 -0
- package/.aether/schemas/colony-registry.xsd +309 -0
- package/.aether/schemas/example-prompt-builder.xml +234 -0
- package/.aether/schemas/pheromone.xsd +163 -0
- package/.aether/schemas/prompt.xsd +416 -0
- package/.aether/schemas/queen-wisdom.xsd +325 -0
- package/.aether/schemas/worker-priming.xsd +276 -0
- package/.aether/templates/QUEEN.md.template +79 -0
- package/.aether/templates/colony-state-reset.jq.template +22 -0
- package/.aether/templates/colony-state.template.json +35 -0
- package/.aether/templates/constraints.template.json +9 -0
- package/.aether/templates/crowned-anthill.template.md +36 -0
- package/.aether/templates/handoff-build-error.template.md +30 -0
- package/.aether/templates/handoff-build-success.template.md +39 -0
- package/.aether/templates/handoff.template.md +40 -0
- package/.aether/templates/learning-observations.template.json +6 -0
- package/.aether/templates/midden.template.json +7 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/.aether/utils/atomic-write.sh +219 -0
- package/.aether/utils/chamber-compare.sh +193 -0
- package/.aether/utils/chamber-utils.sh +297 -0
- package/.aether/utils/colorize-log.sh +132 -0
- package/.aether/utils/error-handler.sh +212 -0
- package/.aether/utils/file-lock.sh +158 -0
- package/.aether/utils/queen-to-md.xsl +395 -0
- package/.aether/utils/semantic-cli.sh +413 -0
- package/.aether/utils/spawn-tree.sh +428 -0
- package/.aether/utils/spawn-with-model.sh +56 -0
- package/.aether/utils/state-loader.sh +215 -0
- package/.aether/utils/swarm-display.sh +268 -0
- package/.aether/utils/watch-spawn-tree.sh +253 -0
- package/.aether/utils/xml-compose.sh +253 -0
- package/.aether/utils/xml-convert.sh +273 -0
- package/.aether/utils/xml-core.sh +186 -0
- package/.aether/utils/xml-query.sh +201 -0
- package/.aether/utils/xml-utils.sh +110 -0
- package/.aether/workers.md +765 -0
- package/.claude/agents/ant/aether-ambassador.md +264 -0
- package/.claude/agents/ant/aether-archaeologist.md +322 -0
- package/.claude/agents/ant/aether-auditor.md +266 -0
- package/.claude/agents/ant/aether-builder.md +187 -0
- package/.claude/agents/ant/aether-chaos.md +268 -0
- package/.claude/agents/ant/aether-chronicler.md +304 -0
- package/.claude/agents/ant/aether-gatekeeper.md +325 -0
- package/.claude/agents/ant/aether-includer.md +373 -0
- package/.claude/agents/ant/aether-keeper.md +271 -0
- package/.claude/agents/ant/aether-measurer.md +317 -0
- package/.claude/agents/ant/aether-probe.md +210 -0
- package/.claude/agents/ant/aether-queen.md +325 -0
- package/.claude/agents/ant/aether-route-setter.md +173 -0
- package/.claude/agents/ant/aether-sage.md +353 -0
- package/.claude/agents/ant/aether-scout.md +142 -0
- package/.claude/agents/ant/aether-surveyor-disciplines.md +416 -0
- package/.claude/agents/ant/aether-surveyor-nest.md +354 -0
- package/.claude/agents/ant/aether-surveyor-pathogens.md +288 -0
- package/.claude/agents/ant/aether-surveyor-provisions.md +359 -0
- package/.claude/agents/ant/aether-tracker.md +265 -0
- package/.claude/agents/ant/aether-watcher.md +244 -0
- package/.claude/agents/ant/aether-weaver.md +247 -0
- package/.claude/commands/ant/archaeology.md +341 -0
- package/.claude/commands/ant/build.md +1160 -0
- package/.claude/commands/ant/chaos.md +349 -0
- package/.claude/commands/ant/colonize.md +270 -0
- package/.claude/commands/ant/continue.md +1070 -0
- package/.claude/commands/ant/council.md +309 -0
- package/.claude/commands/ant/dream.md +265 -0
- package/.claude/commands/ant/entomb.md +487 -0
- package/.claude/commands/ant/feedback.md +78 -0
- package/.claude/commands/ant/flag.md +139 -0
- package/.claude/commands/ant/flags.md +155 -0
- package/.claude/commands/ant/focus.md +58 -0
- package/.claude/commands/ant/help.md +122 -0
- package/.claude/commands/ant/history.md +137 -0
- package/.claude/commands/ant/init.md +409 -0
- package/.claude/commands/ant/interpret.md +267 -0
- package/.claude/commands/ant/lay-eggs.md +201 -0
- package/.claude/commands/ant/maturity.md +102 -0
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +165 -0
- package/.claude/commands/ant/oracle.md +387 -0
- package/.claude/commands/ant/organize.md +227 -0
- package/.claude/commands/ant/pause-colony.md +247 -0
- package/.claude/commands/ant/phase.md +126 -0
- package/.claude/commands/ant/plan.md +544 -0
- package/.claude/commands/ant/redirect.md +58 -0
- package/.claude/commands/ant/resume-colony.md +182 -0
- package/.claude/commands/ant/resume.md +363 -0
- package/.claude/commands/ant/seal.md +306 -0
- package/.claude/commands/ant/status.md +272 -0
- package/.claude/commands/ant/swarm.md +361 -0
- package/.claude/commands/ant/tunnels.md +425 -0
- package/.claude/commands/ant/update.md +209 -0
- package/.claude/commands/ant/verify-castes.md +95 -0
- package/.claude/commands/ant/watch.md +238 -0
- package/.opencode/agents/aether-ambassador.md +140 -0
- package/.opencode/agents/aether-archaeologist.md +108 -0
- package/.opencode/agents/aether-auditor.md +144 -0
- package/.opencode/agents/aether-builder.md +184 -0
- package/.opencode/agents/aether-chaos.md +115 -0
- package/.opencode/agents/aether-chronicler.md +122 -0
- package/.opencode/agents/aether-gatekeeper.md +116 -0
- package/.opencode/agents/aether-includer.md +117 -0
- package/.opencode/agents/aether-keeper.md +177 -0
- package/.opencode/agents/aether-measurer.md +128 -0
- package/.opencode/agents/aether-probe.md +133 -0
- package/.opencode/agents/aether-queen.md +286 -0
- package/.opencode/agents/aether-route-setter.md +130 -0
- package/.opencode/agents/aether-sage.md +106 -0
- package/.opencode/agents/aether-scout.md +101 -0
- package/.opencode/agents/aether-surveyor-disciplines.md +386 -0
- package/.opencode/agents/aether-surveyor-nest.md +324 -0
- package/.opencode/agents/aether-surveyor-pathogens.md +259 -0
- package/.opencode/agents/aether-surveyor-provisions.md +329 -0
- package/.opencode/agents/aether-tracker.md +137 -0
- package/.opencode/agents/aether-watcher.md +174 -0
- package/.opencode/agents/aether-weaver.md +130 -0
- package/.opencode/commands/ant/archaeology.md +338 -0
- package/.opencode/commands/ant/build.md +1200 -0
- package/.opencode/commands/ant/chaos.md +346 -0
- package/.opencode/commands/ant/colonize.md +202 -0
- package/.opencode/commands/ant/continue.md +938 -0
- package/.opencode/commands/ant/council.md +305 -0
- package/.opencode/commands/ant/dream.md +262 -0
- package/.opencode/commands/ant/entomb.md +367 -0
- package/.opencode/commands/ant/feedback.md +80 -0
- package/.opencode/commands/ant/flag.md +137 -0
- package/.opencode/commands/ant/flags.md +153 -0
- package/.opencode/commands/ant/focus.md +56 -0
- package/.opencode/commands/ant/help.md +124 -0
- package/.opencode/commands/ant/history.md +127 -0
- package/.opencode/commands/ant/init.md +337 -0
- package/.opencode/commands/ant/interpret.md +256 -0
- package/.opencode/commands/ant/lay-eggs.md +141 -0
- package/.opencode/commands/ant/maturity.md +92 -0
- package/.opencode/commands/ant/memory-details.md +77 -0
- package/.opencode/commands/ant/migrate-state.md +153 -0
- package/.opencode/commands/ant/oracle.md +338 -0
- package/.opencode/commands/ant/organize.md +224 -0
- package/.opencode/commands/ant/pause-colony.md +220 -0
- package/.opencode/commands/ant/phase.md +123 -0
- package/.opencode/commands/ant/plan.md +531 -0
- package/.opencode/commands/ant/redirect.md +67 -0
- package/.opencode/commands/ant/resume-colony.md +178 -0
- package/.opencode/commands/ant/resume.md +363 -0
- package/.opencode/commands/ant/seal.md +247 -0
- package/.opencode/commands/ant/status.md +272 -0
- package/.opencode/commands/ant/swarm.md +357 -0
- package/.opencode/commands/ant/tunnels.md +406 -0
- package/.opencode/commands/ant/update.md +191 -0
- package/.opencode/commands/ant/verify-castes.md +85 -0
- package/.opencode/commands/ant/watch.md +220 -0
- package/.opencode/opencode.json +3 -0
- package/CHANGELOG.md +325 -0
- package/DISCLAIMER.md +74 -0
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/bin/cli.js +2436 -0
- package/bin/generate-commands.sh +291 -0
- package/bin/lib/caste-colors.js +57 -0
- package/bin/lib/colors.js +76 -0
- package/bin/lib/errors.js +255 -0
- package/bin/lib/event-types.js +190 -0
- package/bin/lib/file-lock.js +695 -0
- package/bin/lib/init.js +454 -0
- package/bin/lib/logger.js +242 -0
- package/bin/lib/model-profiles.js +445 -0
- package/bin/lib/model-verify.js +288 -0
- package/bin/lib/nestmate-loader.js +130 -0
- package/bin/lib/proxy-health.js +253 -0
- package/bin/lib/spawn-logger.js +266 -0
- package/bin/lib/state-guard.js +602 -0
- package/bin/lib/state-sync.js +516 -0
- package/bin/lib/telemetry.js +441 -0
- package/bin/lib/update-transaction.js +1454 -0
- package/bin/npx-install.js +178 -0
- package/bin/sync-to-runtime.sh +6 -0
- package/bin/validate-package.sh +88 -0
- package/package.json +70 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Spawn Tree Reconstruction Module
|
|
3
|
+
# Parses spawn-tree.txt and provides tree traversal functions
|
|
4
|
+
#
|
|
5
|
+
# Usage: source .aether/utils/spawn-tree.sh
|
|
6
|
+
# All functions output JSON to stdout
|
|
7
|
+
|
|
8
|
+
# Data directory - can be overridden
|
|
9
|
+
SPAWN_TREE_DATA_DIR="${SPAWN_TREE_DATA_DIR:-.aether/data}"
|
|
10
|
+
SPAWN_TREE_FILE="${SPAWN_TREE_FILE:-$SPAWN_TREE_DATA_DIR/spawn-tree.txt}"
|
|
11
|
+
|
|
12
|
+
# Parse spawn-tree.txt into structured data
|
|
13
|
+
# Usage: parse_spawn_tree [file_path]
|
|
14
|
+
# Outputs: JSON representation of all spawns
|
|
15
|
+
parse_spawn_tree() {
|
|
16
|
+
local file_path="${1:-$SPAWN_TREE_FILE}"
|
|
17
|
+
|
|
18
|
+
# Check if file exists
|
|
19
|
+
if [[ ! -f "$file_path" ]]; then
|
|
20
|
+
echo '{"spawns":[],"metadata":{"total_count":0,"active_count":0,"completed_count":0,"file_exists":false}}'
|
|
21
|
+
return 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Temporary files for data storage (Bash 3.2 compatible)
|
|
25
|
+
local tmpdir
|
|
26
|
+
tmpdir=$(mktemp -d)
|
|
27
|
+
local names_file="$tmpdir/names"
|
|
28
|
+
local parents_file="$tmpdir/parents"
|
|
29
|
+
local castes_file="$tmpdir/castes"
|
|
30
|
+
local tasks_file="$tmpdir/tasks"
|
|
31
|
+
local statuses_file="$tmpdir/statuses"
|
|
32
|
+
local timestamps_file="$tmpdir/timestamps"
|
|
33
|
+
local completed_file="$tmpdir/completed"
|
|
34
|
+
local children_file="$tmpdir/children"
|
|
35
|
+
|
|
36
|
+
touch "$names_file" "$parents_file" "$castes_file" "$tasks_file" "$statuses_file" "$timestamps_file" "$completed_file" "$children_file"
|
|
37
|
+
|
|
38
|
+
# Read file line by line
|
|
39
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
40
|
+
# Skip empty lines
|
|
41
|
+
[[ -z "$line" ]] && continue
|
|
42
|
+
|
|
43
|
+
# Count pipe separators to determine line type
|
|
44
|
+
local pipe_count
|
|
45
|
+
pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
|
|
46
|
+
|
|
47
|
+
if [[ $pipe_count -eq 6 ]]; then
|
|
48
|
+
# Spawn event: timestamp|parent|caste|child_name|task|model|status
|
|
49
|
+
local timestamp parent caste child_name task model spawn_status
|
|
50
|
+
timestamp=$(echo "$line" | cut -d'|' -f1)
|
|
51
|
+
parent=$(echo "$line" | cut -d'|' -f2)
|
|
52
|
+
caste=$(echo "$line" | cut -d'|' -f3)
|
|
53
|
+
child_name=$(echo "$line" | cut -d'|' -f4)
|
|
54
|
+
task=$(echo "$line" | cut -d'|' -f5)
|
|
55
|
+
model=$(echo "$line" | cut -d'|' -f6)
|
|
56
|
+
spawn_status=$(echo "$line" | cut -d'|' -f7)
|
|
57
|
+
|
|
58
|
+
# Add to files
|
|
59
|
+
echo "$child_name" >> "$names_file"
|
|
60
|
+
echo "$parent" >> "$parents_file"
|
|
61
|
+
echo "$caste" >> "$castes_file"
|
|
62
|
+
echo "$task" >> "$tasks_file"
|
|
63
|
+
echo "$spawn_status" >> "$statuses_file"
|
|
64
|
+
echo "$timestamp" >> "$timestamps_file"
|
|
65
|
+
echo "" >> "$completed_file"
|
|
66
|
+
echo "" >> "$children_file"
|
|
67
|
+
|
|
68
|
+
elif [[ $pipe_count -eq 3 ]]; then
|
|
69
|
+
# Completion event: timestamp|ant_name|status|summary
|
|
70
|
+
local timestamp ant_name complete_status summary
|
|
71
|
+
timestamp=$(echo "$line" | cut -d'|' -f1)
|
|
72
|
+
ant_name=$(echo "$line" | cut -d'|' -f2)
|
|
73
|
+
complete_status=$(echo "$line" | cut -d'|' -f3)
|
|
74
|
+
|
|
75
|
+
# Find the ant and update its status
|
|
76
|
+
local idx=0
|
|
77
|
+
local found=0
|
|
78
|
+
while IFS= read -r name; do
|
|
79
|
+
if [[ "$name" == "$ant_name" ]]; then
|
|
80
|
+
found=1
|
|
81
|
+
break
|
|
82
|
+
fi
|
|
83
|
+
((idx++))
|
|
84
|
+
done < "$names_file"
|
|
85
|
+
|
|
86
|
+
if [[ $found -eq 1 ]]; then
|
|
87
|
+
# Update status at line idx+1 (sed is 1-indexed)
|
|
88
|
+
sed -i.bak "${idx}d" "$statuses_file" 2>/dev/null || sed -i "${idx}d" "$statuses_file"
|
|
89
|
+
sed -i.bak "${idx}i\\
|
|
90
|
+
$complete_status" "$statuses_file" 2>/dev/null || sed -i "${idx}i\\
|
|
91
|
+
$complete_status" "$statuses_file"
|
|
92
|
+
sed -i.bak "${idx}d" "$completed_file" 2>/dev/null || sed -i "${idx}d" "$completed_file"
|
|
93
|
+
sed -i.bak "${idx}i\\
|
|
94
|
+
$timestamp" "$completed_file" 2>/dev/null || sed -i "${idx}i\\
|
|
95
|
+
$timestamp" "$completed_file"
|
|
96
|
+
rm -f "$statuses_file.bak" "$completed_file.bak" 2>/dev/null
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
done < "$file_path"
|
|
100
|
+
|
|
101
|
+
# Build parent-child relationships
|
|
102
|
+
local total_count
|
|
103
|
+
total_count=$(wc -l < "$names_file" | tr -d ' ')
|
|
104
|
+
|
|
105
|
+
if [[ $total_count -gt 0 ]]; then
|
|
106
|
+
local i
|
|
107
|
+
for ((i = 1; i <= total_count; i++)); do
|
|
108
|
+
local parent
|
|
109
|
+
parent=$(sed -n "${i}p" "$parents_file")
|
|
110
|
+
|
|
111
|
+
# Find parent index
|
|
112
|
+
local parent_idx=0
|
|
113
|
+
local found=0
|
|
114
|
+
while IFS= read -r name; do
|
|
115
|
+
if [[ "$name" == "$parent" ]]; then
|
|
116
|
+
found=1
|
|
117
|
+
break
|
|
118
|
+
fi
|
|
119
|
+
((parent_idx++))
|
|
120
|
+
done < "$names_file"
|
|
121
|
+
|
|
122
|
+
if [[ $found -eq 1 ]]; then
|
|
123
|
+
# Add child i-1 to parent's children (0-indexed)
|
|
124
|
+
local child_idx=$((i - 1))
|
|
125
|
+
local current_children
|
|
126
|
+
current_children=$(sed -n "$((parent_idx + 1))p" "$children_file")
|
|
127
|
+
if [[ -z "$current_children" ]]; then
|
|
128
|
+
sed -i.bak "$((parent_idx + 1))d" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))d" "$children_file"
|
|
129
|
+
sed -i.bak "$((parent_idx + 1))i\\
|
|
130
|
+
$child_idx" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))i\\
|
|
131
|
+
$child_idx" "$children_file"
|
|
132
|
+
else
|
|
133
|
+
sed -i.bak "$((parent_idx + 1))d" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))d" "$children_file"
|
|
134
|
+
sed -i.bak "$((parent_idx + 1))i\\
|
|
135
|
+
$current_children $child_idx" "$children_file" 2>/dev/null || sed -i "$((parent_idx + 1))i\\
|
|
136
|
+
$current_children $child_idx" "$children_file"
|
|
137
|
+
fi
|
|
138
|
+
rm -f "$children_file.bak" 2>/dev/null
|
|
139
|
+
fi
|
|
140
|
+
done
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Count statuses
|
|
144
|
+
local active_count=0
|
|
145
|
+
local completed_count=0
|
|
146
|
+
while IFS= read -r status; do
|
|
147
|
+
if [[ "$status" == "active" || "$status" == "spawned" ]]; then
|
|
148
|
+
((active_count++))
|
|
149
|
+
elif [[ "$status" == "completed" || "$status" == "failed" || "$status" == "blocked" ]]; then
|
|
150
|
+
((completed_count++))
|
|
151
|
+
fi
|
|
152
|
+
done < "$statuses_file"
|
|
153
|
+
|
|
154
|
+
# Build spawns array
|
|
155
|
+
local spawns_json="["
|
|
156
|
+
local i
|
|
157
|
+
for ((i = 1; i <= total_count; i++)); do
|
|
158
|
+
if [[ $i -gt 1 ]]; then
|
|
159
|
+
spawns_json+=","
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
local name parent caste task status timestamp completed children
|
|
163
|
+
name=$(sed -n "${i}p" "$names_file")
|
|
164
|
+
parent=$(sed -n "${i}p" "$parents_file")
|
|
165
|
+
caste=$(sed -n "${i}p" "$castes_file")
|
|
166
|
+
task=$(sed -n "${i}p" "$tasks_file")
|
|
167
|
+
status=$(sed -n "${i}p" "$statuses_file")
|
|
168
|
+
timestamp=$(sed -n "${i}p" "$timestamps_file")
|
|
169
|
+
completed=$(sed -n "${i}p" "$completed_file")
|
|
170
|
+
children=$(sed -n "${i}p" "$children_file")
|
|
171
|
+
|
|
172
|
+
# Build children array
|
|
173
|
+
local children_json="["
|
|
174
|
+
if [[ -n "$children" ]]; then
|
|
175
|
+
local first_child=true
|
|
176
|
+
for child_idx in $children; do
|
|
177
|
+
if [[ "$first_child" == "true" ]]; then
|
|
178
|
+
first_child=false
|
|
179
|
+
else
|
|
180
|
+
children_json+=","
|
|
181
|
+
fi
|
|
182
|
+
local child_name
|
|
183
|
+
child_name=$(sed -n "$((child_idx + 1))p" "$names_file")
|
|
184
|
+
children_json+="\"$child_name\""
|
|
185
|
+
done
|
|
186
|
+
fi
|
|
187
|
+
children_json+="]"
|
|
188
|
+
|
|
189
|
+
# Escape task for JSON
|
|
190
|
+
task=$(echo "$task" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
|
|
191
|
+
|
|
192
|
+
spawns_json+="{"
|
|
193
|
+
spawns_json+="\"name\":\"$name\","
|
|
194
|
+
spawns_json+="\"parent\":\"$parent\","
|
|
195
|
+
spawns_json+="\"caste\":\"$caste\","
|
|
196
|
+
spawns_json+="\"task\":\"$task\","
|
|
197
|
+
spawns_json+="\"status\":\"$status\","
|
|
198
|
+
spawns_json+="\"spawned_at\":\"$timestamp\","
|
|
199
|
+
spawns_json+="\"completed_at\":\"$completed\","
|
|
200
|
+
spawns_json+="\"children\":$children_json"
|
|
201
|
+
spawns_json+="}"
|
|
202
|
+
done
|
|
203
|
+
spawns_json+="]"
|
|
204
|
+
|
|
205
|
+
# Output JSON
|
|
206
|
+
echo "{"
|
|
207
|
+
echo " \"spawns\": $spawns_json,"
|
|
208
|
+
echo " \"metadata\": {"
|
|
209
|
+
echo " \"total_count\": $total_count,"
|
|
210
|
+
echo " \"active_count\": $active_count,"
|
|
211
|
+
echo " \"completed_count\": $completed_count,"
|
|
212
|
+
echo " \"file_exists\": true"
|
|
213
|
+
echo " }"
|
|
214
|
+
echo "}"
|
|
215
|
+
|
|
216
|
+
# Cleanup
|
|
217
|
+
rm -rf "$tmpdir"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Get spawn depth for a given ant name
|
|
221
|
+
# Usage: get_spawn_depth <ant_name>
|
|
222
|
+
# Returns: JSON with ant name and depth
|
|
223
|
+
get_spawn_depth() {
|
|
224
|
+
local ant_name="${1:-}"
|
|
225
|
+
|
|
226
|
+
if [[ -z "$ant_name" || "$ant_name" == "Queen" ]]; then
|
|
227
|
+
echo "{\"ant\":\"${ant_name:-Queen}\",\"depth\":0}"
|
|
228
|
+
return 0
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
local file_path="${SPAWN_TREE_FILE}"
|
|
232
|
+
|
|
233
|
+
if [[ ! -f "$file_path" ]]; then
|
|
234
|
+
echo "{\"ant\":\"$ant_name\",\"depth\":1,\"found\":false}"
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# Check if ant exists
|
|
239
|
+
if ! grep -q "|$ant_name|" "$file_path" 2>/dev/null; then
|
|
240
|
+
echo "{\"ant\":\"$ant_name\",\"depth\":1,\"found\":false}"
|
|
241
|
+
return 0
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Calculate depth by traversing parent chain
|
|
245
|
+
local depth=1
|
|
246
|
+
local current="$ant_name"
|
|
247
|
+
local safety=0
|
|
248
|
+
|
|
249
|
+
while [[ $safety -lt 5 ]]; do
|
|
250
|
+
# Find who spawned this ant
|
|
251
|
+
local parent
|
|
252
|
+
parent=$(grep "|$current|" "$file_path" 2>/dev/null | grep "|spawned$" | head -1 | cut -d'|' -f2 || echo "")
|
|
253
|
+
|
|
254
|
+
if [[ -z "$parent" || "$parent" == "Queen" ]]; then
|
|
255
|
+
break
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
((depth++))
|
|
259
|
+
current="$parent"
|
|
260
|
+
((safety++))
|
|
261
|
+
done
|
|
262
|
+
|
|
263
|
+
echo "{\"ant\":\"$ant_name\",\"depth\":$depth,\"found\":true}"
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# Get list of active spawns
|
|
267
|
+
# Usage: get_active_spawns [file_path]
|
|
268
|
+
# Returns: JSON array of active spawns
|
|
269
|
+
get_active_spawns() {
|
|
270
|
+
local file_path="${1:-$SPAWN_TREE_FILE}"
|
|
271
|
+
|
|
272
|
+
if [[ ! -f "$file_path" ]]; then
|
|
273
|
+
echo "[]"
|
|
274
|
+
return 0
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
local active_json="["
|
|
278
|
+
local first=true
|
|
279
|
+
|
|
280
|
+
# Read spawn events and check if completed
|
|
281
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
282
|
+
[[ -z "$line" ]] && continue
|
|
283
|
+
|
|
284
|
+
local pipe_count
|
|
285
|
+
pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
|
|
286
|
+
|
|
287
|
+
if [[ $pipe_count -eq 6 ]]; then
|
|
288
|
+
# Spawn event: timestamp|parent|caste|child_name|task|model|status
|
|
289
|
+
local timestamp parent caste child_name task model spawn_status
|
|
290
|
+
timestamp=$(echo "$line" | cut -d'|' -f1)
|
|
291
|
+
parent=$(echo "$line" | cut -d'|' -f2)
|
|
292
|
+
caste=$(echo "$line" | cut -d'|' -f3)
|
|
293
|
+
child_name=$(echo "$line" | cut -d'|' -f4)
|
|
294
|
+
task=$(echo "$line" | cut -d'|' -f5)
|
|
295
|
+
model=$(echo "$line" | cut -d'|' -f6)
|
|
296
|
+
|
|
297
|
+
# Check if this ant has a completion event
|
|
298
|
+
local is_completed=false
|
|
299
|
+
if grep -q "^[^|]*|$child_name|completed\|^[^|]*|$child_name|failed\|^[^|]*|$child_name|blocked" "$file_path" 2>/dev/null; then
|
|
300
|
+
is_completed=true
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
if [[ "$is_completed" == "false" ]]; then
|
|
304
|
+
if [[ "$first" == "true" ]]; then
|
|
305
|
+
first=false
|
|
306
|
+
else
|
|
307
|
+
active_json+=","
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
# Escape task for JSON
|
|
311
|
+
task=$(echo "$task" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')
|
|
312
|
+
|
|
313
|
+
active_json+="{"
|
|
314
|
+
active_json+="\"name\":\"$child_name\","
|
|
315
|
+
active_json+="\"caste\":\"$caste\","
|
|
316
|
+
active_json+="\"parent\":\"$parent\","
|
|
317
|
+
active_json+="\"task\":\"$task\","
|
|
318
|
+
active_json+="\"spawned_at\":\"$timestamp\""
|
|
319
|
+
active_json+="}"
|
|
320
|
+
fi
|
|
321
|
+
fi
|
|
322
|
+
done < "$file_path"
|
|
323
|
+
|
|
324
|
+
active_json+="]"
|
|
325
|
+
echo "$active_json"
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Get direct children of a spawn
|
|
329
|
+
# Usage: get_spawn_children <ant_name> [file_path]
|
|
330
|
+
# Returns: JSON array of child names
|
|
331
|
+
get_spawn_children() {
|
|
332
|
+
local ant_name="${1:-}"
|
|
333
|
+
local file_path="${2:-$SPAWN_TREE_FILE}"
|
|
334
|
+
|
|
335
|
+
if [[ -z "$ant_name" || ! -f "$file_path" ]]; then
|
|
336
|
+
echo "[]"
|
|
337
|
+
return 0
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
local children_json="["
|
|
341
|
+
local first=true
|
|
342
|
+
|
|
343
|
+
# Find all spawns where parent matches
|
|
344
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
345
|
+
[[ -z "$line" ]] && continue
|
|
346
|
+
|
|
347
|
+
local pipe_count
|
|
348
|
+
pipe_count=$(echo "$line" | tr -cd '|' | wc -c | tr -d ' ')
|
|
349
|
+
|
|
350
|
+
if [[ $pipe_count -eq 6 ]]; then
|
|
351
|
+
local parent child_name
|
|
352
|
+
parent=$(echo "$line" | cut -d'|' -f2)
|
|
353
|
+
child_name=$(echo "$line" | cut -d'|' -f4)
|
|
354
|
+
|
|
355
|
+
if [[ "$parent" == "$ant_name" ]]; then
|
|
356
|
+
if [[ "$first" == "true" ]]; then
|
|
357
|
+
first=false
|
|
358
|
+
else
|
|
359
|
+
children_json+=","
|
|
360
|
+
fi
|
|
361
|
+
children_json+="\"$child_name\""
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
done < "$file_path"
|
|
365
|
+
|
|
366
|
+
children_json+="]"
|
|
367
|
+
echo "$children_json"
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# Get full lineage from ant up to Queen
|
|
371
|
+
# Usage: get_spawn_lineage <ant_name> [file_path]
|
|
372
|
+
# Returns: JSON array from ant up to Queen (inclusive)
|
|
373
|
+
get_spawn_lineage() {
|
|
374
|
+
local ant_name="${1:-}"
|
|
375
|
+
local file_path="${2:-$SPAWN_TREE_FILE}"
|
|
376
|
+
|
|
377
|
+
if [[ -z "$ant_name" ]]; then
|
|
378
|
+
echo "[]"
|
|
379
|
+
return 0
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
if [[ ! -f "$file_path" ]]; then
|
|
383
|
+
echo "[\"$ant_name\",\"Queen\"]"
|
|
384
|
+
return 0
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Build lineage array (ant first, then ancestors)
|
|
388
|
+
local lineage=""
|
|
389
|
+
local current="$ant_name"
|
|
390
|
+
local safety=0
|
|
391
|
+
|
|
392
|
+
lineage="\"$current\""
|
|
393
|
+
|
|
394
|
+
while [[ $safety -lt 5 ]]; do
|
|
395
|
+
# Find who spawned this ant
|
|
396
|
+
local parent
|
|
397
|
+
parent=$(grep "|$current|" "$file_path" 2>/dev/null | grep "|spawned$" | head -1 | cut -d'|' -f2 || echo "")
|
|
398
|
+
|
|
399
|
+
if [[ -z "$parent" || "$parent" == "Queen" ]]; then
|
|
400
|
+
lineage+=",\"Queen\""
|
|
401
|
+
break
|
|
402
|
+
fi
|
|
403
|
+
|
|
404
|
+
lineage+=",\"$parent\""
|
|
405
|
+
current="$parent"
|
|
406
|
+
((safety++))
|
|
407
|
+
done
|
|
408
|
+
|
|
409
|
+
echo "[$lineage]"
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# Reconstruct full tree as JSON
|
|
413
|
+
# Usage: reconstruct_tree_json [file_path]
|
|
414
|
+
# Returns: Complete spawn tree with metadata
|
|
415
|
+
reconstruct_tree_json() {
|
|
416
|
+
local file_path="${1:-$SPAWN_TREE_FILE}"
|
|
417
|
+
parse_spawn_tree "$file_path"
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# Export functions if being sourced (Bash 3.2 compatible)
|
|
421
|
+
if [[ "${BASH_SOURCE[0]:-}" != "${0}" ]]; then
|
|
422
|
+
export -f parse_spawn_tree 2>/dev/null || true
|
|
423
|
+
export -f get_spawn_depth 2>/dev/null || true
|
|
424
|
+
export -f get_active_spawns 2>/dev/null || true
|
|
425
|
+
export -f get_spawn_children 2>/dev/null || true
|
|
426
|
+
export -f get_spawn_lineage 2>/dev/null || true
|
|
427
|
+
export -f reconstruct_tree_json 2>/dev/null || true
|
|
428
|
+
fi
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Aether Colony Spawn Helper with Model Assignment
|
|
3
|
+
# Usage: spawn-with-model.sh <caste> <task_description> [project_root]
|
|
4
|
+
#
|
|
5
|
+
# This script sets up the correct environment variables for model routing
|
|
6
|
+
# through the LiteLLM proxy before spawning Claude Code.
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
AETHER_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
12
|
+
|
|
13
|
+
# Arguments
|
|
14
|
+
CASTE="${1:-}"
|
|
15
|
+
TASK="${2:-}"
|
|
16
|
+
PROJECT_ROOT="${3:-$AETHER_ROOT}"
|
|
17
|
+
|
|
18
|
+
# Validate arguments
|
|
19
|
+
[[ -z "$CASTE" ]] && { echo "Usage: spawn-with-model.sh <caste> <task_description> [project_root]" >&2; exit 1; }
|
|
20
|
+
[[ -z "$TASK" ]] && { echo "Usage: spawn-with-model.sh <caste> <task_description> [project_root]" >&2; exit 1; }
|
|
21
|
+
|
|
22
|
+
# Get model for this caste
|
|
23
|
+
model_info=$(bash "$AETHER_ROOT/.aether/aether-utils.sh" model-profile get "$CASTE" 2>/dev/null || echo '{"ok":true,"result":{"model":"kimi-k2.5"}}')
|
|
24
|
+
model=$(echo "$model_info" | jq -r '.result.model // "kimi-k2.5"')
|
|
25
|
+
|
|
26
|
+
# Log the spawn with model
|
|
27
|
+
ant_name=$(bash "$AETHER_ROOT/.aether/aether-utils.sh" generate-ant-name "$CASTE" 2>/dev/null || echo "${CASTE}-$(date +%s)")
|
|
28
|
+
bash "$AETHER_ROOT/.aether/aether-utils.sh" spawn-log "Queen" "$CASTE" "$ant_name" "$TASK" 2>/dev/null || true
|
|
29
|
+
bash "$AETHER_ROOT/.aether/aether-utils.sh" activity-log "SPAWN" "$ant_name ($CASTE)" "Model: $model - $TASK" 2>/dev/null || true
|
|
30
|
+
|
|
31
|
+
# Export environment for Claude Code
|
|
32
|
+
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://localhost:4000}"
|
|
33
|
+
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-sk-litellm-local}"
|
|
34
|
+
export ANTHROPIC_MODEL="$model"
|
|
35
|
+
|
|
36
|
+
echo "[$(date '+%H:%M:%S')] Spawning $ant_name ($CASTE) with model: $model"
|
|
37
|
+
echo " Task: $TASK"
|
|
38
|
+
echo " Project: $PROJECT_ROOT"
|
|
39
|
+
|
|
40
|
+
# Check proxy health
|
|
41
|
+
if curl -s http://localhost:4000/health | grep -q "healthy" 2>/dev/null; then
|
|
42
|
+
echo " Proxy: healthy"
|
|
43
|
+
else
|
|
44
|
+
echo " Proxy: unavailable (will use default routing)"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Start Claude Code with the environment set
|
|
48
|
+
# Note: This assumes claude is in PATH
|
|
49
|
+
if command -v claude &> /dev/null; then
|
|
50
|
+
claude --cwd "$PROJECT_ROOT"
|
|
51
|
+
else
|
|
52
|
+
echo "Claude Code not found in PATH. Environment prepared:"
|
|
53
|
+
echo " ANTHROPIC_BASE_URL=$ANTHROPIC_BASE_URL"
|
|
54
|
+
echo " ANTHROPIC_MODEL=$ANTHROPIC_MODEL"
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Aether Colony State Loader
|
|
3
|
+
# Loads and validates COLONY_STATE.json with file lock protection
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# source .aether/utils/state-loader.sh
|
|
7
|
+
# load_colony_state
|
|
8
|
+
# # ... use state ...
|
|
9
|
+
# unload_colony_state
|
|
10
|
+
#
|
|
11
|
+
# Provides: load_colony_state, unload_colony_state, get_handoff_summary, display_resumption_context
|
|
12
|
+
|
|
13
|
+
# Aether root detection - use git root if available, otherwise use current directory
|
|
14
|
+
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
15
|
+
AETHER_ROOT="$(git rev-parse --show-toplevel)"
|
|
16
|
+
else
|
|
17
|
+
AETHER_ROOT="$(pwd)"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="${SCRIPT_DIR:-$AETHER_ROOT/.aether}"
|
|
21
|
+
DATA_DIR="$AETHER_ROOT/.aether/data"
|
|
22
|
+
|
|
23
|
+
# Initialize lock state before sourcing (file-lock.sh trap needs these)
|
|
24
|
+
LOCK_ACQUIRED=${LOCK_ACQUIRED:-false}
|
|
25
|
+
CURRENT_LOCK=${CURRENT_LOCK:-""}
|
|
26
|
+
|
|
27
|
+
# Source shared infrastructure if available
|
|
28
|
+
[[ -f "$SCRIPT_DIR/utils/file-lock.sh" ]] && source "$SCRIPT_DIR/utils/file-lock.sh"
|
|
29
|
+
[[ -f "$SCRIPT_DIR/utils/error-handler.sh" ]] && source "$SCRIPT_DIR/utils/error-handler.sh"
|
|
30
|
+
|
|
31
|
+
# State loading globals
|
|
32
|
+
LOADED_STATE=""
|
|
33
|
+
STATE_LOCK_ACQUIRED=false
|
|
34
|
+
HANDOFF_DETECTED=false
|
|
35
|
+
HANDOFF_CONTENT=""
|
|
36
|
+
|
|
37
|
+
# --- load_colony_state ---
|
|
38
|
+
# Main loading function that acquires lock, validates, and loads state
|
|
39
|
+
# Returns: 0 on success, 1 on failure
|
|
40
|
+
# Exports: LOADED_STATE, STATE_LOCK_ACQUIRED, HANDOFF_DETECTED, HANDOFF_CONTENT
|
|
41
|
+
load_colony_state() {
|
|
42
|
+
local state_file="$DATA_DIR/COLONY_STATE.json"
|
|
43
|
+
|
|
44
|
+
# Check file exists
|
|
45
|
+
if [[ ! -f "$state_file" ]]; then
|
|
46
|
+
if type json_err &>/dev/null; then
|
|
47
|
+
json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found" '{"file":"COLONY_STATE.json"}' "Run /ant:init to initialize colony"
|
|
48
|
+
else
|
|
49
|
+
echo '{"ok":false,"error":{"code":"E_FILE_NOT_FOUND","message":"COLONY_STATE.json not found"}}' >&2
|
|
50
|
+
fi
|
|
51
|
+
return 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Acquire lock
|
|
55
|
+
if type acquire_lock &>/dev/null; then
|
|
56
|
+
if ! acquire_lock "$state_file"; then
|
|
57
|
+
if type json_err &>/dev/null; then
|
|
58
|
+
json_err "$E_LOCK_FAILED" "Failed to acquire state lock" '{"file":"COLONY_STATE.json"}' "Wait for other operations to complete"
|
|
59
|
+
else
|
|
60
|
+
echo '{"ok":false,"error":{"code":"E_LOCK_FAILED","message":"Failed to acquire state lock"}}' >&2
|
|
61
|
+
fi
|
|
62
|
+
return 1
|
|
63
|
+
fi
|
|
64
|
+
else
|
|
65
|
+
# Locking unavailable - proceed with warning
|
|
66
|
+
if type json_warn &>/dev/null; then
|
|
67
|
+
json_warn "W_DEGRADED" "File locking unavailable - proceeding without lock"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Validate state before loading
|
|
72
|
+
local validation
|
|
73
|
+
validation=$(bash "$SCRIPT_DIR/aether-utils.sh" validate-state colony 2>/dev/null)
|
|
74
|
+
if ! echo "$validation" | jq -e '.result.pass' >/dev/null 2>&1; then
|
|
75
|
+
# Validation failed - release lock and report error
|
|
76
|
+
if type release_lock &>/dev/null; then
|
|
77
|
+
release_lock
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
local validation_error
|
|
81
|
+
validation_error=$(echo "$validation" | jq -r '.result.checks[] | select(. != "pass") | .' 2>/dev/null | head -1 || echo "unknown validation error")
|
|
82
|
+
|
|
83
|
+
if type json_err &>/dev/null; then
|
|
84
|
+
json_err "$E_VALIDATION_FAILED" "State validation failed" "{\"details\":\"$validation_error\"}" "Check COLONY_STATE.json for errors or run /ant:init to reset"
|
|
85
|
+
else
|
|
86
|
+
echo "{\"ok\":false,\"error\":{\"code\":\"E_VALIDATION_FAILED\",\"message\":\"State validation failed: $validation_error\"}}" >&2
|
|
87
|
+
fi
|
|
88
|
+
return 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Read state into variable
|
|
92
|
+
local state
|
|
93
|
+
state=$(cat "$state_file")
|
|
94
|
+
if [[ -z "$state" ]]; then
|
|
95
|
+
if type release_lock &>/dev/null; then
|
|
96
|
+
release_lock
|
|
97
|
+
fi
|
|
98
|
+
if type json_err &>/dev/null; then
|
|
99
|
+
json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json is empty" '{"file":"COLONY_STATE.json"}' "Run /ant:init to initialize colony"
|
|
100
|
+
else
|
|
101
|
+
echo '{"ok":false,"error":{"code":"E_FILE_NOT_FOUND","message":"COLONY_STATE.json is empty"}}' >&2
|
|
102
|
+
fi
|
|
103
|
+
return 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Check for handoff document
|
|
107
|
+
local handoff_file="$AETHER_ROOT/.aether/HANDOFF.md"
|
|
108
|
+
if [[ -f "$handoff_file" ]]; then
|
|
109
|
+
HANDOFF_DETECTED=true
|
|
110
|
+
HANDOFF_CONTENT=$(cat "$handoff_file")
|
|
111
|
+
else
|
|
112
|
+
HANDOFF_DETECTED=false
|
|
113
|
+
HANDOFF_CONTENT=""
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Export loaded state
|
|
117
|
+
LOADED_STATE="$state"
|
|
118
|
+
STATE_LOCK_ACQUIRED=true
|
|
119
|
+
|
|
120
|
+
return 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# --- unload_colony_state ---
|
|
124
|
+
# Cleanup function that releases lock and unsets state
|
|
125
|
+
# Should be called when done with state
|
|
126
|
+
unload_colony_state() {
|
|
127
|
+
if [[ "$STATE_LOCK_ACQUIRED" == "true" ]]; then
|
|
128
|
+
if type release_lock &>/dev/null; then
|
|
129
|
+
release_lock
|
|
130
|
+
fi
|
|
131
|
+
STATE_LOCK_ACQUIRED=false
|
|
132
|
+
fi
|
|
133
|
+
LOADED_STATE=""
|
|
134
|
+
HANDOFF_DETECTED=false
|
|
135
|
+
HANDOFF_CONTENT=""
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# --- get_handoff_summary ---
|
|
139
|
+
# Extract brief summary from handoff content
|
|
140
|
+
# Returns: "Phase X - Name" format or empty string
|
|
141
|
+
get_handoff_summary() {
|
|
142
|
+
if [[ "$HANDOFF_DETECTED" != "true" || -z "$HANDOFF_CONTENT" ]]; then
|
|
143
|
+
echo ""
|
|
144
|
+
return 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Parse HANDOFF.md for Phase line
|
|
148
|
+
# Format: "Phase: X - Name" or "## Phase X: Name"
|
|
149
|
+
local phase_line
|
|
150
|
+
phase_line=$(echo "$HANDOFF_CONTENT" | grep -E "^(Phase:|## Phase)" | head -1)
|
|
151
|
+
|
|
152
|
+
if [[ -n "$phase_line" ]]; then
|
|
153
|
+
# Extract phase number and name
|
|
154
|
+
local phase_num
|
|
155
|
+
local phase_name
|
|
156
|
+
phase_num=$(echo "$phase_line" | grep -oE "[0-9]+" | head -1)
|
|
157
|
+
phase_name=$(echo "$phase_line" | sed -E 's/.*Phase:?[[:space:]]*[0-9]+[[:space:]-]+//' | sed 's/^[[:space:]]*//')
|
|
158
|
+
|
|
159
|
+
if [[ -n "$phase_num" && -n "$phase_name" ]]; then
|
|
160
|
+
echo "Phase $phase_num - $phase_name"
|
|
161
|
+
elif [[ -n "$phase_num" ]]; then
|
|
162
|
+
echo "Phase $phase_num"
|
|
163
|
+
else
|
|
164
|
+
echo "Resuming colony session"
|
|
165
|
+
fi
|
|
166
|
+
else
|
|
167
|
+
echo "Resuming colony session"
|
|
168
|
+
fi
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# --- display_resumption_context ---
|
|
172
|
+
# Show brief resume message and clean up handoff
|
|
173
|
+
# Returns: 0 on success
|
|
174
|
+
display_resumption_context() {
|
|
175
|
+
if [[ "$HANDOFF_DETECTED" != "true" ]]; then
|
|
176
|
+
return 0
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
local summary
|
|
180
|
+
summary=$(get_handoff_summary)
|
|
181
|
+
|
|
182
|
+
if [[ -n "$summary" ]]; then
|
|
183
|
+
echo "Resuming: $summary"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Remove handoff file after successful display
|
|
187
|
+
local handoff_file="$AETHER_ROOT/.aether/HANDOFF.md"
|
|
188
|
+
if [[ -f "$handoff_file" ]]; then
|
|
189
|
+
rm -f "$handoff_file"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
HANDOFF_DETECTED=false
|
|
193
|
+
HANDOFF_CONTENT=""
|
|
194
|
+
|
|
195
|
+
return 0
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# --- ensure_cleanup ---
|
|
199
|
+
# Internal function to ensure lock is released on script exit
|
|
200
|
+
_ensure_cleanup() {
|
|
201
|
+
if [[ "$STATE_LOCK_ACQUIRED" == "true" ]]; then
|
|
202
|
+
unload_colony_state
|
|
203
|
+
fi
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Register cleanup on exit
|
|
207
|
+
# Note: This works alongside file-lock.sh's trap
|
|
208
|
+
_original_trap_exit() {
|
|
209
|
+
_ensure_cleanup
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Export functions for use in other scripts
|
|
213
|
+
export -f load_colony_state unload_colony_state get_handoff_summary display_resumption_context
|
|
214
|
+
export -f _ensure_cleanup _original_trap_exit
|
|
215
|
+
export LOADED_STATE STATE_LOCK_ACQUIRED HANDOFF_DETECTED HANDOFF_CONTENT
|