aether-colony 3.1.5 → 3.1.16
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/.claude/commands/ant/archaeology.md +12 -0
- package/.claude/commands/ant/build.md +382 -319
- package/.claude/commands/ant/chaos.md +23 -1
- package/.claude/commands/ant/colonize.md +147 -87
- package/.claude/commands/ant/continue.md +213 -23
- package/.claude/commands/ant/council.md +22 -0
- package/.claude/commands/ant/dream.md +18 -0
- package/.claude/commands/ant/entomb.md +178 -6
- package/.claude/commands/ant/init.md +87 -13
- package/.claude/commands/ant/lay-eggs.md +45 -5
- package/.claude/commands/ant/oracle.md +82 -9
- package/.claude/commands/ant/organize.md +2 -2
- package/.claude/commands/ant/pause-colony.md +86 -28
- package/.claude/commands/ant/phase.md +26 -0
- package/.claude/commands/ant/plan.md +204 -111
- package/.claude/commands/ant/resume-colony.md +23 -1
- package/.claude/commands/ant/resume.md +159 -0
- package/.claude/commands/ant/seal.md +177 -3
- package/.claude/commands/ant/swarm.md +78 -97
- package/.claude/commands/ant/verify-castes.md +7 -7
- package/.claude/commands/ant/watch.md +17 -0
- package/.opencode/agents/aether-ambassador.md +97 -0
- package/.opencode/agents/aether-archaeologist.md +91 -0
- package/.opencode/agents/aether-architect.md +66 -0
- package/.opencode/agents/aether-auditor.md +111 -0
- package/.opencode/agents/aether-builder.md +28 -10
- package/.opencode/agents/aether-chaos.md +98 -0
- package/.opencode/agents/aether-chronicler.md +80 -0
- package/.opencode/agents/aether-gatekeeper.md +107 -0
- package/.opencode/agents/aether-guardian.md +107 -0
- package/.opencode/agents/aether-includer.md +108 -0
- package/.opencode/agents/aether-keeper.md +106 -0
- package/.opencode/agents/aether-measurer.md +119 -0
- package/.opencode/agents/aether-probe.md +91 -0
- package/.opencode/agents/aether-queen.md +72 -19
- package/.opencode/agents/aether-route-setter.md +85 -0
- package/.opencode/agents/aether-sage.md +98 -0
- package/.opencode/agents/aether-scout.md +33 -15
- package/.opencode/agents/aether-surveyor-disciplines.md +334 -0
- package/.opencode/agents/aether-surveyor-nest.md +272 -0
- package/.opencode/agents/aether-surveyor-pathogens.md +209 -0
- package/.opencode/agents/aether-surveyor-provisions.md +277 -0
- package/.opencode/agents/aether-tracker.md +91 -0
- package/.opencode/agents/aether-watcher.md +30 -12
- package/.opencode/agents/aether-weaver.md +87 -0
- package/.opencode/agents/workers.md +1034 -0
- package/.opencode/commands/ant/archaeology.md +44 -26
- package/.opencode/commands/ant/build.md +326 -294
- package/.opencode/commands/ant/chaos.md +32 -4
- package/.opencode/commands/ant/colonize.md +119 -93
- package/.opencode/commands/ant/continue.md +98 -10
- package/.opencode/commands/ant/council.md +28 -0
- package/.opencode/commands/ant/dream.md +24 -0
- package/.opencode/commands/ant/entomb.md +73 -1
- package/.opencode/commands/ant/feedback.md +8 -2
- package/.opencode/commands/ant/flag.md +9 -3
- package/.opencode/commands/ant/flags.md +8 -2
- package/.opencode/commands/ant/focus.md +8 -2
- package/.opencode/commands/ant/help.md +12 -0
- package/.opencode/commands/ant/init.md +49 -4
- package/.opencode/commands/ant/lay-eggs.md +30 -2
- package/.opencode/commands/ant/oracle.md +39 -7
- package/.opencode/commands/ant/organize.md +8 -2
- package/.opencode/commands/ant/pause-colony.md +54 -1
- package/.opencode/commands/ant/phase.md +36 -4
- package/.opencode/commands/ant/plan.md +224 -116
- package/.opencode/commands/ant/redirect.md +8 -2
- package/.opencode/commands/ant/resume-colony.md +51 -26
- package/.opencode/commands/ant/seal.md +76 -0
- package/.opencode/commands/ant/status.md +50 -20
- package/.opencode/commands/ant/swarm.md +108 -104
- package/.opencode/commands/ant/tunnels.md +107 -2
- package/CHANGELOG.md +16 -0
- package/README.md +199 -86
- package/bin/cli.js +142 -25
- package/bin/generate-commands.sh +100 -16
- package/bin/lib/caste-colors.js +5 -5
- package/bin/lib/errors.js +16 -0
- package/bin/lib/file-lock.js +279 -44
- package/bin/lib/state-sync.js +206 -23
- package/bin/lib/update-transaction.js +206 -24
- package/bin/sync-to-runtime.sh +138 -0
- package/package.json +2 -2
- package/runtime/CONTEXT.md +160 -0
- package/runtime/aether-utils.sh +1421 -55
- package/runtime/docs/AETHER-2.0-IMPLEMENTATION-PLAN.md +1343 -0
- package/runtime/docs/AETHER-PHEROMONE-SYSTEM-MASTER-SPEC.md +2642 -0
- package/runtime/docs/PHEROMONE-INJECTION.md +240 -0
- package/runtime/docs/PHEROMONE-INTEGRATION.md +192 -0
- package/runtime/docs/PHEROMONE-SYSTEM-DESIGN.md +426 -0
- package/runtime/docs/README.md +94 -0
- package/runtime/docs/VISUAL-OUTPUT-SPEC.md +219 -0
- package/runtime/docs/biological-reference.md +272 -0
- package/runtime/docs/codebase-review.md +399 -0
- package/runtime/docs/command-sync.md +164 -0
- package/runtime/docs/implementation-learnings.md +89 -0
- package/runtime/docs/known-issues.md +217 -0
- package/runtime/docs/namespace.md +148 -0
- package/runtime/docs/planning-discipline.md +159 -0
- package/runtime/exchange/pheromone-xml.sh +574 -0
- package/runtime/exchange/registry-xml.sh +269 -0
- package/runtime/exchange/wisdom-xml.sh +312 -0
- package/runtime/lib/queen-utils.sh +729 -0
- package/runtime/model-profiles.yaml +100 -0
- package/runtime/recover.sh +136 -0
- package/runtime/schemas/aether-types.xsd +255 -0
- package/runtime/schemas/colony-registry.xsd +309 -0
- package/runtime/schemas/pheromone.xsd +163 -0
- package/runtime/schemas/prompt.xsd +416 -0
- package/runtime/schemas/queen-wisdom.xsd +325 -0
- package/runtime/schemas/worker-priming.xsd +276 -0
- package/runtime/templates/QUEEN.md.template +79 -0
- package/runtime/utils/atomic-write.sh +5 -5
- package/runtime/utils/chamber-utils.sh +6 -3
- package/runtime/utils/error-handler.sh +200 -0
- package/runtime/utils/queen-to-md.xsl +395 -0
- package/runtime/utils/spawn-tree.sh +428 -0
- package/runtime/utils/spawn-with-model.sh +56 -0
- package/runtime/utils/state-loader.sh +215 -0
- package/runtime/utils/swarm-display.sh +5 -5
- package/runtime/utils/watch-spawn-tree.sh +90 -22
- package/runtime/utils/xml-compose.sh +247 -0
- package/runtime/utils/xml-core.sh +186 -0
- package/runtime/utils/xml-utils.sh +2196 -0
- package/runtime/verification-loop.md +1 -1
- package/runtime/workers-new-castes.md +516 -0
- package/runtime/workers.md +18 -6
- package/.aether/visualizations/anthill-stages/brood-stable.txt +0 -26
- package/.aether/visualizations/anthill-stages/crowned-anthill.txt +0 -30
- package/.aether/visualizations/anthill-stages/first-mound.txt +0 -18
- package/.aether/visualizations/anthill-stages/open-chambers.txt +0 -24
- package/.aether/visualizations/anthill-stages/sealed-chambers.txt +0 -28
- package/.aether/visualizations/anthill-stages/ventilated-nest.txt +0 -27
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Queen Utilities — QUEEN.md management for Aether colonies
|
|
3
|
+
#
|
|
4
|
+
# Usage: source this file or run functions directly
|
|
5
|
+
# All functions output JSON for composability
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Configuration
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
AETHER_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd 2>/dev/null || echo "$SCRIPT_DIR")"
|
|
12
|
+
RUNTIME_DIR="$AETHER_ROOT/runtime"
|
|
13
|
+
GLOBAL_QUEEN="${HOME}/.aether/QUEEN.md"
|
|
14
|
+
|
|
15
|
+
# Ensure directories exist
|
|
16
|
+
mkdir -p "${HOME}/.aether" 2>/dev/null || true
|
|
17
|
+
|
|
18
|
+
# --- JSON helpers ---
|
|
19
|
+
json_ok() { printf '{"ok":true,"result":%s}\n' "$1"; }
|
|
20
|
+
json_err() { printf '{"ok":false,"error":"%s"}\n' "$1" >&2; return 1; }
|
|
21
|
+
json_warn() { printf '{"ok":true,"warning":"%s"}\n' "$1"; }
|
|
22
|
+
|
|
23
|
+
# --- Template path ---
|
|
24
|
+
get_template_path() {
|
|
25
|
+
echo "$RUNTIME_DIR/templates/QUEEN.md.template"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# --- queen_init_global ---
|
|
29
|
+
# Initialize global QUEEN.md at ~/.aether/QUEEN.md
|
|
30
|
+
# Usage: queen_init_global
|
|
31
|
+
# Returns: JSON with path and status
|
|
32
|
+
queen_init_global() {
|
|
33
|
+
local template_path
|
|
34
|
+
template_path=$(get_template_path)
|
|
35
|
+
|
|
36
|
+
if [[ ! -f "$template_path" ]]; then
|
|
37
|
+
json_err "Template not found: $template_path"
|
|
38
|
+
return 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# If already exists, don't overwrite
|
|
42
|
+
if [[ -f "$GLOBAL_QUEEN" ]]; then
|
|
43
|
+
local version
|
|
44
|
+
version=$(queen_get_version "$GLOBAL_QUEEN" 2>/dev/null || echo "unknown")
|
|
45
|
+
json_ok "{\"initialized\":false,\"path\":\"$GLOBAL_QUEEN\",\"version\":\"$version\",\"reason\":\"already_exists\"}"
|
|
46
|
+
return 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Create from template with current timestamp
|
|
50
|
+
local timestamp
|
|
51
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
52
|
+
|
|
53
|
+
# Replace {TIMESTAMP} placeholders
|
|
54
|
+
sed -e "s/{TIMESTAMP}/$timestamp/g" "$template_path" > "$GLOBAL_QUEEN"
|
|
55
|
+
|
|
56
|
+
json_ok "{\"initialized\":true,\"path\":\"$GLOBAL_QUEEN\",\"version\":\"1.0.0\"}"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# --- queen_init_local ---
|
|
60
|
+
# Initialize local QUEEN.md at repo/.aether/QUEEN.md
|
|
61
|
+
# Usage: queen_init_local [repo_path]
|
|
62
|
+
# Returns: JSON with path and status
|
|
63
|
+
queen_init_local() {
|
|
64
|
+
local repo_path="${1:-$(pwd)}"
|
|
65
|
+
local local_queen="$repo_path/.aether/QUEEN.md"
|
|
66
|
+
local template_path
|
|
67
|
+
template_path=$(get_template_path)
|
|
68
|
+
|
|
69
|
+
if [[ ! -f "$template_path" ]]; then
|
|
70
|
+
json_err "Template not found: $template_path"
|
|
71
|
+
return 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Create .aether directory if needed
|
|
75
|
+
mkdir -p "$repo_path/.aether" 2>/dev/null || true
|
|
76
|
+
|
|
77
|
+
# If already exists, don't overwrite
|
|
78
|
+
if [[ -f "$local_queen" ]]; then
|
|
79
|
+
local version
|
|
80
|
+
version=$(queen_get_version "$local_queen" 2>/dev/null || echo "unknown")
|
|
81
|
+
json_ok "{\"initialized\":false,\"path\":\"$local_queen\",\"version\":\"$version\",\"reason\":\"already_exists\"}"
|
|
82
|
+
return 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Create from template with current timestamp
|
|
86
|
+
local timestamp
|
|
87
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
88
|
+
|
|
89
|
+
# Replace {TIMESTAMP} placeholders
|
|
90
|
+
sed -e "s/{TIMESTAMP}/$timestamp/g" "$template_path" > "$local_queen"
|
|
91
|
+
|
|
92
|
+
json_ok "{\"initialized\":true,\"path\":\"$local_queen\",\"version\":\"1.0.0\"}"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# --- queen_parse_metadata ---
|
|
96
|
+
# Extract JSON metadata block from QUEEN.md
|
|
97
|
+
# Usage: queen_parse_metadata [queen_md_path]
|
|
98
|
+
# Returns: JSON metadata object
|
|
99
|
+
queen_parse_metadata() {
|
|
100
|
+
local queen_path="${1:-$GLOBAL_QUEEN}"
|
|
101
|
+
|
|
102
|
+
if [[ ! -f "$queen_path" ]]; then
|
|
103
|
+
json_err "QUEEN.md not found: $queen_path"
|
|
104
|
+
return 1
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Extract content between <!-- METADATA and -->
|
|
108
|
+
local metadata
|
|
109
|
+
metadata=$(sed -n '/<!-- METADATA/,/-->/p' "$queen_path" | grep -v '<!--' | grep -v '-->')
|
|
110
|
+
|
|
111
|
+
if [[ -z "$metadata" ]]; then
|
|
112
|
+
json_err "No metadata block found in $queen_path"
|
|
113
|
+
return 1
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Validate JSON
|
|
117
|
+
if ! echo "$metadata" | jq '.' >/dev/null 2>&1; then
|
|
118
|
+
json_err "Invalid JSON in metadata block"
|
|
119
|
+
return 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
echo "$metadata"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# --- queen_get_version ---
|
|
126
|
+
# Get the semantic version from QUEEN.md metadata
|
|
127
|
+
# Usage: queen_get_version [queen_md_path]
|
|
128
|
+
# Returns: version string
|
|
129
|
+
queen_get_version() {
|
|
130
|
+
local queen_path="${1:-$GLOBAL_QUEEN}"
|
|
131
|
+
|
|
132
|
+
if [[ ! -f "$queen_path" ]]; then
|
|
133
|
+
json_err "QUEEN.md not found: $queen_path"
|
|
134
|
+
return 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
local metadata
|
|
138
|
+
metadata=$(queen_parse_metadata "$queen_path" 2>/dev/null) || {
|
|
139
|
+
echo "0.0.0"
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
echo "$metadata" | jq -r '.version // "0.0.0"'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# --- queen_increment_version ---
|
|
147
|
+
# Increment the patch version in QUEEN.md
|
|
148
|
+
# Usage: queen_increment_version [queen_md_path] [component: patch|minor|major]
|
|
149
|
+
# Returns: JSON with old and new version
|
|
150
|
+
queen_increment_version() {
|
|
151
|
+
local queen_path="${1:-$GLOBAL_QUEEN}"
|
|
152
|
+
local component="${2:-patch}"
|
|
153
|
+
|
|
154
|
+
if [[ ! -f "$queen_path" ]]; then
|
|
155
|
+
json_err "QUEEN.md not found: $queen_path"
|
|
156
|
+
return 1
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
local old_version
|
|
160
|
+
old_version=$(queen_get_version "$queen_path")
|
|
161
|
+
|
|
162
|
+
# Parse version components
|
|
163
|
+
local major minor patch
|
|
164
|
+
major=$(echo "$old_version" | cut -d. -f1)
|
|
165
|
+
minor=$(echo "$old_version" | cut -d. -f2)
|
|
166
|
+
patch=$(echo "$old_version" | cut -d. -f3)
|
|
167
|
+
|
|
168
|
+
# Increment appropriate component
|
|
169
|
+
case "$component" in
|
|
170
|
+
major)
|
|
171
|
+
major=$((major + 1))
|
|
172
|
+
minor=0
|
|
173
|
+
patch=0
|
|
174
|
+
;;
|
|
175
|
+
minor)
|
|
176
|
+
minor=$((minor + 1))
|
|
177
|
+
patch=0
|
|
178
|
+
;;
|
|
179
|
+
patch|*)
|
|
180
|
+
patch=$((patch + 1))
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
183
|
+
|
|
184
|
+
local new_version="${major}.${minor}.${patch}"
|
|
185
|
+
|
|
186
|
+
# Update version in metadata block
|
|
187
|
+
local temp_file
|
|
188
|
+
temp_file=$(mktemp)
|
|
189
|
+
|
|
190
|
+
# Use jq to update the JSON inside the markdown
|
|
191
|
+
local metadata new_metadata
|
|
192
|
+
metadata=$(queen_parse_metadata "$queen_path")
|
|
193
|
+
new_metadata=$(echo "$metadata" | jq --arg v "$new_version" --arg ts "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" '
|
|
194
|
+
.version = $v | .last_evolved = $ts
|
|
195
|
+
')
|
|
196
|
+
|
|
197
|
+
# Replace metadata block in file
|
|
198
|
+
awk -v new_metadata="$new_metadata" '
|
|
199
|
+
/<!-- METADATA/ { print; print new_metadata; in_metadata=1; next }
|
|
200
|
+
/-->/ && in_metadata { print; in_metadata=0; next }
|
|
201
|
+
in_metadata { next }
|
|
202
|
+
{ print }
|
|
203
|
+
' "$queen_path" > "$temp_file"
|
|
204
|
+
|
|
205
|
+
mv "$temp_file" "$queen_path"
|
|
206
|
+
|
|
207
|
+
# Update header line too
|
|
208
|
+
sed -i.bak "s/^> Wisdom version: .*/> Wisdom version: $new_version/" "$queen_path" && rm -f "$queen_path.bak"
|
|
209
|
+
|
|
210
|
+
json_ok "{\"old_version\":\"$old_version\",\"new_version\":\"$new_version\",\"component\":\"$component\"}"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# --- queen_merge_global_local ---
|
|
214
|
+
# Merge global and local QUEEN.md content
|
|
215
|
+
# Usage: queen_merge_global_local [repo_path]
|
|
216
|
+
# Returns: JSON with merged content summary
|
|
217
|
+
queen_merge_global_local() {
|
|
218
|
+
local repo_path="${1:-$(pwd)}"
|
|
219
|
+
local local_queen="$repo_path/.aether/QUEEN.md"
|
|
220
|
+
|
|
221
|
+
# Ensure global exists
|
|
222
|
+
if [[ ! -f "$GLOBAL_QUEEN" ]]; then
|
|
223
|
+
queen_init_global >/dev/null 2>&1 || true
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# If no local, just use global
|
|
227
|
+
if [[ ! -f "$local_queen" ]]; then
|
|
228
|
+
json_ok "{\"source\":\"global_only\",\"global_path\":\"$GLOBAL_QUEEN\",\"has_local\":false}"
|
|
229
|
+
return 0
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Both exist - count entries
|
|
233
|
+
local global_stats local_stats
|
|
234
|
+
global_stats=$(queen_parse_metadata "$GLOBAL_QUEEN" | jq '{
|
|
235
|
+
philosophies: .stats.total_philosophies,
|
|
236
|
+
patterns: .stats.total_patterns,
|
|
237
|
+
redirects: .stats.total_redirects
|
|
238
|
+
}' 2>/dev/null || echo '{}')
|
|
239
|
+
|
|
240
|
+
local_stats=$(queen_parse_metadata "$local_queen" | jq '{
|
|
241
|
+
philosophies: .stats.total_philosophies,
|
|
242
|
+
patterns: .stats.total_patterns,
|
|
243
|
+
redirects: .stats.total_redirects
|
|
244
|
+
}' 2>/dev/null || echo '{}')
|
|
245
|
+
|
|
246
|
+
json_ok "{
|
|
247
|
+
\"source\":\"merged\",
|
|
248
|
+
\"global_path\":\"$GLOBAL_QUEEN\",
|
|
249
|
+
\"local_path\":\"$local_queen\",
|
|
250
|
+
\"has_local\":true,
|
|
251
|
+
\"global_stats\":$global_stats,
|
|
252
|
+
\"local_stats\":$local_stats
|
|
253
|
+
}"
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# --- queen_ensure_both ---
|
|
257
|
+
# Ensure both global and local QUEEN.md exist
|
|
258
|
+
# Usage: queen_ensure_both [repo_path]
|
|
259
|
+
# Returns: JSON with paths
|
|
260
|
+
queen_ensure_both() {
|
|
261
|
+
local repo_path="${1:-$(pwd)}"
|
|
262
|
+
|
|
263
|
+
local global_result local_result
|
|
264
|
+
global_result=$(queen_init_global)
|
|
265
|
+
local_result=$(queen_init_local "$repo_path")
|
|
266
|
+
|
|
267
|
+
json_ok "{
|
|
268
|
+
\"global\":$global_result,
|
|
269
|
+
\"local\":$local_result
|
|
270
|
+
}"
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# --- queen_get_stats ---
|
|
274
|
+
# Get statistics from QUEEN.md
|
|
275
|
+
# Usage: queen_get_stats [queen_md_path]
|
|
276
|
+
# Returns: JSON with counts
|
|
277
|
+
queen_get_stats() {
|
|
278
|
+
local queen_path="${1:-$GLOBAL_QUEEN}"
|
|
279
|
+
|
|
280
|
+
if [[ ! -f "$queen_path" ]]; then
|
|
281
|
+
json_err "QUEEN.md not found: $queen_path"
|
|
282
|
+
return 1
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
local metadata
|
|
286
|
+
metadata=$(queen_parse_metadata "$queen_path" 2>/dev/null) || {
|
|
287
|
+
json_ok '{"total_philosophies":0,"total_patterns":0,"total_redirects":0,"total_stack_entries":0,"total_decrees":0}'
|
|
288
|
+
return 0
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
echo "$metadata" | jq '.stats // {
|
|
292
|
+
"total_philosophies": 0,
|
|
293
|
+
"total_patterns": 0,
|
|
294
|
+
"total_redirects": 0,
|
|
295
|
+
"total_stack_entries": 0,
|
|
296
|
+
"total_decrees": 0
|
|
297
|
+
}'
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# --- queen_promote_instincts ---
|
|
301
|
+
# Promote validated instincts from COLONY_STATE.json to QUEEN.md patterns
|
|
302
|
+
# Usage: queen_promote_instincts <queen_md_path> <colony_state_path>
|
|
303
|
+
# Returns: JSON with count of promoted instincts
|
|
304
|
+
queen_promote_instincts() {
|
|
305
|
+
local queen_md_path="$1"
|
|
306
|
+
local colony_state_path="$2"
|
|
307
|
+
|
|
308
|
+
# Validate inputs
|
|
309
|
+
if [[ -z "$queen_md_path" || -z "$colony_state_path" ]]; then
|
|
310
|
+
json_err "Usage: queen_promote_instincts <queen_md_path> <colony_state_path>"
|
|
311
|
+
return 1
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
if [[ ! -f "$queen_md_path" ]]; then
|
|
315
|
+
json_err "QUEEN.md not found: $queen_md_path"
|
|
316
|
+
return 1
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
if [[ ! -f "$colony_state_path" ]]; then
|
|
320
|
+
json_err "COLONY_STATE.json not found: $colony_state_path"
|
|
321
|
+
return 1
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
# Extract validated instincts with confidence >= 0.8
|
|
325
|
+
local instincts_json
|
|
326
|
+
instincts_json=$(jq -r '
|
|
327
|
+
.memory.instincts // [] |
|
|
328
|
+
map(select(.confidence >= 0.8 and .status == "validated")) |
|
|
329
|
+
map({id, trigger, action, confidence, domain, evidence})
|
|
330
|
+
' "$colony_state_path" 2>/dev/null) || {
|
|
331
|
+
json_err "Failed to parse COLONY_STATE.json"
|
|
332
|
+
return 1
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
# Count instincts to promote
|
|
336
|
+
local instinct_count
|
|
337
|
+
instinct_count=$(echo "$instincts_json" | jq 'length')
|
|
338
|
+
|
|
339
|
+
if [[ "$instinct_count" -eq 0 ]]; then
|
|
340
|
+
json_ok '{"promoted":0,"duplicates_skipped":0,"queen_md_path":"'"$queen_md_path"'","reason":"no_eligible_instincts"}'
|
|
341
|
+
return 0
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# Get existing patterns from QUEEN.md to check for duplicates
|
|
345
|
+
# Extract trigger+action combinations already in patterns section
|
|
346
|
+
local existing_patterns
|
|
347
|
+
existing_patterns=$(sed -n '/## 🧭 Patterns/,/## ⚠️ Redirects/p' "$queen_md_path" | \
|
|
348
|
+
grep -E '^\*\*Trigger:\*\*' | \
|
|
349
|
+
sed 's/^\*\*Trigger:\*\* //' | \
|
|
350
|
+
tr '\n' '|')
|
|
351
|
+
|
|
352
|
+
local promoted=0
|
|
353
|
+
local duplicates_skipped=0
|
|
354
|
+
local timestamp
|
|
355
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
356
|
+
|
|
357
|
+
# Process each instinct
|
|
358
|
+
local i
|
|
359
|
+
for ((i=0; i<instinct_count; i++)); do
|
|
360
|
+
local instinct trigger action domain evidence
|
|
361
|
+
instinct=$(echo "$instincts_json" | jq -r ".[$i]")
|
|
362
|
+
trigger=$(echo "$instinct" | jq -r '.trigger')
|
|
363
|
+
action=$(echo "$instinct" | jq -r '.action')
|
|
364
|
+
domain=$(echo "$instinct" | jq -r '.domain // "general"')
|
|
365
|
+
evidence=$(echo "$instinct" | jq -r '.evidence | if type == "array" then join("; ") else . end')
|
|
366
|
+
|
|
367
|
+
# Check for duplicate (trigger+action combination)
|
|
368
|
+
local pattern_key="${trigger}${action}"
|
|
369
|
+
if echo "$existing_patterns" | grep -qF "$trigger" 2>/dev/null; then
|
|
370
|
+
# Additional check: verify action also matches
|
|
371
|
+
local existing_action
|
|
372
|
+
existing_action=$(sed -n '/## 🧭 Patterns/,/## ⚠️ Redirects/p' "$queen_md_path" | \
|
|
373
|
+
grep -A1 "\\*\\*Trigger:\\*\\* ${trigger}$" | \
|
|
374
|
+
grep "\\*\\*Action:\\*\\*" | \
|
|
375
|
+
sed 's/^.*\\*\\*Action:\\*\\* //' | head -1)
|
|
376
|
+
if [[ "$existing_action" == "$action" ]]; then
|
|
377
|
+
duplicates_skipped=$((duplicates_skipped + 1))
|
|
378
|
+
continue
|
|
379
|
+
fi
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# Format the pattern entry
|
|
383
|
+
local pattern_entry="
|
|
384
|
+
**Trigger:** ${trigger}
|
|
385
|
+
**Action:** ${action}
|
|
386
|
+
**Domain:** ${domain}
|
|
387
|
+
**Evidence:** ${evidence}
|
|
388
|
+
*Promoted: ${timestamp}*
|
|
389
|
+
"
|
|
390
|
+
|
|
391
|
+
# Insert before the "*No patterns recorded yet*" line or at end of patterns section
|
|
392
|
+
local temp_file
|
|
393
|
+
temp_file=$(mktemp)
|
|
394
|
+
|
|
395
|
+
if grep -q "\\*No patterns recorded yet\\*" "$queen_md_path"; then
|
|
396
|
+
# Replace the placeholder with the pattern
|
|
397
|
+
awk -v entry="$pattern_entry" '
|
|
398
|
+
/## 🧭 Patterns/ { in_patterns=1 }
|
|
399
|
+
in_patterns && /\*No patterns recorded yet\*/ {
|
|
400
|
+
print entry
|
|
401
|
+
in_patterns=0
|
|
402
|
+
next
|
|
403
|
+
}
|
|
404
|
+
{ print }
|
|
405
|
+
' "$queen_md_path" > "$temp_file"
|
|
406
|
+
else
|
|
407
|
+
# Insert before the next section header
|
|
408
|
+
awk -v entry="$pattern_entry" '
|
|
409
|
+
/## 🧭 Patterns/ { in_patterns=1 }
|
|
410
|
+
in_patterns && /^## / && !/## 🧭 Patterns/ {
|
|
411
|
+
print entry
|
|
412
|
+
in_patterns=0
|
|
413
|
+
}
|
|
414
|
+
{ print }
|
|
415
|
+
' "$queen_md_path" > "$temp_file"
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
mv "$temp_file" "$queen_md_path"
|
|
419
|
+
promoted=$((promoted + 1))
|
|
420
|
+
done
|
|
421
|
+
|
|
422
|
+
# Update stats in metadata if any were promoted
|
|
423
|
+
if [[ "$promoted" -gt 0 ]]; then
|
|
424
|
+
local current_stats new_stats
|
|
425
|
+
current_stats=$(queen_get_stats "$queen_md_path")
|
|
426
|
+
local current_patterns
|
|
427
|
+
current_patterns=$(echo "$current_stats" | jq -r '.total_patterns // 0')
|
|
428
|
+
local new_pattern_count=$((current_patterns + promoted))
|
|
429
|
+
|
|
430
|
+
# Update the metadata block
|
|
431
|
+
local temp_file
|
|
432
|
+
temp_file=$(mktemp)
|
|
433
|
+
local metadata new_metadata
|
|
434
|
+
metadata=$(queen_parse_metadata "$queen_md_path")
|
|
435
|
+
new_metadata=$(echo "$metadata" | jq --argjson count "$new_pattern_count" --arg ts "$timestamp" '
|
|
436
|
+
.stats.total_patterns = $count | .last_evolved = $ts
|
|
437
|
+
')
|
|
438
|
+
|
|
439
|
+
awk -v new_metadata="$new_metadata" '
|
|
440
|
+
/<!-- METADATA/ { print; print new_metadata; in_metadata=1; next }
|
|
441
|
+
/-->/ && in_metadata { print; in_metadata=0; next }
|
|
442
|
+
in_metadata { next }
|
|
443
|
+
{ print }
|
|
444
|
+
' "$queen_md_path" > "$temp_file"
|
|
445
|
+
|
|
446
|
+
mv "$temp_file" "$queen_md_path"
|
|
447
|
+
fi
|
|
448
|
+
|
|
449
|
+
json_ok "{
|
|
450
|
+
\"promoted\": $promoted,
|
|
451
|
+
\"duplicates_skipped\": $duplicates_skipped,
|
|
452
|
+
\"queen_md_path\": \"$queen_md_path\",
|
|
453
|
+
\"colony_state_path\": \"$colony_state_path\"
|
|
454
|
+
}"
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
# --- queen_promote_learnings ---
|
|
458
|
+
# Promote validated phase learnings from COLONY_STATE.json to QUEEN.md patterns
|
|
459
|
+
# Usage: queen_promote_learnings <queen_md_path> <colony_state_path>
|
|
460
|
+
# Returns: JSON with count of promoted learnings
|
|
461
|
+
queen_promote_learnings() {
|
|
462
|
+
local queen_md_path="$1"
|
|
463
|
+
local colony_state_path="$2"
|
|
464
|
+
|
|
465
|
+
# Validate inputs
|
|
466
|
+
if [[ -z "$queen_md_path" || -z "$colony_state_path" ]]; then
|
|
467
|
+
json_err "Usage: queen_promote_learnings <queen_md_path> <colony_state_path>"
|
|
468
|
+
return 1
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
if [[ ! -f "$queen_md_path" ]]; then
|
|
472
|
+
json_err "QUEEN.md not found: $queen_md_path"
|
|
473
|
+
return 1
|
|
474
|
+
fi
|
|
475
|
+
|
|
476
|
+
if [[ ! -f "$colony_state_path" ]]; then
|
|
477
|
+
json_err "COLONY_STATE.json not found: $colony_state_path"
|
|
478
|
+
return 1
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
# Extract validated and tested learnings from phase_learnings
|
|
482
|
+
# Learnings are nested under memory.phase_learnings[].learnings[]
|
|
483
|
+
local learnings_json
|
|
484
|
+
learnings_json=$(jq -r '
|
|
485
|
+
.memory.phase_learnings // [] |
|
|
486
|
+
map(.learnings // [] | map(select(.status == "validated" and .tested == true))) |
|
|
487
|
+
flatten |
|
|
488
|
+
map({claim, evidence, source})
|
|
489
|
+
' "$colony_state_path" 2>/dev/null) || {
|
|
490
|
+
json_err "Failed to parse COLONY_STATE.json"
|
|
491
|
+
return 1
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# Count learnings to promote
|
|
495
|
+
local learning_count
|
|
496
|
+
learning_count=$(echo "$learnings_json" | jq 'length')
|
|
497
|
+
|
|
498
|
+
if [[ "$learning_count" -eq 0 ]]; then
|
|
499
|
+
json_ok '{"promoted":0,"duplicates_skipped":0,"queen_md_path":"'"$queen_md_path"'","reason":"no_eligible_learnings"}'
|
|
500
|
+
return 0
|
|
501
|
+
fi
|
|
502
|
+
|
|
503
|
+
# Get existing patterns from QUEEN.md to check for duplicates
|
|
504
|
+
local existing_patterns
|
|
505
|
+
existing_patterns=$(sed -n '/## 🧭 Patterns/,/## ⚠️ Redirects/p' "$queen_md_path" | \
|
|
506
|
+
grep -E '^\*\*Pattern\*\*:|^- \*\*Pattern\*\*:' | \
|
|
507
|
+
sed 's/^\*\*Pattern\*\*: //; s/^- \*\*Pattern\*\*: //' | \
|
|
508
|
+
cut -d'(' -f1 | \
|
|
509
|
+
tr '\n' '|')
|
|
510
|
+
|
|
511
|
+
local promoted=0
|
|
512
|
+
local duplicates_skipped=0
|
|
513
|
+
local timestamp
|
|
514
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
515
|
+
|
|
516
|
+
# Process each learning
|
|
517
|
+
local i
|
|
518
|
+
for ((i=0; i<learning_count; i++)); do
|
|
519
|
+
local learning claim evidence source
|
|
520
|
+
learning=$(echo "$learnings_json" | jq -r ".[$i]")
|
|
521
|
+
claim=$(echo "$learning" | jq -r '.claim // empty')
|
|
522
|
+
evidence=$(echo "$learning" | jq -r '.evidence // "Validated in colony session"')
|
|
523
|
+
source=$(echo "$learning" | jq -r '.source // "unknown"')
|
|
524
|
+
|
|
525
|
+
if [[ -z "$claim" ]]; then
|
|
526
|
+
continue
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
# Check for duplicate (claim already exists)
|
|
530
|
+
local claim_prefix
|
|
531
|
+
claim_prefix=$(echo "$claim" | cut -c1-50)
|
|
532
|
+
if echo "$existing_patterns" | grep -qF "$claim_prefix" 2>/dev/null; then
|
|
533
|
+
duplicates_skipped=$((duplicates_skipped + 1))
|
|
534
|
+
continue
|
|
535
|
+
fi
|
|
536
|
+
|
|
537
|
+
# Format the pattern entry with source attribution
|
|
538
|
+
local pattern_entry="
|
|
539
|
+
**Pattern**: ${claim} (validated in colony: ${source})
|
|
540
|
+
**Evidence**: ${evidence}
|
|
541
|
+
*Promoted: ${timestamp}*
|
|
542
|
+
"
|
|
543
|
+
|
|
544
|
+
# Insert into QUEEN.md patterns section
|
|
545
|
+
local temp_file
|
|
546
|
+
temp_file=$(mktemp)
|
|
547
|
+
|
|
548
|
+
if grep -q "\\*No patterns recorded yet\\*" "$queen_md_path"; then
|
|
549
|
+
# Replace the placeholder with the pattern
|
|
550
|
+
awk -v entry="$pattern_entry" '
|
|
551
|
+
/## 🧭 Patterns/ { in_patterns=1 }
|
|
552
|
+
in_patterns && /\*No patterns recorded yet\*/ {
|
|
553
|
+
print entry
|
|
554
|
+
in_patterns=0
|
|
555
|
+
next
|
|
556
|
+
}
|
|
557
|
+
{ print }
|
|
558
|
+
' "$queen_md_path" > "$temp_file"
|
|
559
|
+
else
|
|
560
|
+
# Insert before the next section header
|
|
561
|
+
awk -v entry="$pattern_entry" '
|
|
562
|
+
/## 🧭 Patterns/ { in_patterns=1 }
|
|
563
|
+
in_patterns && /^## / && !/## 🧭 Patterns/ {
|
|
564
|
+
print entry
|
|
565
|
+
in_patterns=0
|
|
566
|
+
}
|
|
567
|
+
{ print }
|
|
568
|
+
' "$queen_md_path" > "$temp_file"
|
|
569
|
+
fi
|
|
570
|
+
|
|
571
|
+
mv "$temp_file" "$queen_md_path"
|
|
572
|
+
promoted=$((promoted + 1))
|
|
573
|
+
done
|
|
574
|
+
|
|
575
|
+
# Update stats in metadata if any were promoted
|
|
576
|
+
if [[ "$promoted" -gt 0 ]]; then
|
|
577
|
+
local current_stats current_patterns new_pattern_count
|
|
578
|
+
current_stats=$(queen_get_stats "$queen_md_path")
|
|
579
|
+
current_patterns=$(echo "$current_stats" | jq -r '.total_patterns // 0')
|
|
580
|
+
new_pattern_count=$((current_patterns + promoted))
|
|
581
|
+
|
|
582
|
+
# Update the metadata block
|
|
583
|
+
local temp_file metadata new_metadata
|
|
584
|
+
temp_file=$(mktemp)
|
|
585
|
+
metadata=$(queen_parse_metadata "$queen_md_path")
|
|
586
|
+
new_metadata=$(echo "$metadata" | jq --argjson count "$new_pattern_count" --arg ts "$timestamp" '
|
|
587
|
+
.stats.total_patterns = $count | .last_evolved = $ts
|
|
588
|
+
')
|
|
589
|
+
|
|
590
|
+
awk -v new_metadata="$new_metadata" '
|
|
591
|
+
/<!-- METADATA/ { print; print new_metadata; in_metadata=1; next }
|
|
592
|
+
/-->/ && in_metadata { print; in_metadata=0; next }
|
|
593
|
+
in_metadata { next }
|
|
594
|
+
{ print }
|
|
595
|
+
' "$queen_md_path" > "$temp_file"
|
|
596
|
+
|
|
597
|
+
mv "$temp_file" "$queen_md_path"
|
|
598
|
+
fi
|
|
599
|
+
|
|
600
|
+
json_ok "{
|
|
601
|
+
\"promoted\": $promoted,
|
|
602
|
+
\"duplicates_skipped\": $duplicates_skipped,
|
|
603
|
+
\"queen_md_path\": \"$queen_md_path\",
|
|
604
|
+
\"colony_state_path\": \"$colony_state_path\"
|
|
605
|
+
}"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
# --- queen_promote_all ---
|
|
609
|
+
# Promote all validated pheromones (instincts + learnings) from COLONY_STATE.json to QUEEN.md
|
|
610
|
+
# Usage: queen_promote_all <queen_md_path> <colony_state_path>
|
|
611
|
+
# Returns: JSON with combined promotion results
|
|
612
|
+
queen_promote_all() {
|
|
613
|
+
local queen_md_path="$1"
|
|
614
|
+
local colony_state_path="$2"
|
|
615
|
+
|
|
616
|
+
# Validate inputs
|
|
617
|
+
if [[ -z "$queen_md_path" || -z "$colony_state_path" ]]; then
|
|
618
|
+
json_err "Usage: queen_promote_all <queen_md_path> <colony_state_path>"
|
|
619
|
+
return 1
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
if [[ ! -f "$queen_md_path" ]]; then
|
|
623
|
+
json_err "QUEEN.md not found: $queen_md_path"
|
|
624
|
+
return 1
|
|
625
|
+
fi
|
|
626
|
+
|
|
627
|
+
if [[ ! -f "$colony_state_path" ]]; then
|
|
628
|
+
json_err "COLONY_STATE.json not found: $colony_state_path"
|
|
629
|
+
return 1
|
|
630
|
+
fi
|
|
631
|
+
|
|
632
|
+
local instincts_result learnings_result
|
|
633
|
+
local instincts_promoted=0
|
|
634
|
+
local learnings_promoted=0
|
|
635
|
+
local instincts_error=""
|
|
636
|
+
local learnings_error=""
|
|
637
|
+
|
|
638
|
+
# Promote instincts (with error handling)
|
|
639
|
+
if instincts_result=$(queen_promote_instincts "$queen_md_path" "$colony_state_path" 2>&1); then
|
|
640
|
+
instincts_promoted=$(echo "$instincts_result" | jq -r '.promoted // 0')
|
|
641
|
+
else
|
|
642
|
+
instincts_error=$(echo "$instincts_result" | jq -r '.error // "unknown error"' 2>/dev/null || echo "failed to promote instincts")
|
|
643
|
+
fi
|
|
644
|
+
|
|
645
|
+
# Promote learnings (with error handling - continues even if instincts failed)
|
|
646
|
+
if learnings_result=$(queen_promote_learnings "$queen_md_path" "$colony_state_path" 2>&1); then
|
|
647
|
+
learnings_promoted=$(echo "$learnings_result" | jq -r '.promoted // 0')
|
|
648
|
+
else
|
|
649
|
+
learnings_error=$(echo "$learnings_result" | jq -r '.error // "unknown error"' 2>/dev/null || echo "failed to promote learnings")
|
|
650
|
+
fi
|
|
651
|
+
|
|
652
|
+
local total=$((instincts_promoted + learnings_promoted))
|
|
653
|
+
|
|
654
|
+
# Build result JSON
|
|
655
|
+
local result_json
|
|
656
|
+
result_json=$(jq -n \
|
|
657
|
+
--argjson instincts "$instincts_promoted" \
|
|
658
|
+
--argjson learnings "$learnings_promoted" \
|
|
659
|
+
--argjson total "$total" \
|
|
660
|
+
--arg queen_md "$queen_md_path" \
|
|
661
|
+
--arg colony_state "$colony_state_path" \
|
|
662
|
+
--arg instincts_err "$instincts_error" \
|
|
663
|
+
--arg learnings_err "$learnings_error" \
|
|
664
|
+
'{
|
|
665
|
+
instincts_promoted: $instincts,
|
|
666
|
+
learnings_promoted: $learnings,
|
|
667
|
+
total: $total,
|
|
668
|
+
queen_md_path: $queen_md,
|
|
669
|
+
colony_state_path: $colony_state,
|
|
670
|
+
errors: {
|
|
671
|
+
instincts: (if $instincts_err == "" then null else $instincts_err end),
|
|
672
|
+
learnings: (if $learnings_err == "" then null else $learnings_err end)
|
|
673
|
+
}
|
|
674
|
+
}')
|
|
675
|
+
|
|
676
|
+
json_ok "$result_json"
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
# --- Main entry point for command-line usage ---
|
|
680
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
681
|
+
cmd="${1:-help}"
|
|
682
|
+
shift 2>/dev/null || true
|
|
683
|
+
|
|
684
|
+
case "$cmd" in
|
|
685
|
+
init-global)
|
|
686
|
+
queen_init_global
|
|
687
|
+
;;
|
|
688
|
+
init-local)
|
|
689
|
+
queen_init_local "${1:-$(pwd)}"
|
|
690
|
+
;;
|
|
691
|
+
ensure-both)
|
|
692
|
+
queen_ensure_both "${1:-$(pwd)}"
|
|
693
|
+
;;
|
|
694
|
+
parse-metadata)
|
|
695
|
+
queen_parse_metadata "${1:-$GLOBAL_QUEEN}"
|
|
696
|
+
;;
|
|
697
|
+
get-version)
|
|
698
|
+
result=$(queen_get_version "${1:-$GLOBAL_QUEEN}")
|
|
699
|
+
json_ok "\"$result\""
|
|
700
|
+
;;
|
|
701
|
+
increment-version)
|
|
702
|
+
queen_increment_version "${1:-$GLOBAL_QUEEN}" "${2:-patch}"
|
|
703
|
+
;;
|
|
704
|
+
merge)
|
|
705
|
+
queen_merge_global_local "${1:-$(pwd)}"
|
|
706
|
+
;;
|
|
707
|
+
stats)
|
|
708
|
+
queen_get_stats "${1:-$GLOBAL_QUEEN}"
|
|
709
|
+
;;
|
|
710
|
+
promote-instincts)
|
|
711
|
+
queen_promote_instincts "${1:-}" "${2:-}"
|
|
712
|
+
;;
|
|
713
|
+
promote-learnings)
|
|
714
|
+
queen_promote_learnings "${1:-}" "${2:-}"
|
|
715
|
+
;;
|
|
716
|
+
promote-all)
|
|
717
|
+
queen_promote_all "${1:-}" "${2:-}"
|
|
718
|
+
;;
|
|
719
|
+
help|*)
|
|
720
|
+
cat <<'EOF'
|
|
721
|
+
{"ok":true,"commands":[
|
|
722
|
+
"init-global","init-local","ensure-both",
|
|
723
|
+
"parse-metadata","get-version","increment-version",
|
|
724
|
+
"merge","stats","promote-instincts","promote-learnings","promote-all"
|
|
725
|
+
],"description":"Queen Utilities — QUEEN.md management for Aether colonies"}
|
|
726
|
+
EOF
|
|
727
|
+
;;
|
|
728
|
+
esac
|
|
729
|
+
fi
|