-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.sh
More file actions
executable file
·210 lines (188 loc) · 7.34 KB
/
Copy pathsetup.sh
File metadata and controls
executable file
·210 lines (188 loc) · 7.34 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
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env bash
# iris/setup.sh — bootstrap iris into the target Claude Code project.
#
# Usage:
# bash ~/Tools/iris/setup.sh # interactive
# bash ~/Tools/iris/setup.sh --target /path/to/project --yes
#
# What it does:
# 1. Symlinks .claude/{commands,hooks,skills} from iris into the target
# 2. Symlinks scripts/ from iris into the target
# 3. Copies CLAUDE.md (template) and docs/plan.md (template) if not present
# 4. Copies .env.example if not present
# 5. Offers to clone optional skill sources (~/Tools/superpowers + stop-slop)
# 6. Runs scripts/doctor.py and prints the verdict
set -euo pipefail
IRIS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TARGET="${PWD}"
ASSUME_YES=0
while [ $# -gt 0 ]; do
case "$1" in
--target) TARGET="$2"; shift 2 ;;
--yes|-y) ASSUME_YES=1; shift ;;
--help|-h)
sed -n '2,15p' "$0"; exit 0 ;;
*)
echo "unknown flag: $1" >&2; exit 1 ;;
esac
done
TARGET="$(cd "$TARGET" && pwd)"
echo "iris source: $IRIS_ROOT"
echo "target: $TARGET"
if [ "$IRIS_ROOT" = "$TARGET" ]; then
echo "error: target equals source; cd into your project first" >&2
exit 1
fi
ask() {
[ "$ASSUME_YES" = 1 ] && return 0
local prompt="$1"
read -rp "$prompt [y/N] " ans
[[ "$ans" =~ ^[Yy]$ ]]
}
link() {
local src="$1"
local dst="$2"
if [ -e "$dst" ] && [ ! -L "$dst" ]; then
echo " skip (exists): $dst"
return 0
fi
if [ -L "$dst" ] && [ "$(readlink -f "$dst")" = "$(readlink -f "$src")" ]; then
echo " ok (linked): $dst"
return 0
fi
ln -sfn "$src" "$dst"
echo " link : $dst -> $src"
}
# ----- 1. .claude/ symlinks -------------------------------------------------
echo
echo "[1/5] .claude/ surface"
mkdir -p "$TARGET/.claude"
link "$IRIS_ROOT/.claude/commands" "$TARGET/.claude/commands"
link "$IRIS_ROOT/.claude/hooks" "$TARGET/.claude/hooks"
link "$IRIS_ROOT/.claude/settings.json" "$TARGET/.claude/settings.json"
mkdir -p "$TARGET/.claude/skills"
for skill in handovers swarm manager commit-style karpathy-guidelines; do
link "$IRIS_ROOT/.claude/skills/$skill" "$TARGET/.claude/skills/$skill"
done
# ----- 2. scripts/ symlinks -------------------------------------------------
echo
echo "[2/5] scripts/"
mkdir -p "$TARGET/scripts"
for f in "$IRIS_ROOT"/scripts/*.py "$IRIS_ROOT"/scripts/*.sh; do
link "$f" "$TARGET/scripts/$(basename "$f")"
done
# iris_core is a package, not a loose script — link the directory so the
# shared task model, graph, and scheduler resolve from the target.
link "$IRIS_ROOT/scripts/iris_core" "$TARGET/scripts/iris_core"
# ----- 3. templates ---------------------------------------------------------
echo
echo "[3/5] templates"
for tpl in CLAUDE.md .env.example; do
if [ ! -e "$TARGET/$tpl" ]; then
cp "$IRIS_ROOT/$tpl" "$TARGET/$tpl"
echo " copy : $TARGET/$tpl"
else
echo " skip (exists): $TARGET/$tpl"
fi
done
mkdir -p "$TARGET/docs"
if [ ! -e "$TARGET/docs/plan.md" ]; then
cp "$IRIS_ROOT/docs/plan.md" "$TARGET/docs/plan.md"
echo " copy : $TARGET/docs/plan.md"
fi
if [ ! -e "$TARGET/docs/next.md" ]; then
cp "$IRIS_ROOT/docs/next.md" "$TARGET/docs/next.md"
echo " copy : $TARGET/docs/next.md (plan-ahead queue)"
fi
if [ ! -e "$TARGET/.env" ]; then
cp "$IRIS_ROOT/.env.example" "$TARGET/.env"
echo " copy : $TARGET/.env (edit before first use)"
fi
# scaffold the projects dir so /new-task and doctor's projects-dir check are
# coherent on first run (rename via PROJECTS_DIR in .env; default Projects)
PROJECTS_DIR_NAME="${PROJECTS_DIR:-Projects}"
mkdir -p "$TARGET/$PROJECTS_DIR_NAME"
echo " mkdir : $TARGET/$PROJECTS_DIR_NAME/"
# ----- 3b. the target's .gitignore ------------------------------------------
# iris writes runtime state into the project. Without these patterns a consumer
# commits its own run lock, worktrees, and manager task store on the first
# `git add -A` — which /run does on every green task.
#
# Only missing patterns are added. An existing install already has most of
# these, and appending a duplicate block to every upgrade is just litter.
GITIGNORE="$TARGET/.gitignore"
want_ignore() {
local pattern="$1"
local why="$2"
# Match the pattern as a whole line, anchored or not, so an existing
# `handovers/` counts as covering `/handovers/`.
if grep -qxF -- "$pattern" "$GITIGNORE" 2>/dev/null \
|| grep -qxF -- "${pattern#/}" "$GITIGNORE" 2>/dev/null; then
return 0
fi
if [ ! -s "$GITIGNORE" ] || [ -z "${IRIS_IGNORE_HEADER:-}" ]; then
[ -s "$GITIGNORE" ] && printf '\n' >> "$GITIGNORE"
printf '# --- iris runtime state (added by iris/setup.sh) ---\n' >> "$GITIGNORE"
IRIS_IGNORE_HEADER=1
fi
printf '# %s\n%s\n' "$why" "$pattern" >> "$GITIGNORE"
echo " gitignore : + $pattern"
}
want_ignore ".iris-state/" "run lock, /manager task store, worktrees, second brain"
want_ignore ".swarm-locks.json" "advisory per-wave file locks"
want_ignore "/handovers/" "markdown-backend handovers; delete to track them"
want_ignore ".env" "secrets and per-machine config; .env.example is tracked"
want_ignore ".claude/scheduled_tasks.json" "Claude Code cron state, per-machine"
want_ignore ".claude/scheduled_tasks.lock" "Claude Code cron lock"
want_ignore ".claude/settings.local.json" "Claude Code local settings overrides"
[ -z "${IRIS_IGNORE_HEADER:-}" ] && echo " ok (gitignore): every iris pattern already present"
# ----- 4. optional dependencies --------------------------------------------
echo
echo "[4/5] optional skill sources"
if [ ! -d "$HOME/Tools/superpowers" ]; then
if ask " install superpowers (14 skills) at ~/Tools/superpowers ?"; then
git clone --depth 1 https://github.com/obra/superpowers.git "$HOME/Tools/superpowers"
else
echo " skip superpowers"
fi
else
echo " already at ~/Tools/superpowers"
fi
# Wire skill symlinks whether superpowers was just cloned or already present.
if [ -d "$HOME/Tools/superpowers/skills" ]; then
for s in "$HOME"/Tools/superpowers/skills/*/; do
[ -d "$s" ] || continue
ln -sfn "$s" "$TARGET/.claude/skills/$(basename "$s")"
done
echo " link : superpowers skills -> $TARGET/.claude/skills/"
fi
if [ ! -d "$HOME/Tools/stop-slop" ]; then
if ask " install stop-slop at ~/Tools/stop-slop ?"; then
git clone --depth 1 https://github.com/hardikpandya/stop-slop.git "$HOME/Tools/stop-slop"
else
echo " skip stop-slop"
fi
else
echo " already at ~/Tools/stop-slop"
fi
# Wire the stop-slop symlink whether just cloned or already present.
if [ -d "$HOME/Tools/stop-slop" ]; then
ln -sfn "$HOME/Tools/stop-slop" "$TARGET/.claude/skills/stop-slop"
echo " link : stop-slop -> $TARGET/.claude/skills/stop-slop"
fi
# ----- 5. doctor ------------------------------------------------------------
echo
echo "[5/5] doctor"
cd "$TARGET"
python3 scripts/doctor.py || {
echo
echo "doctor reported issues. Resolve them, then re-run scripts/doctor.py."
exit 1
}
cat <<EOF
iris installed into $TARGET
Next:
1. Edit $TARGET/.env with your config (memory backend, optional Slack creds).
2. Edit $TARGET/CLAUDE.md to add your project's standing instructions.
3. Open a Claude Code session at $TARGET and try /status.
EOF