aether-colony 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/.aether/aether-utils.sh +3150 -3349
  2. package/.aether/agents-claude/aether-ambassador.md +265 -0
  3. package/.aether/agents-claude/aether-archaeologist.md +327 -0
  4. package/.aether/agents-claude/aether-architect.md +236 -0
  5. package/.aether/agents-claude/aether-auditor.md +271 -0
  6. package/.aether/agents-claude/aether-builder.md +224 -0
  7. package/.aether/agents-claude/aether-chaos.md +269 -0
  8. package/.aether/agents-claude/aether-chronicler.md +305 -0
  9. package/.aether/agents-claude/aether-gatekeeper.md +330 -0
  10. package/.aether/agents-claude/aether-includer.md +374 -0
  11. package/.aether/agents-claude/aether-keeper.md +272 -0
  12. package/.aether/agents-claude/aether-measurer.md +322 -0
  13. package/.aether/agents-claude/aether-oracle.md +237 -0
  14. package/.aether/agents-claude/aether-probe.md +211 -0
  15. package/.aether/agents-claude/aether-queen.md +330 -0
  16. package/.aether/agents-claude/aether-route-setter.md +178 -0
  17. package/.aether/agents-claude/aether-sage.md +418 -0
  18. package/.aether/agents-claude/aether-scout.md +179 -0
  19. package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
  20. package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
  21. package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
  22. package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
  23. package/.aether/agents-claude/aether-tracker.md +270 -0
  24. package/.aether/agents-claude/aether-watcher.md +280 -0
  25. package/.aether/agents-claude/aether-weaver.md +248 -0
  26. package/.aether/commands/archaeology.yaml +653 -0
  27. package/.aether/commands/build.yaml +1221 -0
  28. package/.aether/commands/chaos.yaml +653 -0
  29. package/.aether/commands/colonize.yaml +438 -0
  30. package/.aether/commands/continue.yaml +1484 -0
  31. package/.aether/commands/council.yaml +304 -0
  32. package/.aether/commands/data-clean.yaml +80 -0
  33. package/.aether/commands/dream.yaml +275 -0
  34. package/.aether/commands/entomb.yaml +863 -0
  35. package/.aether/commands/export-signals.yaml +64 -0
  36. package/.aether/commands/feedback.yaml +158 -0
  37. package/.aether/commands/flag.yaml +160 -0
  38. package/.aether/commands/flags.yaml +177 -0
  39. package/.aether/commands/focus.yaml +112 -0
  40. package/.aether/commands/help.yaml +167 -0
  41. package/.aether/commands/history.yaml +137 -0
  42. package/.aether/commands/import-signals.yaml +79 -0
  43. package/.aether/commands/init.yaml +469 -0
  44. package/.aether/commands/insert-phase.yaml +98 -0
  45. package/.aether/commands/interpret.yaml +285 -0
  46. package/.aether/commands/lay-eggs.yaml +224 -0
  47. package/.aether/commands/maturity.yaml +122 -0
  48. package/.aether/commands/memory-details.yaml +74 -0
  49. package/.aether/commands/migrate-state.yaml +174 -0
  50. package/.aether/commands/oracle.yaml +1224 -0
  51. package/.aether/commands/organize.yaml +446 -0
  52. package/.aether/commands/patrol.yaml +621 -0
  53. package/.aether/commands/pause-colony.yaml +424 -0
  54. package/.aether/commands/phase.yaml +124 -0
  55. package/.aether/commands/pheromones.yaml +153 -0
  56. package/.aether/commands/plan.yaml +1313 -0
  57. package/.aether/commands/preferences.yaml +63 -0
  58. package/.aether/commands/redirect.yaml +123 -0
  59. package/.aether/commands/resume-colony.yaml +373 -0
  60. package/.aether/commands/resume.yaml +398 -0
  61. package/.aether/commands/run.yaml +193 -0
  62. package/.aether/commands/seal.yaml +1205 -0
  63. package/.aether/commands/skill-create.yaml +337 -0
  64. package/.aether/commands/status.yaml +364 -0
  65. package/.aether/commands/swarm.yaml +352 -0
  66. package/.aether/commands/tunnels.yaml +814 -0
  67. package/.aether/commands/update.yaml +131 -0
  68. package/.aether/commands/verify-castes.yaml +159 -0
  69. package/.aether/commands/watch.yaml +454 -0
  70. package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
  71. package/.aether/docs/QUEEN-SYSTEM.md +11 -11
  72. package/.aether/docs/README.md +32 -2
  73. package/.aether/docs/command-playbooks/README.md +23 -0
  74. package/.aether/docs/command-playbooks/build-complete.md +349 -0
  75. package/.aether/docs/command-playbooks/build-context.md +282 -0
  76. package/.aether/docs/command-playbooks/build-full.md +1682 -0
  77. package/.aether/docs/command-playbooks/build-prep.md +283 -0
  78. package/.aether/docs/command-playbooks/build-verify.md +405 -0
  79. package/.aether/docs/command-playbooks/build-wave.md +749 -0
  80. package/.aether/docs/command-playbooks/continue-advance.md +524 -0
  81. package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
  82. package/.aether/docs/command-playbooks/continue-full.md +1724 -0
  83. package/.aether/docs/command-playbooks/continue-gates.md +686 -0
  84. package/.aether/docs/command-playbooks/continue-verify.md +406 -0
  85. package/.aether/docs/context-continuity.md +84 -0
  86. package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
  87. package/.aether/docs/error-codes.md +1 -1
  88. package/.aether/docs/known-issues.md +34 -173
  89. package/.aether/docs/pheromones.md +86 -6
  90. package/.aether/docs/plans/pheromone-display-plan.md +257 -0
  91. package/.aether/docs/queen-commands.md +10 -9
  92. package/.aether/docs/source-of-truth-map.md +132 -0
  93. package/.aether/docs/xml-utilities.md +47 -0
  94. package/.aether/rules/aether-colony.md +23 -13
  95. package/.aether/scripts/incident-test-add.sh +47 -0
  96. package/.aether/scripts/weekly-audit.sh +79 -0
  97. package/.aether/skills/.index.json +649 -0
  98. package/.aether/skills/colony/.manifest.json +16 -0
  99. package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
  100. package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
  101. package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
  102. package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
  103. package/.aether/skills/colony/context-management/SKILL.md +80 -0
  104. package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
  105. package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
  106. package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
  107. package/.aether/skills/colony/state-safety/SKILL.md +84 -0
  108. package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
  109. package/.aether/skills/domain/.manifest.json +24 -0
  110. package/.aether/skills/domain/README.md +33 -0
  111. package/.aether/skills/domain/django/SKILL.md +49 -0
  112. package/.aether/skills/domain/docker/SKILL.md +52 -0
  113. package/.aether/skills/domain/golang/SKILL.md +52 -0
  114. package/.aether/skills/domain/graphql/SKILL.md +51 -0
  115. package/.aether/skills/domain/html-css/SKILL.md +48 -0
  116. package/.aether/skills/domain/nextjs/SKILL.md +45 -0
  117. package/.aether/skills/domain/nodejs/SKILL.md +53 -0
  118. package/.aether/skills/domain/postgresql/SKILL.md +53 -0
  119. package/.aether/skills/domain/prisma/SKILL.md +59 -0
  120. package/.aether/skills/domain/python/SKILL.md +50 -0
  121. package/.aether/skills/domain/rails/SKILL.md +52 -0
  122. package/.aether/skills/domain/react/SKILL.md +45 -0
  123. package/.aether/skills/domain/rest-api/SKILL.md +58 -0
  124. package/.aether/skills/domain/svelte/SKILL.md +47 -0
  125. package/.aether/skills/domain/tailwind/SKILL.md +45 -0
  126. package/.aether/skills/domain/testing/SKILL.md +53 -0
  127. package/.aether/skills/domain/typescript/SKILL.md +58 -0
  128. package/.aether/skills/domain/vue/SKILL.md +49 -0
  129. package/.aether/templates/QUEEN.md.template +23 -41
  130. package/.aether/templates/colony-state-reset.jq.template +1 -0
  131. package/.aether/templates/colony-state.template.json +4 -0
  132. package/.aether/templates/learning-observations.template.json +6 -0
  133. package/.aether/templates/midden.template.json +13 -0
  134. package/.aether/templates/pheromones.template.json +6 -0
  135. package/.aether/templates/session.template.json +9 -0
  136. package/.aether/utils/atomic-write.sh +63 -17
  137. package/.aether/utils/chamber-utils.sh +145 -2
  138. package/.aether/utils/emoji-audit.sh +166 -0
  139. package/.aether/utils/error-handler.sh +21 -7
  140. package/.aether/utils/file-lock.sh +182 -27
  141. package/.aether/utils/flag.sh +267 -0
  142. package/.aether/utils/hive.sh +572 -0
  143. package/.aether/utils/learning.sh +1928 -0
  144. package/.aether/utils/midden.sh +342 -0
  145. package/.aether/utils/oracle/oracle.md +168 -0
  146. package/.aether/utils/oracle/oracle.sh +1023 -0
  147. package/.aether/utils/pheromone.sh +2029 -0
  148. package/.aether/utils/queen.sh +1698 -0
  149. package/.aether/utils/scan.sh +860 -0
  150. package/.aether/utils/semantic-cli.sh +10 -8
  151. package/.aether/utils/session.sh +552 -0
  152. package/.aether/utils/skills.sh +509 -0
  153. package/.aether/utils/spawn-tree.sh +103 -271
  154. package/.aether/utils/spawn.sh +260 -0
  155. package/.aether/utils/state-api.sh +199 -0
  156. package/.aether/utils/state-loader.sh +8 -6
  157. package/.aether/utils/suggest.sh +611 -0
  158. package/.aether/utils/swarm-display.sh +10 -1
  159. package/.aether/utils/swarm.sh +1004 -0
  160. package/.aether/utils/watch-spawn-tree.sh +11 -2
  161. package/.aether/utils/xml-compose.sh +2 -2
  162. package/.aether/utils/xml-convert.sh +9 -5
  163. package/.aether/utils/xml-core.sh +5 -9
  164. package/.aether/utils/xml-query.sh +4 -4
  165. package/.aether/workers.md +86 -67
  166. package/.claude/agents/ant/aether-ambassador.md +2 -1
  167. package/.claude/agents/ant/aether-archaeologist.md +6 -1
  168. package/.claude/agents/ant/aether-architect.md +236 -0
  169. package/.claude/agents/ant/aether-auditor.md +6 -1
  170. package/.claude/agents/ant/aether-builder.md +38 -1
  171. package/.claude/agents/ant/aether-chaos.md +2 -1
  172. package/.claude/agents/ant/aether-chronicler.md +1 -0
  173. package/.claude/agents/ant/aether-gatekeeper.md +6 -1
  174. package/.claude/agents/ant/aether-includer.md +1 -0
  175. package/.claude/agents/ant/aether-keeper.md +1 -0
  176. package/.claude/agents/ant/aether-measurer.md +6 -1
  177. package/.claude/agents/ant/aether-oracle.md +237 -0
  178. package/.claude/agents/ant/aether-probe.md +2 -1
  179. package/.claude/agents/ant/aether-queen.md +6 -1
  180. package/.claude/agents/ant/aether-route-setter.md +6 -1
  181. package/.claude/agents/ant/aether-sage.md +68 -3
  182. package/.claude/agents/ant/aether-scout.md +38 -1
  183. package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
  184. package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
  185. package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
  186. package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
  187. package/.claude/agents/ant/aether-tracker.md +6 -1
  188. package/.claude/agents/ant/aether-watcher.md +37 -1
  189. package/.claude/agents/ant/aether-weaver.md +2 -1
  190. package/.claude/commands/ant/archaeology.md +1 -8
  191. package/.claude/commands/ant/build.md +43 -1159
  192. package/.claude/commands/ant/chaos.md +1 -14
  193. package/.claude/commands/ant/colonize.md +1 -14
  194. package/.claude/commands/ant/continue.md +40 -1026
  195. package/.claude/commands/ant/council.md +9 -16
  196. package/.claude/commands/ant/data-clean.md +81 -0
  197. package/.claude/commands/ant/dream.md +12 -9
  198. package/.claude/commands/ant/entomb.md +62 -87
  199. package/.claude/commands/ant/export-signals.md +57 -0
  200. package/.claude/commands/ant/feedback.md +18 -0
  201. package/.claude/commands/ant/flag.md +12 -0
  202. package/.claude/commands/ant/flags.md +22 -8
  203. package/.claude/commands/ant/focus.md +18 -0
  204. package/.claude/commands/ant/help.md +40 -8
  205. package/.claude/commands/ant/history.md +3 -0
  206. package/.claude/commands/ant/import-signals.md +71 -0
  207. package/.claude/commands/ant/init.md +316 -191
  208. package/.claude/commands/ant/insert-phase.md +101 -0
  209. package/.claude/commands/ant/interpret.md +11 -0
  210. package/.claude/commands/ant/lay-eggs.md +167 -158
  211. package/.claude/commands/ant/maturity.md +22 -11
  212. package/.claude/commands/ant/memory-details.md +77 -0
  213. package/.claude/commands/ant/migrate-state.md +6 -0
  214. package/.claude/commands/ant/oracle.md +317 -62
  215. package/.claude/commands/ant/organize.md +10 -5
  216. package/.claude/commands/ant/patrol.md +620 -0
  217. package/.claude/commands/ant/pause-colony.md +8 -22
  218. package/.claude/commands/ant/phase.md +26 -37
  219. package/.claude/commands/ant/pheromones.md +156 -0
  220. package/.claude/commands/ant/plan.md +175 -52
  221. package/.claude/commands/ant/preferences.md +65 -0
  222. package/.claude/commands/ant/redirect.md +18 -0
  223. package/.claude/commands/ant/resume-colony.md +34 -20
  224. package/.claude/commands/ant/resume.md +51 -7
  225. package/.claude/commands/ant/run.md +195 -0
  226. package/.claude/commands/ant/seal.md +497 -78
  227. package/.claude/commands/ant/skill-create.md +286 -0
  228. package/.claude/commands/ant/status.md +127 -1
  229. package/.claude/commands/ant/swarm.md +11 -23
  230. package/.claude/commands/ant/tunnels.md +1 -0
  231. package/.claude/commands/ant/update.md +58 -135
  232. package/.claude/commands/ant/verify-castes.md +90 -42
  233. package/.claude/commands/ant/watch.md +1 -0
  234. package/.opencode/agents/aether-ambassador.md +1 -1
  235. package/.opencode/agents/aether-architect.md +133 -0
  236. package/.opencode/agents/aether-builder.md +3 -3
  237. package/.opencode/agents/aether-oracle.md +137 -0
  238. package/.opencode/agents/aether-queen.md +1 -1
  239. package/.opencode/agents/aether-route-setter.md +1 -1
  240. package/.opencode/agents/aether-scout.md +1 -1
  241. package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
  242. package/.opencode/agents/aether-surveyor-nest.md +6 -1
  243. package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
  244. package/.opencode/agents/aether-surveyor-provisions.md +6 -1
  245. package/.opencode/agents/aether-tracker.md +1 -1
  246. package/.opencode/agents/aether-watcher.md +1 -1
  247. package/.opencode/agents/aether-weaver.md +1 -1
  248. package/.opencode/commands/ant/archaeology.md +7 -14
  249. package/.opencode/commands/ant/build.md +54 -88
  250. package/.opencode/commands/ant/chaos.md +7 -24
  251. package/.opencode/commands/ant/colonize.md +8 -17
  252. package/.opencode/commands/ant/continue.md +595 -66
  253. package/.opencode/commands/ant/council.md +11 -22
  254. package/.opencode/commands/ant/data-clean.md +77 -0
  255. package/.opencode/commands/ant/dream.md +15 -17
  256. package/.opencode/commands/ant/entomb.md +28 -18
  257. package/.opencode/commands/ant/export-signals.md +54 -0
  258. package/.opencode/commands/ant/feedback.md +24 -5
  259. package/.opencode/commands/ant/flag.md +16 -4
  260. package/.opencode/commands/ant/flags.md +24 -10
  261. package/.opencode/commands/ant/focus.md +22 -5
  262. package/.opencode/commands/ant/help.md +41 -8
  263. package/.opencode/commands/ant/history.md +9 -0
  264. package/.opencode/commands/ant/import-signals.md +68 -0
  265. package/.opencode/commands/ant/init.md +365 -156
  266. package/.opencode/commands/ant/insert-phase.md +107 -0
  267. package/.opencode/commands/ant/interpret.md +16 -0
  268. package/.opencode/commands/ant/lay-eggs.md +184 -112
  269. package/.opencode/commands/ant/maturity.md +18 -2
  270. package/.opencode/commands/ant/memory-details.md +83 -0
  271. package/.opencode/commands/ant/migrate-state.md +12 -0
  272. package/.opencode/commands/ant/oracle.md +322 -67
  273. package/.opencode/commands/ant/organize.md +14 -12
  274. package/.opencode/commands/ant/patrol.md +626 -0
  275. package/.opencode/commands/ant/pause-colony.md +12 -29
  276. package/.opencode/commands/ant/phase.md +30 -40
  277. package/.opencode/commands/ant/pheromones.md +162 -0
  278. package/.opencode/commands/ant/plan.md +184 -56
  279. package/.opencode/commands/ant/preferences.md +71 -0
  280. package/.opencode/commands/ant/redirect.md +22 -5
  281. package/.opencode/commands/ant/resume-colony.md +38 -27
  282. package/.opencode/commands/ant/resume.md +71 -20
  283. package/.opencode/commands/ant/run.md +201 -0
  284. package/.opencode/commands/ant/seal.md +230 -25
  285. package/.opencode/commands/ant/skill-create.md +63 -0
  286. package/.opencode/commands/ant/status.md +124 -31
  287. package/.opencode/commands/ant/swarm.md +3 -345
  288. package/.opencode/commands/ant/tunnels.md +3 -9
  289. package/.opencode/commands/ant/update.md +63 -127
  290. package/.opencode/commands/ant/verify-castes.md +96 -42
  291. package/.opencode/commands/ant/watch.md +7 -0
  292. package/CHANGELOG.md +278 -1
  293. package/README.md +188 -340
  294. package/bin/cli.js +236 -429
  295. package/bin/generate-commands.js +186 -0
  296. package/bin/generate-commands.sh +128 -89
  297. package/bin/lib/spawn-logger.js +0 -15
  298. package/bin/lib/update-transaction.js +285 -35
  299. package/bin/npx-install.js +178 -0
  300. package/bin/validate-package.sh +85 -3
  301. package/package.json +7 -3
  302. package/.aether/CONTEXT.md +0 -160
  303. package/.aether/docs/QUEEN.md +0 -84
  304. package/.aether/exchange/colony-registry.xml +0 -11
  305. package/.aether/exchange/pheromones.xml +0 -87
  306. package/.aether/exchange/queen-wisdom.xml +0 -14
  307. package/.aether/model-profiles.yaml +0 -100
  308. package/.aether/utils/spawn-with-model.sh +0 -56
  309. package/bin/lib/model-profiles.js +0 -445
  310. package/bin/lib/model-verify.js +0 -288
  311. package/bin/lib/proxy-health.js +0 -253
  312. package/bin/lib/telemetry.js +0 -441
@@ -0,0 +1,509 @@
1
+ #!/usr/bin/env bash
2
+ # Skills utility — frontmatter parsing, indexing, detection, matching, injection
3
+ # Provides: _skill_parse_frontmatter, _skill_build_index, _skill_read_index,
4
+ # _skill_detect_codebase, _skill_match, _skill_inject, _skill_list,
5
+ # _skill_manifest_read, _skill_diff, _skill_is_user_created
6
+ #
7
+ # These functions are sourced by aether-utils.sh at startup.
8
+ # All shared infrastructure (json_ok, json_err, DATA_DIR, SCRIPT_DIR) is available.
9
+ #
10
+ # Default skills directory: AETHER_SKILLS_DIR env var, falling back to $HOME/.aether/skills
11
+
12
+ # Parse YAML-like frontmatter from a SKILL.md file
13
+ # Usage: _skill_parse_frontmatter <path-to-SKILL.md>
14
+ # Returns JSON with all frontmatter fields via json_ok
15
+ _skill_parse_frontmatter() {
16
+ local skill_file="$1"
17
+
18
+ if [[ ! -f "$skill_file" ]]; then
19
+ json_err "${E_FILE_NOT_FOUND:-SKILL_NOT_FOUND}" "Skill file not found: $skill_file"
20
+ return 1
21
+ fi
22
+
23
+ local in_frontmatter=false
24
+ local sp_name="" sp_description="" sp_type="" sp_priority="normal" sp_version="1.0"
25
+ local sp_domains_raw="" sp_agent_roles_raw="" sp_detect_files_raw="" sp_detect_packages_raw=""
26
+
27
+ while IFS= read -r line; do
28
+ if [[ "$line" == "---" ]]; then
29
+ if $in_frontmatter; then
30
+ break # End of frontmatter
31
+ else
32
+ in_frontmatter=true
33
+ continue
34
+ fi
35
+ fi
36
+
37
+ if $in_frontmatter; then
38
+ local key="${line%%:*}"
39
+ local value="${line#*: }"
40
+ # Trim whitespace from key
41
+ key=$(echo "$key" | tr -d ' ')
42
+ # Trim whitespace from value
43
+ value=$(echo "$value" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
44
+
45
+ case "$key" in
46
+ name) sp_name="$value" ;;
47
+ description) sp_description="$value" ;;
48
+ type) sp_type="$value" ;;
49
+ priority) sp_priority="$value" ;;
50
+ version) sp_version=$(echo "$value" | tr -d '"'"'") ;;
51
+ domains) sp_domains_raw="$value" ;;
52
+ agent_roles) sp_agent_roles_raw="$value" ;;
53
+ detect_files) sp_detect_files_raw="$value" ;;
54
+ detect_packages) sp_detect_packages_raw="$value" ;;
55
+ esac
56
+ fi
57
+ done < "$skill_file"
58
+
59
+ # Parse bracket arrays: [item1, item2] -> JSON array
60
+ _sp_parse_bracket_array() {
61
+ local raw="$1"
62
+ raw=$(echo "$raw" | sed 's/^\[//' | sed 's/\]$//' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
63
+ if [[ -z "$raw" ]]; then
64
+ echo "[]"
65
+ return
66
+ fi
67
+ local arr="["
68
+ local first=true
69
+ IFS=',' read -ra items <<< "$raw"
70
+ for item in "${items[@]}"; do
71
+ item=$(echo "$item" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//' | tr -d '"'"'")
72
+ if $first; then
73
+ arr+="\"$item\""
74
+ first=false
75
+ else
76
+ arr+=",\"$item\""
77
+ fi
78
+ done
79
+ arr+="]"
80
+ echo "$arr"
81
+ }
82
+
83
+ local sp_domains_json=$(_sp_parse_bracket_array "$sp_domains_raw")
84
+ local sp_roles_json=$(_sp_parse_bracket_array "$sp_agent_roles_raw")
85
+ local sp_detect_files_json=$(_sp_parse_bracket_array "$sp_detect_files_raw")
86
+ local sp_detect_packages_json=$(_sp_parse_bracket_array "$sp_detect_packages_raw")
87
+
88
+ # Use jq for safe JSON construction (handles special chars in description)
89
+ local sp_result
90
+ sp_result=$(jq -n \
91
+ --arg name "$sp_name" \
92
+ --arg description "$sp_description" \
93
+ --arg type "$sp_type" \
94
+ --argjson domains "$sp_domains_json" \
95
+ --argjson agent_roles "$sp_roles_json" \
96
+ --argjson detect_files "$sp_detect_files_json" \
97
+ --argjson detect_packages "$sp_detect_packages_json" \
98
+ --arg priority "$sp_priority" \
99
+ --arg version "$sp_version" \
100
+ --arg file_path "$skill_file" \
101
+ '{
102
+ name: $name,
103
+ description: $description,
104
+ type: $type,
105
+ domains: $domains,
106
+ agent_roles: $agent_roles,
107
+ detect_files: $detect_files,
108
+ detect_packages: $detect_packages,
109
+ priority: $priority,
110
+ version: $version,
111
+ file_path: $file_path
112
+ }')
113
+
114
+ json_ok "$sp_result"
115
+ }
116
+
117
+ # Build index from all SKILL.md files in a skills directory
118
+ # Usage: _skill_build_index [skills_dir]
119
+ # Scans colony/ and domain/ subdirectories for SKILL.md files
120
+ # Writes cache to .index.json and returns the index via json_ok
121
+ _skill_build_index() {
122
+ local skills_dir="${1:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
123
+ local cache_file="$skills_dir/.index.json"
124
+ local bi_entries="[]"
125
+ local bi_count=0
126
+
127
+ for bi_skill_file in "$skills_dir"/colony/*/SKILL.md "$skills_dir"/domain/*/SKILL.md; do
128
+ [[ -f "$bi_skill_file" ]] || continue
129
+ local bi_parsed
130
+ bi_parsed=$(_skill_parse_frontmatter "$bi_skill_file" 2>/dev/null)
131
+ local bi_ok
132
+ bi_ok=$(echo "$bi_parsed" | jq -r '.ok // false' 2>/dev/null)
133
+ if [[ "$bi_ok" == "true" ]]; then
134
+ local bi_entry
135
+ bi_entry=$(echo "$bi_parsed" | jq -r '.result' 2>/dev/null)
136
+ bi_entries=$(echo "$bi_entries" | jq --argjson e "$bi_entry" '. + [$e]' 2>/dev/null)
137
+ bi_count=$((bi_count + 1))
138
+ fi
139
+ done
140
+
141
+ # Build index JSON
142
+ local bi_index_json
143
+ bi_index_json=$(jq -n \
144
+ --arg version "1.0" \
145
+ --arg built_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
146
+ --argjson skill_count "$bi_count" \
147
+ --argjson skills "$bi_entries" \
148
+ '{
149
+ version: $version,
150
+ built_at: $built_at,
151
+ skill_count: $skill_count,
152
+ skills: $skills
153
+ }')
154
+
155
+ # Write cache file
156
+ echo "$bi_index_json" > "$cache_file" 2>/dev/null || true
157
+
158
+ json_ok "$bi_index_json"
159
+ }
160
+
161
+ # Read cached index, rebuild if stale (mtime check)
162
+ # Usage: _skill_read_index [skills_dir]
163
+ # Returns cached index if fresh, rebuilds if any SKILL.md is newer than cache
164
+ _skill_read_index() {
165
+ local skills_dir="${1:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
166
+ local cache_file="$skills_dir/.index.json"
167
+
168
+ # Check if cache exists and is fresh
169
+ if [[ -f "$cache_file" ]]; then
170
+ local ri_cache_mtime
171
+ ri_cache_mtime=$(stat -f %m "$cache_file" 2>/dev/null || stat -c %Y "$cache_file" 2>/dev/null || echo 0)
172
+ local ri_needs_rebuild=false
173
+
174
+ # Check if any SKILL.md is newer than cache
175
+ for ri_skill_file in "$skills_dir"/colony/*/SKILL.md "$skills_dir"/domain/*/SKILL.md; do
176
+ [[ -f "$ri_skill_file" ]] || continue
177
+ local ri_file_mtime
178
+ ri_file_mtime=$(stat -f %m "$ri_skill_file" 2>/dev/null || stat -c %Y "$ri_skill_file" 2>/dev/null || echo 0)
179
+ if [[ "$ri_file_mtime" -gt "$ri_cache_mtime" ]]; then
180
+ ri_needs_rebuild=true
181
+ break
182
+ fi
183
+ done
184
+
185
+ if ! $ri_needs_rebuild; then
186
+ json_ok "$(cat "$cache_file")"
187
+ return
188
+ fi
189
+ fi
190
+
191
+ # Cache missing or stale -- rebuild
192
+ _skill_build_index "$skills_dir"
193
+ }
194
+
195
+ # Detect which domain skills match the current codebase
196
+ # Usage: _skill_detect_codebase [repo_dir] [skills_dir]
197
+ # Checks detect_files (glob patterns via find) and detect_packages (package manifests)
198
+ # Returns JSON with detection scores via json_ok
199
+ _skill_detect_codebase() {
200
+ local repo_dir="${1:-.}"
201
+ local skills_dir="${2:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
202
+
203
+ # Read index
204
+ local dc_index_result
205
+ dc_index_result=$(_skill_read_index "$skills_dir" 2>/dev/null)
206
+ local dc_index
207
+ dc_index=$(echo "$dc_index_result" | jq -r '.result' 2>/dev/null)
208
+
209
+ local dc_detections="[]"
210
+
211
+ # For each domain skill with detect patterns (process substitution to avoid subshell)
212
+ while IFS= read -r dc_skill; do
213
+ [[ -z "$dc_skill" ]] && continue
214
+ local dc_skill_name
215
+ dc_skill_name=$(echo "$dc_skill" | jq -r '.name')
216
+ local dc_score=0
217
+
218
+ # Check detect_files (use find for recursive matching)
219
+ while IFS= read -r dc_pattern; do
220
+ [[ -z "$dc_pattern" ]] && continue
221
+ if find "$repo_dir" -name "$dc_pattern" -print -quit 2>/dev/null | grep -q .; then
222
+ dc_score=$((dc_score + 30))
223
+ fi
224
+ done < <(echo "$dc_skill" | jq -r '.detect_files[]' 2>/dev/null)
225
+
226
+ # Check detect_packages (package manifests)
227
+ while IFS= read -r dc_pkg; do
228
+ [[ -z "$dc_pkg" ]] && continue
229
+ # Check package.json
230
+ if [[ -f "$repo_dir/package.json" ]] && jq -e --arg p "$dc_pkg" '.dependencies[$p] // .devDependencies[$p]' "$repo_dir/package.json" > /dev/null 2>&1; then
231
+ dc_score=$((dc_score + 40))
232
+ fi
233
+ # Check requirements.txt
234
+ if [[ -f "$repo_dir/requirements.txt" ]] && grep -qi "^$dc_pkg" "$repo_dir/requirements.txt" 2>/dev/null; then
235
+ dc_score=$((dc_score + 40))
236
+ fi
237
+ # Check go.mod
238
+ if [[ -f "$repo_dir/go.mod" ]] && grep -qi "$dc_pkg" "$repo_dir/go.mod" 2>/dev/null; then
239
+ dc_score=$((dc_score + 40))
240
+ fi
241
+ # Check Gemfile
242
+ if [[ -f "$repo_dir/Gemfile" ]] && grep -qi "'$dc_pkg'" "$repo_dir/Gemfile" 2>/dev/null; then
243
+ dc_score=$((dc_score + 40))
244
+ fi
245
+ done < <(echo "$dc_skill" | jq -r '.detect_packages[]' 2>/dev/null)
246
+
247
+ if [[ $dc_score -gt 0 ]]; then
248
+ dc_detections=$(echo "$dc_detections" | jq --arg n "$dc_skill_name" --argjson s "$dc_score" '. + [{"name": $n, "score": $s}]' 2>/dev/null)
249
+ fi
250
+ done < <(echo "$dc_index" | jq -c '.skills[] | select(.type == "domain") | select((.detect_files | length > 0) or (.detect_packages | length > 0))' 2>/dev/null)
251
+
252
+ json_ok "$(jq -n --argjson detections "$dc_detections" '{detections: $detections}')"
253
+ }
254
+
255
+ # Smart-match skills to a worker by role, pheromones, and task description
256
+ # Usage: _skill_match <worker_role> [task_description] [skills_dir]
257
+ # Returns top 2 colony + top 2 domain skills (above minimum score) via json_ok
258
+ _skill_match() {
259
+ local worker_role="$1"
260
+ local task_description="${2:-}"
261
+ local skills_dir="${3:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
262
+ local sm_max_colony=2
263
+ local sm_max_domain=2
264
+ local sm_min_score=20
265
+
266
+ # Read index
267
+ local sm_index_result
268
+ sm_index_result=$(_skill_read_index "$skills_dir" 2>/dev/null)
269
+ local sm_skills
270
+ sm_skills=$(echo "$sm_index_result" | jq -r '.result.skills' 2>/dev/null)
271
+
272
+ # Read active pheromones for domain boosting
273
+ local sm_pheromone_domains=""
274
+ if [[ -f "${DATA_DIR:-/dev/null}/pheromones.json" ]]; then
275
+ sm_pheromone_domains=$(jq -r '[.signals[]? | select(.active == true) | .content] | join(" ")' "${DATA_DIR}/pheromones.json" 2>/dev/null || echo "")
276
+ fi
277
+
278
+ # Match colony skills -- filter by role
279
+ local sm_colony_matches
280
+ sm_colony_matches=$(echo "$sm_skills" | jq -c --arg role "$worker_role" \
281
+ '[.[] | select(.type == "colony") | select(.agent_roles | index($role))]' 2>/dev/null)
282
+
283
+ # Score colony skills by pheromone domain overlap (process substitution to avoid subshell)
284
+ local sm_scored_colony="[]"
285
+ while IFS= read -r sm_skill; do
286
+ [[ -z "$sm_skill" ]] && continue
287
+ local sm_score=50 # Base score for colony skills
288
+ while IFS= read -r sm_domain; do
289
+ [[ -z "$sm_domain" ]] && continue
290
+ if echo "$sm_pheromone_domains $task_description" | grep -qi "$sm_domain" 2>/dev/null; then
291
+ sm_score=$((sm_score + 20))
292
+ fi
293
+ done < <(echo "$sm_skill" | jq -r '.domains[]' 2>/dev/null)
294
+
295
+ # Priority boost
296
+ local sm_priority
297
+ sm_priority=$(echo "$sm_skill" | jq -r '.priority' 2>/dev/null)
298
+ case "$sm_priority" in
299
+ high) sm_score=$((sm_score + 30)) ;;
300
+ low) sm_score=$((sm_score - 10)) ;;
301
+ esac
302
+
303
+ sm_scored_colony=$(echo "$sm_scored_colony" | jq --argjson s "$sm_skill" --argjson sc "$sm_score" '. + [$s + {"match_score": $sc}]' 2>/dev/null)
304
+ done < <(echo "$sm_colony_matches" | jq -c '.[]' 2>/dev/null)
305
+
306
+ # Filter by minimum score, sort, and take top N colony
307
+ local sm_top_colony
308
+ sm_top_colony=$(echo "$sm_scored_colony" | jq "[.[] | select(.match_score >= $sm_min_score)] | [sort_by(-.match_score) | limit($sm_max_colony; .[])]" 2>/dev/null)
309
+
310
+ # Match domain skills -- filter by role
311
+ local sm_domain_matches
312
+ sm_domain_matches=$(echo "$sm_skills" | jq -c --arg role "$worker_role" \
313
+ '[.[] | select(.type == "domain") | select(.agent_roles | index($role))]' 2>/dev/null)
314
+
315
+ # Score domain skills (process substitution to avoid subshell)
316
+ local sm_scored_domain="[]"
317
+ while IFS= read -r sm_skill; do
318
+ [[ -z "$sm_skill" ]] && continue
319
+ local sm_score=0
320
+ while IFS= read -r sm_domain; do
321
+ [[ -z "$sm_domain" ]] && continue
322
+ if echo "$sm_pheromone_domains $task_description" | grep -qi "$sm_domain" 2>/dev/null; then
323
+ sm_score=$((sm_score + 15))
324
+ fi
325
+ done < <(echo "$sm_skill" | jq -r '.domains[]' 2>/dev/null)
326
+
327
+ sm_scored_domain=$(echo "$sm_scored_domain" | jq --argjson s "$sm_skill" --argjson sc "$sm_score" '. + [$s + {"match_score": $sc}]' 2>/dev/null)
328
+ done < <(echo "$sm_domain_matches" | jq -c '.[]' 2>/dev/null)
329
+
330
+ # Filter by minimum score, sort, and take top N domain
331
+ local sm_top_domain
332
+ sm_top_domain=$(echo "$sm_scored_domain" | jq "[.[] | select(.match_score >= $sm_min_score)] | [sort_by(-.match_score) | limit($sm_max_domain; .[])]" 2>/dev/null)
333
+
334
+ json_ok "$(jq -n --argjson colony "${sm_top_colony:-[]}" --argjson domain "${sm_top_domain:-[]}" \
335
+ '{colony_skills: $colony, domain_skills: $domain}')"
336
+ }
337
+
338
+ # Load full SKILL.md content for matched skills, assemble within 8K char budget
339
+ # Usage: _skill_inject <match_json>
340
+ # match_json is the JSON result from skill-match (has colony_skills and domain_skills arrays)
341
+ # Injects domain skills first (trimmed first if over budget), then colony skills
342
+ # Logs trimmed skills to stderr
343
+ _skill_inject() {
344
+ local match_json="$1"
345
+ local si_budget=8000
346
+ local si_total_chars=0
347
+
348
+ # Inject domain skills first (these get trimmed first if over budget)
349
+ local si_domain_section=""
350
+ while IFS= read -r si_skill; do
351
+ [[ -z "$si_skill" ]] && continue
352
+ local si_file_path
353
+ si_file_path=$(echo "$si_skill" | jq -r '.file_path')
354
+ local si_skill_name
355
+ si_skill_name=$(echo "$si_skill" | jq -r '.name')
356
+
357
+ if [[ -f "$si_file_path" ]]; then
358
+ # Extract body (everything after second ---)
359
+ local si_body
360
+ si_body=$(awk '/^---$/{c++;next}c>=2' "$si_file_path")
361
+ local si_body_len=${#si_body}
362
+
363
+ if [[ $((si_total_chars + si_body_len)) -le $si_budget ]]; then
364
+ si_domain_section+="### Domain Skill: $si_skill_name"$'\n'"$si_body"$'\n\n'
365
+ si_total_chars=$((si_total_chars + si_body_len + 30))
366
+ else
367
+ echo "[skills] trimmed: $si_skill_name (${si_body_len} chars)" >&2
368
+ fi
369
+ fi
370
+ done < <(echo "$match_json" | jq -c '.domain_skills[]?' 2>/dev/null)
371
+
372
+ # Inject colony skills (trimmed last -- higher priority)
373
+ local si_colony_section=""
374
+ while IFS= read -r si_skill; do
375
+ [[ -z "$si_skill" ]] && continue
376
+ local si_file_path
377
+ si_file_path=$(echo "$si_skill" | jq -r '.file_path')
378
+ local si_skill_name
379
+ si_skill_name=$(echo "$si_skill" | jq -r '.name')
380
+
381
+ if [[ -f "$si_file_path" ]]; then
382
+ local si_body
383
+ si_body=$(awk '/^---$/{c++;next}c>=2' "$si_file_path")
384
+ local si_body_len=${#si_body}
385
+
386
+ if [[ $((si_total_chars + si_body_len)) -le $si_budget ]]; then
387
+ si_colony_section+="### Colony Skill: $si_skill_name"$'\n'"$si_body"$'\n\n'
388
+ si_total_chars=$((si_total_chars + si_body_len + 30))
389
+ else
390
+ echo "[skills] trimmed: $si_skill_name (${si_body_len} chars)" >&2
391
+ fi
392
+ fi
393
+ done < <(echo "$match_json" | jq -c '.colony_skills[]?' 2>/dev/null)
394
+
395
+ # Assemble skill_section
396
+ local si_skill_section=""
397
+ if [[ -n "$si_colony_section" || -n "$si_domain_section" ]]; then
398
+ si_skill_section="## MATCHED SKILLS"$'\n\n'
399
+ [[ -n "$si_colony_section" ]] && si_skill_section+="$si_colony_section"
400
+ [[ -n "$si_domain_section" ]] && si_skill_section+="$si_domain_section"
401
+ fi
402
+
403
+ local si_colony_count si_domain_count
404
+ si_colony_count=$(echo "$match_json" | jq '[.colony_skills[]?] | length' 2>/dev/null || echo 0)
405
+ si_domain_count=$(echo "$match_json" | jq '[.domain_skills[]?] | length' 2>/dev/null || echo 0)
406
+
407
+ # Escape for JSON embedding
408
+ local si_escaped_section
409
+ si_escaped_section=$(echo "$si_skill_section" | jq -Rs '.' 2>/dev/null)
410
+
411
+ json_ok "$(jq -n --argjson skill_section "$si_escaped_section" \
412
+ --argjson colony_count "$si_colony_count" --argjson domain_count "$si_domain_count" \
413
+ --argjson total_chars "$si_total_chars" \
414
+ '{skill_section: $skill_section, colony_count: $colony_count, domain_count: $domain_count, total_chars: $total_chars}')"
415
+ }
416
+
417
+ # List all installed skills
418
+ # Usage: _skill_list [skills_dir]
419
+ # Returns the full index (from cache or rebuilt) via json_ok
420
+ _skill_list() {
421
+ local skills_dir="${1:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
422
+ local sl_index_result
423
+ sl_index_result=$(_skill_read_index "$skills_dir" 2>/dev/null)
424
+ local sl_index
425
+ sl_index=$(echo "$sl_index_result" | jq -r '.result' 2>/dev/null)
426
+
427
+ json_ok "$sl_index"
428
+ }
429
+
430
+ # Read .manifest.json for update safety
431
+ # Usage: _skill_manifest_read <manifest_file>
432
+ # Returns manifest contents or default empty manifest
433
+ _skill_manifest_read() {
434
+ local manifest_file="$1"
435
+ if [[ -f "$manifest_file" ]]; then
436
+ json_ok "$(cat "$manifest_file")"
437
+ else
438
+ json_ok '{"managed_by": "aether", "version": "0.0.0", "skills": []}'
439
+ fi
440
+ }
441
+
442
+ # Compare user skill with shipped version
443
+ # Usage: _skill_diff <skill_name> [skills_dir]
444
+ # Looks for user version in skills_dir and system version in AETHER_SYSTEM_DIR
445
+ _skill_diff() {
446
+ local skill_name="$1"
447
+ local skills_dir="${2:-${AETHER_SKILLS_DIR:-$HOME/.aether/skills}}"
448
+ local system_dir="${AETHER_SYSTEM_DIR:-$HOME/.aether/system/skills}"
449
+
450
+ # Find user skill
451
+ local sd_user_file=""
452
+ for sd_category in colony domain; do
453
+ if [[ -f "$skills_dir/$sd_category/$skill_name/SKILL.md" ]]; then
454
+ sd_user_file="$skills_dir/$sd_category/$skill_name/SKILL.md"
455
+ break
456
+ fi
457
+ done
458
+
459
+ # Find system (shipped) skill
460
+ local sd_system_file=""
461
+ for sd_category in colony domain; do
462
+ if [[ -f "$system_dir/$sd_category/$skill_name/SKILL.md" ]]; then
463
+ sd_system_file="$system_dir/$sd_category/$skill_name/SKILL.md"
464
+ break
465
+ fi
466
+ done
467
+
468
+ if [[ -z "$sd_user_file" ]]; then
469
+ json_err "${E_FILE_NOT_FOUND:-NOT_FOUND}" "No user skill named '$skill_name' found"
470
+ return 1
471
+ fi
472
+
473
+ if [[ -z "$sd_system_file" ]]; then
474
+ json_ok "$(jq -n --arg name "$skill_name" \
475
+ '{status: "user_only", message: ("Skill \u0027" + $name + "\u0027 is user-created with no Aether equivalent")}')"
476
+ return
477
+ fi
478
+
479
+ # Compare
480
+ if diff -q "$sd_user_file" "$sd_system_file" > /dev/null 2>&1; then
481
+ json_ok "{\"status\": \"identical\", \"message\": \"User and Aether versions are identical\"}"
482
+ else
483
+ local sd_diff_output
484
+ sd_diff_output=$(diff -u "$sd_system_file" "$sd_user_file" 2>/dev/null | head -50)
485
+ local sd_escaped_diff
486
+ sd_escaped_diff=$(echo "$sd_diff_output" | jq -Rs '.' 2>/dev/null)
487
+ json_ok "$(jq -n --argjson diff "$sd_escaped_diff" \
488
+ '{status: "different", message: "User and Aether versions differ", diff: $diff}')"
489
+ fi
490
+ }
491
+
492
+ # Check if a skill is user-created (not in manifest)
493
+ # Usage: _skill_is_user_created <skill_name> <manifest_file>
494
+ # Outputs "true" or "false" to stdout
495
+ _skill_is_user_created() {
496
+ local skill_name="$1"
497
+ local manifest_file="$2"
498
+ if [[ ! -f "$manifest_file" ]]; then
499
+ echo "true"
500
+ return
501
+ fi
502
+ local suc_managed
503
+ suc_managed=$(jq -r --arg n "$skill_name" '.skills | index($n)' "$manifest_file" 2>/dev/null)
504
+ if [[ "$suc_managed" == "null" || -z "$suc_managed" ]]; then
505
+ echo "true"
506
+ else
507
+ echo "false"
508
+ fi
509
+ }