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.
- package/.aether/aether-utils.sh +3150 -3349
- 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 +438 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +304 -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 +469 -0
- package/.aether/commands/insert-phase.yaml +98 -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 +1313 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +373 -0
- package/.aether/commands/resume.yaml +398 -0
- package/.aether/commands/run.yaml +193 -0
- package/.aether/commands/seal.yaml +1205 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +364 -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 +1682 -0
- package/.aether/docs/command-playbooks/build-prep.md +283 -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 +1724 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +406 -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/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 +267 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +342 -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 +1698 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +552 -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 +199 -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 +1 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +9 -16
- 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 +316 -191
- package/.claude/commands/ant/insert-phase.md +101 -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 +175 -52
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +34 -20
- package/.claude/commands/ant/resume.md +51 -7
- package/.claude/commands/ant/run.md +195 -0
- package/.claude/commands/ant/seal.md +497 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +127 -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 +8 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +11 -22
- 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 +365 -156
- package/.opencode/commands/ant/insert-phase.md +107 -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 +184 -56
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +38 -27
- package/.opencode/commands/ant/resume.md +71 -20
- package/.opencode/commands/ant/run.md +201 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +124 -31
- 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 +278 -1
- package/README.md +188 -340
- 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 +7 -3
- 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,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
|
+
}
|