design-agent-skills 1.4.2
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/README.md +380 -0
- package/VERSION +1 -0
- package/bin/cli.mjs +45 -0
- package/bootstrap.sh +32 -0
- package/install.sh +689 -0
- package/package.json +36 -0
- package/skills/accessibility-agents/SKILL.md +52 -0
- package/skills/accessibility-agents/stub.yaml +5 -0
- package/skills/accessibility-catalogue/SKILL.md +45 -0
- package/skills/accessibility-catalogue/stub.yaml +3 -0
- package/skills/addyosmani-quality/SKILL.md +72 -0
- package/skills/addyosmani-quality/stub.yaml +6 -0
- package/skills/ai-graphic-design-skill/SKILL.md +65 -0
- package/skills/ai-graphic-design-skill/stub.yaml +5 -0
- package/skills/algorithmic-art/SKILL.md +59 -0
- package/skills/algorithmic-art/stub.yaml +5 -0
- package/skills/animate-css-skill/SKILL.md +55 -0
- package/skills/animate-css-skill/stub.yaml +5 -0
- package/skills/animate-skill/SKILL.md +64 -0
- package/skills/animate-skill/stub.yaml +5 -0
- package/skills/anthropics-skills/SKILL.md +72 -0
- package/skills/anthropics-skills/stub.yaml +12 -0
- package/skills/antvis-chart-skills/SKILL.md +61 -0
- package/skills/antvis-chart-skills/stub.yaml +6 -0
- package/skills/apple-hig-skills/SKILL.md +66 -0
- package/skills/apple-hig-skills/stub.yaml +6 -0
- package/skills/baseline-ui/SKILL.md +54 -0
- package/skills/baseline-ui/stub.yaml +5 -0
- package/skills/bencium-ux-designer/SKILL.md +54 -0
- package/skills/bencium-ux-designer/stub.yaml +5 -0
- package/skills/brand-design-md/SKILL.md +65 -0
- package/skills/brand-design-md/stub.yaml +5 -0
- package/skills/callstack-agent-skills/SKILL.md +56 -0
- package/skills/callstack-agent-skills/stub.yaml +6 -0
- package/skills/cc-slidev/SKILL.md +54 -0
- package/skills/cc-slidev/stub.yaml +5 -0
- package/skills/claud3/SKILL.md +54 -0
- package/skills/claud3/stub.yaml +5 -0
- package/skills/claude-pm-skills/SKILL.md +53 -0
- package/skills/claude-pm-skills/stub.yaml +5 -0
- package/skills/claude-wireframe-skill/SKILL.md +63 -0
- package/skills/claude-wireframe-skill/stub.yaml +5 -0
- package/skills/claude2figma/SKILL.md +54 -0
- package/skills/claude2figma/stub.yaml +5 -0
- package/skills/claudedesignskills/SKILL.md +59 -0
- package/skills/claudedesignskills/stub.yaml +5 -0
- package/skills/cloudai-threejs/SKILL.md +51 -0
- package/skills/cloudai-threejs/stub.yaml +6 -0
- package/skills/cloudflare-web-perf/SKILL.md +60 -0
- package/skills/cloudflare-web-perf/stub.yaml +5 -0
- package/skills/color-expert/SKILL.md +62 -0
- package/skills/color-expert/stub.yaml +5 -0
- package/skills/composio-artifacts/SKILL.md +63 -0
- package/skills/composio-artifacts/stub.yaml +5 -0
- package/skills/content-catalogue/SKILL.md +105 -0
- package/skills/content-catalogue/stub.yaml +3 -0
- package/skills/coreyhaines-marketing/SKILL.md +59 -0
- package/skills/coreyhaines-marketing/stub.yaml +6 -0
- package/skills/creative-director/SKILL.md +60 -0
- package/skills/creative-director/stub.yaml +5 -0
- package/skills/css-animation-skill/SKILL.md +63 -0
- package/skills/css-animation-skill/stub.yaml +5 -0
- package/skills/d3js-skill/SKILL.md +58 -0
- package/skills/d3js-skill/stub.yaml +5 -0
- package/skills/data-analysis-skill/SKILL.md +64 -0
- package/skills/data-analysis-skill/stub.yaml +5 -0
- package/skills/data-viz-agent/SKILL.md +60 -0
- package/skills/data-viz-agent/stub.yaml +5 -0
- package/skills/deanpeters-pm-skills/SKILL.md +76 -0
- package/skills/deanpeters-pm-skills/stub.yaml +6 -0
- package/skills/design-auditor/SKILL.md +62 -0
- package/skills/design-auditor/stub.yaml +5 -0
- package/skills/design-brief/SKILL.md +58 -0
- package/skills/design-brief/stub.yaml +5 -0
- package/skills/design-catalogue/SKILL.md +346 -0
- package/skills/design-catalogue/stub.yaml +3 -0
- package/skills/design-consultation/SKILL.md +58 -0
- package/skills/design-consultation/stub.yaml +5 -0
- package/skills/design-engineering-catalogue/SKILL.md +92 -0
- package/skills/design-engineering-catalogue/stub.yaml +3 -0
- package/skills/design-for-ai/SKILL.md +58 -0
- package/skills/design-for-ai/stub.yaml +5 -0
- package/skills/design-html/SKILL.md +59 -0
- package/skills/design-html/stub.yaml +5 -0
- package/skills/design-lab/SKILL.md +54 -0
- package/skills/design-lab/stub.yaml +5 -0
- package/skills/design-review-garrytan/SKILL.md +58 -0
- package/skills/design-review-garrytan/stub.yaml +5 -0
- package/skills/design-tokens-skill/SKILL.md +68 -0
- package/skills/design-tokens-skill/stub.yaml +5 -0
- package/skills/design-with-claude/SKILL.md +51 -0
- package/skills/design-with-claude/stub.yaml +5 -0
- package/skills/designer-skills/SKILL.md +52 -0
- package/skills/designer-skills/stub.yaml +5 -0
- package/skills/digidai-pm/SKILL.md +60 -0
- package/skills/digidai-pm/stub.yaml +5 -0
- package/skills/distinctive-frontend/SKILL.md +55 -0
- package/skills/distinctive-frontend/stub.yaml +5 -0
- package/skills/email-html-mjml/SKILL.md +54 -0
- package/skills/email-html-mjml/stub.yaml +5 -0
- package/skills/emilkowalski-skill/SKILL.md +60 -0
- package/skills/emilkowalski-skill/stub.yaml +7 -0
- package/skills/excalidraw-agents365/SKILL.md +56 -0
- package/skills/excalidraw-agents365/stub.yaml +5 -0
- package/skills/excalidraw-diagram/SKILL.md +58 -0
- package/skills/excalidraw-diagram/stub.yaml +5 -0
- package/skills/expo-skills/SKILL.md +56 -0
- package/skills/expo-skills/stub.yaml +21 -0
- package/skills/extract-design-md/SKILL.md +58 -0
- package/skills/extract-design-md/stub.yaml +5 -0
- package/skills/extract-design-system/SKILL.md +53 -0
- package/skills/extract-design-system/stub.yaml +5 -0
- package/skills/fal-ai-skills/SKILL.md +56 -0
- package/skills/fal-ai-skills/stub.yaml +6 -0
- package/skills/figma-catalogue/SKILL.md +57 -0
- package/skills/figma-catalogue/stub.yaml +3 -0
- package/skills/figma-official-skills/SKILL.md +80 -0
- package/skills/figma-official-skills/stub.yaml +6 -0
- package/skills/figma-skill/SKILL.md +52 -0
- package/skills/figma-skill/stub.yaml +5 -0
- package/skills/figma-variables-tokens-generator/SKILL.md +54 -0
- package/skills/figma-variables-tokens-generator/stub.yaml +5 -0
- package/skills/fixing-accessibility/SKILL.md +54 -0
- package/skills/fixing-accessibility/stub.yaml +5 -0
- package/skills/fixing-motion-performance/SKILL.md +54 -0
- package/skills/fixing-motion-performance/stub.yaml +5 -0
- package/skills/framer-motion-skills/SKILL.md +55 -0
- package/skills/framer-motion-skills/stub.yaml +5 -0
- package/skills/frontend-design/SKILL.md +65 -0
- package/skills/frontend-design/stub.yaml +5 -0
- package/skills/frontend-slides/SKILL.md +59 -0
- package/skills/frontend-slides/stub.yaml +5 -0
- package/skills/generative-media-skills/SKILL.md +56 -0
- package/skills/generative-media-skills/stub.yaml +4 -0
- package/skills/google-fonts-skill/SKILL.md +69 -0
- package/skills/google-fonts-skill/stub.yaml +4 -0
- package/skills/google-stitch-skills/SKILL.md +71 -0
- package/skills/google-stitch-skills/stub.yaml +6 -0
- package/skills/gsap-skills/SKILL.md +55 -0
- package/skills/gsap-skills/stub.yaml +5 -0
- package/skills/guizang-ppt/SKILL.md +58 -0
- package/skills/guizang-ppt/stub.yaml +5 -0
- package/skills/hand-drawn-diagrams/SKILL.md +57 -0
- package/skills/hand-drawn-diagrams/stub.yaml +5 -0
- package/skills/hig-doctor/SKILL.md +59 -0
- package/skills/hig-doctor/stub.yaml +6 -0
- package/skills/huashu-design/SKILL.md +64 -0
- package/skills/huashu-design/stub.yaml +5 -0
- package/skills/hyperframes/SKILL.md +60 -0
- package/skills/hyperframes/stub.yaml +5 -0
- package/skills/impeccable/SKILL.md +75 -0
- package/skills/impeccable/stub.yaml +8 -0
- package/skills/ink-google/SKILL.md +58 -0
- package/skills/ink-google/stub.yaml +5 -0
- package/skills/interface-design-dammyjay/SKILL.md +55 -0
- package/skills/interface-design-dammyjay/stub.yaml +5 -0
- package/skills/liquid-glass-skill/SKILL.md +54 -0
- package/skills/liquid-glass-skill/stub.yaml +5 -0
- package/skills/logo-designer-skill/SKILL.md +56 -0
- package/skills/logo-designer-skill/stub.yaml +5 -0
- package/skills/make-interfaces-better/SKILL.md +60 -0
- package/skills/make-interfaces-better/stub.yaml +5 -0
- package/skills/markdown-viewer-skills/SKILL.md +70 -0
- package/skills/markdown-viewer-skills/stub.yaml +6 -0
- package/skills/marp-slide-quality/SKILL.md +63 -0
- package/skills/marp-slide-quality/stub.yaml +5 -0
- package/skills/marp-slides/SKILL.md +63 -0
- package/skills/marp-slides/stub.yaml +5 -0
- package/skills/mastepanoski-skills/SKILL.md +52 -0
- package/skills/mastepanoski-skills/stub.yaml +5 -0
- package/skills/microsoft-skills/SKILL.md +51 -0
- package/skills/microsoft-skills/stub.yaml +6 -0
- package/skills/mobile-app-design/SKILL.md +67 -0
- package/skills/mobile-app-design/stub.yaml +5 -0
- package/skills/mobile-app-ui-design/SKILL.md +65 -0
- package/skills/mobile-app-ui-design/stub.yaml +5 -0
- package/skills/motion-catalogue/SKILL.md +69 -0
- package/skills/motion-catalogue/stub.yaml +3 -0
- package/skills/motion-design-skill/SKILL.md +55 -0
- package/skills/motion-design-skill/stub.yaml +5 -0
- package/skills/nanobanan-ppt/SKILL.md +64 -0
- package/skills/nanobanan-ppt/stub.yaml +5 -0
- package/skills/neo-user-journey/SKILL.md +62 -0
- package/skills/neo-user-journey/stub.yaml +5 -0
- package/skills/nimbalyst-skills/SKILL.md +59 -0
- package/skills/nimbalyst-skills/stub.yaml +7 -0
- package/skills/open-design/SKILL.md +55 -0
- package/skills/open-design/stub.yaml +4 -0
- package/skills/openai-skills/SKILL.md +67 -0
- package/skills/openai-skills/stub.yaml +6 -0
- package/skills/p5js-hermes/SKILL.md +62 -0
- package/skills/p5js-hermes/stub.yaml +5 -0
- package/skills/phuryn-pm-skills/SKILL.md +56 -0
- package/skills/phuryn-pm-skills/stub.yaml +6 -0
- package/skills/plan-design-review/SKILL.md +58 -0
- package/skills/plan-design-review/stub.yaml +5 -0
- package/skills/platform-design-skills/SKILL.md +52 -0
- package/skills/platform-design-skills/stub.yaml +6 -0
- package/skills/pm-skills/SKILL.md +55 -0
- package/skills/pm-skills/stub.yaml +5 -0
- package/skills/react-doctor/SKILL.md +55 -0
- package/skills/react-doctor/stub.yaml +5 -0
- package/skills/remotion/SKILL.md +59 -0
- package/skills/remotion/stub.yaml +5 -0
- package/skills/revealjs-skill/SKILL.md +54 -0
- package/skills/revealjs-skill/stub.yaml +5 -0
- package/skills/shadcn-ui/SKILL.md +54 -0
- package/skills/shadcn-ui/stub.yaml +5 -0
- package/skills/shader-dev/SKILL.md +60 -0
- package/skills/shader-dev/stub.yaml +5 -0
- package/skills/simota-agent-skills/SKILL.md +53 -0
- package/skills/simota-agent-skills/stub.yaml +5 -0
- package/skills/sleek-design-mobile-apps/SKILL.md +56 -0
- package/skills/sleek-design-mobile-apps/stub.yaml +4 -0
- package/skills/slidev-skill/SKILL.md +54 -0
- package/skills/slidev-skill/stub.yaml +5 -0
- package/skills/softaworks-agent-toolkit/SKILL.md +58 -0
- package/skills/softaworks-agent-toolkit/stub.yaml +6 -0
- package/skills/software-mansion-skills/SKILL.md +63 -0
- package/skills/software-mansion-skills/stub.yaml +11 -0
- package/skills/superdesign-skill/SKILL.md +53 -0
- package/skills/superdesign-skill/stub.yaml +4 -0
- package/skills/swiftui-claude-skills/SKILL.md +54 -0
- package/skills/swiftui-claude-skills/stub.yaml +5 -0
- package/skills/swiftui-patterns/SKILL.md +54 -0
- package/skills/swiftui-patterns/stub.yaml +5 -0
- package/skills/taste-design-stitch/SKILL.md +58 -0
- package/skills/taste-design-stitch/stub.yaml +5 -0
- package/skills/taste-skill/SKILL.md +74 -0
- package/skills/taste-skill/stub.yaml +5 -0
- package/skills/threejs-claude-skill-package/SKILL.md +54 -0
- package/skills/threejs-claude-skill-package/stub.yaml +5 -0
- package/skills/threejs-ecs-ts/SKILL.md +54 -0
- package/skills/threejs-ecs-ts/stub.yaml +5 -0
- package/skills/tui-design-skill/SKILL.md +56 -0
- package/skills/tui-design-skill/stub.yaml +5 -0
- package/skills/ui-craft/SKILL.md +58 -0
- package/skills/ui-craft/stub.yaml +5 -0
- package/skills/ui-ux-pro-max/SKILL.md +64 -0
- package/skills/ui-ux-pro-max/stub.yaml +7 -0
- package/skills/ux-designer-skill/SKILL.md +62 -0
- package/skills/ux-designer-skill/stub.yaml +5 -0
- package/skills/ux-ui-mastery/SKILL.md +53 -0
- package/skills/ux-ui-mastery/stub.yaml +5 -0
- package/skills/vercel-skills/SKILL.md +65 -0
- package/skills/vercel-skills/stub.yaml +6 -0
- package/skills/wcag-ai-skill/SKILL.md +52 -0
- package/skills/wcag-ai-skill/stub.yaml +5 -0
- package/skills/wcag-audit-patterns/SKILL.md +53 -0
- package/skills/wcag-audit-patterns/stub.yaml +5 -0
- package/skills/webgpu-claude-skill/SKILL.md +54 -0
- package/skills/webgpu-claude-skill/stub.yaml +5 -0
- package/skills/wiggle-claude-skill/SKILL.md +63 -0
- package/skills/wiggle-claude-skill/stub.yaml +5 -0
- package/skills/wireframe-skill/SKILL.md +63 -0
- package/skills/wireframe-skill/stub.yaml +5 -0
- package/skills/wireframer/SKILL.md +59 -0
- package/skills/wireframer/stub.yaml +5 -0
- package/skills/wondelai-skills/SKILL.md +57 -0
- package/skills/wondelai-skills/stub.yaml +11 -0
- package/skills/work-with-design-systems/SKILL.md +67 -0
- package/skills/work-with-design-systems/stub.yaml +5 -0
package/install.sh
ADDED
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# design-agent-skills installer — works with bash 3.2+
|
|
3
|
+
# Usage: ./install.sh [--scope=user|project] [install|status|update|fix|help]
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
SKILLS_SRC="$REPO_DIR/skills"
|
|
8
|
+
LOCKFILE="$REPO_DIR/design-agent-skills.lock"
|
|
9
|
+
|
|
10
|
+
# ── Parse global flags ────────────────────────────────────────────────────────
|
|
11
|
+
SCOPE="user"
|
|
12
|
+
INCLUDE_EXPERIMENTAL=0
|
|
13
|
+
ARGS=()
|
|
14
|
+
for arg in "$@"; do
|
|
15
|
+
case "$arg" in
|
|
16
|
+
--scope=user) SCOPE="user" ;;
|
|
17
|
+
--scope=project) SCOPE="project" ;;
|
|
18
|
+
--include-experimental) INCLUDE_EXPERIMENTAL=1 ;;
|
|
19
|
+
*) ARGS+=("$arg") ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
set -- "${ARGS[@]+"${ARGS[@]}"}"
|
|
23
|
+
|
|
24
|
+
# ── Agent registry (name:config_root:skills_dir) ──────────────────────────────
|
|
25
|
+
USER_AGENTS="
|
|
26
|
+
claude:$HOME/.claude:$HOME/.claude/skills
|
|
27
|
+
cursor:$HOME/.cursor:$HOME/.cursor/skills
|
|
28
|
+
codex:$HOME/.codex:$HOME/.codex/skills
|
|
29
|
+
opencode:$HOME/.config/opencode:$HOME/.config/opencode/skills
|
|
30
|
+
droid:$HOME/.factory:$HOME/.factory/skills
|
|
31
|
+
"
|
|
32
|
+
|
|
33
|
+
PROJECT_AGENTS="
|
|
34
|
+
claude:$(pwd)/.claude:$(pwd)/.claude/skills
|
|
35
|
+
cursor:$(pwd)/.cursor:$(pwd)/.cursor/skills
|
|
36
|
+
opencode:$(pwd)/.opencode:$(pwd)/.opencode/skills
|
|
37
|
+
"
|
|
38
|
+
|
|
39
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
agent_list_for_scope() {
|
|
42
|
+
if [ "$SCOPE" = "project" ]; then
|
|
43
|
+
echo "$PROJECT_AGENTS"
|
|
44
|
+
else
|
|
45
|
+
echo "$USER_AGENTS"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
detected_agents() {
|
|
50
|
+
agent_list_for_scope | grep -v '^$' | while IFS=: read -r name root _skills; do
|
|
51
|
+
if [ "$SCOPE" = "project" ]; then
|
|
52
|
+
# Project scope: include if the config dir exists OR we're creating it
|
|
53
|
+
[ -d "$(dirname "$root")" ] && echo "$name:$root:$_skills"
|
|
54
|
+
else
|
|
55
|
+
[ -d "$root" ] && echo "$name:$root:$_skills"
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
stub_yaml_value() {
|
|
61
|
+
# $1=key $2=stub.yaml path — minimal key: value parser
|
|
62
|
+
grep "^${1}:" "$2" | sed 's/^[^:]*: *//' | tr -d '"'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
stub_yaml_list() {
|
|
66
|
+
# $1=key $2=stub.yaml path — parse block-sequence list (- item per line)
|
|
67
|
+
awk -v key="$1" '
|
|
68
|
+
$0 ~ ("^" key ":") { in_list=1; next }
|
|
69
|
+
in_list && /^[[:space:]]*-[[:space:]]/ {
|
|
70
|
+
line=$0; sub(/^[[:space:]]*-[[:space:]]+/,"",line); gsub(/"/,"",line)
|
|
71
|
+
print line; next
|
|
72
|
+
}
|
|
73
|
+
in_list && /^[^[:space:]]/ { exit }
|
|
74
|
+
' "$2"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
stub_tier() {
|
|
78
|
+
local yaml="$1/stub.yaml"
|
|
79
|
+
[ -f "$yaml" ] && stub_yaml_value tier "$yaml" 2>/dev/null || echo "experimental"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
is_allowed_tier() {
|
|
83
|
+
local src="$1"
|
|
84
|
+
[ "$INCLUDE_EXPERIMENTAL" -eq 1 ] && return 0
|
|
85
|
+
local tier
|
|
86
|
+
tier="$(stub_tier "$src")"
|
|
87
|
+
[ "$tier" != "experimental" ]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
stub_type() {
|
|
91
|
+
local yaml="$1/stub.yaml"
|
|
92
|
+
if [ -f "$yaml" ]; then
|
|
93
|
+
local t
|
|
94
|
+
t="$(stub_yaml_value type "$yaml")"
|
|
95
|
+
echo "${t:-skill}"
|
|
96
|
+
else
|
|
97
|
+
echo "skill"
|
|
98
|
+
fi
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
raw_url_from_stub() {
|
|
102
|
+
# Derive raw GitHub URL from stub.yaml fields
|
|
103
|
+
local yaml="$1"
|
|
104
|
+
local upstream upstream_path version repo branch
|
|
105
|
+
upstream="$(stub_yaml_value upstream "$yaml")"
|
|
106
|
+
upstream_path="$(stub_yaml_value upstream_path "$yaml")"
|
|
107
|
+
version="$(stub_yaml_value version "$yaml")"
|
|
108
|
+
repo="$(echo "$upstream" | sed 's|https://github.com/||')"
|
|
109
|
+
branch="$([ "$version" = "latest" ] && echo "main" || echo "$version")"
|
|
110
|
+
echo "https://raw.githubusercontent.com/$repo/$branch/$upstream_path"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
is_stub() {
|
|
114
|
+
# Stub = SKILL.md still has das: frontmatter block
|
|
115
|
+
local md="$1/SKILL.md"
|
|
116
|
+
[ -f "$md" ] && grep -q "^das:" "$md"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
has_stub_manifest() {
|
|
120
|
+
[ -f "$1/stub.yaml" ]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
sha256_file() {
|
|
124
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
125
|
+
sha256sum "$1" | awk '{print $1}'
|
|
126
|
+
else
|
|
127
|
+
shasum -a 256 "$1" | awk '{print $1}'
|
|
128
|
+
fi
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
lockfile_sha() {
|
|
132
|
+
# $1=skill — returns stored sha256 from lockfile, empty if not found
|
|
133
|
+
[ -f "$LOCKFILE" ] || return
|
|
134
|
+
awk -v s="$1" -F'\t' 'NF>=2 && $1==s { print $2; exit }' "$LOCKFILE"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
lockfile_upsert() {
|
|
138
|
+
# $1=skill $2=sha256 $3=upstream_url
|
|
139
|
+
local skill="$1" sha="$2" url="$3"
|
|
140
|
+
local ts
|
|
141
|
+
ts="$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u)"
|
|
142
|
+
local tmp
|
|
143
|
+
tmp="$(mktemp)"
|
|
144
|
+
# Copy header and all lines except existing entry for this skill
|
|
145
|
+
if [ -f "$LOCKFILE" ]; then
|
|
146
|
+
grep -v "^${skill} " "$LOCKFILE" > "$tmp" || true
|
|
147
|
+
else
|
|
148
|
+
printf '# design-agent-skills.lock — generated by install.sh\n' >> "$tmp"
|
|
149
|
+
printf '# skill\tsha256\tupstream\tlocked_at\n' >> "$tmp"
|
|
150
|
+
fi
|
|
151
|
+
printf '%s\t%s\t%s\t%s\n' "$skill" "$sha" "$url" "$ts" >> "$tmp"
|
|
152
|
+
mv "$tmp" "$LOCKFILE"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
skill_state() {
|
|
156
|
+
local target="$1" src="$2"
|
|
157
|
+
local stype
|
|
158
|
+
stype="$(stub_type "$src")"
|
|
159
|
+
if [ "$stype" = "package" ] || [ "$stype" = "platform" ]; then
|
|
160
|
+
local yaml="$src/stub.yaml"
|
|
161
|
+
local skills_dir
|
|
162
|
+
skills_dir="$(dirname "$target")"
|
|
163
|
+
# Check for block-list installed_as first
|
|
164
|
+
local list_items
|
|
165
|
+
list_items="$(stub_yaml_list installed_as "$yaml" 2>/dev/null || true)"
|
|
166
|
+
if [ -n "$list_items" ]; then
|
|
167
|
+
local total=0 found=0
|
|
168
|
+
while IFS= read -r item; do
|
|
169
|
+
total=$((total + 1))
|
|
170
|
+
local t="$skills_dir/$item"
|
|
171
|
+
if [ -d "$t" ] && ! grep -q "^das:" "$t/SKILL.md" 2>/dev/null; then
|
|
172
|
+
found=$((found + 1))
|
|
173
|
+
fi
|
|
174
|
+
done <<< "$list_items"
|
|
175
|
+
if [ "$found" -eq 0 ]; then
|
|
176
|
+
echo "package"
|
|
177
|
+
elif [ "$found" -eq "$total" ]; then
|
|
178
|
+
echo "installed"
|
|
179
|
+
else
|
|
180
|
+
echo "${found}/${total}"
|
|
181
|
+
fi
|
|
182
|
+
return
|
|
183
|
+
fi
|
|
184
|
+
# Scalar installed_as
|
|
185
|
+
local installed_as
|
|
186
|
+
installed_as="$(stub_yaml_value installed_as "$yaml" 2>/dev/null || true)"
|
|
187
|
+
[ -z "$installed_as" ] && installed_as="$(basename "$src")"
|
|
188
|
+
local installed_target="$skills_dir/$installed_as"
|
|
189
|
+
if [ -d "$installed_target" ] && ! grep -q "^das:" "$installed_target/SKILL.md" 2>/dev/null; then
|
|
190
|
+
echo "installed"
|
|
191
|
+
else
|
|
192
|
+
echo "package"
|
|
193
|
+
fi
|
|
194
|
+
return
|
|
195
|
+
fi
|
|
196
|
+
if [ -L "$target" ] && [ ! -e "$target" ]; then
|
|
197
|
+
echo "BROKEN"
|
|
198
|
+
elif [ -L "$target" ] && is_stub "$src"; then
|
|
199
|
+
echo "stub"
|
|
200
|
+
elif [ -L "$target" ]; then
|
|
201
|
+
echo "upgraded"
|
|
202
|
+
elif [ -d "$target" ]; then
|
|
203
|
+
is_stub "$target" && echo "stub" || echo "upgraded"
|
|
204
|
+
else
|
|
205
|
+
echo "-"
|
|
206
|
+
fi
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
skill_names() {
|
|
210
|
+
for d in "$SKILLS_SRC"/*/; do
|
|
211
|
+
[ -d "$d" ] && basename "${d%/}"
|
|
212
|
+
done
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# ── Commands ──────────────────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
cmd_install() {
|
|
218
|
+
local agent_list
|
|
219
|
+
agent_list="$(detected_agents)"
|
|
220
|
+
[ -z "$agent_list" ] && { echo "No supported agents found."; exit 0; }
|
|
221
|
+
|
|
222
|
+
while IFS=: read -r name _root skills_dir; do
|
|
223
|
+
mkdir -p "$skills_dir"
|
|
224
|
+
local linked=0 skipped=0
|
|
225
|
+
while IFS= read -r skill; do
|
|
226
|
+
local target="$skills_dir/$skill"
|
|
227
|
+
local stype
|
|
228
|
+
stype="$(stub_type "$SKILLS_SRC/$skill")"
|
|
229
|
+
if [ "$stype" = "skill" ] || [ "$stype" = "navigator" ]; then
|
|
230
|
+
if ! is_allowed_tier "$SKILLS_SRC/$skill"; then
|
|
231
|
+
skipped=$((skipped + 1))
|
|
232
|
+
elif [ -L "$target" ] || [ -d "$target" ]; then
|
|
233
|
+
skipped=$((skipped + 1))
|
|
234
|
+
else
|
|
235
|
+
ln -s "$SKILLS_SRC/$skill" "$target"
|
|
236
|
+
linked=$((linked + 1))
|
|
237
|
+
fi
|
|
238
|
+
else
|
|
239
|
+
skipped=$((skipped + 1))
|
|
240
|
+
fi
|
|
241
|
+
done < <(skill_names)
|
|
242
|
+
printf " %-12s +%d linked %d skipped\n" "$name" "$linked" "$skipped"
|
|
243
|
+
done <<< "$agent_list"
|
|
244
|
+
|
|
245
|
+
echo
|
|
246
|
+
if [ "$INCLUDE_EXPERIMENTAL" -eq 0 ]; then
|
|
247
|
+
printf "scope: %s | experimental skills skipped (add --include-experimental to install all)\n" "$SCOPE"
|
|
248
|
+
else
|
|
249
|
+
printf "scope: %s | run './install.sh status' to inspect states.\n" "$SCOPE"
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# Warn about user-scope-only agents when running project scope
|
|
253
|
+
if [ "$SCOPE" = "project" ]; then
|
|
254
|
+
local skipped_agents=""
|
|
255
|
+
for _entry in "codex:$HOME/.codex" "droid:$HOME/.factory"; do
|
|
256
|
+
local _aname _aroot
|
|
257
|
+
IFS=: read -r _aname _aroot <<< "$_entry"
|
|
258
|
+
[ -d "$_aroot" ] && skipped_agents="${skipped_agents:+$skipped_agents, }$_aname"
|
|
259
|
+
done
|
|
260
|
+
if [ -n "$skipped_agents" ]; then
|
|
261
|
+
printf "note: %s detected but not linked (user-scope only) — omit --scope=project to include.\n" "$skipped_agents"
|
|
262
|
+
fi
|
|
263
|
+
fi
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
cmd_status() {
|
|
267
|
+
local agent_list
|
|
268
|
+
agent_list="$(detected_agents)"
|
|
269
|
+
[ -z "$agent_list" ] && { echo "No supported agents found."; exit 0; }
|
|
270
|
+
|
|
271
|
+
local names=() dirs=()
|
|
272
|
+
while IFS=: read -r name _root skills_dir; do
|
|
273
|
+
names+=("$name")
|
|
274
|
+
dirs+=("$skills_dir")
|
|
275
|
+
done <<< "$agent_list"
|
|
276
|
+
|
|
277
|
+
local ncols=${#names[@]}
|
|
278
|
+
|
|
279
|
+
printf "\n%-22s %-4s" "skill" "tier"
|
|
280
|
+
for name in "${names[@]}"; do printf " %-10s" "$name"; done
|
|
281
|
+
echo
|
|
282
|
+
|
|
283
|
+
printf "%-22s %-4s" "──────────────────────" "────"
|
|
284
|
+
for _ in "${names[@]}"; do printf " %-10s" "──────────"; done
|
|
285
|
+
echo
|
|
286
|
+
|
|
287
|
+
while IFS= read -r skill; do
|
|
288
|
+
local src="$SKILLS_SRC/$skill"
|
|
289
|
+
local tier_char
|
|
290
|
+
case "$(stub_tier "$src")" in
|
|
291
|
+
official) tier_char="O" ;;
|
|
292
|
+
community) tier_char="C" ;;
|
|
293
|
+
experimental) tier_char="E" ;;
|
|
294
|
+
*) tier_char="?" ;;
|
|
295
|
+
esac
|
|
296
|
+
printf "%-22s %-4s" "$skill" "$tier_char"
|
|
297
|
+
local i=0
|
|
298
|
+
while [ $i -lt $ncols ]; do
|
|
299
|
+
local target="${dirs[$i]}/$skill"
|
|
300
|
+
printf " %-10s" "$(skill_state "$target" "$src")"
|
|
301
|
+
i=$((i + 1))
|
|
302
|
+
done
|
|
303
|
+
echo
|
|
304
|
+
done < <(skill_names)
|
|
305
|
+
|
|
306
|
+
echo
|
|
307
|
+
printf "scope: %s\n" "$SCOPE"
|
|
308
|
+
echo "tier: O=official C=community E=experimental"
|
|
309
|
+
echo "stub=pending upgraded=full installed=package installed package=not installed BROKEN=run fix"
|
|
310
|
+
echo
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
cmd_update() {
|
|
314
|
+
local dry_run=0 frozen=0
|
|
315
|
+
for arg in "$@"; do
|
|
316
|
+
case "$arg" in
|
|
317
|
+
--dry-run) dry_run=1 ;;
|
|
318
|
+
--frozen) frozen=1 ;;
|
|
319
|
+
esac
|
|
320
|
+
done
|
|
321
|
+
|
|
322
|
+
if [ "$frozen" -eq 1 ] && [ ! -f "$LOCKFILE" ]; then
|
|
323
|
+
echo "Error: --frozen requires a lockfile. Run './install.sh lock' first."
|
|
324
|
+
exit 1
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
local agent_list
|
|
328
|
+
agent_list="$(detected_agents)"
|
|
329
|
+
local refreshed=0 linked=0 shown=0
|
|
330
|
+
|
|
331
|
+
[ "$dry_run" -eq 1 ] && echo "(dry run — no changes will be made)"
|
|
332
|
+
echo
|
|
333
|
+
|
|
334
|
+
while IFS= read -r skill; do
|
|
335
|
+
local src="$SKILLS_SRC/$skill"
|
|
336
|
+
local yaml="$src/stub.yaml"
|
|
337
|
+
|
|
338
|
+
# Package/platform types — print install command instead of symlinking
|
|
339
|
+
local stype
|
|
340
|
+
stype="$(stub_type "$src")"
|
|
341
|
+
if [ "$stype" = "package" ] || [ "$stype" = "platform" ]; then
|
|
342
|
+
local install_cmd
|
|
343
|
+
install_cmd="$(stub_yaml_value install_default "$yaml" 2>/dev/null || true)"
|
|
344
|
+
if [ -n "$install_cmd" ]; then
|
|
345
|
+
printf " package %-18s run: %s\n" "$skill" "$install_cmd"
|
|
346
|
+
shown=$((shown + 1))
|
|
347
|
+
fi
|
|
348
|
+
continue
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
# Upgraded stub with manifest → re-fetch from upstream
|
|
352
|
+
if has_stub_manifest "$src" && ! is_stub "$src"; then
|
|
353
|
+
local url
|
|
354
|
+
url="$(raw_url_from_stub "$yaml")"
|
|
355
|
+
if [ "$dry_run" -eq 1 ]; then
|
|
356
|
+
printf " would refresh %s ← %s\n" "$skill" "$url"
|
|
357
|
+
refreshed=$((refreshed + 1))
|
|
358
|
+
elif [ "$frozen" -eq 1 ]; then
|
|
359
|
+
local locked_sha current_sha
|
|
360
|
+
locked_sha="$(lockfile_sha "$skill")"
|
|
361
|
+
current_sha="$(sha256_file "$src/SKILL.md" 2>/dev/null || true)"
|
|
362
|
+
if [ -n "$locked_sha" ] && [ "$current_sha" != "$locked_sha" ]; then
|
|
363
|
+
printf " DRIFT %s — content changed since lockfile was written\n" "$skill"
|
|
364
|
+
fi
|
|
365
|
+
# Skip re-fetch; keep current content
|
|
366
|
+
else
|
|
367
|
+
# Backup before overwrite
|
|
368
|
+
cp "$src/SKILL.md" "$src/SKILL.md.bak" 2>/dev/null || true
|
|
369
|
+
printf " updating %s ... " "$skill"
|
|
370
|
+
local upstream_url
|
|
371
|
+
upstream_url="$(stub_yaml_value upstream "$yaml" 2>/dev/null || true)"
|
|
372
|
+
if curl -fsSL "$url" -o "$src/SKILL.md" 2>/dev/null; then
|
|
373
|
+
echo "ok"
|
|
374
|
+
refreshed=$((refreshed + 1))
|
|
375
|
+
# Record in lockfile
|
|
376
|
+
local new_sha
|
|
377
|
+
new_sha="$(sha256_file "$src/SKILL.md" 2>/dev/null || true)"
|
|
378
|
+
[ -n "$new_sha" ] && lockfile_upsert "$skill" "$new_sha" "$upstream_url"
|
|
379
|
+
else
|
|
380
|
+
# Restore backup on failure
|
|
381
|
+
mv "$src/SKILL.md.bak" "$src/SKILL.md" 2>/dev/null || true
|
|
382
|
+
echo "failed (upstream unreachable — backup restored)"
|
|
383
|
+
fi
|
|
384
|
+
fi
|
|
385
|
+
continue
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# New stub not yet linked to all agents → add missing links (respects tier filter)
|
|
389
|
+
[ "$stype" = "skill" ] || [ "$stype" = "navigator" ] || continue
|
|
390
|
+
is_allowed_tier "$src" || continue
|
|
391
|
+
while IFS=: read -r name _root skills_dir; do
|
|
392
|
+
local target="$skills_dir/$skill"
|
|
393
|
+
if [ ! -L "$target" ] && [ ! -d "$target" ]; then
|
|
394
|
+
if [ "$dry_run" -eq 1 ]; then
|
|
395
|
+
printf " would link %-18s → %s\n" "$skill" "$name"
|
|
396
|
+
else
|
|
397
|
+
mkdir -p "$skills_dir"
|
|
398
|
+
ln -s "$src" "$target"
|
|
399
|
+
printf " added %-18s %s\n" "$skill" "$name"
|
|
400
|
+
fi
|
|
401
|
+
linked=$((linked + 1))
|
|
402
|
+
fi
|
|
403
|
+
done <<< "$agent_list"
|
|
404
|
+
|
|
405
|
+
done < <(skill_names)
|
|
406
|
+
|
|
407
|
+
if [ "$refreshed" -eq 0 ] && [ "$linked" -eq 0 ] && [ "$shown" -eq 0 ]; then
|
|
408
|
+
echo "Nothing to update."
|
|
409
|
+
else
|
|
410
|
+
if [ "$dry_run" -eq 1 ]; then
|
|
411
|
+
[ "$refreshed" -gt 0 ] && echo "$refreshed skill(s) would be refreshed from upstream."
|
|
412
|
+
[ "$linked" -gt 0 ] && echo "$linked new link(s) would be added."
|
|
413
|
+
[ "$shown" -gt 0 ] && echo "$shown package install command(s) shown."
|
|
414
|
+
else
|
|
415
|
+
[ "$refreshed" -gt 0 ] && echo "$refreshed skill(s) refreshed from upstream."
|
|
416
|
+
[ "$linked" -gt 0 ] && echo "$linked new link(s) added."
|
|
417
|
+
[ "$shown" -gt 0 ] && echo "$shown package install command(s) shown."
|
|
418
|
+
fi
|
|
419
|
+
fi
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
cmd_fix() {
|
|
423
|
+
local agent_list
|
|
424
|
+
agent_list="$(detected_agents)"
|
|
425
|
+
local fixed=0
|
|
426
|
+
|
|
427
|
+
while IFS=: read -r _name _root skills_dir; do
|
|
428
|
+
[ -d "$skills_dir" ] || continue
|
|
429
|
+
for target in "$skills_dir"/*/; do
|
|
430
|
+
target="${target%/}"
|
|
431
|
+
[ -L "$target" ] || continue
|
|
432
|
+
if [ ! -e "$target" ]; then
|
|
433
|
+
rm "$target"
|
|
434
|
+
printf " removed broken link: %s\n" "$target"
|
|
435
|
+
fixed=$((fixed + 1))
|
|
436
|
+
fi
|
|
437
|
+
done
|
|
438
|
+
done <<< "$agent_list"
|
|
439
|
+
|
|
440
|
+
[ "$fixed" -eq 0 ] && echo "No broken links found." || echo "Fixed $fixed broken link(s)."
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
cmd_lock() {
|
|
444
|
+
echo "Generating lockfile: $LOCKFILE"
|
|
445
|
+
local tmp
|
|
446
|
+
tmp="$(mktemp)"
|
|
447
|
+
printf '# design-agent-skills.lock — generated by install.sh\n' > "$tmp"
|
|
448
|
+
printf '# skill\tsha256\tupstream\tlocked_at\n' >> "$tmp"
|
|
449
|
+
|
|
450
|
+
local ts locked=0
|
|
451
|
+
ts="$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u)"
|
|
452
|
+
|
|
453
|
+
while IFS= read -r skill; do
|
|
454
|
+
local src="$SKILLS_SRC/$skill"
|
|
455
|
+
local md="$src/SKILL.md"
|
|
456
|
+
local yaml="$src/stub.yaml"
|
|
457
|
+
[ -f "$md" ] || continue
|
|
458
|
+
is_stub "$src" && continue # only lock upgraded (non-stub) skills
|
|
459
|
+
local sha upstream
|
|
460
|
+
sha="$(sha256_file "$md")"
|
|
461
|
+
upstream="$(stub_yaml_value upstream "$yaml" 2>/dev/null || true)"
|
|
462
|
+
printf '%s\t%s\t%s\t%s\n' "$skill" "$sha" "$upstream" "$ts" >> "$tmp"
|
|
463
|
+
echo " locked $skill"
|
|
464
|
+
locked=$((locked + 1))
|
|
465
|
+
done < <(skill_names)
|
|
466
|
+
|
|
467
|
+
mv "$tmp" "$LOCKFILE"
|
|
468
|
+
if [ "$locked" -eq 0 ]; then
|
|
469
|
+
echo "No upgraded skills found — lockfile written (empty)."
|
|
470
|
+
else
|
|
471
|
+
printf "%d skill(s) locked.\n" "$locked"
|
|
472
|
+
fi
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
cmd_doctor() {
|
|
476
|
+
local strict=0 show_substr=0
|
|
477
|
+
for arg in "$@"; do
|
|
478
|
+
case "$arg" in
|
|
479
|
+
--strict) strict=1 ;;
|
|
480
|
+
--substr) show_substr=1 ;;
|
|
481
|
+
esac
|
|
482
|
+
done
|
|
483
|
+
|
|
484
|
+
local tmp
|
|
485
|
+
tmp="$(mktemp)"
|
|
486
|
+
|
|
487
|
+
local skill_count
|
|
488
|
+
skill_count=$(skill_names | wc -l | tr -d ' ')
|
|
489
|
+
printf "Scanning %d skills for trigger collisions...\n\n" "$skill_count"
|
|
490
|
+
|
|
491
|
+
# Collect (trigger TAB skill) pairs into temp file
|
|
492
|
+
while IFS= read -r skill; do
|
|
493
|
+
local md="$SKILLS_SRC/$skill/SKILL.md"
|
|
494
|
+
[ -f "$md" ] || continue
|
|
495
|
+
awk -v skill="$skill" '
|
|
496
|
+
BEGIN { in_front=0; in_trig=0 }
|
|
497
|
+
/^---$/ { if(in_front==0){in_front=1}else{exit}; next }
|
|
498
|
+
in_front && /^triggers:/ { in_trig=1; next }
|
|
499
|
+
in_front && in_trig && /^[[:space:]]*-[[:space:]]/ {
|
|
500
|
+
line=$0; sub(/^[[:space:]]*-[[:space:]]+/,"",line); gsub(/"/,"",line)
|
|
501
|
+
print line "\t" skill; next
|
|
502
|
+
}
|
|
503
|
+
in_front && in_trig && /^[^[:space:]]/ { in_trig=0 }
|
|
504
|
+
' "$md"
|
|
505
|
+
done < <(skill_names) > "$tmp"
|
|
506
|
+
|
|
507
|
+
local issues=0
|
|
508
|
+
|
|
509
|
+
# ── Exact collisions ─────────────────────────────────────────────────────────
|
|
510
|
+
local exact_out
|
|
511
|
+
exact_out=$(sort "$tmp" | awk -F'\t' '
|
|
512
|
+
{
|
|
513
|
+
key=$1; skill=$2
|
|
514
|
+
if (key != prev_key) {
|
|
515
|
+
if (cnt > 1) print prev_key "\t" list
|
|
516
|
+
prev_key=key; list=skill; cnt=1
|
|
517
|
+
} else { list=list ", " skill; cnt++ }
|
|
518
|
+
}
|
|
519
|
+
END { if (cnt > 1) print prev_key "\t" list }
|
|
520
|
+
')
|
|
521
|
+
|
|
522
|
+
if [ -n "$exact_out" ]; then
|
|
523
|
+
local exact_count
|
|
524
|
+
exact_count=$(printf '%s\n' "$exact_out" | grep -c . || true)
|
|
525
|
+
printf "Exact trigger collisions (%d):\n" "$exact_count"
|
|
526
|
+
printf '%s\n' "$exact_out" | awk -F'\t' '{ printf " %-34s → %s\n", "\"" $1 "\"", $2 }'
|
|
527
|
+
echo
|
|
528
|
+
issues=$((issues + exact_count))
|
|
529
|
+
fi
|
|
530
|
+
|
|
531
|
+
# ── Substring overlaps (opt-in via --substr) ──────────────────────────────────
|
|
532
|
+
if [ "$show_substr" -eq 1 ]; then
|
|
533
|
+
local substr_out
|
|
534
|
+
substr_out=$(cut -f1 "$tmp" | sort -u | awk '
|
|
535
|
+
{ t[NR]=$0 }
|
|
536
|
+
END {
|
|
537
|
+
n=NR
|
|
538
|
+
for(i=1;i<=n;i++) for(j=1;j<=n;j++) {
|
|
539
|
+
if(i!=j && index(t[i],t[j])>0 && length(t[j])<length(t[i]))
|
|
540
|
+
printf "%s\t%s\n", t[j], t[i]
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
' | sort -u)
|
|
544
|
+
|
|
545
|
+
if [ -n "$substr_out" ]; then
|
|
546
|
+
local substr_count
|
|
547
|
+
substr_count=$(printf '%s\n' "$substr_out" | grep -c . || true)
|
|
548
|
+
printf "Substring overlaps — shorter trigger subsumed by longer (%d):\n" "$substr_count"
|
|
549
|
+
printf '%s\n' "$substr_out" | awk -F'\t' '{ printf " %-20s ⊇ %s\n", "\"" $1 "\"", "\"" $2 "\"" }'
|
|
550
|
+
echo
|
|
551
|
+
issues=$((issues + substr_count))
|
|
552
|
+
fi
|
|
553
|
+
fi
|
|
554
|
+
|
|
555
|
+
rm -f "$tmp"
|
|
556
|
+
|
|
557
|
+
# ── Symlink fragility scan ──────────────────────────────────────────────────
|
|
558
|
+
local orphaned=0 relocated=0
|
|
559
|
+
while IFS=: read -r _name _root skills_dir; do
|
|
560
|
+
[ -d "$skills_dir" ] || continue
|
|
561
|
+
while IFS= read -r link; do
|
|
562
|
+
[ -L "$link" ] || continue
|
|
563
|
+
local target
|
|
564
|
+
target="$(readlink "$link")"
|
|
565
|
+
if [ ! -e "$link" ]; then
|
|
566
|
+
orphaned=$((orphaned + 1))
|
|
567
|
+
elif [ "${target#"$SKILLS_SRC"}" = "$target" ]; then
|
|
568
|
+
relocated=$((relocated + 1))
|
|
569
|
+
fi
|
|
570
|
+
done < <(find "$skills_dir" -maxdepth 1 -type l 2>/dev/null)
|
|
571
|
+
done < <(detected_agents)
|
|
572
|
+
|
|
573
|
+
if [ "$orphaned" -gt 0 ]; then
|
|
574
|
+
printf "BROKEN %d orphaned symlink(s) — target missing (repo moved?).\n" "$orphaned"
|
|
575
|
+
printf " Run: ./install.sh fix && ./install.sh install\n\n"
|
|
576
|
+
issues=$((issues + orphaned))
|
|
577
|
+
fi
|
|
578
|
+
if [ "$relocated" -gt 0 ]; then
|
|
579
|
+
printf "RELOCATED %d symlink(s) point outside current install dir.\n" "$relocated"
|
|
580
|
+
printf " Likely caused by moving the repo. Run: ./install.sh fix && ./install.sh install\n\n"
|
|
581
|
+
issues=$((issues + relocated))
|
|
582
|
+
fi
|
|
583
|
+
if [ "$orphaned" -eq 0 ] && [ "$relocated" -eq 0 ]; then
|
|
584
|
+
echo "Symlinks OK — all links resolve inside current install dir."
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
echo
|
|
588
|
+
if [ "$issues" -eq 0 ]; then
|
|
589
|
+
echo "All checks OK — no issues found."
|
|
590
|
+
else
|
|
591
|
+
printf "%d issue(s) found.\n" "$issues"
|
|
592
|
+
if [ "$strict" -eq 1 ]; then
|
|
593
|
+
echo "Exiting 1 (--strict)."
|
|
594
|
+
exit 1
|
|
595
|
+
fi
|
|
596
|
+
fi
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
cmd_remove() {
|
|
600
|
+
local skill="${1:-}"
|
|
601
|
+
if [ -z "$skill" ]; then
|
|
602
|
+
echo "Usage: ./install.sh remove <skill-name>"
|
|
603
|
+
echo " ./install.sh remove --all"
|
|
604
|
+
exit 1
|
|
605
|
+
fi
|
|
606
|
+
|
|
607
|
+
local agent_list
|
|
608
|
+
agent_list="$(detected_agents)"
|
|
609
|
+
local removed=0
|
|
610
|
+
|
|
611
|
+
if [ "$skill" = "--all" ]; then
|
|
612
|
+
while IFS=: read -r _name _root skills_dir; do
|
|
613
|
+
[ -d "$skills_dir" ] || continue
|
|
614
|
+
while IFS= read -r sk; do
|
|
615
|
+
local target="$skills_dir/$sk"
|
|
616
|
+
[ -L "$target" ] || continue
|
|
617
|
+
rm "$target"
|
|
618
|
+
printf " removed: %s\n" "$target"
|
|
619
|
+
removed=$((removed + 1))
|
|
620
|
+
done < <(skill_names)
|
|
621
|
+
done <<< "$agent_list"
|
|
622
|
+
printf "%d link(s) removed.\n" "$removed"
|
|
623
|
+
return
|
|
624
|
+
fi
|
|
625
|
+
|
|
626
|
+
if [ ! -d "$SKILLS_SRC/$skill" ]; then
|
|
627
|
+
echo "Unknown skill: $skill"
|
|
628
|
+
echo "Run './install.sh status' to see available skills."
|
|
629
|
+
exit 1
|
|
630
|
+
fi
|
|
631
|
+
|
|
632
|
+
while IFS=: read -r name _root skills_dir; do
|
|
633
|
+
local target="$skills_dir/$skill"
|
|
634
|
+
if [ -L "$target" ] || [ -d "$target" ]; then
|
|
635
|
+
rm -rf "$target"
|
|
636
|
+
printf " removed from %s\n" "$name"
|
|
637
|
+
removed=$((removed + 1))
|
|
638
|
+
fi
|
|
639
|
+
done <<< "$agent_list"
|
|
640
|
+
|
|
641
|
+
[ "$removed" -eq 0 ] \
|
|
642
|
+
&& echo "No links found for: $skill" \
|
|
643
|
+
|| printf "%d link(s) removed.\n" "$removed"
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
cmd_help() {
|
|
647
|
+
cat <<HELP
|
|
648
|
+
Usage: ./install.sh [flags] [command]
|
|
649
|
+
|
|
650
|
+
Global flags:
|
|
651
|
+
--scope=user Link to agent user-level dirs (default): ~/.claude/skills/
|
|
652
|
+
--scope=project Link to project-level dirs: ./.claude/skills/
|
|
653
|
+
--include-experimental Also install experimental-tier skills (default: official+community only)
|
|
654
|
+
|
|
655
|
+
Commands:
|
|
656
|
+
install Symlink all stubs to detected agents (default)
|
|
657
|
+
status Show stub / upgraded / installed / BROKEN state per agent
|
|
658
|
+
update Re-fetch upgraded skills from upstream; add links for new stubs
|
|
659
|
+
--dry-run Show what would change without making any changes
|
|
660
|
+
--frozen Skip re-fetching; warn if content has drifted from lockfile
|
|
661
|
+
lock Generate design-agent-skills.lock from current upgraded skill state
|
|
662
|
+
fix Remove broken symlinks
|
|
663
|
+
remove <skill> Remove a specific skill's links from all agents
|
|
664
|
+
remove --all Remove all skill links (uninstall)
|
|
665
|
+
doctor Scan for trigger collisions and orphaned/relocated symlinks
|
|
666
|
+
--strict Exit 1 if any issues found (useful for CI)
|
|
667
|
+
--substr Also report substring overlaps (broad triggers subsuming narrow ones)
|
|
668
|
+
help Show this message
|
|
669
|
+
|
|
670
|
+
Skill tiers: official (28) community (35) experimental (60)
|
|
671
|
+
User-scope agents: claude cursor codex opencode droid
|
|
672
|
+
Project-scope agents: claude cursor opencode
|
|
673
|
+
HELP
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
# ── Entry ─────────────────────────────────────────────────────────────────────
|
|
677
|
+
CMD="${1:-install}"
|
|
678
|
+
shift 2>/dev/null || true
|
|
679
|
+
case "$CMD" in
|
|
680
|
+
install) cmd_install ;;
|
|
681
|
+
status) cmd_status ;;
|
|
682
|
+
update) cmd_update "$@" ;;
|
|
683
|
+
fix) cmd_fix ;;
|
|
684
|
+
remove) cmd_remove "$@" ;;
|
|
685
|
+
lock) cmd_lock ;;
|
|
686
|
+
doctor) cmd_doctor "$@" ;;
|
|
687
|
+
help|-h) cmd_help ;;
|
|
688
|
+
*) echo "Unknown command: $CMD"; cmd_help; exit 1 ;;
|
|
689
|
+
esac
|