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
|
@@ -98,7 +98,8 @@ If the command fails (non-zero exit or JSON has ok: false):
|
|
|
98
98
|
If successful:
|
|
99
99
|
1. Parse the state JSON from result field
|
|
100
100
|
2. Check if goal is null - if so: "No colony initialized. Run /ant:init first." and stop
|
|
101
|
-
3.
|
|
101
|
+
3. Check if `milestone` == `"Crowned Anthill"` - if so: "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop
|
|
102
|
+
4. Extract current_phase and phase name from plan.phases[current_phase - 1].name
|
|
102
103
|
4. Display brief resumption context:
|
|
103
104
|
```
|
|
104
105
|
🔄 Resuming: Phase X - Name
|
|
@@ -225,7 +226,7 @@ Parse the JSON result (`.result.blockers`):
|
|
|
225
226
|
|
|
226
227
|
### Step 2: Update State
|
|
227
228
|
|
|
228
|
-
|
|
229
|
+
Update `.aether/data/COLONY_STATE.json` using state-mutate (atomic, locked, validated):
|
|
229
230
|
- Set `state` to `"EXECUTING"`
|
|
230
231
|
- Set `current_phase` to the phase number
|
|
231
232
|
- Set the phase's `status` to `"in_progress"` in `plan.phases[N]`
|
|
@@ -234,7 +235,14 @@ Read then update `.aether/data/COLONY_STATE.json`:
|
|
|
234
235
|
|
|
235
236
|
If `events` exceeds 100 entries, keep only the last 100.
|
|
236
237
|
|
|
237
|
-
|
|
238
|
+
Run using the Bash tool with description "Updating colony state for build...":
|
|
239
|
+
```bash
|
|
240
|
+
bash .aether/aether-utils.sh state-mutate \
|
|
241
|
+
--argjson phase "$PHASE_NUMBER" \
|
|
242
|
+
--arg phase_name "$PHASE_NAME" \
|
|
243
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
244
|
+
'.state = "EXECUTING" | .current_phase = $phase | .plan.phases |= map(if .id == $phase then .status = "in_progress" else . end) | .build_started_at = $timestamp | .events = ((.events[-99:]) + [$timestamp + "|phase_started|build|Phase " + ($phase|tostring) + ": " + $phase_name + " started"])'
|
|
245
|
+
```
|
|
238
246
|
|
|
239
247
|
Validate the state file:
|
|
240
248
|
Run using the Bash tool with description "Validating colony state...":
|
|
@@ -253,7 +261,7 @@ git rev-parse --git-dir 2>/dev/null
|
|
|
253
261
|
|
|
254
262
|
- **If succeeds** (is a git repo):
|
|
255
263
|
1. Check for changes in Aether-managed directories only: `.aether .claude/commands/ant .claude/commands/st .opencode bin`
|
|
256
|
-
2. **If changes exist**: Run using the Bash tool with description "Creating git checkpoint...": `git stash push -m "aether-checkpoint: pre-phase-$PHASE_NUMBER" -- .aether .claude/commands/ant .claude/commands/st .opencode bin`
|
|
264
|
+
2. **If changes exist**: Run using the Bash tool with description "Creating git checkpoint...": `git stash push -m "aether-checkpoint: pre-phase-$PHASE_NUMBER" -- .aether .claude/commands/ant .claude/commands/st .opencode bin ":(exclude).aether/data/"`
|
|
257
265
|
- IMPORTANT: Never use `--include-untracked` — it stashes ALL files including user work!
|
|
258
266
|
- Run using the Bash tool with description "Verifying checkpoint...": `git stash list | head -1 | grep "aether-checkpoint"` — warn if empty
|
|
259
267
|
- Store checkpoint as `{type: "stash", ref: "aether-checkpoint: pre-phase-$PHASE_NUMBER"}`
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
### Step 5.3.5: Generate Compact Review Context for Review Cycles
|
|
2
|
+
|
|
3
|
+
Before spawning review agents, generate compact colony context for CI/autopilot agent review cycles.
|
|
4
|
+
|
|
5
|
+
Run using the Bash tool with description "Generating compact review context...":
|
|
6
|
+
```bash
|
|
7
|
+
REVIEW_CONTEXT=$(bash "$AETHER_UTILS" pr-context --compact 2>/dev/null) || true
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- If empty, continue without review context (non-blocking)
|
|
11
|
+
- Provides compact colony context for CI/autopilot agent review cycles
|
|
12
|
+
- Output is available for Watcher and subsequent review agents but does not gate them
|
|
13
|
+
|
|
1
14
|
### Step 5.4: Spawn Watcher for Verification
|
|
2
15
|
|
|
3
16
|
**MANDATORY: Always spawn a Watcher — testing must be independent.**
|
|
@@ -403,3 +416,41 @@ bash .aether/aether-utils.sh memory-capture \
|
|
|
403
416
|
```
|
|
404
417
|
|
|
405
418
|
This ensures verification failures are persisted as blockers that survive context resets. Chaos Ant findings are flagged in Step 5.7.
|
|
419
|
+
|
|
420
|
+
### Step 5.9: Midden Collection for Merged Branches (NON-BLOCKING)
|
|
421
|
+
|
|
422
|
+
**Per D-04: Wire midden-collect into /ant:run flow (build-verify phase).**
|
|
423
|
+
|
|
424
|
+
If this build is running on main after a merge (detected via COLONY_STATE or git log), attempt midden collection:
|
|
425
|
+
|
|
426
|
+
Run using the Bash tool with description "Collecting midden from merged branch...":
|
|
427
|
+
```bash
|
|
428
|
+
# Only runs if merge context is available
|
|
429
|
+
if [[ -n "${last_merged_branch:-}" && -n "${last_merge_sha:-}" ]]; then
|
|
430
|
+
collect_result=$(bash .aether/aether-utils.sh midden-collect \
|
|
431
|
+
--branch "$last_merged_branch" --merge-sha "$last_merge_sha" \
|
|
432
|
+
2>/dev/null || echo '{"ok":false}')
|
|
433
|
+
collect_ok=$(echo "$collect_result" | jq -r '.ok // false' 2>/dev/null)
|
|
434
|
+
if [[ "$collect_ok" == "true" ]]; then
|
|
435
|
+
collect_status=$(echo "$collect_result" | jq -r '.result.status // "unknown"' 2>/dev/null)
|
|
436
|
+
new_entries=$(echo "$collect_result" | jq -r '.result.entries_collected // 0' 2>/dev/null)
|
|
437
|
+
if [[ "$collect_status" == "collected" && "$new_entries" -gt 0 ]]; then
|
|
438
|
+
echo "Midden: collected $new_entries entries from $last_merged_branch"
|
|
439
|
+
fi
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
# Run cross-PR analysis after collection (per D-05)
|
|
443
|
+
analysis_result=$(bash .aether/aether-utils.sh midden-cross-pr-analysis --window 14 \
|
|
444
|
+
2>/dev/null || echo '{"ok":false}')
|
|
445
|
+
analysis_ok=$(echo "$analysis_result" | jq -r '.ok // false' 2>/dev/null)
|
|
446
|
+
if [[ "$analysis_ok" == "true" ]]; then
|
|
447
|
+
systemic=$(echo "$analysis_result" | jq -r '.result.systemic_categories // [] | length' 2>/dev/null || echo "0")
|
|
448
|
+
if [[ "$systemic" -gt 0 ]]; then
|
|
449
|
+
categories=$(echo "$analysis_result" | jq -r '.result.systemic_categories | join(", ")' 2>/dev/null)
|
|
450
|
+
echo "Midden: cross-PR systemic: $categories"
|
|
451
|
+
fi
|
|
452
|
+
fi
|
|
453
|
+
fi
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
This step is NON-BLOCKING -- build verification proceeds regardless of collection or analysis outcome.
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
### Step 2.0.7: Generate Review Context (pr-context)
|
|
2
|
+
|
|
3
|
+
After verification passes, generate structured colony context for the review and learning steps that follow.
|
|
4
|
+
|
|
5
|
+
Run using the Bash tool with description "Generating review context...":
|
|
6
|
+
```bash
|
|
7
|
+
REVIEW_CONTEXT=$(bash "$AETHER_UTILS" pr-context 2>/dev/null) || true
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- If REVIEW_CONTEXT is empty or fails, continue without it (non-blocking)
|
|
11
|
+
- This provides structured colony context for review/learning steps
|
|
12
|
+
- Output is available for subsequent steps but does not gate them
|
|
13
|
+
|
|
1
14
|
### Step 2: Update State
|
|
2
15
|
|
|
3
16
|
Find current phase in `plan.phases`.
|
|
@@ -285,20 +298,116 @@ Update COLONY_STATE.json:
|
|
|
285
298
|
- Keep max 30 instincts (remove lowest confidence)
|
|
286
299
|
- Keep max 100 events
|
|
287
300
|
|
|
288
|
-
Write the updated state through
|
|
301
|
+
Write the updated state through targeted `state-mutate` calls. Each call acquires a lock, creates a backup, applies the jq expression, validates, and writes atomically. Do NOT use the Write tool or `state-write` with full JSON content — always use `state-mutate` to avoid stale-context corruption.
|
|
289
302
|
|
|
290
|
-
Run using the Bash tool with description "
|
|
303
|
+
Run using the Bash tool with description "Advancing colony state...":
|
|
291
304
|
```bash
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
305
|
+
# Mark current phase completed
|
|
306
|
+
bash .aether/aether-utils.sh state-mutate --argjson pid "$current_phase" \
|
|
307
|
+
'.plan.phases |= map(if .id == $pid then .status = "completed" else . end)'
|
|
308
|
+
|
|
309
|
+
# Append learning (if any — skip if no learnings were extracted in Step 2)
|
|
310
|
+
# bash .aether/aether-utils.sh state-mutate --argjson learning "$learning_json" \
|
|
311
|
+
# '.memory.phase_learnings += [$learning]'
|
|
312
|
+
|
|
313
|
+
# Advance to next phase
|
|
314
|
+
bash .aether/aether-utils.sh state-mutate \
|
|
315
|
+
--argjson pid "$current_phase" \
|
|
316
|
+
--argjson next "$next_phase" \
|
|
317
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
318
|
+
'.current_phase = $next | .state = "READY" | .build_started_at = null | .events += [$timestamp + "|phase_advanced|continue|Completed Phase " + ($pid|tostring) + ", advancing to Phase " + ($next|tostring)]'
|
|
295
319
|
```
|
|
296
320
|
|
|
297
|
-
|
|
321
|
+
Run using the Bash tool with description "Enforcing memory caps...":
|
|
322
|
+
```bash
|
|
323
|
+
# Cap enforcement — keep arrays bounded
|
|
324
|
+
bash .aether/aether-utils.sh state-mutate \
|
|
325
|
+
'.memory.phase_learnings = (.memory.phase_learnings[-20:]) | .memory.decisions = (.memory.decisions[-30:]) | .memory.instincts = (.memory.instincts | sort_by(.confidence) | .[-30:]) | .events = (.events[-100:])'
|
|
326
|
+
```
|
|
298
327
|
|
|
299
328
|
Validate the state file:
|
|
300
329
|
Run using the Bash tool with description "Validating colony state...": `bash .aether/aether-utils.sh validate-state colony`
|
|
301
330
|
|
|
331
|
+
### Step 2.0.5: Pheromone Merge-Back (SILENT, NON-BLOCKING)
|
|
332
|
+
|
|
333
|
+
If a `pheromone-branch-export.json` exists in `.aether/exchange/` (written by seal ceremony on a PR branch and merged to main), run merge-back to collect branch-discovered signals into main's pheromone store. This entire step is silent and non-blocking -- continue proceeds even if merge-back fails.
|
|
334
|
+
|
|
335
|
+
Run using the Bash tool with description "Checking for pheromone merge-back file...":
|
|
336
|
+
```bash
|
|
337
|
+
# Check if a branch pheromone export was merged into main
|
|
338
|
+
export_file=".aether/exchange/pheromone-branch-export.json"
|
|
339
|
+
if [[ -f "$export_file" ]]; then
|
|
340
|
+
merge_result=$(bash .aether/aether-utils.sh pheromone-merge-back --export-file "$export_file" 2>/dev/null || echo '{"ok":false}')
|
|
341
|
+
merge_ok=$(echo "$merge_result" | jq -r '.ok // false' 2>/dev/null)
|
|
342
|
+
if [[ "$merge_ok" == "true" ]]; then
|
|
343
|
+
new_count=$(echo "$merge_result" | jq -r '.result.new_signals_written // 0' 2>/dev/null)
|
|
344
|
+
skipped=$(echo "$merge_result" | jq -r '.result.skipped_count // 0' 2>/dev/null)
|
|
345
|
+
conflicts=$(echo "$merge_result" | jq -r '.result.conflicts_resolved // [] | length' 2>/dev/null)
|
|
346
|
+
if [[ "$new_count" -gt 0 || "$conflicts" -gt 0 ]]; then
|
|
347
|
+
echo "Pheromone merge-back: $new_count new, $conflicts conflicts resolved, $skipped skipped (from merged branch)"
|
|
348
|
+
fi
|
|
349
|
+
# Clean up export file after successful merge
|
|
350
|
+
rm -f "$export_file" 2>/dev/null || true
|
|
351
|
+
else
|
|
352
|
+
echo "Pheromone merge-back: failed (non-blocking)"
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Step 2.0.6: Midden Collection (NON-BLOCKING)
|
|
358
|
+
|
|
359
|
+
After pheromone merge-back, collect failure records from any recently merged branch worktrees. This step is silent and non-blocking -- continue proceeds even if collection fails.
|
|
360
|
+
|
|
361
|
+
**Per D-04: Wire midden-collect into /ant:continue flow.**
|
|
362
|
+
|
|
363
|
+
If the colony uses a PR-based workflow and a merge just happened, attempt to collect the branch's midden entries:
|
|
364
|
+
|
|
365
|
+
Run using the Bash tool with description "Collecting branch midden entries...":
|
|
366
|
+
```bash
|
|
367
|
+
# Check if there's a recently merged branch to collect from
|
|
368
|
+
# The merge info comes from git log or COLONY_STATE context
|
|
369
|
+
last_merge_branch="${last_merged_branch:-}"
|
|
370
|
+
last_merge_sha="${last_merge_sha:-}"
|
|
371
|
+
|
|
372
|
+
if [[ -n "$last_merge_branch" && -n "$last_merge_sha" ]]; then
|
|
373
|
+
collect_result=$(bash .aether/aether-utils.sh midden-collect \
|
|
374
|
+
--branch "$last_merge_branch" --merge-sha "$last_merge_sha" \
|
|
375
|
+
2>/dev/null || echo '{"ok":false}')
|
|
376
|
+
collect_ok=$(echo "$collect_result" | jq -r '.ok // false' 2>/dev/null)
|
|
377
|
+
if [[ "$collect_ok" == "true" ]]; then
|
|
378
|
+
collect_status=$(echo "$collect_result" | jq -r '.result.status // "unknown"' 2>/dev/null)
|
|
379
|
+
if [[ "$collect_status" == "collected" ]]; then
|
|
380
|
+
new_entries=$(echo "$collect_result" | jq -r '.result.entries_collected // 0' 2>/dev/null)
|
|
381
|
+
echo "Midden: collected $new_entries failure entries from branch $last_merge_branch"
|
|
382
|
+
fi
|
|
383
|
+
fi
|
|
384
|
+
fi
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
This step is NON-BLOCKING -- continue proceeds regardless of collection outcome. If `last_merge_branch` and `last_merge_sha` are not set (e.g., no recent merge), this step is silently skipped.
|
|
388
|
+
|
|
389
|
+
### Step 2.0.7: Cross-PR Midden Analysis (NON-BLOCKING)
|
|
390
|
+
|
|
391
|
+
After midden collection (Step 2.0.6), run cross-PR analysis to detect systemic failure patterns across multiple merged branches. Auto-emits REDIRECT pheromones to hub if systemic patterns found.
|
|
392
|
+
|
|
393
|
+
**Per D-05: Wire midden-cross-pr-analysis into /ant:continue flow.**
|
|
394
|
+
|
|
395
|
+
Run using the Bash tool with description "Running cross-PR midden analysis...":
|
|
396
|
+
```bash
|
|
397
|
+
analysis_result=$(bash .aether/aether-utils.sh midden-cross-pr-analysis --window 14 \
|
|
398
|
+
2>/dev/null || echo '{"ok":false}')
|
|
399
|
+
analysis_ok=$(echo "$analysis_result" | jq -r '.ok // false' 2>/dev/null)
|
|
400
|
+
if [[ "$analysis_ok" == "true" ]]; then
|
|
401
|
+
systemic=$(echo "$analysis_result" | jq -r '.result.systemic_categories // [] | length' 2>/dev/null || echo "0")
|
|
402
|
+
if [[ "$systemic" -gt 0 ]]; then
|
|
403
|
+
categories=$(echo "$analysis_result" | jq -r '.result.systemic_categories | join(", ")' 2>/dev/null)
|
|
404
|
+
echo "Midden: cross-PR systemic pattern detected in: $categories (REDIRECT emitted)"
|
|
405
|
+
fi
|
|
406
|
+
fi
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
This step is NON-BLOCKING -- advance proceeds regardless of analysis outcome.
|
|
410
|
+
|
|
302
411
|
### Step 2.1: Auto-Emit Phase Pheromones (SILENT)
|
|
303
412
|
|
|
304
413
|
**This entire step produces NO user-visible output.** All pheromone operations run silently — learnings are deposited in the background. If any pheromone call fails, log the error and continue. Phase advancement must never fail due to pheromone errors.
|
|
@@ -26,6 +26,7 @@ Extract: `goal`, `state`, `current_phase`, `plan.phases`, `errors`, `memory`, `e
|
|
|
26
26
|
|
|
27
27
|
**Validation:**
|
|
28
28
|
- If `goal: null` -> output "No colony initialized. Run /ant:init first." and stop.
|
|
29
|
+
- If `milestone` == `"Crowned Anthill"` -> output "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop.
|
|
29
30
|
- If `plan.phases` is empty -> output "No project plan. Run /ant:plan first." and stop.
|
|
30
31
|
|
|
31
32
|
### Step 1.5: Load State and Show Resumption Context
|
|
@@ -26,6 +26,7 @@ Extract: `goal`, `state`, `current_phase`, `plan.phases`, `errors`, `memory`, `e
|
|
|
26
26
|
|
|
27
27
|
**Validation:**
|
|
28
28
|
- If `goal: null` -> output "No colony initialized. Run /ant:init first." and stop.
|
|
29
|
+
- If `milestone` == `"Crowned Anthill"` -> output "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop.
|
|
29
30
|
- If `plan.phases` is empty -> output "No project plan. Run /ant:plan first." and stop.
|
|
30
31
|
|
|
31
32
|
### Step 1.5: Load State and Show Resumption Context
|
|
@@ -404,3 +405,35 @@ Cross-reference worker claims against reality. This step catches fabricated succ
|
|
|
404
405
|
**CRITICAL:** Do NOT proceed to Step 1.6. Do NOT advance the phase. The verification failure is a hard block just like a test failure.
|
|
405
406
|
|
|
406
407
|
Continue to Step 1.6.
|
|
408
|
+
|
|
409
|
+
### Step 2.0.6: Midden Collection (NON-BLOCKING)
|
|
410
|
+
|
|
411
|
+
After verification passes, collect failure records from any recently merged branch worktrees. This step is silent and non-blocking -- continue proceeds even if collection fails.
|
|
412
|
+
|
|
413
|
+
**Per D-04: Wire midden-collect into /ant:continue flow.**
|
|
414
|
+
|
|
415
|
+
If the colony uses a PR-based workflow and a merge just happened, attempt to collect the branch's midden entries:
|
|
416
|
+
|
|
417
|
+
Run using the Bash tool with description "Collecting branch midden entries...":
|
|
418
|
+
```bash
|
|
419
|
+
# Check if there's a recently merged branch to collect from
|
|
420
|
+
# The merge info comes from git log or COLONY_STATE context
|
|
421
|
+
last_merge_branch="${last_merged_branch:-}"
|
|
422
|
+
last_merge_sha="${last_merge_sha:-}"
|
|
423
|
+
|
|
424
|
+
if [[ -n "$last_merge_branch" && -n "$last_merge_sha" ]]; then
|
|
425
|
+
collect_result=$(bash .aether/aether-utils.sh midden-collect \
|
|
426
|
+
--branch "$last_merge_branch" --merge-sha "$last_merge_sha" \
|
|
427
|
+
2>/dev/null || echo '{"ok":false}')
|
|
428
|
+
collect_ok=$(echo "$collect_result" | jq -r '.ok // false' 2>/dev/null)
|
|
429
|
+
if [[ "$collect_ok" == "true" ]]; then
|
|
430
|
+
collect_status=$(echo "$collect_result" | jq -r '.result.status // "unknown"' 2>/dev/null)
|
|
431
|
+
if [[ "$collect_status" == "collected" ]]; then
|
|
432
|
+
new_entries=$(echo "$collect_result" | jq -r '.result.entries_collected // 0' 2>/dev/null)
|
|
433
|
+
echo "Midden: collected $new_entries failure entries from branch $last_merge_branch"
|
|
434
|
+
fi
|
|
435
|
+
fi
|
|
436
|
+
fi
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
This step is NON-BLOCKING -- continue proceeds regardless of collection outcome. If `last_merge_branch` and `last_merge_sha` are not set (e.g., no recent merge), this step is silently skipped.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Clash detection utility -- detects file conflicts across git worktrees.
|
|
3
|
+
#
|
|
4
|
+
# When multiple agents work in parallel worktrees, they could accidentally
|
|
5
|
+
# edit the same file from different worktrees. This script checks for that.
|
|
6
|
+
#
|
|
7
|
+
# Usage: clash-detect --file <path> [--worktree <path>]
|
|
8
|
+
# Returns JSON: {ok:true, result:{conflict:false}} or
|
|
9
|
+
# {ok:true, result:{conflict:true, conflicting_worktrees:[...]}}
|
|
10
|
+
#
|
|
11
|
+
# Environment:
|
|
12
|
+
# AETHER_ROOT - repo root (auto-detected if not set)
|
|
13
|
+
# WORKTREE_DIR - current worktree path (to exclude self from checks)
|
|
14
|
+
#
|
|
15
|
+
# When sourced by aether-utils.sh, provides _clash_detect function.
|
|
16
|
+
# When run directly, executes _clash_detect with provided arguments.
|
|
17
|
+
|
|
18
|
+
# Only set strict mode when run directly (not sourced)
|
|
19
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
20
|
+
set -uo pipefail
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Set AETHER_ROOT if not already set (e.g., when sourced)
|
|
24
|
+
: "${AETHER_ROOT:=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
25
|
+
|
|
26
|
+
# Fallback error constant for standalone execution (when not sourced by aether-utils.sh)
|
|
27
|
+
: "${E_VALIDATION_FAILED:=E_VALIDATION_FAILED}"
|
|
28
|
+
|
|
29
|
+
# Allowlist of path patterns that bypass clash detection (branch-local state)
|
|
30
|
+
# These files live in .aether/data/ and are unique per worktree/branch
|
|
31
|
+
_CLASH_ALLOWLIST=(
|
|
32
|
+
".aether/data/"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Check if a file path matches the allowlist
|
|
36
|
+
_clash_is_allowlisted() {
|
|
37
|
+
local file="$1"
|
|
38
|
+
for pattern in "${_CLASH_ALLOWLIST[@]}"; do
|
|
39
|
+
if [[ "$file" == "$pattern"* ]]; then
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Normalize a path for comparison on macOS (handles /var vs /private/var)
|
|
47
|
+
_clash_normalize_path() {
|
|
48
|
+
local p="$1"
|
|
49
|
+
if [[ -z "$p" ]]; then
|
|
50
|
+
echo ""
|
|
51
|
+
return
|
|
52
|
+
fi
|
|
53
|
+
if command -v realpath >/dev/null 2>&1; then
|
|
54
|
+
realpath "$p" 2>/dev/null && return
|
|
55
|
+
fi
|
|
56
|
+
(cd "$p" 2>/dev/null && pwd) || echo "$p"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# JSON output helpers (standalone, for direct execution mode)
|
|
60
|
+
_clash_json_ok() {
|
|
61
|
+
echo "{\"ok\":true,\"result\":$1}"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_clash_json_err() {
|
|
65
|
+
echo "{\"ok\":false,\"error\":$1}" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Bridge: use aether-utils JSON helpers when sourced, standalone when direct
|
|
70
|
+
_cok() {
|
|
71
|
+
if type json_ok &>/dev/null; then
|
|
72
|
+
json_ok "$1"
|
|
73
|
+
else
|
|
74
|
+
_clash_json_ok "$1"
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_cerr() {
|
|
79
|
+
if type json_err &>/dev/null; then
|
|
80
|
+
json_err "${2:-$E_UNKNOWN}" "$1"
|
|
81
|
+
else
|
|
82
|
+
_clash_json_err "\"$1\""
|
|
83
|
+
fi
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Main clash detection logic
|
|
87
|
+
_clash_detect() {
|
|
88
|
+
local file=""
|
|
89
|
+
local worktree="${WORKTREE_DIR:-}"
|
|
90
|
+
|
|
91
|
+
while [[ $# -gt 0 ]]; do
|
|
92
|
+
case "$1" in
|
|
93
|
+
--file) file="${2:-}"; shift 2 ;;
|
|
94
|
+
--worktree) worktree="${2:-}"; shift 2 ;;
|
|
95
|
+
*) shift ;;
|
|
96
|
+
esac
|
|
97
|
+
done
|
|
98
|
+
|
|
99
|
+
if [[ -z "$file" ]]; then
|
|
100
|
+
_cerr "Usage: clash-detect --file <path> [--worktree <path>]" "$E_VALIDATION_FAILED"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# Allowlisted files never clash (branch-local state)
|
|
104
|
+
if _clash_is_allowlisted "$file"; then
|
|
105
|
+
_cok '{"conflict":false}'
|
|
106
|
+
return
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Verify we're in a git repo
|
|
110
|
+
if ! git -C "$AETHER_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
111
|
+
_cok '{"conflict":false}'
|
|
112
|
+
return
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Normalize the current worktree path for comparison
|
|
116
|
+
if [[ -n "$worktree" ]]; then
|
|
117
|
+
worktree="$(_clash_normalize_path "$worktree")" || worktree=""
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# Enumerate all worktrees
|
|
121
|
+
local conflicting=()
|
|
122
|
+
while IFS= read -r line; do
|
|
123
|
+
local wt_path
|
|
124
|
+
wt_path=$(echo "$line" | awk '{print $1}')
|
|
125
|
+
[[ -z "$wt_path" ]] && continue
|
|
126
|
+
|
|
127
|
+
local abs_wt_path
|
|
128
|
+
abs_wt_path="$(_clash_normalize_path "$wt_path")" || continue
|
|
129
|
+
|
|
130
|
+
# Skip if this is the current worktree (self-check)
|
|
131
|
+
if [[ -n "$worktree" && "$abs_wt_path" == "$worktree" ]]; then
|
|
132
|
+
continue
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Check if this worktree has uncommitted changes to the target file
|
|
136
|
+
local file_status
|
|
137
|
+
file_status=$(git -C "$abs_wt_path" status --porcelain -- "$file" 2>/dev/null) || continue
|
|
138
|
+
|
|
139
|
+
if [[ -n "$file_status" ]]; then
|
|
140
|
+
local branch_name
|
|
141
|
+
branch_name=$(git -C "$abs_wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
142
|
+
conflicting+=("\"$branch_name\"")
|
|
143
|
+
fi
|
|
144
|
+
done < <(git -C "$AETHER_ROOT" worktree list 2>/dev/null | tail -n +2)
|
|
145
|
+
|
|
146
|
+
if [[ ${#conflicting[@]} -eq 0 ]]; then
|
|
147
|
+
_cok '{"conflict":false}'
|
|
148
|
+
else
|
|
149
|
+
local conflict_list
|
|
150
|
+
conflict_list=$(printf '%s,' "${conflicting[@]}")
|
|
151
|
+
conflict_list="[${conflict_list%,}]"
|
|
152
|
+
_cok "{\"conflict\":true,\"conflicting_worktrees\":$conflict_list}"
|
|
153
|
+
fi
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# _clash_setup
|
|
157
|
+
# Install or uninstall the clash detection PreToolUse hook.
|
|
158
|
+
#
|
|
159
|
+
# Usage: clash-setup [--install] [--uninstall]
|
|
160
|
+
# Returns JSON: {ok:true, result:{hook_installed:true/false}}
|
|
161
|
+
#
|
|
162
|
+
# Environment:
|
|
163
|
+
# CLASH_SETTINGS_PATH - path to settings.json (default: .claude/settings.json)
|
|
164
|
+
_clash_setup() {
|
|
165
|
+
local action=""
|
|
166
|
+
local settings_path="${CLASH_SETTINGS_PATH:-$AETHER_ROOT/.claude/settings.json}"
|
|
167
|
+
|
|
168
|
+
while [[ $# -gt 0 ]]; do
|
|
169
|
+
case "$1" in
|
|
170
|
+
--install) action="install"; shift ;;
|
|
171
|
+
--uninstall) action="uninstall"; shift ;;
|
|
172
|
+
*) shift ;;
|
|
173
|
+
esac
|
|
174
|
+
done
|
|
175
|
+
|
|
176
|
+
if [[ -z "$action" ]]; then
|
|
177
|
+
_cerr "Usage: clash-setup [--install] [--uninstall]" "$E_VALIDATION_FAILED"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Ensure settings file exists
|
|
181
|
+
if [[ ! -f "$settings_path" ]]; then
|
|
182
|
+
echo '{}' > "$settings_path" 2>/dev/null || {
|
|
183
|
+
_cerr "Cannot create settings file at $settings_path" "$E_FILE_NOT_FOUND"
|
|
184
|
+
}
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# Read current settings
|
|
188
|
+
local settings
|
|
189
|
+
settings=$(cat "$settings_path" 2>/dev/null) || settings='{}'
|
|
190
|
+
|
|
191
|
+
local hook_command='node .aether/utils/hooks/clash-pre-tool-use.js'
|
|
192
|
+
local hook_entry="{\"type\":\"command\",\"command\":\"$hook_command\",\"timeout\":5}"
|
|
193
|
+
|
|
194
|
+
if [[ "$action" == "install" ]]; then
|
|
195
|
+
# Check if hook already exists in PreToolUse
|
|
196
|
+
if echo "$settings" | jq -e '.hooks.PreToolUse[]?.hooks[]?.command' 2>/dev/null \
|
|
197
|
+
| grep -q "clash-pre-tool-use"; then
|
|
198
|
+
# Already installed
|
|
199
|
+
_cok '{"hook_installed":true}'
|
|
200
|
+
return
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Add the hook to PreToolUse array
|
|
204
|
+
settings=$(echo "$settings" | jq --arg entry "$hook_entry" '
|
|
205
|
+
if .hooks.PreToolUse then
|
|
206
|
+
.hooks.PreToolUse += [{"matcher": "Edit|Write", "hooks": [($entry | fromjson)]}]
|
|
207
|
+
else
|
|
208
|
+
.hooks.PreToolUse = [{"matcher": "Edit|Write", "hooks": [($entry | fromjson)]}]
|
|
209
|
+
end
|
|
210
|
+
' 2>/dev/null)
|
|
211
|
+
|
|
212
|
+
if [[ $? -ne 0 ]] || [[ -z "$settings" ]]; then
|
|
213
|
+
_cerr "Failed to update settings file" "$E_UNKNOWN"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
echo "$settings" > "$settings_path"
|
|
217
|
+
|
|
218
|
+
_cok '{"hook_installed":true}'
|
|
219
|
+
|
|
220
|
+
elif [[ "$action" == "uninstall" ]]; then
|
|
221
|
+
# Remove the hook from PreToolUse
|
|
222
|
+
settings=$(echo "$settings" | jq '
|
|
223
|
+
if .hooks.PreToolUse then
|
|
224
|
+
.hooks.PreToolUse = [.hooks.PreToolUse[] |
|
|
225
|
+
select(.hooks[]?.command | . != "node .aether/utils/hooks/clash-pre-tool-use.js")
|
|
226
|
+
]
|
|
227
|
+
end
|
|
228
|
+
' 2>/dev/null)
|
|
229
|
+
|
|
230
|
+
echo "$settings" > "$settings_path"
|
|
231
|
+
|
|
232
|
+
_cok '{"hook_installed":false}'
|
|
233
|
+
fi
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# Run if executed directly (not sourced)
|
|
237
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
238
|
+
_clash_detect "$@"
|
|
239
|
+
fi
|