Repository navigation
Expand file tree
/
Copy pathsetup
More file actions
executable file
·199 lines (186 loc) · 8.81 KB
/
Copy pathsetup
File metadata and controls
executable file
·199 lines (186 loc) · 8.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/usr/bin/env bash
set -e
# Resolve the *physical* path so legacy-symlink cleanup below can't delete the
# directory we're currently sourcing setup from. Without -P, IDSTACK_DIR may be
# the legacy ~/.claude/skills/idstack symlink path, and removing it would leave
# the new plugin symlink pointing at a deleted target.
IDSTACK_DIR="$(cd "$(dirname "$0")" && pwd -P)"
# Shared legacy-VERSION classifier (also sourced by bin/idstack-doctor; pinned
# by test/test-version-classifier.sh).
. "$IDSTACK_DIR/bin/lib/version-classify.sh"
# Default install: user-scope plugin so /idstack:<skill> is discovered.
LEGACY_SKILLS_DIR="$HOME/.claude/skills"
SCOPE="global"
CLAUDE_SCOPE="user" # claude plugin scope: user (global) or project (--local)
KEEP_LEGACY=0 # --keep-legacy: leave every legacy install shape in place
for arg in "$@"; do
case "$arg" in
--local)
LEGACY_SKILLS_DIR="$(pwd)/.claude/skills"
SCOPE="local"
CLAUDE_SCOPE="project"
;;
--keep-legacy) KEEP_LEGACY=1 ;;
-h|--help)
echo "Command: ./setup [--local] [--keep-legacy]"
echo " --local Installs idstack at project scope (./.claude/), not at user scope."
echo " --keep-legacy Keeps the legacy installs in \$HOME/.claude/skills/. These are the"
echo " pre-v2.0.1.0 dispatcher directory, the v2.0 dispatcher symlink and"
echo " the pre-v2 per-skill symlinks. By default, setup removes them."
exit 0
;;
*) echo "ERROR: unknown argument: $arg" >&2; echo "Run ./setup --help for more information." >&2; exit 2 ;;
esac
done
# Regenerate skill files so installs are always fresh against the current .tmpl
# source. Cheap (<1s); the source-of-truth invariant is worth more than the cost.
"$IDSTACK_DIR/bin/idstack-gen-skills" >/dev/null
echo " regenerated skill files"
echo "Setup installs idstack (scope: $CLAUDE_SCOPE)..."
echo " source: $IDSTACK_DIR"
echo ""
# Verify plugin + marketplace manifests are present (sanity check; setup should
# not silently install something Claude Code can't discover).
if [ ! -f "$IDSTACK_DIR/.claude-plugin/plugin.json" ]; then
echo "ERROR: missing .claude-plugin/plugin.json at $IDSTACK_DIR" >&2
echo " A plugin layout must have this file. Setup stopped." >&2
exit 1
fi
if [ ! -f "$IDSTACK_DIR/.claude-plugin/marketplace.json" ]; then
echo "ERROR: missing .claude-plugin/marketplace.json at $IDSTACK_DIR" >&2
echo " Claude Code installs plugins through a marketplace manifest. Setup stopped." >&2
exit 1
fi
# Clean up legacy v2.0 + pre-v2 installs under $LEGACY_SKILLS_DIR. Each scope
# only touches its own scope's legacy path: a --local install cleans up legacy
# entries under ./.claude/skills/, and never mutates the user's global skills
# directory.
# v2.0 install (single dispatcher symlink). Only registered /idstack, never
# exposed the 11 namespaced sub-skills.
LEGACY_SKILLS_LINK="$LEGACY_SKILLS_DIR/idstack"
if [ -L "$LEGACY_SKILLS_LINK" ]; then
legacy_target=$(readlink "$LEGACY_SKILLS_LINK" 2>/dev/null || true)
if [ "$KEEP_LEGACY" = "1" ]; then
echo " KEPT legacy symlink: $LEGACY_SKILLS_LINK (--keep-legacy)"
echo " NOTE: This symlink overrides the plugin namespace. It can cause errors in /idstack:<skill> commands."
else
rm "$LEGACY_SKILLS_LINK"
echo " removed legacy symlink: $LEGACY_SKILLS_LINK${legacy_target:+ (was -> $legacy_target)}"
fi
elif [ -d "$LEGACY_SKILLS_LINK" ] && [ "$LEGACY_SKILLS_LINK" != "$IDSTACK_DIR" ]; then
# Pre-v2.0.1.0 install was a real clone with a top-level dispatcher
# SKILL.md. Left in place, it shadows the plugin: Claude Code matches the
# dispatcher on /idstack and the dispatcher then tries to invoke bare-name
# sub-skills that the plugin-only layout no longer exposes. Detect that
# signature and remove it.
legacy_is_dispatcher=0
if [ -f "$LEGACY_SKILLS_LINK/SKILL.md" ] && \
grep -q '^name: idstack' "$LEGACY_SKILLS_LINK/SKILL.md" 2>/dev/null; then
legacy_is_dispatcher=1
fi
legacy_is_old_version=0
if [ -f "$LEGACY_SKILLS_LINK/VERSION" ]; then
legacy_version=$(tr -d '[:space:]' < "$LEGACY_SKILLS_LINK/VERSION")
# Classified by the shared classifier in bin/lib/version-classify.sh
# (pinned by test/test-version-classifier.sh).
if [ "$(classify_version "$legacy_version")" = "legacy" ]; then
legacy_is_old_version=1
fi
fi
if [ "$legacy_is_dispatcher" = "1" ] || [ "$legacy_is_old_version" = "1" ]; then
if [ "$KEEP_LEGACY" = "1" ]; then
echo " KEPT legacy install directory: $LEGACY_SKILLS_LINK (--keep-legacy)"
echo " NOTE: This directory overrides the plugin namespace. It can cause errors in /idstack:<skill> commands."
else
rm -rf "$LEGACY_SKILLS_LINK"
echo " removed legacy install directory: $LEGACY_SKILLS_LINK (pre-v2.0.1.0 dispatcher)"
fi
else
echo " WARNING: directory at $LEGACY_SKILLS_LINK is not a known idstack install."
echo " Setup did not remove it. If you do not want it, remove it manually:"
echo " rm -rf $LEGACY_SKILLS_LINK"
fi
fi
# Pre-v2 individual skill symlinks. Only remove ones that point into idstack.
LEGACY_SKILLS="needs-analysis learning-objectives course-quality-review course-import assessment-design course-builder course-export accessibility-review red-team pipeline learn"
for skill in $LEGACY_SKILLS; do
legacy="$LEGACY_SKILLS_DIR/$skill"
if [ -L "$legacy" ]; then
link_target=$(readlink "$legacy" 2>/dev/null || true)
if echo "$link_target" | grep -q "idstack"; then
if [ "$KEEP_LEGACY" = "1" ]; then
echo " KEPT legacy symlink: $legacy (--keep-legacy)"
else
rm "$legacy"
echo " removed legacy symlink: $legacy"
fi
fi
fi
done
# Vestigial bare-symlink cleanup. Older setup versions symlinked the repo into
# ~/.claude/plugins/idstack (or ./.claude/plugins/idstack for --local). Current
# Claude Code discovers plugins through the marketplace registry, not bare
# symlinks, so that link is dead weight. Only ever removes a *symlink* — a real
# directory there might be someone's clone, and is left untouched. Scope-aware:
# a --local install cleans only ./.claude/plugins and never mutates $HOME.
if [ "$SCOPE" = "local" ]; then
vestigial="$(pwd)/.claude/plugins/idstack"
else
vestigial="$HOME/.claude/plugins/idstack"
fi
if [ -L "$vestigial" ]; then
rm "$vestigial"
echo " removed legacy plugin symlink: $vestigial (pre-marketplace install method)"
fi
# Claude Code install — register this repo as a single-plugin marketplace, then
# install from it. Both commands are idempotent, so re-running setup is safe.
# The marketplace source is this repo's path; `marketplace update` re-reads it,
# and `plugin update` picks up a newer committed version on `git pull && ./setup`.
if command -v claude >/dev/null 2>&1; then
# The two required calls fail loud with a manual-recovery hint — under
# `set -e` a bare nonzero exit here used to abort setup silently, with no
# message at all.
if ! claude plugin marketplace add "$IDSTACK_DIR"; then
echo "" >&2
echo "ERROR: The 'claude plugin marketplace add' command stopped with an error." >&2
echo "Run this command manually. Then run ./setup again:" >&2
echo " claude plugin marketplace add \"$IDSTACK_DIR\"" >&2
exit 1
fi
claude plugin marketplace update idstack >/dev/null 2>&1 || true
if ! claude plugin install idstack@idstack --scope "$CLAUDE_SCOPE"; then
echo "" >&2
echo "ERROR: The 'claude plugin install' command stopped with an error." >&2
echo "Install idstack manually:" >&2
echo " claude plugin install idstack@idstack --scope $CLAUDE_SCOPE" >&2
exit 1
fi
claude plugin update idstack@idstack --scope "$CLAUDE_SCOPE" >/dev/null 2>&1 || true
echo ""
echo "Setup installed idstack in Claude Code (scope: $CLAUDE_SCOPE)."
else
echo " WARNING: 'claude' not found on PATH. Setup did not install idstack in Claude Code."
echo " Install Claude Code. Then run ./setup again. You can also install idstack manually:"
echo " claude plugin marketplace add \"$IDSTACK_DIR\""
echo " claude plugin install idstack@idstack --scope $CLAUDE_SCOPE"
fi
echo ""
echo " If Claude Code is open, close it and open it again."
echo " Claude Code reads plugins only when a session starts."
echo ""
echo " To use a skill, run /idstack:<skill>"
echo ""
echo " If you have a course, import it and examine it:"
echo " /idstack:course-import -> /idstack:course-quality-review"
echo ""
echo " To start a new course:"
echo " /idstack:needs-analysis"
echo ""
echo " Run the full pipeline:"
echo " /idstack:pipeline"
echo ""
echo " All skills: needs-analysis, learning-objectives, assessment-design,"
echo " course-builder, course-quality-review, accessibility-review,"
echo " red-team, course-export, course-import, pipeline, learn"
echo ""
echo " More information: https://idstack.org"