aether-colony 5.1.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aether/aether-utils.sh +157 -42
- package/.aether/agents/aether-ambassador.md +140 -0
- package/.aether/agents/aether-archaeologist.md +108 -0
- package/.aether/agents/aether-architect.md +133 -0
- package/.aether/agents/aether-auditor.md +144 -0
- package/.aether/agents/aether-builder.md +184 -0
- package/.aether/agents/aether-chaos.md +115 -0
- package/.aether/agents/aether-chronicler.md +122 -0
- package/.aether/agents/aether-gatekeeper.md +116 -0
- package/.aether/agents/aether-includer.md +117 -0
- package/.aether/agents/aether-keeper.md +177 -0
- package/.aether/agents/aether-measurer.md +128 -0
- package/.aether/agents/aether-oracle.md +137 -0
- package/.aether/agents/aether-probe.md +133 -0
- package/.aether/agents/aether-queen.md +286 -0
- package/.aether/agents/aether-route-setter.md +130 -0
- package/.aether/agents/aether-sage.md +106 -0
- package/.aether/agents/aether-scout.md +101 -0
- package/.aether/agents/aether-surveyor-disciplines.md +391 -0
- package/.aether/agents/aether-surveyor-nest.md +329 -0
- package/.aether/agents/aether-surveyor-pathogens.md +264 -0
- package/.aether/agents/aether-surveyor-provisions.md +334 -0
- package/.aether/agents/aether-tracker.md +137 -0
- package/.aether/agents/aether-watcher.md +174 -0
- package/.aether/agents/aether-weaver.md +130 -0
- package/.aether/commands/claude/archaeology.md +334 -0
- package/.aether/commands/claude/build.md +65 -0
- package/.aether/commands/claude/chaos.md +336 -0
- package/.aether/commands/claude/colonize.md +259 -0
- package/.aether/commands/claude/continue.md +60 -0
- package/.aether/commands/claude/council.md +507 -0
- package/.aether/commands/claude/data-clean.md +81 -0
- package/.aether/commands/claude/dream.md +268 -0
- package/.aether/commands/claude/entomb.md +498 -0
- package/.aether/commands/claude/export-signals.md +57 -0
- package/.aether/commands/claude/feedback.md +96 -0
- package/.aether/commands/claude/flag.md +151 -0
- package/.aether/commands/claude/flags.md +169 -0
- package/.aether/commands/claude/focus.md +76 -0
- package/.aether/commands/claude/help.md +154 -0
- package/.aether/commands/claude/history.md +140 -0
- package/.aether/commands/claude/import-signals.md +71 -0
- package/.aether/commands/claude/init.md +505 -0
- package/.aether/commands/claude/insert-phase.md +105 -0
- package/.aether/commands/claude/interpret.md +278 -0
- package/.aether/commands/claude/lay-eggs.md +210 -0
- package/.aether/commands/claude/maturity.md +113 -0
- package/.aether/commands/claude/memory-details.md +77 -0
- package/.aether/commands/claude/migrate-state.md +171 -0
- package/.aether/commands/claude/oracle.md +642 -0
- package/.aether/commands/claude/organize.md +232 -0
- package/.aether/commands/claude/patrol.md +620 -0
- package/.aether/commands/claude/pause-colony.md +233 -0
- package/.aether/commands/claude/phase.md +115 -0
- package/.aether/commands/claude/pheromones.md +156 -0
- package/.aether/commands/claude/plan.md +693 -0
- package/.aether/commands/claude/preferences.md +65 -0
- package/.aether/commands/claude/quick.md +100 -0
- package/.aether/commands/claude/redirect.md +76 -0
- package/.aether/commands/claude/resume-colony.md +197 -0
- package/.aether/commands/claude/resume.md +388 -0
- package/.aether/commands/claude/run.md +231 -0
- package/.aether/commands/claude/seal.md +774 -0
- package/.aether/commands/claude/skill-create.md +286 -0
- package/.aether/commands/claude/status.md +410 -0
- package/.aether/commands/claude/swarm.md +349 -0
- package/.aether/commands/claude/tunnels.md +426 -0
- package/.aether/commands/claude/update.md +132 -0
- package/.aether/commands/claude/verify-castes.md +143 -0
- package/.aether/commands/claude/watch.md +239 -0
- package/.aether/commands/colonize.yaml +4 -0
- package/.aether/commands/council.yaml +205 -0
- package/.aether/commands/init.yaml +46 -13
- package/.aether/commands/insert-phase.yaml +4 -0
- package/.aether/commands/opencode/archaeology.md +331 -0
- package/.aether/commands/opencode/build.md +1168 -0
- package/.aether/commands/opencode/chaos.md +329 -0
- package/.aether/commands/opencode/colonize.md +195 -0
- package/.aether/commands/opencode/continue.md +1436 -0
- package/.aether/commands/opencode/council.md +437 -0
- package/.aether/commands/opencode/data-clean.md +77 -0
- package/.aether/commands/opencode/dream.md +260 -0
- package/.aether/commands/opencode/entomb.md +377 -0
- package/.aether/commands/opencode/export-signals.md +54 -0
- package/.aether/commands/opencode/feedback.md +99 -0
- package/.aether/commands/opencode/flag.md +149 -0
- package/.aether/commands/opencode/flags.md +167 -0
- package/.aether/commands/opencode/focus.md +73 -0
- package/.aether/commands/opencode/help.md +157 -0
- package/.aether/commands/opencode/history.md +136 -0
- package/.aether/commands/opencode/import-signals.md +68 -0
- package/.aether/commands/opencode/init.md +518 -0
- package/.aether/commands/opencode/insert-phase.md +111 -0
- package/.aether/commands/opencode/interpret.md +272 -0
- package/.aether/commands/opencode/lay-eggs.md +213 -0
- package/.aether/commands/opencode/maturity.md +108 -0
- package/.aether/commands/opencode/memory-details.md +83 -0
- package/.aether/commands/opencode/migrate-state.md +165 -0
- package/.aether/commands/opencode/oracle.md +593 -0
- package/.aether/commands/opencode/organize.md +226 -0
- package/.aether/commands/opencode/patrol.md +626 -0
- package/.aether/commands/opencode/pause-colony.md +203 -0
- package/.aether/commands/opencode/phase.md +113 -0
- package/.aether/commands/opencode/pheromones.md +162 -0
- package/.aether/commands/opencode/plan.md +684 -0
- package/.aether/commands/opencode/preferences.md +71 -0
- package/.aether/commands/opencode/quick.md +91 -0
- package/.aether/commands/opencode/redirect.md +84 -0
- package/.aether/commands/opencode/resume-colony.md +190 -0
- package/.aether/commands/opencode/resume.md +394 -0
- package/.aether/commands/opencode/run.md +237 -0
- package/.aether/commands/opencode/seal.md +452 -0
- package/.aether/commands/opencode/skill-create.md +63 -0
- package/.aether/commands/opencode/status.md +307 -0
- package/.aether/commands/opencode/swarm.md +15 -0
- package/.aether/commands/opencode/tunnels.md +400 -0
- package/.aether/commands/opencode/update.md +127 -0
- package/.aether/commands/opencode/verify-castes.md +139 -0
- package/.aether/commands/opencode/watch.md +227 -0
- package/.aether/commands/plan.yaml +53 -2
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/resume-colony.yaml +6 -4
- package/.aether/commands/resume.yaml +9 -0
- package/.aether/commands/run.yaml +37 -1
- package/.aether/commands/seal.yaml +9 -0
- package/.aether/commands/status.yaml +45 -1
- package/.aether/docs/command-playbooks/build-full.md +3 -2
- package/.aether/docs/command-playbooks/build-prep.md +12 -4
- package/.aether/docs/command-playbooks/build-verify.md +51 -0
- package/.aether/docs/command-playbooks/continue-advance.md +115 -6
- package/.aether/docs/command-playbooks/continue-full.md +1 -0
- package/.aether/docs/command-playbooks/continue-verify.md +33 -0
- package/.aether/utils/clash-detect.sh +239 -0
- package/.aether/utils/council.sh +425 -0
- package/.aether/utils/error-handler.sh +3 -3
- package/.aether/utils/flag.sh +23 -12
- package/.aether/utils/hive.sh +2 -2
- package/.aether/utils/hooks/clash-pre-tool-use.js +99 -0
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +2 -2
- package/.aether/utils/merge-driver-lockfile.sh +35 -0
- package/.aether/utils/midden.sh +712 -0
- package/.aether/utils/pheromone.sh +1376 -108
- package/.aether/utils/queen.sh +31 -21
- package/.aether/utils/session.sh +264 -0
- package/.aether/utils/spawn-tree.sh +7 -7
- package/.aether/utils/spawn.sh +2 -2
- package/.aether/utils/state-api.sh +216 -5
- package/.aether/utils/swarm.sh +1 -1
- package/.aether/utils/worktree.sh +189 -0
- package/.claude/commands/ant/colonize.md +2 -0
- package/.claude/commands/ant/council.md +205 -0
- package/.claude/commands/ant/init.md +53 -14
- package/.claude/commands/ant/insert-phase.md +4 -0
- package/.claude/commands/ant/plan.md +27 -1
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/resume-colony.md +3 -2
- package/.claude/commands/ant/resume.md +9 -0
- package/.claude/commands/ant/run.md +37 -1
- package/.claude/commands/ant/seal.md +9 -0
- package/.claude/commands/ant/status.md +45 -1
- package/.opencode/commands/ant/colonize.md +2 -0
- package/.opencode/commands/ant/council.md +143 -0
- package/.opencode/commands/ant/init.md +53 -13
- package/.opencode/commands/ant/insert-phase.md +4 -0
- package/.opencode/commands/ant/plan.md +26 -1
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/resume-colony.md +3 -2
- package/.opencode/commands/ant/resume.md +9 -0
- package/.opencode/commands/ant/run.md +37 -1
- package/.opencode/commands/ant/status.md +2 -0
- package/CHANGELOG.md +116 -0
- package/README.md +34 -8
- package/bin/cli.js +103 -61
- package/bin/lib/banner.js +14 -0
- package/bin/lib/init.js +8 -7
- package/bin/lib/interactive-setup.js +251 -0
- package/bin/npx-entry.js +21 -0
- package/bin/npx-install.js +9 -167
- package/bin/validate-package.sh +23 -0
- package/package.json +11 -3
- package/.aether/docs/plans/pheromone-display-plan.md +0 -257
- package/.aether/schemas/example-prompt-builder.xml +0 -234
- package/.aether/scripts/incident-test-add.sh +0 -47
- package/.aether/scripts/weekly-audit.sh +0 -79
package/.aether/utils/queen.sh
CHANGED
|
@@ -488,8 +488,9 @@ _queen_promote() {
|
|
|
488
488
|
ev_separator=$(grep -n "^|------|" "$tmp_file" | tail -1 | cut -d: -f1 || true)
|
|
489
489
|
|
|
490
490
|
# Use awk for cross-platform insertion (only if separator found)
|
|
491
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
491
492
|
if [[ -n "$ev_separator" ]]; then
|
|
492
|
-
|
|
493
|
+
AETHER_EV_ENTRY="$ev_entry" awk -v line="$ev_separator" 'NR==line{print; print ENVIRON["AETHER_EV_ENTRY"]; next}1' "$tmp_file" > "${tmp_file}.ev" && mv "${tmp_file}.ev" "$tmp_file"
|
|
493
494
|
fi
|
|
494
495
|
|
|
495
496
|
# Update METADATA stats in temp file
|
|
@@ -528,9 +529,10 @@ _queen_promote() {
|
|
|
528
529
|
ev_log_entry="{\"timestamp\": \"$ts\", \"action\": \"promote\", \"wisdom_type\": \"$wisdom_type\", \"content_hash\": \"$content_hash\", \"colony\": \"$colony_name\"}"
|
|
529
530
|
|
|
530
531
|
# Check if evolution_log exists in metadata, add if not
|
|
532
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
531
533
|
if ! grep -q '"evolution_log"' "$tmp_file"; then
|
|
532
534
|
# Add evolution_log array after stats
|
|
533
|
-
|
|
535
|
+
AETHER_EV_LOG_ENTRY="$ev_log_entry" awk '
|
|
534
536
|
/"stats": \{/ {
|
|
535
537
|
print
|
|
536
538
|
# Read until closing brace of stats
|
|
@@ -540,19 +542,19 @@ _queen_promote() {
|
|
|
540
542
|
}
|
|
541
543
|
# Add comma and evolution_log
|
|
542
544
|
print ","
|
|
543
|
-
print " \"evolution_log\": ["
|
|
545
|
+
print " \"evolution_log\": [" ENVIRON["AETHER_EV_LOG_ENTRY"] "]"
|
|
544
546
|
next
|
|
545
547
|
}
|
|
546
548
|
{ print }
|
|
547
549
|
' "$tmp_file" > "${tmp_file}.evlog" && mv "${tmp_file}.evlog" "$tmp_file"
|
|
548
550
|
else
|
|
549
551
|
# Append to existing evolution_log array
|
|
550
|
-
|
|
552
|
+
AETHER_EV_LOG_ENTRY="$ev_log_entry" awk '
|
|
551
553
|
/"evolution_log": \[/ {
|
|
552
554
|
# Check if array is empty or has items
|
|
553
555
|
if (/\]/) {
|
|
554
556
|
# Empty array - replace with entry
|
|
555
|
-
gsub(/"evolution_log": \[\]/, "\"evolution_log\": ["
|
|
557
|
+
gsub(/"evolution_log": \[\]/, "\"evolution_log\": [" ENVIRON["AETHER_EV_LOG_ENTRY"] "]")
|
|
556
558
|
} else {
|
|
557
559
|
# Has items - need to add before closing bracket
|
|
558
560
|
# For now, just print and handle in next iteration
|
|
@@ -566,7 +568,7 @@ _queen_promote() {
|
|
|
566
568
|
getline
|
|
567
569
|
if (/\]/) {
|
|
568
570
|
# Was empty, now add entry
|
|
569
|
-
print
|
|
571
|
+
print ENVIRON["AETHER_EV_LOG_ENTRY"]
|
|
570
572
|
print "]"
|
|
571
573
|
} else {
|
|
572
574
|
# Has items, add comma and entry before closing
|
|
@@ -574,7 +576,7 @@ _queen_promote() {
|
|
|
574
576
|
while (getline > 0) {
|
|
575
577
|
if (/^\s*\]/) {
|
|
576
578
|
print ","
|
|
577
|
-
print
|
|
579
|
+
print ENVIRON["AETHER_EV_LOG_ENTRY"]
|
|
578
580
|
print "]"
|
|
579
581
|
break
|
|
580
582
|
}
|
|
@@ -628,12 +630,18 @@ _queen_promote() {
|
|
|
628
630
|
if [[ -n "$meta_section" ]]; then
|
|
629
631
|
# SUPPRESS:OK -- read-default: returns fallback on failure
|
|
630
632
|
updated_meta=$(echo "$meta_section" | jq --arg hash "$content_hash" --argjson cols "$colonies_json" '.colonies_contributed[$hash] = $cols' 2>/dev/null || echo "$meta_section")
|
|
631
|
-
# Replace metadata section
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
$
|
|
635
|
-
|
|
636
|
-
|
|
633
|
+
# Replace metadata section using head/tail to handle multi-line content safely
|
|
634
|
+
# awk -v cannot handle embedded newlines in variable values (C-escape interpretation)
|
|
635
|
+
local meta_start_line meta_end_line
|
|
636
|
+
meta_start_line=$(grep -n "^<!-- METADATA$" "$tmp_file" | head -1 | cut -d: -f1)
|
|
637
|
+
meta_end_line=$(grep -n "^-->$" "$tmp_file" | head -1 | cut -d: -f1)
|
|
638
|
+
if [[ -n "$meta_start_line" && -n "$meta_end_line" ]]; then
|
|
639
|
+
{
|
|
640
|
+
head -n $((meta_start_line - 1)) "$tmp_file"
|
|
641
|
+
printf '<!-- METADATA\n%s\n-->\n' "$updated_meta"
|
|
642
|
+
tail -n +$((meta_end_line + 1)) "$tmp_file"
|
|
643
|
+
} > "${tmp_file}.metaupd" && mv "${tmp_file}.metaupd" "$tmp_file"
|
|
644
|
+
fi
|
|
637
645
|
fi
|
|
638
646
|
fi
|
|
639
647
|
|
|
@@ -837,7 +845,8 @@ _queen_write_learnings() {
|
|
|
837
845
|
local ev_separator
|
|
838
846
|
ev_separator=$(grep -n "^|------|" "$tmp_file" | tail -1 | cut -d: -f1 || true)
|
|
839
847
|
if [[ -n "$ev_separator" ]]; then
|
|
840
|
-
|
|
848
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
849
|
+
AETHER_EV_ENTRY="$ev_entry" awk -v line="$ev_separator" 'NR==line{print; print ENVIRON["AETHER_EV_ENTRY"]; next}1' "$tmp_file" > "${tmp_file}.ev" && mv "${tmp_file}.ev" "$tmp_file"
|
|
841
850
|
fi
|
|
842
851
|
|
|
843
852
|
# Update METADATA stats: increment total_build_learnings
|
|
@@ -967,7 +976,8 @@ _queen_promote_instinct() {
|
|
|
967
976
|
local ev_separator
|
|
968
977
|
ev_separator=$(grep -n "^|------|" "$tmp_file" | tail -1 | cut -d: -f1 || true)
|
|
969
978
|
if [[ -n "$ev_separator" ]]; then
|
|
970
|
-
|
|
979
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
980
|
+
AETHER_EV_ENTRY="$ev_entry" awk -v line="$ev_separator" 'NR==line{print; print ENVIRON["AETHER_EV_ENTRY"]; next}1' "$tmp_file" > "${tmp_file}.ev" && mv "${tmp_file}.ev" "$tmp_file"
|
|
971
981
|
fi
|
|
972
982
|
|
|
973
983
|
# Update METADATA stats: increment total_instincts
|
|
@@ -1110,7 +1120,8 @@ _queen_seed_from_hive() {
|
|
|
1110
1120
|
local ev_separator
|
|
1111
1121
|
ev_separator=$(grep -n "^|------|" "$tmp_file" | tail -1 | cut -d: -f1 || true)
|
|
1112
1122
|
if [[ -n "$ev_separator" ]]; then
|
|
1113
|
-
|
|
1123
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
1124
|
+
AETHER_EV_ENTRY="$ev_entry" awk -v line="$ev_separator" 'NR==line{print; print ENVIRON["AETHER_EV_ENTRY"]; next}1' "$tmp_file" > "${tmp_file}.ev" && mv "${tmp_file}.ev" "$tmp_file"
|
|
1114
1125
|
fi
|
|
1115
1126
|
|
|
1116
1127
|
# Update METADATA stats: increment total_codebase_patterns
|
|
@@ -1380,8 +1391,7 @@ _colony_depth() {
|
|
|
1380
1391
|
local new_depth="${2:-}"
|
|
1381
1392
|
case "$new_depth" in
|
|
1382
1393
|
light|standard|deep|full)
|
|
1383
|
-
|
|
1384
|
-
jq --arg d "$new_depth" '.colony_depth = $d' "$DATA_DIR/COLONY_STATE.json" > "$tmp" && mv "$tmp" "$DATA_DIR/COLONY_STATE.json"
|
|
1394
|
+
NEW_DEPTH="$new_depth" _state_mutate '.colony_depth = env.NEW_DEPTH'
|
|
1385
1395
|
json_ok "$(jq -n --arg depth "$new_depth" '{depth: $depth, updated: true}')"
|
|
1386
1396
|
;;
|
|
1387
1397
|
*)
|
|
@@ -1465,8 +1475,7 @@ _queen_write_charter() {
|
|
|
1465
1475
|
local current_name
|
|
1466
1476
|
current_name=$(jq -r '.colony_name // empty' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null) || true
|
|
1467
1477
|
if [[ -z "$current_name" && -n "$cw_colony_name" ]]; then
|
|
1468
|
-
|
|
1469
|
-
jq --arg cn "$cw_colony_name" '.colony_name = $cn' "$DATA_DIR/COLONY_STATE.json" > "$tmp_state" && mv "$tmp_state" "$DATA_DIR/COLONY_STATE.json"
|
|
1478
|
+
CW_COLONY_NAME="$cw_colony_name" _state_mutate '.colony_name = env.CW_COLONY_NAME'
|
|
1470
1479
|
fi
|
|
1471
1480
|
fi
|
|
1472
1481
|
|
|
@@ -1601,7 +1610,8 @@ _queen_write_charter() {
|
|
|
1601
1610
|
local ev_separator
|
|
1602
1611
|
ev_separator=$(grep -n "^|------|" "$tmp_file" | tail -1 | cut -d: -f1 || true)
|
|
1603
1612
|
if [[ -n "$ev_separator" ]]; then
|
|
1604
|
-
|
|
1613
|
+
# Use ENVIRON instead of -v for user content to avoid C-escape interpretation (\n, \t, \\)
|
|
1614
|
+
AETHER_EV_ENTRY="$ev_entry" awk -v line="$ev_separator" 'NR==line{print; print ENVIRON["AETHER_EV_ENTRY"]; next}1' "$tmp_file" > "${tmp_file}.ev" && mv "${tmp_file}.ev" "$tmp_file"
|
|
1605
1615
|
fi
|
|
1606
1616
|
|
|
1607
1617
|
# Update METADATA stats -- count non-charter list items in each section, add charter entries
|
package/.aether/utils/session.sh
CHANGED
|
@@ -550,3 +550,267 @@ _session_summary() {
|
|
|
550
550
|
[[ "$cleared" == "true" ]] && echo "Status: Context was cleared"
|
|
551
551
|
fi
|
|
552
552
|
}
|
|
553
|
+
|
|
554
|
+
# ============================================================================
|
|
555
|
+
# _pending_decision_add
|
|
556
|
+
# Add a decision to the pending decisions queue
|
|
557
|
+
# Usage: pending-decision-add --type <type> --description <desc> [--phase N] [--source <src>]
|
|
558
|
+
# Types: visual_checkpoint, replan, escalation, runtime_verification, user_input
|
|
559
|
+
# ============================================================================
|
|
560
|
+
_pending_decision_add() {
|
|
561
|
+
local pd_type=""
|
|
562
|
+
local pd_description=""
|
|
563
|
+
local pd_phase="null"
|
|
564
|
+
local pd_source=""
|
|
565
|
+
|
|
566
|
+
while [[ $# -gt 0 ]]; do
|
|
567
|
+
case "$1" in
|
|
568
|
+
--type) pd_type="$2"; shift 2 ;;
|
|
569
|
+
--description) pd_description="$2"; shift 2 ;;
|
|
570
|
+
--phase) pd_phase="$2"; shift 2 ;;
|
|
571
|
+
--source) pd_source="$2"; shift 2 ;;
|
|
572
|
+
*) shift ;;
|
|
573
|
+
esac
|
|
574
|
+
done
|
|
575
|
+
|
|
576
|
+
[[ -z "$pd_type" ]] && json_err "$E_VALIDATION_FAILED" "pending-decision-add requires --type"
|
|
577
|
+
[[ -z "$pd_description" ]] && json_err "$E_VALIDATION_FAILED" "pending-decision-add requires --description"
|
|
578
|
+
|
|
579
|
+
local pd_file="$COLONY_DATA_DIR/pending-decisions.json"
|
|
580
|
+
local pd_id="pd_$(date +%s)_$$"
|
|
581
|
+
local pd_now
|
|
582
|
+
pd_now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
583
|
+
|
|
584
|
+
# Acquire lock for concurrent access
|
|
585
|
+
if type acquire_lock &>/dev/null; then
|
|
586
|
+
acquire_lock "$pd_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on pending-decisions.json"
|
|
587
|
+
trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
|
|
588
|
+
fi
|
|
589
|
+
|
|
590
|
+
# Initialize file if missing
|
|
591
|
+
if [[ ! -f "$pd_file" ]]; then
|
|
592
|
+
echo '{"version":"1.0","decisions":[]}' > "$pd_file"
|
|
593
|
+
fi
|
|
594
|
+
|
|
595
|
+
local pd_current
|
|
596
|
+
pd_current=$(cat "$pd_file" 2>/dev/null || echo '{"version":"1.0","decisions":[]}') # SUPPRESS:OK -- read-default: file may not exist yet
|
|
597
|
+
|
|
598
|
+
# Build new decision entry
|
|
599
|
+
local pd_phase_val
|
|
600
|
+
if [[ "$pd_phase" == "null" ]]; then
|
|
601
|
+
pd_phase_val="null"
|
|
602
|
+
else
|
|
603
|
+
pd_phase_val="$pd_phase"
|
|
604
|
+
fi
|
|
605
|
+
|
|
606
|
+
local pd_updated
|
|
607
|
+
pd_updated=$(echo "$pd_current" | jq \
|
|
608
|
+
--arg id "$pd_id" \
|
|
609
|
+
--arg type "$pd_type" \
|
|
610
|
+
--arg description "$pd_description" \
|
|
611
|
+
--argjson phase "${pd_phase_val}" \
|
|
612
|
+
--arg source "$pd_source" \
|
|
613
|
+
--arg created_at "$pd_now" \
|
|
614
|
+
'.decisions += [{
|
|
615
|
+
id: $id,
|
|
616
|
+
type: $type,
|
|
617
|
+
description: $description,
|
|
618
|
+
phase: $phase,
|
|
619
|
+
source: $source,
|
|
620
|
+
created_at: $created_at,
|
|
621
|
+
resolved: false
|
|
622
|
+
}]' 2>/dev/null) || {
|
|
623
|
+
type release_lock &>/dev/null && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
624
|
+
json_err "$E_JSON_INVALID" "Failed to append decision to pending-decisions.json"
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
atomic_write "$pd_file" "$pd_updated" || {
|
|
628
|
+
type release_lock &>/dev/null && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
629
|
+
json_err "$E_JSON_INVALID" "Failed to write pending-decisions.json"
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
type release_lock &>/dev/null && { release_lock 2>/dev/null || true; trap - EXIT; } # SUPPRESS:OK -- cleanup: lock may not be held
|
|
633
|
+
|
|
634
|
+
local pd_count
|
|
635
|
+
pd_count=$(echo "$pd_updated" | jq '.decisions | length')
|
|
636
|
+
|
|
637
|
+
json_ok "$(jq -n --arg id "$pd_id" --argjson count "$pd_count" \
|
|
638
|
+
'{id: $id, decision_count: $count}')"
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
# ============================================================================
|
|
642
|
+
# _pending_decision_list
|
|
643
|
+
# List decisions from the pending decisions queue
|
|
644
|
+
# Usage: pending-decision-list [--unresolved] [--type <type>]
|
|
645
|
+
# Default: show only unresolved
|
|
646
|
+
# ============================================================================
|
|
647
|
+
_pending_decision_list() {
|
|
648
|
+
local pd_unresolved_only="true"
|
|
649
|
+
local pd_filter_type=""
|
|
650
|
+
|
|
651
|
+
while [[ $# -gt 0 ]]; do
|
|
652
|
+
case "$1" in
|
|
653
|
+
--unresolved) pd_unresolved_only="true"; shift ;;
|
|
654
|
+
--type) pd_filter_type="$2"; shift 2 ;;
|
|
655
|
+
*) shift ;;
|
|
656
|
+
esac
|
|
657
|
+
done
|
|
658
|
+
|
|
659
|
+
local pd_file="$COLONY_DATA_DIR/pending-decisions.json"
|
|
660
|
+
|
|
661
|
+
if [[ ! -f "$pd_file" ]]; then
|
|
662
|
+
json_ok '{"total":0,"unresolved":0,"decisions":[]}'
|
|
663
|
+
exit 0
|
|
664
|
+
fi
|
|
665
|
+
|
|
666
|
+
local pd_data
|
|
667
|
+
pd_data=$(cat "$pd_file" 2>/dev/null || echo '{"version":"1.0","decisions":[]}') # SUPPRESS:OK -- read-default: file may not exist yet
|
|
668
|
+
|
|
669
|
+
# Build jq filter
|
|
670
|
+
local pd_filter='.decisions'
|
|
671
|
+
|
|
672
|
+
# Apply type filter if provided
|
|
673
|
+
if [[ -n "$pd_filter_type" ]]; then
|
|
674
|
+
pd_filter="$pd_filter | map(select(.type == \"$pd_filter_type\"))"
|
|
675
|
+
fi
|
|
676
|
+
|
|
677
|
+
# Apply resolved filter (default: only unresolved)
|
|
678
|
+
if [[ "$pd_unresolved_only" == "true" ]]; then
|
|
679
|
+
pd_filter="$pd_filter | map(select(.resolved == false))"
|
|
680
|
+
fi
|
|
681
|
+
|
|
682
|
+
local pd_total pd_unresolved pd_decisions
|
|
683
|
+
pd_total=$(echo "$pd_data" | jq '.decisions | length')
|
|
684
|
+
pd_unresolved=$(echo "$pd_data" | jq '[.decisions[] | select(.resolved == false)] | length')
|
|
685
|
+
pd_decisions=$(echo "$pd_data" | jq "$pd_filter")
|
|
686
|
+
|
|
687
|
+
json_ok "$(jq -n --argjson total "$pd_total" --argjson unresolved "$pd_unresolved" \
|
|
688
|
+
--argjson decisions "$pd_decisions" \
|
|
689
|
+
'{total: $total, unresolved: $unresolved, decisions: $decisions}')"
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
# ============================================================================
|
|
693
|
+
# _pending_decision_resolve
|
|
694
|
+
# Mark a pending decision as resolved
|
|
695
|
+
# Usage: pending-decision-resolve --id <id> --resolution <text>
|
|
696
|
+
# ============================================================================
|
|
697
|
+
_pending_decision_resolve() {
|
|
698
|
+
local pd_id=""
|
|
699
|
+
local pd_resolution=""
|
|
700
|
+
|
|
701
|
+
while [[ $# -gt 0 ]]; do
|
|
702
|
+
case "$1" in
|
|
703
|
+
--id) pd_id="$2"; shift 2 ;;
|
|
704
|
+
--resolution) pd_resolution="$2"; shift 2 ;;
|
|
705
|
+
*) shift ;;
|
|
706
|
+
esac
|
|
707
|
+
done
|
|
708
|
+
|
|
709
|
+
[[ -z "$pd_id" ]] && json_err "$E_VALIDATION_FAILED" "pending-decision-resolve requires --id"
|
|
710
|
+
[[ -z "$pd_resolution" ]] && json_err "$E_VALIDATION_FAILED" "pending-decision-resolve requires --resolution"
|
|
711
|
+
|
|
712
|
+
local pd_file="$COLONY_DATA_DIR/pending-decisions.json"
|
|
713
|
+
|
|
714
|
+
if [[ ! -f "$pd_file" ]]; then
|
|
715
|
+
json_err "$E_RESOURCE_NOT_FOUND" "No pending decisions file found"
|
|
716
|
+
fi
|
|
717
|
+
|
|
718
|
+
# Acquire lock for concurrent access
|
|
719
|
+
if type acquire_lock &>/dev/null; then
|
|
720
|
+
acquire_lock "$pd_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on pending-decisions.json"
|
|
721
|
+
trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
|
|
722
|
+
fi
|
|
723
|
+
|
|
724
|
+
local pd_data
|
|
725
|
+
pd_data=$(cat "$pd_file" 2>/dev/null || echo '{"version":"1.0","decisions":[]}') # SUPPRESS:OK -- read-default: file may not exist yet
|
|
726
|
+
|
|
727
|
+
# Check if ID exists
|
|
728
|
+
local pd_exists
|
|
729
|
+
pd_exists=$(echo "$pd_data" | jq --arg id "$pd_id" '[.decisions[] | select(.id == $id)] | length')
|
|
730
|
+
if [[ "$pd_exists" -eq 0 ]]; then
|
|
731
|
+
type release_lock &>/dev/null && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
732
|
+
json_err "$E_RESOURCE_NOT_FOUND" "Decision not found: $pd_id"
|
|
733
|
+
fi
|
|
734
|
+
|
|
735
|
+
local pd_now
|
|
736
|
+
pd_now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
737
|
+
|
|
738
|
+
local pd_updated
|
|
739
|
+
pd_updated=$(echo "$pd_data" | jq \
|
|
740
|
+
--arg id "$pd_id" \
|
|
741
|
+
--arg resolution "$pd_resolution" \
|
|
742
|
+
--arg resolved_at "$pd_now" \
|
|
743
|
+
'(.decisions[] | select(.id == $id)) |= (. + {resolved: true, resolution: $resolution, resolved_at: $resolved_at})' 2>/dev/null) || {
|
|
744
|
+
type release_lock &>/dev/null && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
745
|
+
json_err "$E_JSON_INVALID" "Failed to resolve decision in pending-decisions.json"
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
atomic_write "$pd_file" "$pd_updated" || {
|
|
749
|
+
type release_lock &>/dev/null && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
750
|
+
json_err "$E_JSON_INVALID" "Failed to write pending-decisions.json"
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
type release_lock &>/dev/null && { release_lock 2>/dev/null || true; trap - EXIT; } # SUPPRESS:OK -- cleanup: lock may not be held
|
|
754
|
+
|
|
755
|
+
json_ok "$(jq -n --arg id "$pd_id" '{resolved: true, id: $id}')"
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
# ============================================================================
|
|
759
|
+
# _autopilot_headless_check
|
|
760
|
+
# Check whether headless mode is active in run-state.json
|
|
761
|
+
# Usage: autopilot-headless-check
|
|
762
|
+
# Returns: {"ok":true,"result":{"headless":true|false}}
|
|
763
|
+
# ============================================================================
|
|
764
|
+
_autopilot_headless_check() {
|
|
765
|
+
local ah_state_file="$COLONY_DATA_DIR/run-state.json"
|
|
766
|
+
|
|
767
|
+
if [[ ! -f "$ah_state_file" ]]; then
|
|
768
|
+
json_ok '{"headless":false}'
|
|
769
|
+
exit 0
|
|
770
|
+
fi
|
|
771
|
+
|
|
772
|
+
local ah_headless
|
|
773
|
+
ah_headless=$(jq -r '.headless // false' "$ah_state_file" 2>/dev/null || echo "false") # SUPPRESS:OK -- read-default: field may not exist
|
|
774
|
+
|
|
775
|
+
# Normalize to boolean
|
|
776
|
+
if [[ "$ah_headless" == "true" ]]; then
|
|
777
|
+
json_ok '{"headless":true}'
|
|
778
|
+
else
|
|
779
|
+
json_ok '{"headless":false}'
|
|
780
|
+
fi
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
# ============================================================================
|
|
784
|
+
# _autopilot_set_headless
|
|
785
|
+
# Set the headless flag in run-state.json
|
|
786
|
+
# Usage: autopilot-set-headless <true|false>
|
|
787
|
+
# Returns: {"ok":true,"result":{"headless":true|false,"updated":true}}
|
|
788
|
+
# ============================================================================
|
|
789
|
+
_autopilot_set_headless() {
|
|
790
|
+
local ah_value="${1:-}"
|
|
791
|
+
|
|
792
|
+
if [[ "$ah_value" != "true" && "$ah_value" != "false" ]]; then
|
|
793
|
+
json_err "$E_VALIDATION_FAILED" "autopilot-set-headless requires true or false argument"
|
|
794
|
+
fi
|
|
795
|
+
|
|
796
|
+
local ah_state_file="$COLONY_DATA_DIR/run-state.json"
|
|
797
|
+
|
|
798
|
+
if [[ ! -f "$ah_state_file" ]]; then
|
|
799
|
+
json_err "$E_FILE_NOT_FOUND" "run-state.json not found — autopilot not active"
|
|
800
|
+
fi
|
|
801
|
+
|
|
802
|
+
local ah_headless_bool
|
|
803
|
+
[[ "$ah_value" == "true" ]] && ah_headless_bool=true || ah_headless_bool=false
|
|
804
|
+
|
|
805
|
+
local ah_current ah_updated
|
|
806
|
+
ah_current=$(cat "$ah_state_file" 2>/dev/null || echo '{}') # SUPPRESS:OK -- read-default: file may not exist yet
|
|
807
|
+
ah_updated=$(echo "$ah_current" | jq --argjson headless "$ah_headless_bool" '.headless = $headless' 2>/dev/null) || {
|
|
808
|
+
json_err "$E_JSON_INVALID" "Failed to update headless flag in run-state.json"
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
atomic_write "$ah_state_file" "$ah_updated" || {
|
|
812
|
+
json_err "$E_JSON_INVALID" "Failed to write run-state.json"
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
json_ok "$(jq -n --argjson headless "$ah_headless_bool" '{headless: $headless, updated: true}')"
|
|
816
|
+
}
|
|
@@ -51,9 +51,9 @@ parse_spawn_tree() {
|
|
|
51
51
|
printf "\"spawns\":["
|
|
52
52
|
for (i = 0; i < n; i++) {
|
|
53
53
|
if (i > 0) printf ","
|
|
54
|
-
nm = names[i]; gsub(/\\/, "\\\\", nm); gsub(/"/, "\\\"", nm); gsub(/\t/, "\\t", nm)
|
|
55
|
-
pr = parents[i]; gsub(/\\/, "\\\\", pr); gsub(/"/, "\\\"", pr); gsub(/\t/, "\\t", pr)
|
|
56
|
-
tk = tasks[i]; gsub(/\\/, "\\\\", tk); gsub(/"/, "\\\"", tk); gsub(/\t/, "\\t", tk)
|
|
54
|
+
nm = names[i]; gsub(/\\/, "\\\\", nm); gsub(/"/, "\\\"", nm); gsub(/\t/, "\\t", nm); gsub(/\n/, "\\n", nm); gsub(/\r/, "\\r", nm)
|
|
55
|
+
pr = parents[i]; gsub(/\\/, "\\\\", pr); gsub(/"/, "\\\"", pr); gsub(/\t/, "\\t", pr); gsub(/\n/, "\\n", pr); gsub(/\r/, "\\r", pr)
|
|
56
|
+
tk = tasks[i]; gsub(/\\/, "\\\\", tk); gsub(/"/, "\\\"", tk); gsub(/\t/, "\\t", tk); gsub(/\n/, "\\n", tk); gsub(/\r/, "\\r", tk)
|
|
57
57
|
printf "{\"name\":\"%s\",\"parent\":\"%s\",\"caste\":\"%s\",", nm, pr, castes[i]
|
|
58
58
|
printf "\"task\":\"%s\",\"status\":\"%s\",", tk, statuses[i]
|
|
59
59
|
printf "\"spawned_at\":\"%s\",\"completed_at\":\"%s\",", timestamps[i], completed_at[i]
|
|
@@ -63,7 +63,7 @@ parse_spawn_tree() {
|
|
|
63
63
|
for (j = 1; j <= length(cidxs); j++) {
|
|
64
64
|
if (j > 1) printf ","
|
|
65
65
|
cn = names[cidxs[j]+0]
|
|
66
|
-
gsub(/\\/, "\\\\", cn); gsub(/"/, "\\\"", cn); gsub(/\t/, "\\t", cn)
|
|
66
|
+
gsub(/\\/, "\\\\", cn); gsub(/"/, "\\\"", cn); gsub(/\t/, "\\t", cn); gsub(/\n/, "\\n", cn); gsub(/\r/, "\\r", cn)
|
|
67
67
|
printf "\"%s\"", cn
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -149,9 +149,9 @@ get_active_spawns() {
|
|
|
149
149
|
if (!(spawn_names[i] in done_set)) {
|
|
150
150
|
if (!first) printf ","
|
|
151
151
|
first = 0
|
|
152
|
-
nm = spawn_names[i]; gsub(/\\/, "\\\\", nm); gsub(/"/, "\\\"", nm); gsub(/\t/, "\\t", nm)
|
|
153
|
-
pr = spawn_parents[i]; gsub(/\\/, "\\\\", pr); gsub(/"/, "\\\"", pr); gsub(/\t/, "\\t", pr)
|
|
154
|
-
tk = spawn_tasks[i]; gsub(/\\/, "\\\\", tk); gsub(/"/, "\\\"", tk); gsub(/\t/, "\\t", tk)
|
|
152
|
+
nm = spawn_names[i]; gsub(/\\/, "\\\\", nm); gsub(/"/, "\\\"", nm); gsub(/\t/, "\\t", nm); gsub(/\n/, "\\n", nm); gsub(/\r/, "\\r", nm)
|
|
153
|
+
pr = spawn_parents[i]; gsub(/\\/, "\\\\", pr); gsub(/"/, "\\\"", pr); gsub(/\t/, "\\t", pr); gsub(/\n/, "\\n", pr); gsub(/\r/, "\\r", pr)
|
|
154
|
+
tk = spawn_tasks[i]; gsub(/\\/, "\\\\", tk); gsub(/"/, "\\\"", tk); gsub(/\t/, "\\t", tk); gsub(/\n/, "\\n", tk); gsub(/\r/, "\\r", tk)
|
|
155
155
|
printf "{\"name\":\"%s\",\"caste\":\"%s\",\"parent\":\"%s\",\"task\":\"%s\",\"spawned_at\":\"%s\"}", nm, spawn_castes[i], pr, tk, spawn_ts[i]
|
|
156
156
|
}
|
|
157
157
|
}
|
package/.aether/utils/spawn.sh
CHANGED
|
@@ -25,7 +25,7 @@ _spawn_log() {
|
|
|
25
25
|
emoji=$(get_caste_emoji "$child_caste")
|
|
26
26
|
parent_emoji=$(get_caste_emoji "$parent_id")
|
|
27
27
|
# Log to activity log with spawn format, emojis, and model info
|
|
28
|
-
echo "[$ts] ⚡ SPAWN $parent_emoji $parent_id -> $emoji $child_name ($child_caste): $task_summary [model: $model]" >> "$COLONY_DATA_DIR/activity.log"
|
|
28
|
+
[[ "${AETHER_TESTING:-}" != "1" ]] && echo "[$ts] ⚡ SPAWN $parent_emoji $parent_id -> $emoji $child_name ($child_caste): $task_summary [model: $model]" >> "$COLONY_DATA_DIR/activity.log"
|
|
29
29
|
# Log to spawn tree file for visualization (NEW FORMAT: includes model field)
|
|
30
30
|
echo "$ts_full|$parent_id|$child_caste|$child_name|$task_summary|$model|$status" >> "$COLONY_DATA_DIR/spawn-tree.txt"
|
|
31
31
|
# Return emoji-formatted result for display (jq-safe: child_name may contain JSON-special chars)
|
|
@@ -46,7 +46,7 @@ _spawn_complete() {
|
|
|
46
46
|
status_icon="✅"
|
|
47
47
|
[[ "$status" == "failed" ]] && status_icon="❌"
|
|
48
48
|
[[ "$status" == "blocked" ]] && status_icon="🚫"
|
|
49
|
-
echo "[$ts] $status_icon $emoji $ant_name: $status${summary:+ - $summary}" >> "$COLONY_DATA_DIR/activity.log"
|
|
49
|
+
[[ "${AETHER_TESTING:-}" != "1" ]] && echo "[$ts] $status_icon $emoji $ant_name: $status${summary:+ - $summary}" >> "$COLONY_DATA_DIR/activity.log"
|
|
50
50
|
# Update spawn tree
|
|
51
51
|
echo "$ts_full|$ant_name|$status|$summary" >> "$COLONY_DATA_DIR/spawn-tree.txt"
|
|
52
52
|
# Log failed spawns to events array as pipe-delimited strings (matching template format)
|