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,860 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scan utility -- repo scanning for smart init research data
|
|
3
|
+
# Provides: _scan_init_research, _scan_tech_stack, _scan_directory_structure,
|
|
4
|
+
# _scan_git_history, _scan_survey_status, _scan_prior_colonies, _scan_complexity
|
|
5
|
+
#
|
|
6
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
7
|
+
# All shared infrastructure (json_ok, json_err, DATA_DIR, SCRIPT_DIR) is available.
|
|
8
|
+
|
|
9
|
+
# Directories to exclude from scanning
|
|
10
|
+
_SCAN_EXCLUDE_DIRS=(
|
|
11
|
+
node_modules
|
|
12
|
+
.git
|
|
13
|
+
.aether
|
|
14
|
+
dist
|
|
15
|
+
build
|
|
16
|
+
__pycache__
|
|
17
|
+
.next
|
|
18
|
+
target
|
|
19
|
+
vendor
|
|
20
|
+
.venv
|
|
21
|
+
venv
|
|
22
|
+
coverage
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Build -not -path flags from _SCAN_EXCLUDE_DIRS for use with find
|
|
26
|
+
_scan_find_exclude_flags() {
|
|
27
|
+
local flags=""
|
|
28
|
+
for dir in "${_SCAN_EXCLUDE_DIRS[@]}"; do
|
|
29
|
+
flags+=" -not -path '*/${dir}/*'"
|
|
30
|
+
done
|
|
31
|
+
printf '%s' "$flags"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Scan tech stack -- detect languages, frameworks, and package managers
|
|
35
|
+
# Usage: _scan_tech_stack <repo_root>
|
|
36
|
+
# Returns: raw JSON via stdout (caller wraps in json_ok)
|
|
37
|
+
_scan_tech_stack() {
|
|
38
|
+
local root="${1:-.}"
|
|
39
|
+
local languages="[]" frameworks="[]" package_managers="[]"
|
|
40
|
+
|
|
41
|
+
# Language detection via file presence
|
|
42
|
+
[[ -f "$root/tsconfig.json" ]] && languages=$(echo "$languages" | jq '. + ["typescript"]')
|
|
43
|
+
[[ -f "$root/package.json" ]] && languages=$(echo "$languages" | jq '. + ["javascript"]')
|
|
44
|
+
[[ -f "$root/requirements.txt" || -f "$root/pyproject.toml" ]] && languages=$(echo "$languages" | jq '. + ["python"]')
|
|
45
|
+
[[ -f "$root/go.mod" ]] && languages=$(echo "$languages" | jq '. + ["go"]')
|
|
46
|
+
[[ -f "$root/Cargo.toml" ]] && languages=$(echo "$languages" | jq '. + ["rust"]')
|
|
47
|
+
[[ -f "$root/Gemfile" ]] && languages=$(echo "$languages" | jq '. + ["ruby"]')
|
|
48
|
+
[[ -f "$root/pom.xml" || -f "$root/build.gradle" ]] && languages=$(echo "$languages" | jq '. + ["java"]')
|
|
49
|
+
|
|
50
|
+
# Framework detection via file presence and package.json deps
|
|
51
|
+
if [[ -f "$root/next.config.js" || -f "$root/next.config.ts" || -f "$root/next.config.mjs" ]]; then
|
|
52
|
+
frameworks=$(echo "$frameworks" | jq '. + ["nextjs"]')
|
|
53
|
+
fi
|
|
54
|
+
if [[ -f "$root/angular.json" ]]; then
|
|
55
|
+
frameworks=$(echo "$frameworks" | jq '. + ["angular"]')
|
|
56
|
+
fi
|
|
57
|
+
if [[ -f "$root/vue.config.js" || -f "$root/vite.config.ts" || -f "$root/vite.config.js" ]]; then
|
|
58
|
+
frameworks=$(echo "$frameworks" | jq '. + ["vue"]')
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Framework detection from package.json dependencies (targeted jq, no full reads)
|
|
62
|
+
if [[ -f "$root/package.json" ]]; then
|
|
63
|
+
local pkg_deps
|
|
64
|
+
pkg_deps=$(jq -r '[(.dependencies // {} | keys[]), (.devDependencies // {} | keys[])] | join("\n")' "$root/package.json" 2>/dev/null || true)
|
|
65
|
+
|
|
66
|
+
if echo "$pkg_deps" | grep -qx 'react'; then
|
|
67
|
+
frameworks=$(echo "$frameworks" | jq '. + ["react"]')
|
|
68
|
+
fi
|
|
69
|
+
if echo "$pkg_deps" | grep -qx 'express'; then
|
|
70
|
+
frameworks=$(echo "$frameworks" | jq '. + ["express"]')
|
|
71
|
+
fi
|
|
72
|
+
if echo "$pkg_deps" | grep -qx 'fastify'; then
|
|
73
|
+
frameworks=$(echo "$frameworks" | jq '. + ["fastify"]')
|
|
74
|
+
fi
|
|
75
|
+
if echo "$pkg_deps" | grep -qx 'svelte'; then
|
|
76
|
+
frameworks=$(echo "$frameworks" | jq '. + ["svelte"]')
|
|
77
|
+
fi
|
|
78
|
+
if echo "$pkg_deps" | grep -qx 'nest'; then
|
|
79
|
+
frameworks=$(echo "$frameworks" | jq '. + ["nestjs"]')
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Package manager detection
|
|
84
|
+
if [[ -f "$root/package.json" ]]; then
|
|
85
|
+
if [[ -f "$root/pnpm-lock.yaml" ]]; then
|
|
86
|
+
package_managers=$(echo "$package_managers" | jq '. + ["pnpm"]')
|
|
87
|
+
elif [[ -f "$root/yarn.lock" ]]; then
|
|
88
|
+
package_managers=$(echo "$package_managers" | jq '. + ["yarn"]')
|
|
89
|
+
elif [[ -f "$root/package-lock.json" ]]; then
|
|
90
|
+
package_managers=$(echo "$package_managers" | jq '. + ["npm"]')
|
|
91
|
+
else
|
|
92
|
+
package_managers=$(echo "$package_managers" | jq '. + ["npm"]')
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
[[ -f "$root/go.mod" ]] && package_managers=$(echo "$package_managers" | jq '. + ["go-modules"]')
|
|
96
|
+
[[ -f "$root/Cargo.toml" ]] && package_managers=$(echo "$package_managers" | jq '. + ["cargo"]')
|
|
97
|
+
if [[ -f "$root/Gemfile" ]]; then
|
|
98
|
+
package_managers=$(echo "$package_managers" | jq '. + ["bundler"]')
|
|
99
|
+
fi
|
|
100
|
+
[[ -f "$root/requirements.txt" ]] && package_managers=$(echo "$package_managers" | jq '. + ["pip"]')
|
|
101
|
+
if [[ -f "$root/pyproject.toml" ]]; then
|
|
102
|
+
package_managers=$(echo "$package_managers" | jq '. + ["poetry"]')
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
jq -n \
|
|
106
|
+
--argjson langs "$languages" \
|
|
107
|
+
--argjson fwks "$frameworks" \
|
|
108
|
+
--argjson pkg_mgrs "$package_managers" \
|
|
109
|
+
'{languages: $langs, frameworks: $fwks, package_managers: $pkg_mgrs}'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Scan directory structure -- measure repo surface
|
|
113
|
+
# Usage: _scan_directory_structure <repo_root>
|
|
114
|
+
# Returns: raw JSON via stdout
|
|
115
|
+
_scan_directory_structure() {
|
|
116
|
+
local root="${1:-.}"
|
|
117
|
+
local exclude_flags
|
|
118
|
+
exclude_flags=$(_scan_find_exclude_flags)
|
|
119
|
+
|
|
120
|
+
# Count files (cap depth at 5 for performance)
|
|
121
|
+
local file_count
|
|
122
|
+
file_count=$(find "$root" -maxdepth 5 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
123
|
+
|
|
124
|
+
# Calculate max directory depth
|
|
125
|
+
local max_depth
|
|
126
|
+
max_depth=$(find "$root" -type d $exclude_flags 2>/dev/null | awk -F/ '{print NF-2}' | sort -rn | head -1)
|
|
127
|
+
[[ -z "$max_depth" || "$max_depth" == "0" ]] && max_depth=1
|
|
128
|
+
|
|
129
|
+
# List top-level directories (exclude hidden dirs and excluded dirs)
|
|
130
|
+
local top_dirs
|
|
131
|
+
top_dirs=$(ls -1d "$root"/*/ 2>/dev/null | while read -r d; do
|
|
132
|
+
local dirname
|
|
133
|
+
dirname=$(basename "$d")
|
|
134
|
+
# Skip hidden dirs
|
|
135
|
+
[[ "$dirname" == .* ]] && continue
|
|
136
|
+
# Skip excluded dirs
|
|
137
|
+
local skip=false
|
|
138
|
+
for excluded in "${_SCAN_EXCLUDE_DIRS[@]}"; do
|
|
139
|
+
[[ "$dirname" == "$excluded" ]] && skip=true && break
|
|
140
|
+
done
|
|
141
|
+
[[ "$skip" == "true" ]] && continue
|
|
142
|
+
echo "$dirname"
|
|
143
|
+
done | jq -R . | jq -s .)
|
|
144
|
+
|
|
145
|
+
jq -n \
|
|
146
|
+
--argjson dirs "$top_dirs" \
|
|
147
|
+
--argjson file_count "$file_count" \
|
|
148
|
+
--argjson max_depth "$max_depth" \
|
|
149
|
+
'{top_level_dirs: $dirs, file_count: $file_count, max_depth: $max_depth}'
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Scan git history -- summarize git repo state
|
|
153
|
+
# Usage: _scan_git_history <repo_root>
|
|
154
|
+
# Returns: raw JSON via stdout
|
|
155
|
+
_scan_git_history() {
|
|
156
|
+
local root="${1:-.}"
|
|
157
|
+
|
|
158
|
+
# Check for .git directory
|
|
159
|
+
if [[ ! -d "$root/.git" ]]; then
|
|
160
|
+
jq -n '{is_git_repo: false, commit_count: 0, recent_commits: []}'
|
|
161
|
+
return
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Count commits
|
|
165
|
+
local commit_count
|
|
166
|
+
commit_count=$(git -C "$root" rev-list --count HEAD 2>/dev/null || echo 0)
|
|
167
|
+
|
|
168
|
+
# Get recent commits (oneline format)
|
|
169
|
+
local recent_log
|
|
170
|
+
recent_log=$(git -C "$root" log --oneline -n 10 2>/dev/null || echo "")
|
|
171
|
+
|
|
172
|
+
# Parse recent commits into JSON array
|
|
173
|
+
local recent_commits="[]"
|
|
174
|
+
if [[ -n "$recent_log" ]]; then
|
|
175
|
+
recent_commits=$(echo "$recent_log" | while read -r line; do
|
|
176
|
+
local hash message
|
|
177
|
+
hash=$(echo "$line" | awk '{print $1}')
|
|
178
|
+
message=$(echo "$line" | cut -d' ' -f2-)
|
|
179
|
+
jq -n --arg hash "$hash" --arg message "$message" '{hash: $hash, message: $message}'
|
|
180
|
+
done | jq -s '.')
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
jq -n \
|
|
184
|
+
--argjson commit_count "$commit_count" \
|
|
185
|
+
--argjson recent_commits "$recent_commits" \
|
|
186
|
+
'{is_git_repo: true, commit_count: $commit_count, recent_commits: $recent_commits}'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Scan survey status -- check territory survey freshness (SCAN-02)
|
|
190
|
+
# Usage: _scan_survey_status <repo_root>
|
|
191
|
+
# Returns: raw JSON via stdout
|
|
192
|
+
_scan_survey_status() {
|
|
193
|
+
local root="${1:-.}"
|
|
194
|
+
local survey_dir="$root/.aether/data/survey"
|
|
195
|
+
local state_file="$root/.aether/data/COLONY_STATE.json"
|
|
196
|
+
|
|
197
|
+
# Check if survey directory exists
|
|
198
|
+
if [[ ! -d "$survey_dir" ]]; then
|
|
199
|
+
jq -n '{has_survey: false, is_stale: false, suggestion: {action: "colonize", reason: "No territory survey found. Run /ant:colonize to map the codebase before planning."}}'
|
|
200
|
+
return
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Check survey completeness (7 required docs)
|
|
204
|
+
local required="PROVISIONS.md TRAILS.md BLUEPRINT.md CHAMBERS.md DISCIPLINES.md SENTINEL-PROTOCOLS.md PATHOGENS.md"
|
|
205
|
+
local missing=""
|
|
206
|
+
for doc in $required; do
|
|
207
|
+
[[ ! -f "$survey_dir/$doc" ]] && missing="$missing $doc"
|
|
208
|
+
done
|
|
209
|
+
|
|
210
|
+
if [[ -n "$missing" ]]; then
|
|
211
|
+
local missing_json
|
|
212
|
+
missing_json=$(echo "$missing" | jq -R 'split(" ") | map(select(length > 0))')
|
|
213
|
+
jq -n \
|
|
214
|
+
--argjson missing "$missing_json" \
|
|
215
|
+
'{has_survey: true, is_stale: false, is_complete: false, missing: $missing, suggestion: {action: "colonize", reason: "Survey is incomplete (missing documents). Run /ant:colonize --force-resurvey to remap."}}'
|
|
216
|
+
return
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# Check staleness from COLONY_STATE.json territory_surveyed field
|
|
220
|
+
local surveyed_at=""
|
|
221
|
+
if [[ -f "$state_file" ]]; then
|
|
222
|
+
surveyed_at=$(jq -r '.territory_surveyed // empty' "$state_file" 2>/dev/null || echo "")
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
if [[ -z "$surveyed_at" ]]; then
|
|
226
|
+
# No timestamp in state -- fall back to file modification times
|
|
227
|
+
local oldest_ts
|
|
228
|
+
if [[ "$(uname)" == "Linux" ]]; then
|
|
229
|
+
oldest_ts=$(find "$survey_dir" -name "*.md" -exec stat -c %Y {} \; 2>/dev/null | sort -n | head -1)
|
|
230
|
+
else
|
|
231
|
+
oldest_ts=$(find "$survey_dir" -name "*.md" -exec stat -f %m {} \; 2>/dev/null | sort -n | head -1)
|
|
232
|
+
fi
|
|
233
|
+
if [[ -n "$oldest_ts" ]]; then
|
|
234
|
+
local now_ts
|
|
235
|
+
now_ts=$(date +%s)
|
|
236
|
+
local age_days=$(( (now_ts - oldest_ts) / 86400 ))
|
|
237
|
+
if [[ "$age_days" -gt 7 ]]; then
|
|
238
|
+
jq -n \
|
|
239
|
+
--argjson age "$age_days" \
|
|
240
|
+
'{has_survey: true, is_stale: true, age_days: $age, suggestion: {action: "colonize", reason: "Survey is \($age) days old. Run /ant:colonize --force-resurvey for fresh data."}}'
|
|
241
|
+
return
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
else
|
|
245
|
+
# Parse ISO-8601 timestamp and compare
|
|
246
|
+
local surveyed_epoch
|
|
247
|
+
if [[ "$(uname)" == "Linux" ]]; then
|
|
248
|
+
surveyed_epoch=$(date -d "$surveyed_at" "+%s" 2>/dev/null || echo 0)
|
|
249
|
+
else
|
|
250
|
+
surveyed_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$surveyed_at" "+%s" 2>/dev/null || echo 0)
|
|
251
|
+
fi
|
|
252
|
+
local now_epoch
|
|
253
|
+
now_epoch=$(date +%s)
|
|
254
|
+
local age_days=$(( (now_epoch - surveyed_epoch) / 86400 ))
|
|
255
|
+
if [[ "$age_days" -gt 7 ]]; then
|
|
256
|
+
jq -n \
|
|
257
|
+
--argjson age "$age_days" \
|
|
258
|
+
--arg surveyed_at "$surveyed_at" \
|
|
259
|
+
'{has_survey: true, is_stale: true, age_days: $age, surveyed_at: $surveyed_at, suggestion: {action: "colonize", reason: "Survey is \($age) days old. Run /ant:colonize --force-resurvey for fresh data."}}'
|
|
260
|
+
return
|
|
261
|
+
fi
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# Survey is fresh and complete
|
|
265
|
+
jq -n '{has_survey: true, is_stale: false, is_complete: true}'
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# Scan prior colonies -- detect active colony state and archived colonies
|
|
269
|
+
# Usage: _scan_prior_colonies <repo_root>
|
|
270
|
+
# Returns: raw JSON via stdout
|
|
271
|
+
_scan_prior_colonies() {
|
|
272
|
+
local root="${1:-.}"
|
|
273
|
+
local chambers_dir="$root/.aether/chambers"
|
|
274
|
+
local state_file="$root/.aether/data/COLONY_STATE.json"
|
|
275
|
+
|
|
276
|
+
local colonies="[]"
|
|
277
|
+
local has_active="false"
|
|
278
|
+
local active_goal=""
|
|
279
|
+
|
|
280
|
+
# Check for active colony
|
|
281
|
+
if [[ -f "$state_file" ]]; then
|
|
282
|
+
local goal state
|
|
283
|
+
goal=$(jq -r '.goal // empty' "$state_file" 2>/dev/null || echo "")
|
|
284
|
+
state=$(jq -r '.state // empty' "$state_file" 2>/dev/null || echo "")
|
|
285
|
+
if [[ -n "$goal" && "$state" != "SEALED" ]]; then
|
|
286
|
+
has_active="true"
|
|
287
|
+
active_goal="$goal"
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
# Check for archived colonies in chambers
|
|
292
|
+
if [[ -d "$chambers_dir" ]]; then
|
|
293
|
+
for chamber in "$chambers_dir"/*/; do
|
|
294
|
+
[[ -d "$chamber" ]] || continue
|
|
295
|
+
local chamber_name
|
|
296
|
+
chamber_name=$(basename "$chamber")
|
|
297
|
+
|
|
298
|
+
# Skip hidden dirs
|
|
299
|
+
[[ "$chamber_name" == .* ]] && continue
|
|
300
|
+
|
|
301
|
+
local chamber_state="$chamber/COLONY_STATE.json"
|
|
302
|
+
[[ -f "$chamber_state" ]] || continue
|
|
303
|
+
|
|
304
|
+
local chamber_goal chamber_date
|
|
305
|
+
chamber_goal=$(jq -r '.goal // "unknown"' "$chamber_state" 2>/dev/null || echo "unknown")
|
|
306
|
+
chamber_date=$(jq -r '.initialized_at // "unknown"' "$chamber_state" 2>/dev/null || echo "unknown")
|
|
307
|
+
|
|
308
|
+
colonies=$(echo "$colonies" | jq \
|
|
309
|
+
--arg name "$chamber_name" \
|
|
310
|
+
--arg goal "$chamber_goal" \
|
|
311
|
+
--arg date "$chamber_date" \
|
|
312
|
+
'. + [{name: $name, goal: $goal, initialized_at: $date}]')
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
jq -n \
|
|
317
|
+
--argjson has_active "$has_active" \
|
|
318
|
+
--arg active_goal "$active_goal" \
|
|
319
|
+
--argjson colonies "$colonies" \
|
|
320
|
+
'{has_active_colony: $has_active, active_goal: $active_goal, archived_colonies: $colonies}'
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# Scan complexity -- estimate repo complexity (SCAN-03)
|
|
324
|
+
# Usage: _scan_complexity <repo_root>
|
|
325
|
+
# Returns: raw JSON via stdout
|
|
326
|
+
_scan_complexity() {
|
|
327
|
+
local root="${1:-.}"
|
|
328
|
+
local exclude_flags
|
|
329
|
+
exclude_flags=$(_scan_find_exclude_flags)
|
|
330
|
+
|
|
331
|
+
# File count (excluding common directories)
|
|
332
|
+
local file_count
|
|
333
|
+
file_count=$(find "$root" -maxdepth 5 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
334
|
+
|
|
335
|
+
# Max directory depth
|
|
336
|
+
local max_depth
|
|
337
|
+
max_depth=$(find "$root" -type d $exclude_flags 2>/dev/null | awk -F/ '{print NF-2}' | sort -rn | head -1)
|
|
338
|
+
[[ -z "$max_depth" || "$max_depth" == "0" ]] && max_depth=1
|
|
339
|
+
|
|
340
|
+
# Dependency count from package manifests
|
|
341
|
+
local dep_count=0
|
|
342
|
+
if [[ -f "$root/package.json" ]]; then
|
|
343
|
+
dep_count=$(jq '[.dependencies // {}, .devDependencies // {}] | add | keys | length' "$root/package.json" 2>/dev/null || echo 0)
|
|
344
|
+
elif [[ -f "$root/Cargo.toml" ]]; then
|
|
345
|
+
dep_count=$(grep -c '^\[' "$root/Cargo.toml" 2>/dev/null || echo 0)
|
|
346
|
+
elif [[ -f "$root/go.mod" ]]; then
|
|
347
|
+
dep_count=$(grep -c '^[a-z]' "$root/go.mod" 2>/dev/null || echo 0)
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
# Classification thresholds
|
|
351
|
+
local size="small"
|
|
352
|
+
if [[ "$file_count" -gt 500 ]] || [[ "$max_depth" -gt 8 ]] || [[ "$dep_count" -gt 50 ]]; then
|
|
353
|
+
size="large"
|
|
354
|
+
elif [[ "$file_count" -gt 100 ]] || [[ "$max_depth" -gt 5 ]] || [[ "$dep_count" -gt 15 ]]; then
|
|
355
|
+
size="medium"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
jq -n \
|
|
359
|
+
--arg size "$size" \
|
|
360
|
+
--argjson file_count "$file_count" \
|
|
361
|
+
--argjson max_depth "$max_depth" \
|
|
362
|
+
--argjson dep_count "$dep_count" \
|
|
363
|
+
'{size: $size, metrics: {file_count: $file_count, max_directory_depth: $max_depth, dependency_count: $dep_count}}'
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# Scan colony context -- extract prior colony summaries and existing charter content
|
|
367
|
+
# Usage: _scan_colony_context <repo_root>
|
|
368
|
+
# Returns: raw JSON via stdout
|
|
369
|
+
_scan_colony_context() {
|
|
370
|
+
local root="${1:-.}"
|
|
371
|
+
local chambers_dir="$root/.aether/chambers"
|
|
372
|
+
local queen_file="$root/.aether/QUEEN.md"
|
|
373
|
+
|
|
374
|
+
local prior_colonies="[]"
|
|
375
|
+
|
|
376
|
+
# Extract prior colony summaries from chambers (max 3, most recent first)
|
|
377
|
+
if [[ -d "$chambers_dir" ]]; then
|
|
378
|
+
# Sort chamber dirs by name descending (date-prefixed names = reverse alpha = most recent first)
|
|
379
|
+
local chamber_list
|
|
380
|
+
chamber_list=$(ls -1d "$chambers_dir"/*/ 2>/dev/null | sort -r)
|
|
381
|
+
|
|
382
|
+
local count=0
|
|
383
|
+
while IFS= read -r chamber; do
|
|
384
|
+
[[ -z "$chamber" ]] && continue
|
|
385
|
+
[[ "$count" -ge 3 ]] && break
|
|
386
|
+
|
|
387
|
+
local chamber_name
|
|
388
|
+
chamber_name=$(basename "$chamber")
|
|
389
|
+
# Skip hidden dirs
|
|
390
|
+
[[ "$chamber_name" == .* ]] && continue
|
|
391
|
+
|
|
392
|
+
local manifest="$chamber/manifest.json"
|
|
393
|
+
local crowned="$chamber/CROWNED-ANTHILL.md"
|
|
394
|
+
|
|
395
|
+
# Skip if neither manifest nor crowned exists
|
|
396
|
+
[[ ! -f "$manifest" && ! -f "$crowned" ]] && continue
|
|
397
|
+
|
|
398
|
+
local goal="" phases="" outcome="" summary=""
|
|
399
|
+
|
|
400
|
+
if [[ -f "$manifest" ]]; then
|
|
401
|
+
goal=$(jq -r '.goal // "unknown"' "$manifest" 2>/dev/null || echo "unknown")
|
|
402
|
+
# Handle phases_completed being either a number or an array (older manifest formats)
|
|
403
|
+
local phases_completed total_phases
|
|
404
|
+
phases_completed=$(jq -r 'if (.phases_completed | type) == "array" then (.phases_completed | length) else (.phases_completed // 0) end' "$manifest" 2>/dev/null || echo "0")
|
|
405
|
+
total_phases=$(jq -r '.total_phases // 0' "$manifest" 2>/dev/null || echo "0")
|
|
406
|
+
phases="${phases_completed}/${total_phases}"
|
|
407
|
+
outcome=$(jq -r '.milestone // "unknown"' "$manifest" 2>/dev/null || echo "unknown")
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
if [[ -f "$crowned" ]]; then
|
|
411
|
+
# Extract "The Work" section: lines between "## The Work" and next "## " header (or EOF)
|
|
412
|
+
# Use sed to get the range, strip header lines, take first 2 content lines, join
|
|
413
|
+
summary=$(sed -n '/^## The Work$/,/^## /p' "$crowned" 2>/dev/null \
|
|
414
|
+
| grep -v '^## ' \
|
|
415
|
+
| sed '/^$/d' \
|
|
416
|
+
| head -2 \
|
|
417
|
+
| tr '\n' ' ' \
|
|
418
|
+
| sed 's/ */ /g; s/^ *//; s/ *$//')
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
prior_colonies=$(echo "$prior_colonies" | jq \
|
|
422
|
+
--arg goal "$goal" \
|
|
423
|
+
--arg phases "$phases" \
|
|
424
|
+
--arg outcome "$outcome" \
|
|
425
|
+
--arg summary "$summary" \
|
|
426
|
+
'. + [{goal: $goal, phases: $phases, outcome: $outcome, summary: $summary}]')
|
|
427
|
+
|
|
428
|
+
count=$((count + 1))
|
|
429
|
+
done <<< "$chamber_list"
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# Extract existing charter content from QUEEN.md
|
|
433
|
+
local charter_intent="" charter_vision="" charter_governance=""
|
|
434
|
+
|
|
435
|
+
if [[ -f "$queen_file" ]]; then
|
|
436
|
+
charter_intent=$(grep '\[charter\] \*\*Intent\*\*:' "$queen_file" 2>/dev/null \
|
|
437
|
+
| sed 's/.*\*\*Intent\*\*: //' \
|
|
438
|
+
| sed 's/ (Colony:.*//' || true)
|
|
439
|
+
charter_vision=$(grep '\[charter\] \*\*Vision\*\*:' "$queen_file" 2>/dev/null \
|
|
440
|
+
| sed 's/.*\*\*Vision\*\*: //' \
|
|
441
|
+
| sed 's/ (Colony:.*//' || true)
|
|
442
|
+
charter_governance=$(grep '\[charter\] \*\*Governance\*\*:' "$queen_file" 2>/dev/null \
|
|
443
|
+
| sed 's/.*\*\*Governance\*\*: //' \
|
|
444
|
+
| sed 's/ (Colony:.*//' || true)
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
jq -n \
|
|
448
|
+
--argjson prior_colonies "$prior_colonies" \
|
|
449
|
+
--arg intent "$charter_intent" \
|
|
450
|
+
--arg vision "$charter_vision" \
|
|
451
|
+
--arg governance "$charter_governance" \
|
|
452
|
+
'{prior_colonies: $prior_colonies, existing_charter: {intent: $intent, vision: $vision, governance: $governance}}'
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# Scan governance -- detect governance-related config files and produce prescriptive rules
|
|
456
|
+
# Usage: _scan_governance <repo_root>
|
|
457
|
+
# Returns: raw JSON via stdout
|
|
458
|
+
_scan_governance() {
|
|
459
|
+
local root="${1:-.}"
|
|
460
|
+
local exclude_flags
|
|
461
|
+
exclude_flags=$(_scan_find_exclude_flags)
|
|
462
|
+
|
|
463
|
+
local rules="[]"
|
|
464
|
+
local sources_checked=0
|
|
465
|
+
|
|
466
|
+
# 1. CONTRIBUTING.md
|
|
467
|
+
sources_checked=$((sources_checked + 1))
|
|
468
|
+
if [[ -f "$root/CONTRIBUTING.md" ]]; then
|
|
469
|
+
local contrib_summary
|
|
470
|
+
contrib_summary=$(head -20 "$root/CONTRIBUTING.md" 2>/dev/null \
|
|
471
|
+
| tr '\n' ' ' \
|
|
472
|
+
| sed 's/ */ /g' \
|
|
473
|
+
| cut -c1-200)
|
|
474
|
+
# Skip if file is effectively empty (just whitespace)
|
|
475
|
+
if [[ -n "$(echo "$contrib_summary" | tr -d '[:space:]')" ]]; then
|
|
476
|
+
rules=$(echo "$rules" | jq \
|
|
477
|
+
--arg rule "Follow CONTRIBUTING.md guidelines" \
|
|
478
|
+
--arg source "CONTRIBUTING.md" \
|
|
479
|
+
--arg detail "$contrib_summary" \
|
|
480
|
+
'. + [{rule: $rule, source: $source, detail: $detail, strength: "required"}]')
|
|
481
|
+
fi
|
|
482
|
+
fi
|
|
483
|
+
|
|
484
|
+
# 2. Test configs -- only emit "TDD required" if test FILES also exist
|
|
485
|
+
sources_checked=$((sources_checked + 1))
|
|
486
|
+
local has_test_config=false
|
|
487
|
+
for tc in "$root"/jest.config.* "$root"/vitest.config.* "$root/pytest.ini"; do
|
|
488
|
+
if [[ -f "$tc" ]]; then
|
|
489
|
+
has_test_config=true
|
|
490
|
+
break
|
|
491
|
+
fi
|
|
492
|
+
done
|
|
493
|
+
# Also check pyproject.toml for pytest section
|
|
494
|
+
if [[ "$has_test_config" == "false" && -f "$root/pyproject.toml" ]]; then
|
|
495
|
+
if grep -q '\[tool\.pytest' "$root/pyproject.toml" 2>/dev/null; then
|
|
496
|
+
has_test_config=true
|
|
497
|
+
fi
|
|
498
|
+
fi
|
|
499
|
+
# Check Cargo.toml for [test] section
|
|
500
|
+
if [[ "$has_test_config" == "false" && -f "$root/Cargo.toml" ]]; then
|
|
501
|
+
if grep -q '^\[test\]' "$root/Cargo.toml" 2>/dev/null; then
|
|
502
|
+
has_test_config=true
|
|
503
|
+
fi
|
|
504
|
+
fi
|
|
505
|
+
# Check for go test files
|
|
506
|
+
if [[ "$has_test_config" == "false" ]]; then
|
|
507
|
+
local go_test_count
|
|
508
|
+
go_test_count=$(find "$root" -maxdepth 4 -type f -name "*_test.go" $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
509
|
+
if [[ "$go_test_count" -gt 0 ]]; then
|
|
510
|
+
has_test_config=true
|
|
511
|
+
fi
|
|
512
|
+
fi
|
|
513
|
+
|
|
514
|
+
if [[ "$has_test_config" == "true" ]]; then
|
|
515
|
+
# Cross-reference: check if test files actually exist
|
|
516
|
+
local test_file_count
|
|
517
|
+
test_file_count=$(find "$root" -maxdepth 4 -type f \
|
|
518
|
+
\( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" -o -name "*_test.go" \) \
|
|
519
|
+
$exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
520
|
+
|
|
521
|
+
# Also check tests/ and __tests__/ directories
|
|
522
|
+
if [[ "$test_file_count" -eq 0 ]]; then
|
|
523
|
+
for tdir in "$root/tests" "$root/__tests__" "$root/test"; do
|
|
524
|
+
if [[ -d "$tdir" ]]; then
|
|
525
|
+
local dir_count
|
|
526
|
+
dir_count=$(find "$tdir" -maxdepth 3 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
527
|
+
test_file_count=$((test_file_count + dir_count))
|
|
528
|
+
fi
|
|
529
|
+
done
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
if [[ "$test_file_count" -gt 0 ]]; then
|
|
533
|
+
rules=$(echo "$rules" | jq \
|
|
534
|
+
--arg rule "TDD required -- test config and existing tests detected" \
|
|
535
|
+
--arg source "test configuration" \
|
|
536
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
537
|
+
fi
|
|
538
|
+
fi
|
|
539
|
+
|
|
540
|
+
# 3. Linter/formatter configs
|
|
541
|
+
sources_checked=$((sources_checked + 1))
|
|
542
|
+
# ESLint
|
|
543
|
+
local has_eslint=false
|
|
544
|
+
for ec in "$root"/.eslintrc.* "$root"/eslint.config.*; do
|
|
545
|
+
if [[ -f "$ec" ]]; then
|
|
546
|
+
has_eslint=true
|
|
547
|
+
break
|
|
548
|
+
fi
|
|
549
|
+
done
|
|
550
|
+
if [[ "$has_eslint" == "true" ]]; then
|
|
551
|
+
rules=$(echo "$rules" | jq \
|
|
552
|
+
--arg rule "ESLint enforced -- follow existing lint rules" \
|
|
553
|
+
--arg source "ESLint" \
|
|
554
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
555
|
+
fi
|
|
556
|
+
|
|
557
|
+
# Prettier
|
|
558
|
+
local has_prettier=false
|
|
559
|
+
for pc in "$root"/.prettierrc*; do
|
|
560
|
+
if [[ -f "$pc" ]]; then
|
|
561
|
+
has_prettier=true
|
|
562
|
+
break
|
|
563
|
+
fi
|
|
564
|
+
done
|
|
565
|
+
if [[ "$has_prettier" == "true" ]]; then
|
|
566
|
+
rules=$(echo "$rules" | jq \
|
|
567
|
+
--arg rule "Prettier formatting enforced -- maintain consistent code style" \
|
|
568
|
+
--arg source "Prettier" \
|
|
569
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
# rustfmt
|
|
573
|
+
if [[ -f "$root/rustfmt.toml" ]]; then
|
|
574
|
+
rules=$(echo "$rules" | jq \
|
|
575
|
+
--arg rule "rustfmt enforced -- maintain Rust formatting standards" \
|
|
576
|
+
--arg source "rustfmt" \
|
|
577
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
578
|
+
fi
|
|
579
|
+
|
|
580
|
+
# .flake8
|
|
581
|
+
if [[ -f "$root/.flake8" ]]; then
|
|
582
|
+
rules=$(echo "$rules" | jq \
|
|
583
|
+
--arg rule "Flake8 enforced -- follow Python linting rules" \
|
|
584
|
+
--arg source "Flake8" \
|
|
585
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
586
|
+
fi
|
|
587
|
+
|
|
588
|
+
# pyproject.toml [tool.black]
|
|
589
|
+
if [[ -f "$root/pyproject.toml" ]]; then
|
|
590
|
+
if grep -q '\[tool\.black\]' "$root/pyproject.toml" 2>/dev/null; then
|
|
591
|
+
rules=$(echo "$rules" | jq \
|
|
592
|
+
--arg rule "Black formatter enforced -- maintain Python code style" \
|
|
593
|
+
--arg source "Black" \
|
|
594
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
595
|
+
fi
|
|
596
|
+
fi
|
|
597
|
+
|
|
598
|
+
# 4. CI/CD
|
|
599
|
+
sources_checked=$((sources_checked + 1))
|
|
600
|
+
local has_ci=false
|
|
601
|
+
if [[ -d "$root/.github/workflows" ]]; then
|
|
602
|
+
has_ci=true
|
|
603
|
+
elif [[ -f "$root/Jenkinsfile" ]]; then
|
|
604
|
+
has_ci=true
|
|
605
|
+
elif [[ -f "$root/.gitlab-ci.yml" ]]; then
|
|
606
|
+
has_ci=true
|
|
607
|
+
elif [[ -d "$root/.circleci" ]]; then
|
|
608
|
+
has_ci=true
|
|
609
|
+
fi
|
|
610
|
+
|
|
611
|
+
if [[ "$has_ci" == "true" ]]; then
|
|
612
|
+
rules=$(echo "$rules" | jq \
|
|
613
|
+
--arg rule "CI/CD pipeline active -- ensure all checks pass before merging" \
|
|
614
|
+
--arg source "CI configuration" \
|
|
615
|
+
'. + [{rule: $rule, source: $source, strength: "required"}]')
|
|
616
|
+
fi
|
|
617
|
+
|
|
618
|
+
jq -n \
|
|
619
|
+
--argjson rules "$rules" \
|
|
620
|
+
--argjson sources_checked "$sources_checked" \
|
|
621
|
+
'{rules: $rules, sources_checked: $sources_checked}'
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
# Scan pheromone suggestions -- derive FOCUS/REDIRECT signals from codebase patterns
|
|
625
|
+
# Usage: _scan_pheromone_suggestions <repo_root>
|
|
626
|
+
# Returns: raw JSON array via stdout (max 5, sorted by priority descending)
|
|
627
|
+
_scan_pheromone_suggestions() {
|
|
628
|
+
local root="${1:-.}"
|
|
629
|
+
local exclude_flags
|
|
630
|
+
exclude_flags=$(_scan_find_exclude_flags)
|
|
631
|
+
|
|
632
|
+
local suggestions="[]"
|
|
633
|
+
|
|
634
|
+
# 1. Security -- .env files (priority 9, REDIRECT)
|
|
635
|
+
if [[ -f "$root/.env" || -f "$root/.env.local" || -f "$root/.env.example" ]]; then
|
|
636
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
637
|
+
--arg content 'Environment files detected -- never commit secrets or .env files' \
|
|
638
|
+
--arg reason 'Found .env files in repo root' \
|
|
639
|
+
'. + [{type: "REDIRECT", content: $content, reason: $reason, priority: 9}]')
|
|
640
|
+
fi
|
|
641
|
+
|
|
642
|
+
# 2. Testing gap (priority 9, REDIRECT): test config exists but NO test files
|
|
643
|
+
local has_test_config=false
|
|
644
|
+
for tc in "$root"/jest.config.* "$root"/vitest.config.* "$root/pytest.ini"; do
|
|
645
|
+
if [[ -f "$tc" ]]; then
|
|
646
|
+
has_test_config=true
|
|
647
|
+
break
|
|
648
|
+
fi
|
|
649
|
+
done
|
|
650
|
+
if [[ "$has_test_config" == "false" && -f "$root/pyproject.toml" ]]; then
|
|
651
|
+
grep -q '\[tool\.pytest' "$root/pyproject.toml" 2>/dev/null && has_test_config=true
|
|
652
|
+
fi
|
|
653
|
+
|
|
654
|
+
local test_file_count=0
|
|
655
|
+
if [[ "$has_test_config" == "true" ]]; then
|
|
656
|
+
test_file_count=$(find "$root" -maxdepth 4 -type f \
|
|
657
|
+
\( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" -o -name "*_test.go" \) \
|
|
658
|
+
$exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
659
|
+
# Also check tests/ and __tests__/ directories
|
|
660
|
+
if [[ "$test_file_count" -eq 0 ]]; then
|
|
661
|
+
for tdir in "$root/tests" "$root/__tests__" "$root/test"; do
|
|
662
|
+
if [[ -d "$tdir" ]]; then
|
|
663
|
+
local dir_count
|
|
664
|
+
dir_count=$(find "$tdir" -maxdepth 3 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
665
|
+
test_file_count=$((test_file_count + dir_count))
|
|
666
|
+
fi
|
|
667
|
+
done
|
|
668
|
+
fi
|
|
669
|
+
fi
|
|
670
|
+
|
|
671
|
+
if [[ "$has_test_config" == "true" && "$test_file_count" -eq 0 ]]; then
|
|
672
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
673
|
+
--arg content 'Test config exists but no test files -- do not skip writing tests' \
|
|
674
|
+
--arg reason 'Test configuration found without corresponding test files' \
|
|
675
|
+
'. + [{type: "REDIRECT", content: $content, reason: $reason, priority: 9}]')
|
|
676
|
+
fi
|
|
677
|
+
|
|
678
|
+
# 3. Testing present (priority 8, FOCUS): test config + test files exist
|
|
679
|
+
if [[ "$has_test_config" == "true" && "$test_file_count" -gt 0 ]]; then
|
|
680
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
681
|
+
--arg content "Testing infrastructure present ($test_file_count test files) -- maintain TDD discipline" \
|
|
682
|
+
--arg reason "Detected test config and $test_file_count test files" \
|
|
683
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 8}]')
|
|
684
|
+
fi
|
|
685
|
+
|
|
686
|
+
# Also check for tests when no formal test config exists (e.g., npm test script)
|
|
687
|
+
if [[ "$has_test_config" == "false" ]]; then
|
|
688
|
+
test_file_count=$(find "$root" -maxdepth 4 -type f \
|
|
689
|
+
\( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" -o -name "*_test.go" \) \
|
|
690
|
+
$exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
691
|
+
if [[ "$test_file_count" -eq 0 ]]; then
|
|
692
|
+
for tdir in "$root/tests" "$root/__tests__" "$root/test"; do
|
|
693
|
+
if [[ -d "$tdir" ]]; then
|
|
694
|
+
local dir_count
|
|
695
|
+
dir_count=$(find "$tdir" -maxdepth 3 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
696
|
+
test_file_count=$((test_file_count + dir_count))
|
|
697
|
+
fi
|
|
698
|
+
done
|
|
699
|
+
fi
|
|
700
|
+
if [[ "$test_file_count" -gt 0 ]]; then
|
|
701
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
702
|
+
--arg content "Testing infrastructure present ($test_file_count test files) -- maintain TDD discipline" \
|
|
703
|
+
--arg reason "Detected $test_file_count test files" \
|
|
704
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 8}]')
|
|
705
|
+
fi
|
|
706
|
+
fi
|
|
707
|
+
|
|
708
|
+
# 4. Security deps (priority 8, FOCUS): check package.json for security-related packages
|
|
709
|
+
if [[ -f "$root/package.json" ]]; then
|
|
710
|
+
local pkg_deps
|
|
711
|
+
pkg_deps=$(jq -r '[(.dependencies // {} | keys[]), (.devDependencies // {} | keys[])] | join("\n")' "$root/package.json" 2>/dev/null || true)
|
|
712
|
+
local has_security_dep=false
|
|
713
|
+
for dep in helmet cors bcrypt passport jsonwebtoken; do
|
|
714
|
+
if echo "$pkg_deps" | grep -qx "$dep"; then
|
|
715
|
+
has_security_dep=true
|
|
716
|
+
break
|
|
717
|
+
fi
|
|
718
|
+
done
|
|
719
|
+
if [[ "$has_security_dep" == "true" ]]; then
|
|
720
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
721
|
+
--arg content 'Security dependencies detected -- maintain security best practices' \
|
|
722
|
+
--arg reason 'Found security-related packages (helmet, cors, bcrypt, passport, or jsonwebtoken)' \
|
|
723
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 8}]')
|
|
724
|
+
fi
|
|
725
|
+
fi
|
|
726
|
+
|
|
727
|
+
# 5. Linter/formatter (priority 7, FOCUS)
|
|
728
|
+
local has_linter=false
|
|
729
|
+
for lc in "$root"/.eslintrc.* "$root"/eslint.config.* "$root"/.prettierrc* "$root/.flake8" "$root/rustfmt.toml"; do
|
|
730
|
+
if [[ -f "$lc" ]]; then
|
|
731
|
+
has_linter=true
|
|
732
|
+
break
|
|
733
|
+
fi
|
|
734
|
+
done
|
|
735
|
+
if [[ "$has_linter" == "true" ]]; then
|
|
736
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
737
|
+
--arg content 'Code quality tools configured -- follow existing lint/format rules' \
|
|
738
|
+
--arg reason 'Detected linter/formatter configuration' \
|
|
739
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 7}]')
|
|
740
|
+
fi
|
|
741
|
+
|
|
742
|
+
# 6. CI/CD (priority 7, FOCUS)
|
|
743
|
+
if [[ -d "$root/.github/workflows" || -f "$root/Jenkinsfile" || -f "$root/.gitlab-ci.yml" ]]; then
|
|
744
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
745
|
+
--arg content 'CI/CD pipeline active -- ensure changes pass all checks' \
|
|
746
|
+
--arg reason 'Detected CI/CD configuration' \
|
|
747
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 7}]')
|
|
748
|
+
fi
|
|
749
|
+
|
|
750
|
+
# 7. TypeScript strict (priority 6, FOCUS)
|
|
751
|
+
if [[ -f "$root/tsconfig.json" ]]; then
|
|
752
|
+
if grep -q '"strict"[[:space:]]*:[[:space:]]*true' "$root/tsconfig.json" 2>/dev/null; then
|
|
753
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
754
|
+
--arg content 'TypeScript strict mode enabled -- maintain type safety' \
|
|
755
|
+
--arg reason 'tsconfig.json has strict: true' \
|
|
756
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 6}]')
|
|
757
|
+
fi
|
|
758
|
+
fi
|
|
759
|
+
|
|
760
|
+
# 8. CONTRIBUTING.md (priority 6, FOCUS)
|
|
761
|
+
if [[ -f "$root/CONTRIBUTING.md" ]]; then
|
|
762
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
763
|
+
--arg content 'Contribution guidelines documented -- follow project conventions' \
|
|
764
|
+
--arg reason 'CONTRIBUTING.md found in repo root' \
|
|
765
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 6}]')
|
|
766
|
+
fi
|
|
767
|
+
|
|
768
|
+
# 9. Large codebase (priority 6, FOCUS): file_count > 500
|
|
769
|
+
local file_count
|
|
770
|
+
file_count=$(find "$root" -maxdepth 5 -type f $exclude_flags 2>/dev/null | wc -l | tr -d ' ')
|
|
771
|
+
if [[ "$file_count" -gt 500 ]]; then
|
|
772
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
773
|
+
--arg content 'Large codebase -- scope changes carefully, prefer incremental work' \
|
|
774
|
+
--arg reason "Detected $file_count files (threshold: 500)" \
|
|
775
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 6}]')
|
|
776
|
+
fi
|
|
777
|
+
|
|
778
|
+
# 10. Docker (priority 5, FOCUS)
|
|
779
|
+
if [[ -f "$root/Dockerfile" || -f "$root/docker-compose.yml" || -f "$root/docker-compose.yaml" ]]; then
|
|
780
|
+
suggestions=$(echo "$suggestions" | jq \
|
|
781
|
+
--arg content 'Containerized deployment -- test in container environment' \
|
|
782
|
+
--arg reason 'Detected Docker configuration' \
|
|
783
|
+
'. + [{type: "FOCUS", content: $content, reason: $reason, priority: 5}]')
|
|
784
|
+
fi
|
|
785
|
+
|
|
786
|
+
# Sort by priority descending and truncate to max 5
|
|
787
|
+
echo "$suggestions" | jq '[sort_by(-.priority)[:5] | .[] ]'
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
# Main entry point: scan repo and produce structured research JSON
|
|
791
|
+
# Usage: _scan_init_research [--target <dir>]
|
|
792
|
+
# Options:
|
|
793
|
+
# --target <dir> Directory to scan (default: $AETHER_ROOT or current dir)
|
|
794
|
+
_scan_init_research() {
|
|
795
|
+
local target_dir=""
|
|
796
|
+
|
|
797
|
+
# Parse arguments
|
|
798
|
+
while [[ $# -gt 0 ]]; do
|
|
799
|
+
case "$1" in
|
|
800
|
+
--target)
|
|
801
|
+
target_dir="$2"
|
|
802
|
+
shift 2
|
|
803
|
+
;;
|
|
804
|
+
*)
|
|
805
|
+
shift
|
|
806
|
+
;;
|
|
807
|
+
esac
|
|
808
|
+
done
|
|
809
|
+
|
|
810
|
+
# Default target
|
|
811
|
+
target_dir="${target_dir:-${AETHER_ROOT:-.}}"
|
|
812
|
+
|
|
813
|
+
# Validate target exists
|
|
814
|
+
if [[ ! -d "$target_dir" ]]; then
|
|
815
|
+
json_err "$E_FILE_NOT_FOUND" "Target directory does not exist: $target_dir"
|
|
816
|
+
return 1
|
|
817
|
+
fi
|
|
818
|
+
|
|
819
|
+
# Run sub-scans (each returns raw JSON, caller wraps in json_ok)
|
|
820
|
+
local tech_stack directory_structure git_history survey_status prior_colonies complexity
|
|
821
|
+
local colony_context governance pheromone_suggestions
|
|
822
|
+
|
|
823
|
+
tech_stack=$(_scan_tech_stack "$target_dir")
|
|
824
|
+
directory_structure=$(_scan_directory_structure "$target_dir")
|
|
825
|
+
git_history=$(_scan_git_history "$target_dir")
|
|
826
|
+
survey_status=$(_scan_survey_status "$target_dir")
|
|
827
|
+
prior_colonies=$(_scan_prior_colonies "$target_dir")
|
|
828
|
+
complexity=$(_scan_complexity "$target_dir")
|
|
829
|
+
colony_context=$(_scan_colony_context "$target_dir")
|
|
830
|
+
governance=$(_scan_governance "$target_dir")
|
|
831
|
+
pheromone_suggestions=$(_scan_pheromone_suggestions "$target_dir")
|
|
832
|
+
|
|
833
|
+
# Assemble final output via jq
|
|
834
|
+
local result
|
|
835
|
+
result=$(jq -n \
|
|
836
|
+
--argjson tech_stack "$tech_stack" \
|
|
837
|
+
--argjson directory_structure "$directory_structure" \
|
|
838
|
+
--argjson git_history "$git_history" \
|
|
839
|
+
--argjson survey_status "$survey_status" \
|
|
840
|
+
--argjson prior_colonies "$prior_colonies" \
|
|
841
|
+
--argjson complexity "$complexity" \
|
|
842
|
+
--argjson colony_context "$colony_context" \
|
|
843
|
+
--argjson governance "$governance" \
|
|
844
|
+
--argjson pheromone_suggestions "$pheromone_suggestions" \
|
|
845
|
+
'{
|
|
846
|
+
schema_version: 1,
|
|
847
|
+
tech_stack: $tech_stack,
|
|
848
|
+
directory_structure: $directory_structure,
|
|
849
|
+
git_history: $git_history,
|
|
850
|
+
survey_status: $survey_status,
|
|
851
|
+
prior_colonies: $prior_colonies,
|
|
852
|
+
complexity: $complexity,
|
|
853
|
+
colony_context: $colony_context,
|
|
854
|
+
governance: $governance,
|
|
855
|
+
pheromone_suggestions: $pheromone_suggestions,
|
|
856
|
+
scanned_at: (now | todate)
|
|
857
|
+
}')
|
|
858
|
+
|
|
859
|
+
json_ok "$result"
|
|
860
|
+
}
|