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.
Files changed (185) hide show
  1. package/.aether/aether-utils.sh +157 -42
  2. package/.aether/agents/aether-ambassador.md +140 -0
  3. package/.aether/agents/aether-archaeologist.md +108 -0
  4. package/.aether/agents/aether-architect.md +133 -0
  5. package/.aether/agents/aether-auditor.md +144 -0
  6. package/.aether/agents/aether-builder.md +184 -0
  7. package/.aether/agents/aether-chaos.md +115 -0
  8. package/.aether/agents/aether-chronicler.md +122 -0
  9. package/.aether/agents/aether-gatekeeper.md +116 -0
  10. package/.aether/agents/aether-includer.md +117 -0
  11. package/.aether/agents/aether-keeper.md +177 -0
  12. package/.aether/agents/aether-measurer.md +128 -0
  13. package/.aether/agents/aether-oracle.md +137 -0
  14. package/.aether/agents/aether-probe.md +133 -0
  15. package/.aether/agents/aether-queen.md +286 -0
  16. package/.aether/agents/aether-route-setter.md +130 -0
  17. package/.aether/agents/aether-sage.md +106 -0
  18. package/.aether/agents/aether-scout.md +101 -0
  19. package/.aether/agents/aether-surveyor-disciplines.md +391 -0
  20. package/.aether/agents/aether-surveyor-nest.md +329 -0
  21. package/.aether/agents/aether-surveyor-pathogens.md +264 -0
  22. package/.aether/agents/aether-surveyor-provisions.md +334 -0
  23. package/.aether/agents/aether-tracker.md +137 -0
  24. package/.aether/agents/aether-watcher.md +174 -0
  25. package/.aether/agents/aether-weaver.md +130 -0
  26. package/.aether/commands/claude/archaeology.md +334 -0
  27. package/.aether/commands/claude/build.md +65 -0
  28. package/.aether/commands/claude/chaos.md +336 -0
  29. package/.aether/commands/claude/colonize.md +259 -0
  30. package/.aether/commands/claude/continue.md +60 -0
  31. package/.aether/commands/claude/council.md +507 -0
  32. package/.aether/commands/claude/data-clean.md +81 -0
  33. package/.aether/commands/claude/dream.md +268 -0
  34. package/.aether/commands/claude/entomb.md +498 -0
  35. package/.aether/commands/claude/export-signals.md +57 -0
  36. package/.aether/commands/claude/feedback.md +96 -0
  37. package/.aether/commands/claude/flag.md +151 -0
  38. package/.aether/commands/claude/flags.md +169 -0
  39. package/.aether/commands/claude/focus.md +76 -0
  40. package/.aether/commands/claude/help.md +154 -0
  41. package/.aether/commands/claude/history.md +140 -0
  42. package/.aether/commands/claude/import-signals.md +71 -0
  43. package/.aether/commands/claude/init.md +505 -0
  44. package/.aether/commands/claude/insert-phase.md +105 -0
  45. package/.aether/commands/claude/interpret.md +278 -0
  46. package/.aether/commands/claude/lay-eggs.md +210 -0
  47. package/.aether/commands/claude/maturity.md +113 -0
  48. package/.aether/commands/claude/memory-details.md +77 -0
  49. package/.aether/commands/claude/migrate-state.md +171 -0
  50. package/.aether/commands/claude/oracle.md +642 -0
  51. package/.aether/commands/claude/organize.md +232 -0
  52. package/.aether/commands/claude/patrol.md +620 -0
  53. package/.aether/commands/claude/pause-colony.md +233 -0
  54. package/.aether/commands/claude/phase.md +115 -0
  55. package/.aether/commands/claude/pheromones.md +156 -0
  56. package/.aether/commands/claude/plan.md +693 -0
  57. package/.aether/commands/claude/preferences.md +65 -0
  58. package/.aether/commands/claude/quick.md +100 -0
  59. package/.aether/commands/claude/redirect.md +76 -0
  60. package/.aether/commands/claude/resume-colony.md +197 -0
  61. package/.aether/commands/claude/resume.md +388 -0
  62. package/.aether/commands/claude/run.md +231 -0
  63. package/.aether/commands/claude/seal.md +774 -0
  64. package/.aether/commands/claude/skill-create.md +286 -0
  65. package/.aether/commands/claude/status.md +410 -0
  66. package/.aether/commands/claude/swarm.md +349 -0
  67. package/.aether/commands/claude/tunnels.md +426 -0
  68. package/.aether/commands/claude/update.md +132 -0
  69. package/.aether/commands/claude/verify-castes.md +143 -0
  70. package/.aether/commands/claude/watch.md +239 -0
  71. package/.aether/commands/colonize.yaml +4 -0
  72. package/.aether/commands/council.yaml +205 -0
  73. package/.aether/commands/init.yaml +46 -13
  74. package/.aether/commands/insert-phase.yaml +4 -0
  75. package/.aether/commands/opencode/archaeology.md +331 -0
  76. package/.aether/commands/opencode/build.md +1168 -0
  77. package/.aether/commands/opencode/chaos.md +329 -0
  78. package/.aether/commands/opencode/colonize.md +195 -0
  79. package/.aether/commands/opencode/continue.md +1436 -0
  80. package/.aether/commands/opencode/council.md +437 -0
  81. package/.aether/commands/opencode/data-clean.md +77 -0
  82. package/.aether/commands/opencode/dream.md +260 -0
  83. package/.aether/commands/opencode/entomb.md +377 -0
  84. package/.aether/commands/opencode/export-signals.md +54 -0
  85. package/.aether/commands/opencode/feedback.md +99 -0
  86. package/.aether/commands/opencode/flag.md +149 -0
  87. package/.aether/commands/opencode/flags.md +167 -0
  88. package/.aether/commands/opencode/focus.md +73 -0
  89. package/.aether/commands/opencode/help.md +157 -0
  90. package/.aether/commands/opencode/history.md +136 -0
  91. package/.aether/commands/opencode/import-signals.md +68 -0
  92. package/.aether/commands/opencode/init.md +518 -0
  93. package/.aether/commands/opencode/insert-phase.md +111 -0
  94. package/.aether/commands/opencode/interpret.md +272 -0
  95. package/.aether/commands/opencode/lay-eggs.md +213 -0
  96. package/.aether/commands/opencode/maturity.md +108 -0
  97. package/.aether/commands/opencode/memory-details.md +83 -0
  98. package/.aether/commands/opencode/migrate-state.md +165 -0
  99. package/.aether/commands/opencode/oracle.md +593 -0
  100. package/.aether/commands/opencode/organize.md +226 -0
  101. package/.aether/commands/opencode/patrol.md +626 -0
  102. package/.aether/commands/opencode/pause-colony.md +203 -0
  103. package/.aether/commands/opencode/phase.md +113 -0
  104. package/.aether/commands/opencode/pheromones.md +162 -0
  105. package/.aether/commands/opencode/plan.md +684 -0
  106. package/.aether/commands/opencode/preferences.md +71 -0
  107. package/.aether/commands/opencode/quick.md +91 -0
  108. package/.aether/commands/opencode/redirect.md +84 -0
  109. package/.aether/commands/opencode/resume-colony.md +190 -0
  110. package/.aether/commands/opencode/resume.md +394 -0
  111. package/.aether/commands/opencode/run.md +237 -0
  112. package/.aether/commands/opencode/seal.md +452 -0
  113. package/.aether/commands/opencode/skill-create.md +63 -0
  114. package/.aether/commands/opencode/status.md +307 -0
  115. package/.aether/commands/opencode/swarm.md +15 -0
  116. package/.aether/commands/opencode/tunnels.md +400 -0
  117. package/.aether/commands/opencode/update.md +127 -0
  118. package/.aether/commands/opencode/verify-castes.md +139 -0
  119. package/.aether/commands/opencode/watch.md +227 -0
  120. package/.aether/commands/plan.yaml +53 -2
  121. package/.aether/commands/quick.yaml +104 -0
  122. package/.aether/commands/resume-colony.yaml +6 -4
  123. package/.aether/commands/resume.yaml +9 -0
  124. package/.aether/commands/run.yaml +37 -1
  125. package/.aether/commands/seal.yaml +9 -0
  126. package/.aether/commands/status.yaml +45 -1
  127. package/.aether/docs/command-playbooks/build-full.md +3 -2
  128. package/.aether/docs/command-playbooks/build-prep.md +12 -4
  129. package/.aether/docs/command-playbooks/build-verify.md +51 -0
  130. package/.aether/docs/command-playbooks/continue-advance.md +115 -6
  131. package/.aether/docs/command-playbooks/continue-full.md +1 -0
  132. package/.aether/docs/command-playbooks/continue-verify.md +33 -0
  133. package/.aether/utils/clash-detect.sh +239 -0
  134. package/.aether/utils/council.sh +425 -0
  135. package/.aether/utils/error-handler.sh +3 -3
  136. package/.aether/utils/flag.sh +23 -12
  137. package/.aether/utils/hive.sh +2 -2
  138. package/.aether/utils/hooks/clash-pre-tool-use.js +99 -0
  139. package/.aether/utils/immune.sh +508 -0
  140. package/.aether/utils/learning.sh +2 -2
  141. package/.aether/utils/merge-driver-lockfile.sh +35 -0
  142. package/.aether/utils/midden.sh +712 -0
  143. package/.aether/utils/pheromone.sh +1376 -108
  144. package/.aether/utils/queen.sh +31 -21
  145. package/.aether/utils/session.sh +264 -0
  146. package/.aether/utils/spawn-tree.sh +7 -7
  147. package/.aether/utils/spawn.sh +2 -2
  148. package/.aether/utils/state-api.sh +216 -5
  149. package/.aether/utils/swarm.sh +1 -1
  150. package/.aether/utils/worktree.sh +189 -0
  151. package/.claude/commands/ant/colonize.md +2 -0
  152. package/.claude/commands/ant/council.md +205 -0
  153. package/.claude/commands/ant/init.md +53 -14
  154. package/.claude/commands/ant/insert-phase.md +4 -0
  155. package/.claude/commands/ant/plan.md +27 -1
  156. package/.claude/commands/ant/quick.md +100 -0
  157. package/.claude/commands/ant/resume-colony.md +3 -2
  158. package/.claude/commands/ant/resume.md +9 -0
  159. package/.claude/commands/ant/run.md +37 -1
  160. package/.claude/commands/ant/seal.md +9 -0
  161. package/.claude/commands/ant/status.md +45 -1
  162. package/.opencode/commands/ant/colonize.md +2 -0
  163. package/.opencode/commands/ant/council.md +143 -0
  164. package/.opencode/commands/ant/init.md +53 -13
  165. package/.opencode/commands/ant/insert-phase.md +4 -0
  166. package/.opencode/commands/ant/plan.md +26 -1
  167. package/.opencode/commands/ant/quick.md +91 -0
  168. package/.opencode/commands/ant/resume-colony.md +3 -2
  169. package/.opencode/commands/ant/resume.md +9 -0
  170. package/.opencode/commands/ant/run.md +37 -1
  171. package/.opencode/commands/ant/status.md +2 -0
  172. package/CHANGELOG.md +116 -0
  173. package/README.md +34 -8
  174. package/bin/cli.js +103 -61
  175. package/bin/lib/banner.js +14 -0
  176. package/bin/lib/init.js +8 -7
  177. package/bin/lib/interactive-setup.js +251 -0
  178. package/bin/npx-entry.js +21 -0
  179. package/bin/npx-install.js +9 -167
  180. package/bin/validate-package.sh +23 -0
  181. package/package.json +11 -3
  182. package/.aether/docs/plans/pheromone-display-plan.md +0 -257
  183. package/.aether/schemas/example-prompt-builder.xml +0 -234
  184. package/.aether/scripts/incident-test-add.sh +0 -47
  185. package/.aether/scripts/weekly-audit.sh +0 -79
@@ -1,6 +1,7 @@
1
1
  #!/bin/bash
2
2
  # State API facade -- centralized COLONY_STATE.json access
3
- # Provides: _state_read, _state_write, _state_read_field, _state_mutate, _state_migrate
3
+ # Provides: _state_read, _state_write, _state_read_field, _state_mutate, _state_migrate,
4
+ # _colony_vital_signs
4
5
  #
5
6
  # These functions are sourced by aether-utils.sh at startup.
6
7
  # All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
@@ -95,11 +96,32 @@ _state_write() {
95
96
 
96
97
  _state_mutate() {
97
98
  # Read-modify-write COLONY_STATE.json with a jq expression
98
- # Usage: state-mutate '<jq_expression>'
99
+ # Usage: state-mutate [--arg NAME VALUE] [--argjson NAME VALUE] '<jq_expression>'
100
+ # Supports jq --arg, --argjson, --slurpfile, --rawfile flags (forwarded to jq)
99
101
  # Acquires lock, creates backup, applies jq, validates, writes atomically
100
102
  # Returns: json_ok with mutated:true, or json_err on failure
101
103
 
102
- sm_expr="${1:-}"
104
+ # Parse jq flags (--arg, --argjson, --slurpfile, --rawfile) from arguments
105
+ # The jq expression is always the last argument (after all flags)
106
+ local sm_jq_flags=()
107
+ local sm_expr=""
108
+ local i=0
109
+ local args=("$@")
110
+
111
+ while [[ $i -lt ${#args[@]} ]]; do
112
+ case "${args[$i]}" in
113
+ --arg|--argjson|--slurpfile|--rawfile)
114
+ # Flag requires a name and value — consume next two args
115
+ sm_jq_flags+=("${args[$i]}" "${args[$((i+1))]}" "${args[$((i+2))]}")
116
+ i=$((i + 3))
117
+ ;;
118
+ *)
119
+ # Last argument is the jq expression
120
+ sm_expr="${args[$i]}"
121
+ i=$((i + 1))
122
+ ;;
123
+ esac
124
+ done
103
125
 
104
126
  if [[ -z "$sm_expr" ]]; then
105
127
  json_err "$E_VALIDATION_FAILED" "state-mutate requires a jq expression argument"
@@ -121,8 +143,8 @@ _state_mutate() {
121
143
  fi
122
144
  fi
123
145
 
124
- # Apply jq expression to current state
125
- sm_updated=$(jq "$sm_expr" "$sm_state_file" 2>/dev/null) || {
146
+ # Apply jq expression to current state (with forwarded flags)
147
+ sm_updated=$(jq ${sm_jq_flags[@]+"${sm_jq_flags[@]}"} "$sm_expr" "$sm_state_file" 2>/dev/null) || {
126
148
  release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
127
149
  json_err "$E_JSON_INVALID" "jq expression failed: $sm_expr"
128
150
  }
@@ -197,3 +219,192 @@ _state_migrate() {
197
219
  [[ "$sm_lock_held" == "true" ]] && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
198
220
  fi
199
221
  }
222
+
223
+ # ============================================================================
224
+ # _colony_vital_signs
225
+ # Compute colony health metrics from existing data files
226
+ # Usage: colony-vital-signs
227
+ # Returns: JSON with build_velocity, error_rate, signal_health, memory_pressure,
228
+ # colony_age_hours, and overall_health (0-100)
229
+ # Gracefully degrades: missing files produce zero/default values
230
+ # ============================================================================
231
+ _colony_vital_signs() {
232
+ local cvs_state_file="$COLONY_DATA_DIR/COLONY_STATE.json"
233
+ local cvs_midden_file="$COLONY_DATA_DIR/midden/midden.json"
234
+ local cvs_phero_file="$COLONY_DATA_DIR/pheromones.json"
235
+ local cvs_session_file="$COLONY_DATA_DIR/session.json"
236
+
237
+ # --- Compute 24h window boundary ---
238
+ local cvs_now
239
+ cvs_now=$(date -u +%s 2>/dev/null || echo "0")
240
+ local cvs_window_start=$(( cvs_now - 86400 ))
241
+
242
+ # ---- build_velocity: count phase_completed events in last 24h ----
243
+ local cvs_phases_per_day=0
244
+ if [[ -f "$cvs_state_file" ]]; then
245
+ cvs_phases_per_day=$(jq --argjson win "$cvs_window_start" '
246
+ [.events[]? |
247
+ select(. != null) |
248
+ select(test("\\|phase_completed\\|")) |
249
+ capture("^(?P<ts>[^|]+)\\|") |
250
+ .ts |
251
+ gsub("[TZ:-]"; " ") |
252
+ split(" ") |
253
+ if length >= 6 then
254
+ (.[0:6] | join(" ")) |
255
+ # convert to comparable string for ordering -- full ISO compare
256
+ . as $s | $s
257
+ else . end
258
+ ] | length
259
+ ' "$cvs_state_file" 2>/dev/null || echo "0")
260
+
261
+ # Simpler approach: use string comparison on ISO timestamps
262
+ # Compute the 24h-ago timestamp as ISO string
263
+ local cvs_cutoff_iso
264
+ cvs_cutoff_iso=$(date -u -r "$cvs_window_start" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
265
+ || date -u -d "@$cvs_window_start" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
266
+ || echo "")
267
+
268
+ if [[ -n "$cvs_cutoff_iso" ]]; then
269
+ cvs_phases_per_day=$(jq --arg cutoff "$cvs_cutoff_iso" '
270
+ [.events[]? |
271
+ select(. != null and (type == "string")) |
272
+ select(test("\\|phase_completed\\|")) |
273
+ split("|") | .[0] |
274
+ select(. >= $cutoff)
275
+ ] | length
276
+ ' "$cvs_state_file" 2>/dev/null || echo "0")
277
+ fi
278
+ fi
279
+ # Normalize: ensure integer
280
+ cvs_phases_per_day=$(( cvs_phases_per_day + 0 )) 2>/dev/null || cvs_phases_per_day=0
281
+
282
+ # Determine trend (simple heuristic: any builds = steady, 0 = idle)
283
+ local cvs_bv_trend="idle"
284
+ [[ "$cvs_phases_per_day" -ge 1 ]] && cvs_bv_trend="steady"
285
+ [[ "$cvs_phases_per_day" -ge 3 ]] && cvs_bv_trend="accelerating"
286
+
287
+ # ---- error_rate: unreviewed midden entries in last 24h ----
288
+ local cvs_errors_per_day=0
289
+ local cvs_err_status="clean"
290
+ if [[ -f "$cvs_midden_file" ]]; then
291
+ local cvs_cutoff_iso_err
292
+ cvs_cutoff_iso_err=$(date -u -r "$cvs_window_start" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
293
+ || date -u -d "@$cvs_window_start" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null \
294
+ || echo "")
295
+
296
+ if [[ -n "$cvs_cutoff_iso_err" ]]; then
297
+ cvs_errors_per_day=$(jq --arg cutoff "$cvs_cutoff_iso_err" '
298
+ [(.entries // [])[]? |
299
+ select(.reviewed == false or .reviewed == null) |
300
+ select((.timestamp // "") >= $cutoff)
301
+ ] | length
302
+ ' "$cvs_midden_file" 2>/dev/null || echo "0")
303
+ else
304
+ # Fallback: count all unreviewed
305
+ cvs_errors_per_day=$(jq '
306
+ [(.entries // [])[]? | select(.reviewed == false or .reviewed == null)] | length
307
+ ' "$cvs_midden_file" 2>/dev/null || echo "0")
308
+ fi
309
+ fi
310
+ cvs_errors_per_day=$(( cvs_errors_per_day + 0 )) 2>/dev/null || cvs_errors_per_day=0
311
+
312
+ if [[ "$cvs_errors_per_day" -eq 0 ]]; then
313
+ cvs_err_status="clean"
314
+ elif [[ "$cvs_errors_per_day" -le 2 ]]; then
315
+ cvs_err_status="nominal"
316
+ elif [[ "$cvs_errors_per_day" -le 5 ]]; then
317
+ cvs_err_status="elevated"
318
+ else
319
+ cvs_err_status="critical"
320
+ fi
321
+
322
+ # ---- signal_health: count active pheromones ----
323
+ local cvs_active_count=0
324
+ local cvs_sig_status="dormant"
325
+ if [[ -f "$cvs_phero_file" ]]; then
326
+ cvs_active_count=$(jq '
327
+ [.signals[]? | select(.active == true)] | length
328
+ ' "$cvs_phero_file" 2>/dev/null || echo "0")
329
+ fi
330
+ cvs_active_count=$(( cvs_active_count + 0 )) 2>/dev/null || cvs_active_count=0
331
+
332
+ if [[ "$cvs_active_count" -eq 0 ]]; then
333
+ cvs_sig_status="dormant"
334
+ elif [[ "$cvs_active_count" -le 3 ]]; then
335
+ cvs_sig_status="guided"
336
+ else
337
+ cvs_sig_status="active"
338
+ fi
339
+
340
+ # ---- memory_pressure: count instincts ----
341
+ local cvs_instinct_count=0
342
+ local cvs_mem_status="empty"
343
+ if [[ -f "$cvs_state_file" ]]; then
344
+ # instincts may be a JSON string (serialized array) or a real array
345
+ local cvs_raw_instincts
346
+ cvs_raw_instincts=$(jq -r '.memory.instincts // "[]"' "$cvs_state_file" 2>/dev/null || echo "[]")
347
+ # Handle both string-encoded and native array
348
+ cvs_instinct_count=$(echo "$cvs_raw_instincts" | jq -r 'if type == "string" then (. | fromjson | length) elif type == "array" then length else 0 end' 2>/dev/null || echo "0")
349
+ fi
350
+ cvs_instinct_count=$(( cvs_instinct_count + 0 )) 2>/dev/null || cvs_instinct_count=0
351
+
352
+ if [[ "$cvs_instinct_count" -eq 0 ]]; then
353
+ cvs_mem_status="empty"
354
+ elif [[ "$cvs_instinct_count" -le 5 ]]; then
355
+ cvs_mem_status="growing"
356
+ elif [[ "$cvs_instinct_count" -le 15 ]]; then
357
+ cvs_mem_status="healthy"
358
+ else
359
+ cvs_mem_status="rich"
360
+ fi
361
+
362
+ # ---- colony_age_hours: hours since initialized_at ----
363
+ local cvs_age_hours=0
364
+ if [[ -f "$cvs_state_file" ]]; then
365
+ local cvs_init_at
366
+ cvs_init_at=$(jq -r '.initialized_at // empty' "$cvs_state_file" 2>/dev/null || echo "")
367
+ if [[ -n "$cvs_init_at" ]]; then
368
+ local cvs_init_ts
369
+ cvs_init_ts=$(date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$cvs_init_at" '+%s' 2>/dev/null \
370
+ || date -u -d "$cvs_init_at" '+%s' 2>/dev/null \
371
+ || echo "0")
372
+ if [[ "$cvs_init_ts" -gt 0 && "$cvs_now" -gt "$cvs_init_ts" ]]; then
373
+ cvs_age_hours=$(( (cvs_now - cvs_init_ts) / 3600 ))
374
+ fi
375
+ fi
376
+ fi
377
+
378
+ # ---- overall_health: weighted 0-100 score ----
379
+ # Components (max points each):
380
+ # recent builds (+30): has at least one phase_completed in 24h
381
+ # low errors (+30): zero unreviewed errors in 24h
382
+ # signals exist (+20): at least one active pheromone
383
+ # instincts growing (+20): at least one instinct
384
+ local cvs_score=0
385
+ [[ "$cvs_phases_per_day" -ge 1 ]] && cvs_score=$(( cvs_score + 30 ))
386
+ [[ "$cvs_errors_per_day" -eq 0 ]] && cvs_score=$(( cvs_score + 30 ))
387
+ [[ "$cvs_active_count" -ge 1 ]] && cvs_score=$(( cvs_score + 20 ))
388
+ [[ "$cvs_instinct_count" -ge 1 ]] && cvs_score=$(( cvs_score + 20 ))
389
+ [[ "$cvs_score" -gt 100 ]] && cvs_score=100
390
+
391
+ json_ok "$(jq -n \
392
+ --argjson phases_per_day "$cvs_phases_per_day" \
393
+ --arg bv_trend "$cvs_bv_trend" \
394
+ --argjson errors_per_day "$cvs_errors_per_day" \
395
+ --arg err_status "$cvs_err_status" \
396
+ --argjson active_count "$cvs_active_count" \
397
+ --arg sig_status "$cvs_sig_status" \
398
+ --argjson instinct_count "$cvs_instinct_count" \
399
+ --arg mem_status "$cvs_mem_status" \
400
+ --argjson age_hours "$cvs_age_hours" \
401
+ --argjson overall_health "$cvs_score" \
402
+ '{
403
+ build_velocity: {phases_per_day: $phases_per_day, trend: $bv_trend},
404
+ error_rate: {errors_per_day: $errors_per_day, status: $err_status},
405
+ signal_health: {active_count: $active_count, status: $sig_status},
406
+ memory_pressure: {instinct_count: $instinct_count, status: $mem_status},
407
+ colony_age_hours: $age_hours,
408
+ overall_health: $overall_health
409
+ }')"
410
+ }
@@ -32,7 +32,7 @@ _autofix_checkpoint() {
32
32
  label="${1:-autofix-$(date +%s)}"
33
33
  stash_name="aether-checkpoint: $label"
34
34
  # Only stash Aether-managed directories, never touch user files
35
- if git stash push -m "$stash_name" -- $target_dirs >/dev/null 2>&1; then # SUPPRESS:OK -- existence-test: stash operation may fail
35
+ if git stash push -m "$stash_name" -- $target_dirs ":(exclude).aether/data/" >/dev/null 2>&1; then # SUPPRESS:OK -- existence-test: stash operation may fail
36
36
  json_ok "$(jq -n --arg ref "$stash_name" '{type: "stash", ref: $ref}')"
37
37
  else
38
38
  # Stash failed (possibly due to conflicts), record commit hash
@@ -0,0 +1,189 @@
1
+ #!/bin/bash
2
+ # Worktree utility functions -- extracted from aether-utils.sh
3
+ # Provides: _worktree_create, _worktree_cleanup
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
7
+ # release_lock, DATA_DIR, COLONY_DATA_DIR, SCRIPT_DIR, AETHER_ROOT, error
8
+ # constants) is available.
9
+
10
+ # Default worktree location relative to AETHER_ROOT
11
+ WORKTREE_BASE_DIR="${AETHER_ROOT}/.aether/worktrees"
12
+
13
+ # _worktree_create
14
+ # Creates a git worktree for an agent working on a specific task.
15
+ #
16
+ # Usage: _worktree_create --branch <branch-name> [--base <base-branch>] [--task-id <task-id>]
17
+ # Returns JSON: {ok:true, result:{path, branch, base, worktree_dir, task_id}}
18
+ _worktree_create() {
19
+ local branch=""
20
+ local base=""
21
+ local task_id=""
22
+
23
+ # Parse arguments
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --branch) branch="${2:-}"; shift 2 ;;
27
+ --base) base="${2:-}"; shift 2 ;;
28
+ --task-id) task_id="${2:-}"; shift 2 ;;
29
+ *) shift ;;
30
+ esac
31
+ done
32
+
33
+ # Validate required arguments
34
+ if [[ -z "$branch" ]]; then
35
+ json_err "$E_VALIDATION_FAILED" "Usage: worktree-create --branch <branch-name> [--base <base-branch>] [--task-id <task-id>]"
36
+ fi
37
+
38
+ # Sanitize branch name: reject obviously dangerous patterns
39
+ if [[ "$branch" == *..* ]] || [[ "$branch" == */* ]] || [[ "$branch" == *\\* ]]; then
40
+ json_err "$E_VALIDATION_FAILED" "Branch name must not contain '..', '/', or backslashes"
41
+ fi
42
+
43
+ # Default base to current branch
44
+ if [[ -z "$base" ]]; then
45
+ base=$(git -C "$AETHER_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
46
+ fi
47
+
48
+ local worktree_dir="$WORKTREE_BASE_DIR/$branch"
49
+
50
+ # Check if worktree already exists
51
+ if [[ -d "$worktree_dir" ]]; then
52
+ json_err "$E_VALIDATION_FAILED" "Worktree already exists for branch '$branch' at $worktree_dir"
53
+ fi
54
+
55
+ # Check if branch already exists as a git branch (would indicate duplicate)
56
+ if git -C "$AETHER_ROOT" show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
57
+ json_err "$E_VALIDATION_FAILED" "Branch '$branch' already exists"
58
+ fi
59
+
60
+ # Ensure base branch exists
61
+ if ! git -C "$AETHER_ROOT" show-ref --verify --quiet "refs/heads/$base" 2>/dev/null; then
62
+ json_err "$E_GIT_ERROR" "Base branch '$base' does not exist"
63
+ fi
64
+
65
+ # Ensure parent directory exists
66
+ mkdir -p "$WORKTREE_BASE_DIR"
67
+
68
+ # Create the worktree (git worktree add creates the branch automatically)
69
+ if ! git -C "$AETHER_ROOT" worktree add "$worktree_dir" -b "$branch" "$base" >/dev/null 2>&1; then
70
+ json_err "$E_GIT_ERROR" "Failed to create worktree for branch '$branch'"
71
+ fi
72
+
73
+ # Copy .aether/data/ structure to the new worktree so the agent has colony context
74
+ # Per state-contract-design.md, branch-local state lives in .aether/data/ (gitignored)
75
+ # and each worktree gets its own independent copy for colony context isolation.
76
+ if [[ -d "$AETHER_ROOT/.aether/data" ]]; then
77
+ mkdir -p "$worktree_dir/.aether/data"
78
+ cp -r "$AETHER_ROOT/.aether/data/." "$worktree_dir/.aether/data/" 2>/dev/null || true # SUPPRESS:OK -- copy: data dir may be empty
79
+ fi
80
+
81
+ # Copy exchange scripts so xml-utils.sh can source pheromone-xml.sh etc.
82
+ if [[ -d "$AETHER_ROOT/.aether/exchange" ]]; then
83
+ mkdir -p "$worktree_dir/.aether/exchange"
84
+ cp -r "$AETHER_ROOT/.aether/exchange/." "$worktree_dir/.aether/exchange/" 2>/dev/null || true
85
+ fi
86
+
87
+ # Inject main's pheromone signals into the worktree (per D-01)
88
+ # Non-blocking: if injection fails, worktree still works without injected signals
89
+ if [[ -f "$worktree_dir/.aether/data/pheromones.json" ]]; then
90
+ local main_head
91
+ main_head=$(git -C "$AETHER_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown")
92
+ (
93
+ cd "$worktree_dir" 2>/dev/null && \
94
+ DATA_DIR="$worktree_dir/.aether/data" \
95
+ COLONY_DATA_DIR="$worktree_dir/.aether/data" \
96
+ bash "$AETHER_ROOT/.aether/aether-utils.sh" \
97
+ pheromone-snapshot-inject --from-branch "$base" --from-commit "$main_head" \
98
+ >/dev/null 2>&1 || true
99
+ )
100
+ fi
101
+
102
+ # Build result JSON
103
+ local result
104
+ result=$(jq -n \
105
+ --arg path "$worktree_dir" \
106
+ --arg branch "$branch" \
107
+ --arg base "$base" \
108
+ --arg worktree_dir "$worktree_dir" \
109
+ --arg task_id "${task_id:-null}" \
110
+ '{
111
+ path: $path,
112
+ branch: $branch,
113
+ base: $base,
114
+ worktree_dir: $worktree_dir,
115
+ task_id: (if $task_id == "null" then null else $task_id end)
116
+ }')
117
+
118
+ json_ok "$result"
119
+ }
120
+
121
+ # _worktree_cleanup
122
+ # Removes a git worktree and cleans up tracking.
123
+ #
124
+ # Usage: _worktree_cleanup --branch <branch-name> [--force]
125
+ # Returns JSON: {ok:true, result:{removed, branch, path}}
126
+ _worktree_cleanup() {
127
+ local branch=""
128
+ local force=false
129
+
130
+ # Parse arguments
131
+ while [[ $# -gt 0 ]]; do
132
+ case "$1" in
133
+ --branch) branch="${2:-}"; shift 2 ;;
134
+ --force) force=true; shift ;;
135
+ *) shift ;;
136
+ esac
137
+ done
138
+
139
+ # Validate required arguments
140
+ if [[ -z "$branch" ]]; then
141
+ json_err "$E_VALIDATION_FAILED" "Usage: worktree-cleanup --branch <branch-name> [--force]"
142
+ fi
143
+
144
+ # Sanitize branch name
145
+ if [[ "$branch" == *..* ]] || [[ "$branch" == */* ]] || [[ "$branch" == *\\* ]]; then
146
+ json_err "$E_VALIDATION_FAILED" "Branch name must not contain '..', '/', or backslashes"
147
+ fi
148
+
149
+ local worktree_dir="$WORKTREE_BASE_DIR/$branch"
150
+
151
+ # Check if worktree exists
152
+ if [[ ! -d "$worktree_dir" ]]; then
153
+ json_err "$E_RESOURCE_NOT_FOUND" "No worktree found for branch '$branch'"
154
+ fi
155
+
156
+ # Check for uncommitted changes (unless --force)
157
+ # Exclude .aether/ files since they are branch-local state copies, not user changes
158
+ if [[ "$force" == "false" ]]; then
159
+ local dirty_count
160
+ dirty_count=$(git -C "$worktree_dir" status --porcelain 2>/dev/null \
161
+ | grep -v '\.aether/' \
162
+ | wc -l \
163
+ | tr -d ' ') || dirty_count=0
164
+
165
+ if [[ "$dirty_count" -gt 0 ]]; then
166
+ json_err "$E_VALIDATION_FAILED" "Worktree '$branch' has $dirty_count uncommitted changes. Use --force to remove anyway."
167
+ fi
168
+ fi
169
+
170
+ # Remove the worktree using git worktree remove
171
+ if ! git -C "$AETHER_ROOT" worktree remove "$worktree_dir" --force 2>/dev/null; then
172
+ # Fallback: manual cleanup if git worktree remove fails
173
+ rm -rf "$worktree_dir" 2>/dev/null || true
174
+ # Also prune stale worktree entries
175
+ git -C "$AETHER_ROOT" worktree prune 2>/dev/null || true
176
+ fi
177
+
178
+ # Attempt to delete the branch (best-effort -- may fail if branch is checked out elsewhere)
179
+ git -C "$AETHER_ROOT" branch -D "$branch" >/dev/null 2>&1 || true
180
+
181
+ # Build result JSON
182
+ local result
183
+ result=$(jq -n \
184
+ --arg branch "$branch" \
185
+ --arg path "$worktree_dir" \
186
+ '{removed: true, branch: $branch, path: $path}')
187
+
188
+ json_ok "$result"
189
+ }
@@ -69,6 +69,8 @@ Read `.aether/data/COLONY_STATE.json`.
69
69
 
70
70
  **If the file exists:** continue.
71
71
 
72
+ **If `milestone` == `"Crowned Anthill"`:** output "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`.", stop.
73
+
72
74
  **If `plan.phases` is not empty:** output "Colony already has phases. Use /ant:continue.", stop.
73
75
 
74
76
  ### Step 2: Quick Surface Scan (for session context)
@@ -9,9 +9,208 @@ You are the **Queen Ant Colony**. Convene the council to clarify user intent and
9
9
  ## Instructions
10
10
 
11
11
  Parse `$ARGUMENTS`:
12
+ - If starts with `--deliberate`: set `deliberate_mode = true`, extract proposal text after `--deliberate`
12
13
  - If contains `--no-visual`: set `visual_mode = false` (visual is ON by default)
13
14
  - Otherwise: set `visual_mode = true`
14
15
 
16
+ **If `deliberate_mode` is true:** Skip to [Deliberation Mode](#deliberation-mode) and stop after it.
17
+
18
+ ---
19
+
20
+ ## Deliberation Mode
21
+
22
+ When `--deliberate "<proposal>"` is passed, run a structured Advocate/Challenger/Sage deliberation.
23
+
24
+ ### Step D1: Check Budget
25
+
26
+ Run using the Bash tool with description "Checking deliberation budget...":
27
+ ```bash
28
+ bash .aether/aether-utils.sh council-budget-check
29
+ ```
30
+
31
+ Parse result. If `allowed` is `false`:
32
+ ```
33
+ 📜🐜🏛️🐜📜 COUNCIL — BUDGET EXHAUSTED
34
+
35
+ Spawn budget is fully allocated. Complete or close existing deliberations before starting new ones.
36
+ Run /ant:council to review existing deliberations.
37
+ ```
38
+ Stop here.
39
+
40
+ ### Step D2: Open Deliberation
41
+
42
+ Run using the Bash tool with description "Opening deliberation...":
43
+ ```bash
44
+ bash .aether/aether-utils.sh council-deliberate --proposal "<proposal>"
45
+ ```
46
+
47
+ Capture `deliberation_id` from `result.id`.
48
+
49
+ Display:
50
+ ```
51
+ 📜🐜🏛️🐜📜 COUNCIL DELIBERATION OPENED
52
+
53
+ Proposal: "<proposal>"
54
+ ID: {deliberation_id}
55
+ Budget: {budget} spawns available
56
+
57
+ Convening Advocate, Challenger, and Sage...
58
+ ```
59
+
60
+ ### Step D3: Spawn Advocate Scout
61
+
62
+ Spawn an Advocate scout to argue **in favor** of the proposal.
63
+
64
+
65
+ Use the Task tool to spawn a scout with the following system prompt:
66
+ ```
67
+ You are the Advocate on the Ant Colony council.
68
+ Your role is to argue STRONGLY IN FAVOR of the proposal.
69
+ Present the strongest possible case for why this proposal should be adopted.
70
+ Be specific. Focus on concrete benefits and outcomes.
71
+ Keep your argument under 200 words.
72
+ Reply with ONLY your argument text — no preamble or meta-commentary.
73
+ ```
74
+
75
+ And user message:
76
+ ```
77
+ Proposal: "<proposal>"
78
+
79
+ Present your strongest argument in favor of this proposal.
80
+ ```
81
+
82
+ Capture the advocate's argument as `advocate_argument`.
83
+
84
+
85
+ Record the argument:
86
+ ```bash
87
+ bash .aether/aether-utils.sh council-advocate \
88
+ --deliberation-id "<deliberation_id>" \
89
+ --argument "<advocate_argument>"
90
+ ```
91
+
92
+ Display:
93
+ ```
94
+ ⚖️ ADVOCATE:
95
+ {advocate_argument}
96
+ ```
97
+
98
+ ### Step D4: Spawn Challenger Scout
99
+
100
+ Spawn a Challenger scout to argue **against** the proposal.
101
+
102
+
103
+ Use the Task tool to spawn a scout with the following system prompt:
104
+ ```
105
+ You are the Challenger on the Ant Colony council.
106
+ Your role is to argue STRONGLY AGAINST the proposal.
107
+ Present the strongest possible case for why this proposal should be rejected or deferred.
108
+ Be specific. Focus on concrete risks, costs, and downsides.
109
+ Keep your argument under 200 words.
110
+ Reply with ONLY your argument text — no preamble or meta-commentary.
111
+ ```
112
+
113
+ And user message:
114
+ ```
115
+ Proposal: "<proposal>"
116
+
117
+ Present your strongest argument against this proposal.
118
+ ```
119
+
120
+ Capture the challenger's argument as `challenger_argument`.
121
+
122
+
123
+ Record the argument:
124
+ ```bash
125
+ bash .aether/aether-utils.sh council-challenger \
126
+ --deliberation-id "<deliberation_id>" \
127
+ --argument "<challenger_argument>"
128
+ ```
129
+
130
+ Display:
131
+ ```
132
+ ⚔️ CHALLENGER:
133
+ {challenger_argument}
134
+ ```
135
+
136
+ ### Step D5: Spawn Sage Scout
137
+
138
+ Spawn a Sage scout to synthesize both positions and provide a recommendation.
139
+
140
+
141
+ Use the Task tool to spawn a scout with the following system prompt:
142
+ ```
143
+ You are the Sage on the Ant Colony council.
144
+ You have heard both the Advocate and Challenger arguments.
145
+ Your role is to synthesize both positions into balanced wisdom and provide a clear recommendation.
146
+ Your recommendation must be one of: adopt, reject, defer, or adopt-with-conditions.
147
+ Keep your synthesis under 150 words.
148
+ Reply with JSON only:
149
+ {"synthesis": "<your balanced synthesis>", "recommendation": "adopt|reject|defer|adopt-with-conditions"}
150
+ ```
151
+
152
+ And user message:
153
+ ```
154
+ Proposal: "<proposal>"
155
+
156
+ Advocate argued: "<advocate_argument>"
157
+
158
+ Challenger argued: "<challenger_argument>"
159
+
160
+ Synthesize both positions and provide your recommendation.
161
+ ```
162
+
163
+ Parse the JSON response. Capture `synthesis` and `recommendation`.
164
+
165
+
166
+ Record the sage synthesis:
167
+ ```bash
168
+ bash .aether/aether-utils.sh council-sage \
169
+ --deliberation-id "<deliberation_id>" \
170
+ --synthesis "<synthesis>" \
171
+ --recommendation "<recommendation>"
172
+ ```
173
+
174
+ ### Step D6: Display Result
175
+
176
+ ```
177
+ 📜🐜🏛️🐜📜 COUNCIL DELIBERATION COMPLETE
178
+
179
+ Proposal: "<proposal>"
180
+
181
+ ⚖️ Advocate:
182
+ {advocate_argument}
183
+
184
+ ⚔️ Challenger:
185
+ {challenger_argument}
186
+
187
+ 🧙 Sage Synthesis:
188
+ {synthesis}
189
+
190
+ Recommendation: {recommendation}
191
+ ```
192
+
193
+ If recommendation is `adopt` or `adopt-with-conditions`:
194
+ ```
195
+ ✅ Council recommends proceeding.
196
+ Run /ant:focus "<proposal summary>" to inject guidance for the colony.
197
+ ```
198
+
199
+ If recommendation is `reject`:
200
+ ```
201
+ ❌ Council recommends against this proposal.
202
+ Run /ant:redirect "<proposal summary>" if you want to make this a hard constraint.
203
+ ```
204
+
205
+ If recommendation is `defer`:
206
+ ```
207
+ ⏸️ Council recommends deferring this decision.
208
+ Return when more context is available.
209
+ ```
210
+
211
+ ---
212
+
213
+
15
214
 
16
215
  ### Step 0: Initialize Visual Mode (if enabled)
17
216
 
@@ -29,6 +228,12 @@ No colony initialized. Run /ant:init first.
29
228
  ```
30
229
  Stop here.
31
230
 
231
+ If `milestone` == `"Crowned Anthill"`:
232
+ ```
233
+ This colony has been sealed. Start a new colony with /ant:init "new goal".
234
+ ```
235
+ Stop here.
236
+
32
237
  Capture the current state for context:
33
238
  - `prior_state` = state field value (READY, EXECUTING, PLANNING, etc.)
34
239
  - `current_phase` = current_phase field value