aether-colony 5.0.0 → 5.2.1
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 +3226 -3345
- package/.aether/agents-claude/aether-ambassador.md +265 -0
- package/.aether/agents-claude/aether-archaeologist.md +327 -0
- package/.aether/agents-claude/aether-architect.md +236 -0
- package/.aether/agents-claude/aether-auditor.md +271 -0
- package/.aether/agents-claude/aether-builder.md +224 -0
- package/.aether/agents-claude/aether-chaos.md +269 -0
- package/.aether/agents-claude/aether-chronicler.md +305 -0
- package/.aether/agents-claude/aether-gatekeeper.md +330 -0
- package/.aether/agents-claude/aether-includer.md +374 -0
- package/.aether/agents-claude/aether-keeper.md +272 -0
- package/.aether/agents-claude/aether-measurer.md +322 -0
- package/.aether/agents-claude/aether-oracle.md +237 -0
- package/.aether/agents-claude/aether-probe.md +211 -0
- package/.aether/agents-claude/aether-queen.md +330 -0
- package/.aether/agents-claude/aether-route-setter.md +178 -0
- package/.aether/agents-claude/aether-sage.md +418 -0
- package/.aether/agents-claude/aether-scout.md +179 -0
- package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
- package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
- package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
- package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
- package/.aether/agents-claude/aether-tracker.md +270 -0
- package/.aether/agents-claude/aether-watcher.md +280 -0
- package/.aether/agents-claude/aether-weaver.md +248 -0
- package/.aether/commands/archaeology.yaml +653 -0
- package/.aether/commands/build.yaml +1221 -0
- package/.aether/commands/chaos.yaml +653 -0
- package/.aether/commands/colonize.yaml +442 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +509 -0
- package/.aether/commands/data-clean.yaml +80 -0
- package/.aether/commands/dream.yaml +275 -0
- package/.aether/commands/entomb.yaml +863 -0
- package/.aether/commands/export-signals.yaml +64 -0
- package/.aether/commands/feedback.yaml +158 -0
- package/.aether/commands/flag.yaml +160 -0
- package/.aether/commands/flags.yaml +177 -0
- package/.aether/commands/focus.yaml +112 -0
- package/.aether/commands/help.yaml +167 -0
- package/.aether/commands/history.yaml +137 -0
- package/.aether/commands/import-signals.yaml +79 -0
- package/.aether/commands/init.yaml +502 -0
- package/.aether/commands/insert-phase.yaml +102 -0
- package/.aether/commands/interpret.yaml +285 -0
- package/.aether/commands/lay-eggs.yaml +224 -0
- package/.aether/commands/maturity.yaml +122 -0
- package/.aether/commands/memory-details.yaml +74 -0
- package/.aether/commands/migrate-state.yaml +174 -0
- package/.aether/commands/oracle.yaml +1224 -0
- package/.aether/commands/organize.yaml +446 -0
- package/.aether/commands/patrol.yaml +621 -0
- package/.aether/commands/pause-colony.yaml +424 -0
- package/.aether/commands/phase.yaml +124 -0
- package/.aether/commands/pheromones.yaml +153 -0
- package/.aether/commands/plan.yaml +1364 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +375 -0
- package/.aether/commands/resume.yaml +407 -0
- package/.aether/commands/run.yaml +229 -0
- package/.aether/commands/seal.yaml +1214 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +408 -0
- package/.aether/commands/swarm.yaml +352 -0
- package/.aether/commands/tunnels.yaml +814 -0
- package/.aether/commands/update.yaml +131 -0
- package/.aether/commands/verify-castes.yaml +159 -0
- package/.aether/commands/watch.yaml +454 -0
- package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
- package/.aether/docs/QUEEN-SYSTEM.md +11 -11
- package/.aether/docs/README.md +32 -2
- package/.aether/docs/command-playbooks/README.md +23 -0
- package/.aether/docs/command-playbooks/build-complete.md +349 -0
- package/.aether/docs/command-playbooks/build-context.md +282 -0
- package/.aether/docs/command-playbooks/build-full.md +1683 -0
- package/.aether/docs/command-playbooks/build-prep.md +284 -0
- package/.aether/docs/command-playbooks/build-verify.md +405 -0
- package/.aether/docs/command-playbooks/build-wave.md +749 -0
- package/.aether/docs/command-playbooks/continue-advance.md +524 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
- package/.aether/docs/command-playbooks/continue-full.md +1725 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +407 -0
- package/.aether/docs/context-continuity.md +84 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
- package/.aether/docs/error-codes.md +1 -1
- package/.aether/docs/known-issues.md +34 -173
- package/.aether/docs/pheromones.md +86 -6
- package/.aether/docs/plans/pheromone-display-plan.md +257 -0
- package/.aether/docs/queen-commands.md +10 -9
- package/.aether/docs/source-of-truth-map.md +132 -0
- package/.aether/docs/xml-utilities.md +47 -0
- package/.aether/rules/aether-colony.md +23 -13
- package/.aether/scripts/incident-test-add.sh +47 -0
- package/.aether/scripts/weekly-audit.sh +79 -0
- package/.aether/skills/.index.json +649 -0
- package/.aether/skills/colony/.manifest.json +16 -0
- package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
- package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
- package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
- package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
- package/.aether/skills/colony/context-management/SKILL.md +80 -0
- package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
- package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
- package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
- package/.aether/skills/colony/state-safety/SKILL.md +84 -0
- package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
- package/.aether/skills/domain/.manifest.json +24 -0
- package/.aether/skills/domain/README.md +33 -0
- package/.aether/skills/domain/django/SKILL.md +49 -0
- package/.aether/skills/domain/docker/SKILL.md +52 -0
- package/.aether/skills/domain/golang/SKILL.md +52 -0
- package/.aether/skills/domain/graphql/SKILL.md +51 -0
- package/.aether/skills/domain/html-css/SKILL.md +48 -0
- package/.aether/skills/domain/nextjs/SKILL.md +45 -0
- package/.aether/skills/domain/nodejs/SKILL.md +53 -0
- package/.aether/skills/domain/postgresql/SKILL.md +53 -0
- package/.aether/skills/domain/prisma/SKILL.md +59 -0
- package/.aether/skills/domain/python/SKILL.md +50 -0
- package/.aether/skills/domain/rails/SKILL.md +52 -0
- package/.aether/skills/domain/react/SKILL.md +45 -0
- package/.aether/skills/domain/rest-api/SKILL.md +58 -0
- package/.aether/skills/domain/svelte/SKILL.md +47 -0
- package/.aether/skills/domain/tailwind/SKILL.md +45 -0
- package/.aether/skills/domain/testing/SKILL.md +53 -0
- package/.aether/skills/domain/typescript/SKILL.md +58 -0
- package/.aether/skills/domain/vue/SKILL.md +49 -0
- package/.aether/templates/QUEEN.md.template +23 -41
- package/.aether/templates/colony-state-reset.jq.template +1 -0
- package/.aether/templates/colony-state.template.json +4 -0
- package/.aether/templates/learning-observations.template.json +6 -0
- package/.aether/templates/midden.template.json +13 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/.aether/utils/atomic-write.sh +63 -17
- package/.aether/utils/chamber-utils.sh +145 -2
- package/.aether/utils/council.sh +425 -0
- package/.aether/utils/emoji-audit.sh +166 -0
- package/.aether/utils/error-handler.sh +21 -7
- package/.aether/utils/file-lock.sh +182 -27
- package/.aether/utils/flag.sh +278 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +520 -0
- package/.aether/utils/oracle/oracle.md +168 -0
- package/.aether/utils/oracle/oracle.sh +1023 -0
- package/.aether/utils/pheromone.sh +2029 -0
- package/.aether/utils/queen.sh +1710 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +816 -0
- package/.aether/utils/skills.sh +509 -0
- package/.aether/utils/spawn-tree.sh +103 -271
- package/.aether/utils/spawn.sh +260 -0
- package/.aether/utils/state-api.sh +389 -0
- package/.aether/utils/state-loader.sh +8 -6
- package/.aether/utils/suggest.sh +611 -0
- package/.aether/utils/swarm-display.sh +10 -1
- package/.aether/utils/swarm.sh +1004 -0
- package/.aether/utils/watch-spawn-tree.sh +11 -2
- package/.aether/utils/xml-compose.sh +2 -2
- package/.aether/utils/xml-convert.sh +9 -5
- package/.aether/utils/xml-core.sh +5 -9
- package/.aether/utils/xml-query.sh +4 -4
- package/.aether/workers.md +86 -67
- package/.claude/agents/ant/aether-ambassador.md +2 -1
- package/.claude/agents/ant/aether-archaeologist.md +6 -1
- package/.claude/agents/ant/aether-architect.md +236 -0
- package/.claude/agents/ant/aether-auditor.md +6 -1
- package/.claude/agents/ant/aether-builder.md +38 -1
- package/.claude/agents/ant/aether-chaos.md +2 -1
- package/.claude/agents/ant/aether-chronicler.md +1 -0
- package/.claude/agents/ant/aether-gatekeeper.md +6 -1
- package/.claude/agents/ant/aether-includer.md +1 -0
- package/.claude/agents/ant/aether-keeper.md +1 -0
- package/.claude/agents/ant/aether-measurer.md +6 -1
- package/.claude/agents/ant/aether-oracle.md +237 -0
- package/.claude/agents/ant/aether-probe.md +2 -1
- package/.claude/agents/ant/aether-queen.md +6 -1
- package/.claude/agents/ant/aether-route-setter.md +6 -1
- package/.claude/agents/ant/aether-sage.md +68 -3
- package/.claude/agents/ant/aether-scout.md +38 -1
- package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
- package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
- package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
- package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
- package/.claude/agents/ant/aether-tracker.md +6 -1
- package/.claude/agents/ant/aether-watcher.md +37 -1
- package/.claude/agents/ant/aether-weaver.md +2 -1
- package/.claude/commands/ant/archaeology.md +1 -8
- package/.claude/commands/ant/build.md +43 -1159
- package/.claude/commands/ant/chaos.md +1 -14
- package/.claude/commands/ant/colonize.md +3 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +213 -15
- package/.claude/commands/ant/data-clean.md +81 -0
- package/.claude/commands/ant/dream.md +12 -9
- package/.claude/commands/ant/entomb.md +62 -87
- package/.claude/commands/ant/export-signals.md +57 -0
- package/.claude/commands/ant/feedback.md +18 -0
- package/.claude/commands/ant/flag.md +12 -0
- package/.claude/commands/ant/flags.md +22 -8
- package/.claude/commands/ant/focus.md +18 -0
- package/.claude/commands/ant/help.md +40 -8
- package/.claude/commands/ant/history.md +3 -0
- package/.claude/commands/ant/import-signals.md +71 -0
- package/.claude/commands/ant/init.md +349 -191
- package/.claude/commands/ant/insert-phase.md +105 -0
- package/.claude/commands/ant/interpret.md +11 -0
- package/.claude/commands/ant/lay-eggs.md +167 -158
- package/.claude/commands/ant/maturity.md +22 -11
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +6 -0
- package/.claude/commands/ant/oracle.md +317 -62
- package/.claude/commands/ant/organize.md +10 -5
- package/.claude/commands/ant/patrol.md +620 -0
- package/.claude/commands/ant/pause-colony.md +8 -22
- package/.claude/commands/ant/phase.md +26 -37
- package/.claude/commands/ant/pheromones.md +156 -0
- package/.claude/commands/ant/plan.md +199 -50
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +37 -22
- package/.claude/commands/ant/resume.md +60 -7
- package/.claude/commands/ant/run.md +231 -0
- package/.claude/commands/ant/seal.md +506 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +171 -1
- package/.claude/commands/ant/swarm.md +11 -23
- package/.claude/commands/ant/tunnels.md +1 -0
- package/.claude/commands/ant/update.md +58 -135
- package/.claude/commands/ant/verify-castes.md +90 -42
- package/.claude/commands/ant/watch.md +1 -0
- package/.opencode/agents/aether-ambassador.md +1 -1
- package/.opencode/agents/aether-architect.md +133 -0
- package/.opencode/agents/aether-builder.md +3 -3
- package/.opencode/agents/aether-oracle.md +137 -0
- package/.opencode/agents/aether-queen.md +1 -1
- package/.opencode/agents/aether-route-setter.md +1 -1
- package/.opencode/agents/aether-scout.md +1 -1
- package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
- package/.opencode/agents/aether-surveyor-nest.md +6 -1
- package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
- package/.opencode/agents/aether-surveyor-provisions.md +6 -1
- package/.opencode/agents/aether-tracker.md +1 -1
- package/.opencode/agents/aether-watcher.md +1 -1
- package/.opencode/agents/aether-weaver.md +1 -1
- package/.opencode/commands/ant/archaeology.md +7 -14
- package/.opencode/commands/ant/build.md +54 -88
- package/.opencode/commands/ant/chaos.md +7 -24
- package/.opencode/commands/ant/colonize.md +10 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +150 -18
- package/.opencode/commands/ant/data-clean.md +77 -0
- package/.opencode/commands/ant/dream.md +15 -17
- package/.opencode/commands/ant/entomb.md +28 -18
- package/.opencode/commands/ant/export-signals.md +54 -0
- package/.opencode/commands/ant/feedback.md +24 -5
- package/.opencode/commands/ant/flag.md +16 -4
- package/.opencode/commands/ant/flags.md +24 -10
- package/.opencode/commands/ant/focus.md +22 -5
- package/.opencode/commands/ant/help.md +41 -8
- package/.opencode/commands/ant/history.md +9 -0
- package/.opencode/commands/ant/import-signals.md +68 -0
- package/.opencode/commands/ant/init.md +396 -154
- package/.opencode/commands/ant/insert-phase.md +111 -0
- package/.opencode/commands/ant/interpret.md +16 -0
- package/.opencode/commands/ant/lay-eggs.md +184 -112
- package/.opencode/commands/ant/maturity.md +18 -2
- package/.opencode/commands/ant/memory-details.md +83 -0
- package/.opencode/commands/ant/migrate-state.md +12 -0
- package/.opencode/commands/ant/oracle.md +322 -67
- package/.opencode/commands/ant/organize.md +14 -12
- package/.opencode/commands/ant/patrol.md +626 -0
- package/.opencode/commands/ant/pause-colony.md +12 -29
- package/.opencode/commands/ant/phase.md +30 -40
- package/.opencode/commands/ant/pheromones.md +162 -0
- package/.opencode/commands/ant/plan.md +210 -57
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +41 -29
- package/.opencode/commands/ant/resume.md +80 -20
- package/.opencode/commands/ant/run.md +237 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +125 -30
- package/.opencode/commands/ant/swarm.md +3 -345
- package/.opencode/commands/ant/tunnels.md +3 -9
- package/.opencode/commands/ant/update.md +63 -127
- package/.opencode/commands/ant/verify-castes.md +96 -42
- package/.opencode/commands/ant/watch.md +7 -0
- package/CHANGELOG.md +368 -1
- package/README.md +195 -324
- package/bin/cli.js +236 -429
- package/bin/generate-commands.js +186 -0
- package/bin/generate-commands.sh +128 -89
- package/bin/lib/spawn-logger.js +0 -15
- package/bin/lib/update-transaction.js +285 -35
- package/bin/npx-install.js +178 -0
- package/bin/validate-package.sh +85 -3
- package/package.json +16 -4
- package/.aether/CONTEXT.md +0 -160
- package/.aether/docs/QUEEN.md +0 -84
- package/.aether/exchange/colony-registry.xml +0 -11
- package/.aether/exchange/pheromones.xml +0 -87
- package/.aether/exchange/queen-wisdom.xml +0 -14
- package/.aether/model-profiles.yaml +0 -100
- package/.aether/utils/spawn-with-model.sh +0 -56
- package/bin/lib/model-profiles.js +0 -445
- package/bin/lib/model-verify.js +0 -288
- package/bin/lib/proxy-health.js +0 -253
- package/bin/lib/telemetry.js +0 -441
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Suggest utility functions -- extracted from aether-utils.sh
|
|
3
|
+
# Provides: _suggest_analyze, _suggest_record, _suggest_check,
|
|
4
|
+
# _suggest_clear, _suggest_approve, _suggest_quick_dismiss
|
|
5
|
+
# Also includes: get_type_emoji (helper used only by _suggest_approve)
|
|
6
|
+
# Note: suggest-clear is deprecated (moved with domain per user decision)
|
|
7
|
+
|
|
8
|
+
# ============================================================================
|
|
9
|
+
# get_type_emoji
|
|
10
|
+
# Helper function for suggest-approve display (bash 3.2 compatible)
|
|
11
|
+
# Usage: get_type_emoji <TYPE>
|
|
12
|
+
# Returns: emoji string for the given pheromone type
|
|
13
|
+
# ============================================================================
|
|
14
|
+
get_type_emoji() {
|
|
15
|
+
case "$1" in
|
|
16
|
+
FOCUS) echo "🎯" ;;
|
|
17
|
+
REDIRECT) echo "🚫" ;;
|
|
18
|
+
FEEDBACK) echo "💬" ;;
|
|
19
|
+
*) echo "📝" ;;
|
|
20
|
+
esac
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ============================================================================
|
|
24
|
+
# _suggest_analyze
|
|
25
|
+
# Analyze codebase and return pheromone suggestions based on code patterns
|
|
26
|
+
# Usage: _suggest_analyze [--source-dir DIR] [--max-suggestions N] [--dry-run]
|
|
27
|
+
# Returns: JSON with suggestions array and analysis metadata
|
|
28
|
+
# ============================================================================
|
|
29
|
+
_suggest_analyze() {
|
|
30
|
+
# Disable ERR trap for this command (grep returns 1 on no match, which triggers trap)
|
|
31
|
+
trap '' ERR
|
|
32
|
+
|
|
33
|
+
source_dir=""
|
|
34
|
+
max_suggestions=5
|
|
35
|
+
dry_run=false
|
|
36
|
+
|
|
37
|
+
# Parse arguments - note: $1 is already shifted by the main dispatch
|
|
38
|
+
# So $1 here is the first argument after 'suggest-analyze'
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--source-dir) source_dir="$2"; shift 2 ;;
|
|
42
|
+
--max-suggestions) max_suggestions="$2"; shift 2 ;;
|
|
43
|
+
--dry-run) dry_run=true; shift ;;
|
|
44
|
+
*) shift ;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# Auto-detect source directory if not provided
|
|
49
|
+
if [[ -z "$source_dir" ]]; then
|
|
50
|
+
if [[ -d "$AETHER_ROOT/src" ]]; then
|
|
51
|
+
source_dir="$AETHER_ROOT/src"
|
|
52
|
+
elif [[ -d "$AETHER_ROOT/lib" ]]; then
|
|
53
|
+
source_dir="$AETHER_ROOT/lib"
|
|
54
|
+
else
|
|
55
|
+
source_dir="$AETHER_ROOT"
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Validate source directory
|
|
60
|
+
if [[ ! -d "$source_dir" ]]; then
|
|
61
|
+
json_err "$E_FILE_NOT_FOUND" "Source directory not found: $source_dir"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Build JSON array of suggestions using jq
|
|
65
|
+
# We use jq to handle deduplication since bash 3.2 doesn't support associative arrays
|
|
66
|
+
pheromones_file="$COLONY_DATA_DIR/pheromones.json"
|
|
67
|
+
session_file="$COLONY_DATA_DIR/session.json"
|
|
68
|
+
|
|
69
|
+
# Create temp file for collecting raw suggestions
|
|
70
|
+
raw_suggestions=$(mktemp)
|
|
71
|
+
echo "[]" > "$raw_suggestions"
|
|
72
|
+
|
|
73
|
+
analyzed_count=0
|
|
74
|
+
patterns_found=0
|
|
75
|
+
|
|
76
|
+
# Define exclusions (use word boundaries to avoid matching partial paths)
|
|
77
|
+
exclude_pattern="node_modules/|/.aether/|/dist/|/build/|/\\.git/|/coverage/|\\.min\\.js"
|
|
78
|
+
|
|
79
|
+
# Find files to analyze (respecting exclusions)
|
|
80
|
+
while IFS= read -r file || [[ -n "$file" ]]; do
|
|
81
|
+
analyzed_count=$((analyzed_count + 1))
|
|
82
|
+
|
|
83
|
+
# Skip excluded paths
|
|
84
|
+
if echo "$file" | grep -qE "$exclude_pattern"; then
|
|
85
|
+
continue
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Get file extension
|
|
89
|
+
ext="${file##*.}"
|
|
90
|
+
|
|
91
|
+
# Check file size (large files > 300 lines)
|
|
92
|
+
line_count=$(wc -l < "$file" 2>/dev/null || echo "0") # SUPPRESS:OK -- read-default: file may not exist
|
|
93
|
+
if [[ $line_count -gt 300 ]]; then
|
|
94
|
+
patterns_found=$((patterns_found + 1))
|
|
95
|
+
content="Large file: consider refactoring ($line_count lines)"
|
|
96
|
+
reason="File exceeds 300 lines, consider breaking into smaller modules"
|
|
97
|
+
hash=$(echo -n "$file:FOCUS:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || { # SUPPRESS:OK -- read-default: hash generation with fallback
|
|
98
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
99
|
+
hash="$(date +%s%N)"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Append suggestion to raw_suggestions using jq
|
|
103
|
+
new_suggestion=$(jq -n --arg type "FOCUS" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "7" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
104
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Check for TODO/FIXME/XXX comments
|
|
108
|
+
if [[ "$ext" =~ ^(ts|tsx|js|jsx|py|sh|md)$ ]]; then
|
|
109
|
+
todo_matches=$( (grep -n "TODO\\|FIXME\\|XXX" "$file" 2>/dev/null || true) | wc -l | tr -d ' \n') # SUPPRESS:OK -- read-default: file may not exist
|
|
110
|
+
if [[ $todo_matches -gt 0 ]]; then
|
|
111
|
+
patterns_found=$((patterns_found + 1))
|
|
112
|
+
content="$todo_matches pending TODO/FIXME comments"
|
|
113
|
+
reason="Unresolved markers indicate technical debt"
|
|
114
|
+
# SUPPRESS:OK -- read-default: hash generation with fallback
|
|
115
|
+
hash=$(echo -n "$file:FEEDBACK:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || {
|
|
116
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
117
|
+
hash="$(date +%s%N)"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
new_suggestion=$(jq -n --arg type "FEEDBACK" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "4" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
121
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Check for debug artifacts (console.log, debugger)
|
|
126
|
+
if [[ "$ext" =~ ^(ts|tsx|js|jsx)$ ]]; then
|
|
127
|
+
# SUPPRESS:OK -- read-default: returns fallback on failure
|
|
128
|
+
debug_matches=$( (grep -n "console\\.log\\|debugger" "$file" 2>/dev/null || true) | wc -l | tr -d ' \n')
|
|
129
|
+
if [[ $debug_matches -gt 0 ]]; then
|
|
130
|
+
patterns_found=$((patterns_found + 1))
|
|
131
|
+
content="Remove debug artifacts before commit ($debug_matches found)"
|
|
132
|
+
reason="Debug statements should not be committed to production code"
|
|
133
|
+
# SUPPRESS:OK -- read-default: hash generation with fallback
|
|
134
|
+
hash=$(echo -n "$file:REDIRECT:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || {
|
|
135
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
136
|
+
hash="$(date +%s%N)"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
new_suggestion=$(jq -n --arg type "REDIRECT" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "9" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
140
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Check for type safety gaps (: any, : unknown)
|
|
145
|
+
if [[ "$ext" =~ ^(ts|tsx)$ ]]; then
|
|
146
|
+
type_gaps=$( (grep -n ": any\\|: unknown" "$file" 2>/dev/null || true) | wc -l | tr -d ' \n') # SUPPRESS:OK -- read-default: file may not exist
|
|
147
|
+
if [[ $type_gaps -gt 0 ]]; then
|
|
148
|
+
patterns_found=$((patterns_found + 1))
|
|
149
|
+
content="Type safety gaps detected ($type_gaps instances)"
|
|
150
|
+
reason="Using 'any' or 'unknown' bypasses TypeScript's type checking"
|
|
151
|
+
# SUPPRESS:OK -- read-default: hash generation with fallback
|
|
152
|
+
hash=$(echo -n "$file:FEEDBACK:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || {
|
|
153
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
154
|
+
hash="$(date +%s%N)"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
new_suggestion=$(jq -n --arg type "FEEDBACK" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "5" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
158
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Check for high complexity (function count)
|
|
163
|
+
if [[ "$ext" =~ ^(ts|tsx|js|jsx|py|sh)$ ]]; then
|
|
164
|
+
# SUPPRESS:OK -- existence-test: grep returns 1 when no matches
|
|
165
|
+
func_count=$(grep -cE "^function|^def |^const.*=.*function|^const.*=.*=>" "$file" 2>/dev/null | tr -d ' \n' || echo "0")
|
|
166
|
+
if [[ $func_count -gt 20 ]]; then
|
|
167
|
+
patterns_found=$((patterns_found + 1))
|
|
168
|
+
content="Complex module: test carefully ($func_count functions)"
|
|
169
|
+
reason="High function count may indicate multiple concerns; verify test coverage"
|
|
170
|
+
hash=$(echo -n "$file:FOCUS:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || { # SUPPRESS:OK -- read-default: hash generation with fallback
|
|
171
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
172
|
+
hash="$(date +%s%N)"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
new_suggestion=$(jq -n --arg type "FOCUS" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "6" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
176
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Check for test coverage gaps
|
|
181
|
+
if [[ "$ext" =~ ^(ts|tsx|js|jsx|py)$ ]] && [[ ! "$file" =~ \\.test\\. ]] && [[ ! "$file" =~ \\.spec\\. ]]; then
|
|
182
|
+
base_name=$(basename "$file" ".${ext}")
|
|
183
|
+
dir_name=$(dirname "$file")
|
|
184
|
+
|
|
185
|
+
# Look for corresponding test file
|
|
186
|
+
if [[ -f "$dir_name/$base_name.test.$ext" ]] || [[ -f "$dir_name/$base_name.spec.$ext" ]] || \
|
|
187
|
+
[[ -f "$dir_name/__tests__/$base_name.test.$ext" ]] || [[ -f "$dir_name/../tests/$base_name.test.$ext" ]]; then
|
|
188
|
+
: # Test file exists
|
|
189
|
+
else
|
|
190
|
+
# Only suggest for files with functions (not config/pure data files)
|
|
191
|
+
# SUPPRESS:OK -- existence-test: grep returns 1 when no matches
|
|
192
|
+
if grep -qE "^function|^def |^const.*=.*function|^const.*=.*=>|^export.*function|^class " "$file" 2>/dev/null || false; then
|
|
193
|
+
patterns_found=$((patterns_found + 1))
|
|
194
|
+
content="Add tests for uncovered module: $base_name"
|
|
195
|
+
reason="No corresponding test file found for module with functions"
|
|
196
|
+
# SUPPRESS:OK -- read-default: hash generation with fallback
|
|
197
|
+
hash=$(echo -n "$file:FOCUS:$content" | shasum -a 256 2>/dev/null | cut -d' ' -f1) || {
|
|
198
|
+
_aether_log_error "Could not generate content hash -- using timestamp fallback"
|
|
199
|
+
hash="$(date +%s%N)"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
new_suggestion=$(jq -n --arg type "FOCUS" --arg content "$content" --arg file "$file" --arg reason "$reason" --arg hash "$hash" --arg priority "5" '{type: $type, content: $content, file: $file, reason: $reason, hash: $hash, priority: ($priority | tonumber)}')
|
|
203
|
+
jq --argjson suggestion "$new_suggestion" '. += [$suggestion]' "$raw_suggestions" > "${raw_suggestions}.tmp" && mv "${raw_suggestions}.tmp" "$raw_suggestions"
|
|
204
|
+
fi
|
|
205
|
+
fi
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# SUPPRESS:OK -- existence-test: directory may not exist
|
|
209
|
+
done < <(find "$source_dir" -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.py" -o -name "*.sh" -o -name "*.md" \) 2>/dev/null | head -100)
|
|
210
|
+
|
|
211
|
+
# Deduplicate against existing pheromones and session suggestions using jq
|
|
212
|
+
# Get existing signal content hashes
|
|
213
|
+
existing_hashes="[]"
|
|
214
|
+
if [[ -f "$pheromones_file" ]]; then
|
|
215
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
216
|
+
existing_hashes=$(jq -r '[.signals[] | select(.active == true) | .content.text] | @json' "$pheromones_file" 2>/dev/null || echo "[]")
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
session_hashes="[]"
|
|
220
|
+
if [[ -f "$session_file" ]]; then
|
|
221
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
222
|
+
session_hashes=$(jq -r '[.suggested_pheromones[]?.hash // empty] | @json' "$session_file" 2>/dev/null || echo "[]")
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
# Filter suggestions: remove duplicates and sort by priority
|
|
226
|
+
suggestions_json=$(jq --argjson existing "$existing_hashes" --argjson session "$session_hashes" --argjson max "$max_suggestions" '
|
|
227
|
+
# Remove suggestions whose content matches existing signals
|
|
228
|
+
map(select(.content as $c | $existing | index($c) | not)) |
|
|
229
|
+
# Remove suggestions whose hash is in session
|
|
230
|
+
map(select(.hash as $h | $session | index($h) | not)) |
|
|
231
|
+
# Sort by priority descending and limit
|
|
232
|
+
sort_by(.priority) | reverse | .[:$max]
|
|
233
|
+
' "$raw_suggestions" 2>/dev/null || echo "[]") # SUPPRESS:OK -- read-default: file may not exist yet
|
|
234
|
+
|
|
235
|
+
# Clean up temp file
|
|
236
|
+
rm -f "$raw_suggestions"
|
|
237
|
+
|
|
238
|
+
# Build result
|
|
239
|
+
result=$(jq -n \
|
|
240
|
+
--argjson suggestions "$suggestions_json" \
|
|
241
|
+
--argjson analyzed "$analyzed_count" \
|
|
242
|
+
--argjson patterns "$patterns_found" \
|
|
243
|
+
'{suggestions: $suggestions, analyzed_files: $analyzed, patterns_found: $patterns}')
|
|
244
|
+
|
|
245
|
+
if [[ "$dry_run" == "true" ]]; then
|
|
246
|
+
echo "Dry run - analyzed: $source_dir" >&2
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# Re-enable ERR trap before exiting
|
|
250
|
+
trap 'if type error_handler &>/dev/null; then error_handler ${LINENO} "$BASH_COMMAND" $?; fi' ERR
|
|
251
|
+
|
|
252
|
+
json_ok "$result"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# ============================================================================
|
|
256
|
+
# _suggest_record
|
|
257
|
+
# Record a suggested pheromone hash to session.json for deduplication
|
|
258
|
+
# Usage: _suggest_record <hash> <type>
|
|
259
|
+
# Returns: JSON success/failure
|
|
260
|
+
# ============================================================================
|
|
261
|
+
_suggest_record() {
|
|
262
|
+
record_hash="${1:-}"
|
|
263
|
+
record_type="${2:-FEEDBACK}"
|
|
264
|
+
|
|
265
|
+
if [[ -z "$record_hash" ]]; then
|
|
266
|
+
json_err "$E_VALIDATION_FAILED" "suggest-record requires <hash> argument"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
session_file="$COLONY_DATA_DIR/session.json"
|
|
270
|
+
|
|
271
|
+
# Initialize suggested_pheromones array if missing
|
|
272
|
+
if [[ -f "$session_file" ]]; then
|
|
273
|
+
# Check if suggested_pheromones field exists
|
|
274
|
+
has_field=$(jq 'has("suggested_pheromones")' "$session_file" 2>/dev/null || echo "false") # SUPPRESS:OK -- read-default: file may not exist yet
|
|
275
|
+
if [[ "$has_field" != "true" ]]; then
|
|
276
|
+
# Add the field
|
|
277
|
+
jq '. + {"suggested_pheromones": []}' "$session_file" > "${session_file}.tmp" || {
|
|
278
|
+
_aether_log_error "Could not add suggestions field to session file"
|
|
279
|
+
rm -f "${session_file}.tmp"
|
|
280
|
+
}
|
|
281
|
+
if [[ -s "${session_file}.tmp" ]]; then
|
|
282
|
+
mv "${session_file}.tmp" "$session_file" || _aether_log_error "Could not finalize session field addition"
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# Append new suggestion
|
|
287
|
+
record_entry=$(jq -n --arg hash "$record_hash" --arg type "$record_type" --arg suggested_at "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" '{hash: $hash, type: $type, suggested_at: $suggested_at}')
|
|
288
|
+
jq --argjson entry "$record_entry" '.suggested_pheromones += [$entry]' "$session_file" > "${session_file}.tmp" || {
|
|
289
|
+
_aether_log_error "Could not record suggestion in session file"
|
|
290
|
+
rm -f "${session_file}.tmp"
|
|
291
|
+
}
|
|
292
|
+
if [[ -s "${session_file}.tmp" ]]; then
|
|
293
|
+
mv "${session_file}.tmp" "$session_file" || _aether_log_error "Could not finalize suggestion recording"
|
|
294
|
+
fi
|
|
295
|
+
else
|
|
296
|
+
# Create session.json with suggested_pheromones
|
|
297
|
+
record_entry=$(jq -n --arg hash "$record_hash" --arg type "$record_type" --arg suggested_at "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" '{hash: $hash, type: $type, suggested_at: $suggested_at}')
|
|
298
|
+
local sr_init_content
|
|
299
|
+
sr_init_content=$(jq -n --argjson entry "$record_entry" '{suggested_pheromones: [$entry]}')
|
|
300
|
+
atomic_write "$session_file" "$sr_init_content" || _aether_log_error "Could not create session file with suggestion"
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
json_ok '{"recorded":true}'
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# ============================================================================
|
|
307
|
+
# _suggest_check
|
|
308
|
+
# Check if a hash was already suggested this session
|
|
309
|
+
# Usage: _suggest_check <hash>
|
|
310
|
+
# Returns: JSON {already_suggested: true/false}
|
|
311
|
+
# ============================================================================
|
|
312
|
+
_suggest_check() {
|
|
313
|
+
check_hash="${1:-}"
|
|
314
|
+
|
|
315
|
+
if [[ -z "$check_hash" ]]; then
|
|
316
|
+
json_err "$E_VALIDATION_FAILED" "suggest-check requires <hash> argument"
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
session_file="$COLONY_DATA_DIR/session.json"
|
|
320
|
+
already_suggested="false"
|
|
321
|
+
|
|
322
|
+
if [[ -f "$session_file" ]]; then
|
|
323
|
+
# SUPPRESS:OK -- read-default: query may return empty
|
|
324
|
+
count=$(jq --arg hash "$check_hash" '[.suggested_pheromones[]? | select(.hash == $hash)] | length' "$session_file" 2>/dev/null || echo "0")
|
|
325
|
+
if [[ "$count" -gt 0 ]]; then
|
|
326
|
+
already_suggested="true"
|
|
327
|
+
fi
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
json_ok "$(jq -n --argjson already "$already_suggested" '{already_suggested: $already}')"
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# ============================================================================
|
|
334
|
+
# _suggest_clear
|
|
335
|
+
# Clear the suggested_pheromones array from session.json
|
|
336
|
+
# Usage: _suggest_clear
|
|
337
|
+
# Returns: JSON success with count cleared
|
|
338
|
+
# NOTE: This subcommand is deprecated
|
|
339
|
+
# ============================================================================
|
|
340
|
+
_suggest_clear() {
|
|
341
|
+
_deprecation_warning "suggest-clear"
|
|
342
|
+
session_file="$COLONY_DATA_DIR/session.json"
|
|
343
|
+
cleared_count=0
|
|
344
|
+
|
|
345
|
+
if [[ -f "$session_file" ]]; then
|
|
346
|
+
cleared_count=$(jq '.suggested_pheromones | length' "$session_file" 2>/dev/null || echo "0") # SUPPRESS:OK -- read-default: file may not exist yet
|
|
347
|
+
jq 'del(.suggested_pheromones)' "$session_file" > "${session_file}.tmp" || {
|
|
348
|
+
_aether_log_error "Could not clear suggestions from session file"
|
|
349
|
+
rm -f "${session_file}.tmp"
|
|
350
|
+
}
|
|
351
|
+
if [[ -s "${session_file}.tmp" ]]; then
|
|
352
|
+
mv "${session_file}.tmp" "$session_file" || _aether_log_error "Could not finalize suggestion clearing"
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
json_ok "$(jq -n --argjson cleared "$cleared_count" '{cleared: $cleared}')"
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# ============================================================================
|
|
360
|
+
# _suggest_approve
|
|
361
|
+
# Orchestrate pheromone suggestion approval workflow
|
|
362
|
+
# Usage: _suggest_approve [--verbose] [--dry-run] [--yes] [--no-suggest]
|
|
363
|
+
# Returns: JSON summary {approved, rejected, skipped, signals_created}
|
|
364
|
+
# ============================================================================
|
|
365
|
+
_suggest_approve() {
|
|
366
|
+
verbose=false
|
|
367
|
+
dry_run=false
|
|
368
|
+
skip_confirm=false
|
|
369
|
+
no_suggest=false
|
|
370
|
+
|
|
371
|
+
# Parse arguments
|
|
372
|
+
for arg in "$@"; do
|
|
373
|
+
case "$arg" in
|
|
374
|
+
--verbose) verbose=true ;;
|
|
375
|
+
--dry-run) dry_run=true ;;
|
|
376
|
+
--yes) skip_confirm=true ;;
|
|
377
|
+
--no-suggest) no_suggest=true ;;
|
|
378
|
+
esac
|
|
379
|
+
done
|
|
380
|
+
|
|
381
|
+
# Handle --no-suggest flag - exit immediately
|
|
382
|
+
if [[ "$no_suggest" == "true" ]]; then
|
|
383
|
+
json_ok '{"approved":0,"rejected":0,"skipped":0,"signals_created":[],"reason":"--no-suggest flag"}'
|
|
384
|
+
exit 0
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Check for non-interactive mode (no tty)
|
|
388
|
+
if [[ ! -t 0 ]] && [[ "$skip_confirm" != "true" ]]; then
|
|
389
|
+
echo "Non-interactive mode: skipping suggestions (use --yes to auto-approve)" >&2
|
|
390
|
+
json_ok '{"approved":0,"rejected":0,"skipped":0,"signals_created":[],"reason":"non-interactive mode"}'
|
|
391
|
+
exit 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
# Get suggestions from suggest-analyze
|
|
395
|
+
suggestions_result=$(bash "$0" suggest-analyze 2>/dev/null || echo '{"suggestions":[]}') # SUPPRESS:OK -- read-default: subcommand may fail
|
|
396
|
+
suggestions_json=$(echo "$suggestions_result" | jq '.result.suggestions // []')
|
|
397
|
+
|
|
398
|
+
# Check if there are any suggestions
|
|
399
|
+
suggestion_count=$(echo "$suggestions_json" | jq 'length')
|
|
400
|
+
if [[ "$suggestion_count" -eq 0 ]]; then
|
|
401
|
+
# Exit silently when no suggestions
|
|
402
|
+
json_ok '{"approved":0,"rejected":0,"skipped":0,"signals_created":[]}'
|
|
403
|
+
exit 0
|
|
404
|
+
fi
|
|
405
|
+
|
|
406
|
+
# Arrays to track results
|
|
407
|
+
approved_suggestions=()
|
|
408
|
+
rejected_suggestions=()
|
|
409
|
+
skipped_suggestions=()
|
|
410
|
+
signals_created=()
|
|
411
|
+
|
|
412
|
+
# Display header (to stderr so stdout is valid JSON)
|
|
413
|
+
echo "" >&2
|
|
414
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
|
415
|
+
echo " S U G G E S T E D P H E R O M O N E S" >&2
|
|
416
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
|
|
417
|
+
echo "" >&2
|
|
418
|
+
echo "Based on code analysis, the colony suggests these signals:" >&2
|
|
419
|
+
echo "" >&2
|
|
420
|
+
|
|
421
|
+
# Process suggestions one at a time
|
|
422
|
+
for ((i=0; i<suggestion_count; i++)); do
|
|
423
|
+
suggestion=$(echo "$suggestions_json" | jq ".[$i]")
|
|
424
|
+
stype=$(echo "$suggestion" | jq -r '.type')
|
|
425
|
+
content=$(echo "$suggestion" | jq -r '.content')
|
|
426
|
+
file=$(echo "$suggestion" | jq -r '.file')
|
|
427
|
+
reason=$(echo "$suggestion" | jq -r '.reason')
|
|
428
|
+
priority=$(echo "$suggestion" | jq -r '.priority // 5')
|
|
429
|
+
hash=$(echo "$suggestion" | jq -r '.hash')
|
|
430
|
+
|
|
431
|
+
emoji=$(get_type_emoji "$stype")
|
|
432
|
+
|
|
433
|
+
# Display suggestion (to stderr so stdout is valid JSON)
|
|
434
|
+
echo "───────────────────────────────────────────────────" >&2
|
|
435
|
+
echo "Suggestion $((i+1)) of $suggestion_count" >&2
|
|
436
|
+
echo "───────────────────────────────────────────────────" >&2
|
|
437
|
+
echo "" >&2
|
|
438
|
+
echo "$emoji $stype (priority: $priority/10)" >&2
|
|
439
|
+
echo "" >&2
|
|
440
|
+
echo "$content" >&2
|
|
441
|
+
echo "" >&2
|
|
442
|
+
echo "Detected in: $file" >&2
|
|
443
|
+
echo "Reason: $reason" >&2
|
|
444
|
+
echo "" >&2
|
|
445
|
+
echo "───────────────────────────────────────────────────" >&2
|
|
446
|
+
|
|
447
|
+
# Handle dry-run mode
|
|
448
|
+
if [[ "$dry_run" == "true" ]]; then
|
|
449
|
+
echo "Dry run: would approve" >&2
|
|
450
|
+
approved_suggestions+=("$suggestion")
|
|
451
|
+
echo "" >&2
|
|
452
|
+
continue
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
# Handle --yes mode (auto-approve all)
|
|
456
|
+
if [[ "$skip_confirm" == "true" ]]; then
|
|
457
|
+
approved_suggestions+=("$suggestion")
|
|
458
|
+
echo "✓ Auto-approved (--yes mode)" >&2
|
|
459
|
+
echo "" >&2
|
|
460
|
+
continue
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
# Prompt for action (to stderr so stdout is valid JSON)
|
|
464
|
+
echo -n "[A]pprove [R]eject [S]kip [D]ismiss All Your choice: " >&2
|
|
465
|
+
read -r choice
|
|
466
|
+
|
|
467
|
+
case "$choice" in
|
|
468
|
+
[Aa]|"approve"|"Approve")
|
|
469
|
+
approved_suggestions+=("$suggestion")
|
|
470
|
+
echo "✓ Approved" >&2
|
|
471
|
+
;;
|
|
472
|
+
[Rr]|"reject"|"Reject")
|
|
473
|
+
rejected_suggestions+=("$suggestion")
|
|
474
|
+
# Record hash to prevent re-suggestion
|
|
475
|
+
bash "$0" suggest-record "$hash" "$stype" >/dev/null 2>&1 || _aether_log_error "Could not record suggestion"
|
|
476
|
+
echo "✗ Rejected" >&2
|
|
477
|
+
;;
|
|
478
|
+
[Dd]|"dismiss"|"Dismiss"|"dismiss all"|"Dismiss All")
|
|
479
|
+
# Dismiss all remaining suggestions
|
|
480
|
+
for ((j=i; j<suggestion_count; j++)); do
|
|
481
|
+
remaining=$(echo "$suggestions_json" | jq ".[$j]")
|
|
482
|
+
skipped_suggestions+=("$remaining")
|
|
483
|
+
done
|
|
484
|
+
echo "→ Dismissed all remaining suggestions" >&2
|
|
485
|
+
break
|
|
486
|
+
;;
|
|
487
|
+
[Ss]|""|"skip"|"Skip")
|
|
488
|
+
skipped_suggestions+=("$suggestion")
|
|
489
|
+
echo "→ Skipped" >&2
|
|
490
|
+
;;
|
|
491
|
+
*)
|
|
492
|
+
# Invalid input - default to skip
|
|
493
|
+
skipped_suggestions+=("$suggestion")
|
|
494
|
+
echo "→ Skipped (invalid input)" >&2
|
|
495
|
+
;;
|
|
496
|
+
esac
|
|
497
|
+
echo "" >&2
|
|
498
|
+
done
|
|
499
|
+
|
|
500
|
+
# Execute approvals for approved suggestions
|
|
501
|
+
approved_count=0
|
|
502
|
+
if [[ ${#approved_suggestions[@]} -gt 0 ]]; then
|
|
503
|
+
echo "" >&2
|
|
504
|
+
echo "Creating pheromone signals for ${#approved_suggestions[@]} approved suggestion(s)..." >&2
|
|
505
|
+
echo "" >&2
|
|
506
|
+
|
|
507
|
+
for suggestion in "${approved_suggestions[@]}"; do
|
|
508
|
+
stype=$(echo "$suggestion" | jq -r '.type')
|
|
509
|
+
content=$(echo "$suggestion" | jq -r '.content')
|
|
510
|
+
reason=$(echo "$suggestion" | jq -r '.reason')
|
|
511
|
+
hash=$(echo "$suggestion" | jq -r '.hash')
|
|
512
|
+
|
|
513
|
+
if [[ "$dry_run" == "true" ]]; then
|
|
514
|
+
echo "Dry run: would create $stype signal: \"$content\"" >&2
|
|
515
|
+
((approved_count++))
|
|
516
|
+
signals_created+=("dry_run_sig_$approved_count")
|
|
517
|
+
continue
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
# Call pheromone-write to create the signal
|
|
521
|
+
signal_result=$(bash "$0" pheromone-write "$stype" "$content" --source "system:suggestion" --reason "$reason" --ttl "phase_end" 2>&1)
|
|
522
|
+
|
|
523
|
+
if echo "$signal_result" | jq -e '.ok' >/dev/null 2>&1; then # SUPPRESS:OK -- validation: testing JSON field
|
|
524
|
+
signal_id=$(echo "$signal_result" | jq -r '.result.signal_id // "unknown"')
|
|
525
|
+
signals_created+=("$signal_id")
|
|
526
|
+
echo "✓ Added $stype signal" >&2
|
|
527
|
+
|
|
528
|
+
# Record hash to prevent duplicates
|
|
529
|
+
bash "$0" suggest-record "$hash" "$stype" >/dev/null 2>&1 || _aether_log_error "Could not record suggestion"
|
|
530
|
+
((approved_count++))
|
|
531
|
+
else
|
|
532
|
+
echo "✗ Failed to create signal: $content" >&2
|
|
533
|
+
echo " Error: $(echo "$signal_result" | jq -r '.error.message // "Unknown error"')" >&2
|
|
534
|
+
fi
|
|
535
|
+
done
|
|
536
|
+
fi
|
|
537
|
+
|
|
538
|
+
# Record rejected suggestions (already recorded during loop, but ensure consistency)
|
|
539
|
+
rejected_count=${#rejected_suggestions[@]}
|
|
540
|
+
|
|
541
|
+
# Skipped suggestions (not recorded, may suggest again)
|
|
542
|
+
skipped_count=${#skipped_suggestions[@]}
|
|
543
|
+
|
|
544
|
+
# Display summary (to stderr so stdout is valid JSON)
|
|
545
|
+
echo "" >&2
|
|
546
|
+
echo "═══════════════════════════════════════════════════" >&2
|
|
547
|
+
echo "Summary: $approved_count approved, $rejected_count rejected, $skipped_count skipped" >&2
|
|
548
|
+
echo "═══════════════════════════════════════════════════" >&2
|
|
549
|
+
echo "" >&2
|
|
550
|
+
|
|
551
|
+
# Build result with signals_created as JSON array (handle empty array case)
|
|
552
|
+
if [[ ${#signals_created[@]} -gt 0 ]]; then
|
|
553
|
+
signals_json=$(printf '%s\n' "${signals_created[@]}" | jq -R . | jq -s .)
|
|
554
|
+
else
|
|
555
|
+
signals_json="[]"
|
|
556
|
+
fi
|
|
557
|
+
result=$(jq -n \
|
|
558
|
+
--argjson approved "$approved_count" \
|
|
559
|
+
--argjson rejected "$rejected_count" \
|
|
560
|
+
--argjson skipped "$skipped_count" \
|
|
561
|
+
--argjson signals "$signals_json" \
|
|
562
|
+
'{approved: $approved, rejected: $rejected, skipped: $skipped, signals_created: $signals}')
|
|
563
|
+
|
|
564
|
+
json_ok "$result"
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# ============================================================================
|
|
568
|
+
# _suggest_quick_dismiss
|
|
569
|
+
# Quick dismiss all current suggestions - records hashes to prevent re-suggestion
|
|
570
|
+
# Usage: _suggest_quick_dismiss
|
|
571
|
+
# Returns: JSON {dismissed, hashes_recorded}
|
|
572
|
+
# ============================================================================
|
|
573
|
+
_suggest_quick_dismiss() {
|
|
574
|
+
# Get current suggestions
|
|
575
|
+
suggestions_result=$(bash "$0" suggest-analyze 2>/dev/null || echo '{"suggestions":[]}') # SUPPRESS:OK -- read-default: subcommand may fail
|
|
576
|
+
suggestions_json=$(echo "$suggestions_result" | jq '.result.suggestions // []')
|
|
577
|
+
|
|
578
|
+
dismissed_count=0
|
|
579
|
+
hashes_recorded=()
|
|
580
|
+
|
|
581
|
+
suggestion_count=$(echo "$suggestions_json" | jq 'length')
|
|
582
|
+
|
|
583
|
+
if [[ "$suggestion_count" -gt 0 ]]; then
|
|
584
|
+
for ((i=0; i<suggestion_count; i++)); do
|
|
585
|
+
suggestion=$(echo "$suggestions_json" | jq ".[$i]")
|
|
586
|
+
hash=$(echo "$suggestion" | jq -r '.hash')
|
|
587
|
+
stype=$(echo "$suggestion" | jq -r '.type')
|
|
588
|
+
|
|
589
|
+
# Record hash to prevent re-suggestion
|
|
590
|
+
bash "$0" suggest-record "$hash" "$stype" >/dev/null 2>&1 || _aether_log_error "Could not record suggestion"
|
|
591
|
+
hashes_recorded+=("$hash")
|
|
592
|
+
((dismissed_count++))
|
|
593
|
+
done
|
|
594
|
+
fi
|
|
595
|
+
|
|
596
|
+
# Output message to stderr so stdout is valid JSON only
|
|
597
|
+
echo "Suggestions dismissed. Run with --yes to auto-approve in future." >&2
|
|
598
|
+
|
|
599
|
+
# Build result with hashes as JSON array (handle empty array case)
|
|
600
|
+
if [[ ${#hashes_recorded[@]} -gt 0 ]]; then
|
|
601
|
+
hashes_json=$(printf '%s\n' "${hashes_recorded[@]}" | jq -R . | jq -s .)
|
|
602
|
+
else
|
|
603
|
+
hashes_json="[]"
|
|
604
|
+
fi
|
|
605
|
+
result=$(jq -n \
|
|
606
|
+
--argjson dismissed "$dismissed_count" \
|
|
607
|
+
--argjson hashes "$hashes_json" \
|
|
608
|
+
'{dismissed: $dismissed, hashes_recorded: $hashes}')
|
|
609
|
+
|
|
610
|
+
json_ok "$result"
|
|
611
|
+
}
|
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
SWARM_ID="${1:-current}"
|
|
6
6
|
DATA_DIR="${DATA_DIR:-.aether/data}"
|
|
7
|
-
|
|
7
|
+
# Resolve COLONY_DATA_DIR for per-colony files (standalone script)
|
|
8
|
+
COLONY_DATA_DIR="${COLONY_DATA_DIR:-$DATA_DIR}"
|
|
9
|
+
if [[ -f "$DATA_DIR/COLONY_STATE.json" ]]; then
|
|
10
|
+
_cn=$(jq -r '.colony_name // empty' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null)
|
|
11
|
+
if [[ -n "$_cn" ]]; then
|
|
12
|
+
_cn_safe=$(echo "$_cn" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
|
|
13
|
+
[[ -n "$_cn_safe" ]] && COLONY_DATA_DIR="$DATA_DIR/colonies/$_cn_safe"
|
|
14
|
+
fi
|
|
15
|
+
fi
|
|
16
|
+
DISPLAY_FILE="$COLONY_DATA_DIR/swarm-display.json"
|
|
8
17
|
|
|
9
18
|
# ANSI colors (matching caste-colors.js)
|
|
10
19
|
BLUE='\033[34m'
|