-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstep-1-install.sh
More file actions
executable file
·522 lines (472 loc) · 20.7 KB
/
Copy pathstep-1-install.sh
File metadata and controls
executable file
·522 lines (472 loc) · 20.7 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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
#!/usr/bin/env bash
set -uo pipefail
# =============================================================================
# Step 1 — Get Claude Running
# Installs: Xcode CLT/build-essential, Homebrew, Git, Node.js, Claude Code
# Usage: curl -fsSL <hosted-url>/step-1/step-1-install.sh | bash
# =============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
ERRORS=0
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
soft_fail() { echo -e "${RED}[FAIL]${NC} $1 (non-critical, continuing...)"; ERRORS=$((ERRORS + 1)); }
# -----------------------------------------------------------------------------
# 1. Detect OS
# -----------------------------------------------------------------------------
detect_os() {
case "$(uname -s)" in
Darwin) OS="mac" ;;
Linux) OS="linux" ;;
MINGW*|MSYS*|CYGWIN*) fail "Windows is not supported. This script is for macOS and Linux only." ;;
*) fail "Unsupported OS: $(uname -s). This script supports macOS and Linux only." ;;
esac
info "Detected OS: $OS"
case "${SHELL:-/bin/bash}" in
*/zsh) USER_SHELL="zsh"; SHELL_RC="$HOME/.zshrc" ;;
*/bash) USER_SHELL="bash"; SHELL_RC="$HOME/.bashrc" ;;
*) USER_SHELL="bash"; SHELL_RC="$HOME/.bashrc" ;;
esac
info "Detected shell: $USER_SHELL ($SHELL_RC)"
}
# -----------------------------------------------------------------------------
# 2. Preflight checks
# -----------------------------------------------------------------------------
preflight_checks() {
if [ "$(id -u)" -eq 0 ]; then
fail "Do not run this script as root or with sudo. Run as your normal user account."
fi
if ! curl -fsSL --connect-timeout 5 https://raw.githubusercontent.com/ &>/dev/null; then
fail "No internet connection detected. This script requires internet access."
fi
success "Internet connectivity verified"
}
# -----------------------------------------------------------------------------
# 3. Update package index (Linux only)
# -----------------------------------------------------------------------------
update_package_index() {
if [ "$OS" = "linux" ]; then
if command -v apt-get &>/dev/null; then
info "Updating apt package index..."
sudo apt-get update -qq
success "Package index updated"
fi
fi
}
# -----------------------------------------------------------------------------
# 4. Xcode CLT (macOS) / build-essential (Linux)
# -----------------------------------------------------------------------------
install_build_tools() {
if [ "$OS" = "mac" ]; then
if xcode-select -p &>/dev/null; then
success "Xcode Command Line Tools already installed"
else
info "Installing Xcode Command Line Tools..."
xcode-select --install 2>/dev/null || true
echo ""
echo -e " ${YELLOW}A popup window should appear on your screen.${NC}"
echo -e " ${YELLOW}Click 'Install' and wait for it to finish.${NC}"
echo -e " ${YELLOW}This can take a few minutes...${NC}"
echo ""
CLT_WAIT=0
CLT_MAX=180
until xcode-select -p &>/dev/null; do
sleep 5
CLT_WAIT=$((CLT_WAIT + 1))
if [ "$CLT_WAIT" -ge "$CLT_MAX" ]; then
fail "Xcode Command Line Tools installation timed out after 15 minutes. Please install manually: xcode-select --install"
fi
done
success "Xcode Command Line Tools installed"
fi
else
if dpkg -s build-essential &>/dev/null 2>&1; then
success "build-essential already installed"
elif command -v gcc &>/dev/null && command -v make &>/dev/null; then
success "Build tools already available (gcc + make)"
else
info "Installing build-essential..."
if command -v apt-get &>/dev/null; then
sudo apt-get install -y -qq build-essential || soft_fail "build-essential installation failed"
elif command -v dnf &>/dev/null; then
sudo dnf groupinstall -y "Development Tools" || soft_fail "Development Tools installation failed"
else
warn "Could not install build tools — install gcc and make manually if needed"
return
fi
command -v gcc &>/dev/null && success "Build tools installed"
fi
fi
}
# -----------------------------------------------------------------------------
# 5. Homebrew (macOS only)
# -----------------------------------------------------------------------------
install_homebrew() {
if [ "$OS" != "mac" ]; then return; fi
if command -v brew &>/dev/null; then
success "Homebrew already installed"
else
info "Installing Homebrew..."
info "You may be prompted for your password."
# Re-cache sudo in case Xcode CLT install took a long time (5 min timeout)
sudo -v 2>/dev/null || true
# NONINTERACTIVE=1 skips the "Press RETURN" prompt — the installer auto-sets
# this when stdin isn't a TTY, but being explicit is safer. sudo -v above
# ensures the cached credential is fresh so sudo -n succeeds.
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
if [ -f /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
if [ "$USER_SHELL" = "bash" ]; then
BREW_PROFILE="$HOME/.bash_profile"
else
BREW_PROFILE="$HOME/.zprofile"
fi
if ! grep -q 'homebrew' "$BREW_PROFILE" 2>/dev/null; then
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$BREW_PROFILE"
fi
elif [ -f /usr/local/bin/brew ]; then
eval "$(/usr/local/bin/brew shellenv)"
fi
command -v brew &>/dev/null || fail "Homebrew installation failed"
success "Homebrew installed"
fi
}
# -----------------------------------------------------------------------------
# 6. Git
# -----------------------------------------------------------------------------
install_git() {
if command -v git &>/dev/null; then
success "Git already installed ($(git --version))"
return
fi
info "Installing Git..."
if [ "$OS" = "mac" ]; then
brew install git
else
if command -v apt-get &>/dev/null; then
sudo apt-get install -y -qq git
elif command -v dnf &>/dev/null; then
sudo dnf install -y git
else
fail "Could not install Git — no supported package manager found"
fi
fi
command -v git &>/dev/null || fail "Git installation failed"
success "Git installed ($(git --version))"
}
# -----------------------------------------------------------------------------
# 7. Node.js via nvm
# -----------------------------------------------------------------------------
install_node() {
if command -v node &>/dev/null; then
NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_MAJOR" -ge 18 ]; then
success "Node.js $(node -v) already installed (meets v18+ requirement)"
return
else
warn "Node.js $(node -v) found but too old — need v18+. Installing via nvm..."
fi
fi
if [ ! -d "$HOME/.nvm" ]; then
info "Installing nvm..."
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
fi
export NVM_DIR="$HOME/.nvm"
# shellcheck source=/dev/null
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
command -v nvm &>/dev/null || fail "nvm installation failed"
info "Installing Node.js LTS..."
nvm install --lts
nvm use --lts
nvm alias default 'lts/*'
command -v node &>/dev/null || fail "Node.js installation failed"
success "Node.js $(node -v) installed via nvm"
}
# -----------------------------------------------------------------------------
# 8. Claude Code
# -----------------------------------------------------------------------------
install_claude_code() {
if command -v claude &>/dev/null; then
success "Claude Code already installed"
else
info "Installing Claude Code..."
npm install -g @anthropic-ai/claude-code 2>/dev/null \
|| sudo npm install -g @anthropic-ai/claude-code
command -v claude &>/dev/null || fail "Claude Code installation failed"
success "Claude Code installed"
fi
# Add Claude Code shortcuts (check each individually so re-runs fill gaps)
ALIASES_ADDED=0
if ! grep -q '# Claude Code shortcuts' "$SHELL_RC" 2>/dev/null; then
echo "" >> "$SHELL_RC"
echo "# Claude Code shortcuts" >> "$SHELL_RC"
fi
for alias_line in \
"alias cskip='claude --dangerously-skip-permissions'" \
"alias ctg='claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official'" \
"alias cc='claude'" \
"alias ccr='claude --resume'" \
"alias ccc='claude --continue'"; do
ALIAS_NAME=$(echo "$alias_line" | sed "s/alias \([^=]*\)=.*/\1/")
if ! grep -q "alias ${ALIAS_NAME}=" "$SHELL_RC" 2>/dev/null; then
echo "$alias_line" >> "$SHELL_RC"
ALIASES_ADDED=$((ALIASES_ADDED + 1))
fi
done
if [ "$ALIASES_ADDED" -gt 0 ]; then
success "Added $ALIASES_ADDED new shortcut(s) to $SHELL_RC"
else
success "All shortcuts already configured (cskip, ctg, cc, ccr, ccc)"
fi
# Add ~/.local/bin to PATH if not already present
if ! grep -q '\.local/bin' "$SHELL_RC" 2>/dev/null; then
echo "" >> "$SHELL_RC"
echo '# Local bin (cbrain, cbraintg)' >> "$SHELL_RC"
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$SHELL_RC"
success "Added ~/.local/bin to PATH in $SHELL_RC"
else
success "$HOME/.local/bin already in PATH"
fi
# Install cbrain command (2ndBrain + skip-permissions)
info "Installing cbrain command to ~/.local/bin..."
cat > "$HOME/.local/bin/cbrain" << 'CBRAIN_EOF'
#!/usr/bin/env bash
# cbrain — Launch Claude Code in 2ndBrain Obsidian vault with full permissions
for candidate in \
"$HOME/Desktop/WORK/OBSIDIAN/2ndBrain" \
"$HOME/Desktop/2ndBrain" \
"$HOME/Desktop/Second-Brain" \
"$HOME/Desktop/Vault" \
"$HOME/Documents/2ndBrain" \
"$HOME/Documents/Second-Brain"; do
if [ -d "$candidate" ]; then
VAULT="$candidate"
break
fi
done
if [ -z "${VAULT:-}" ]; then
echo "Error: Could not find your 2ndBrain vault."
echo "Run Step 7 first, or set VAULT env var: VAULT=~/path/to/vault cbrain"
exit 1
fi
cd "$VAULT" && exec claude --dangerously-skip-permissions "$@"
CBRAIN_EOF
chmod +x "$HOME/.local/bin/cbrain"
success "cbrain command installed to ~/.local/bin/cbrain"
# Install cbraintg command (cbrain + Telegram channel)
info "Installing cbraintg command to ~/.local/bin..."
cat > "$HOME/.local/bin/cbraintg" << 'CBRAINTG_EOF'
#!/usr/bin/env bash
# cbraintg — Launch Claude Code in 2ndBrain vault with full permissions + Telegram
for candidate in \
"$HOME/Desktop/WORK/OBSIDIAN/2ndBrain" \
"$HOME/Desktop/2ndBrain" \
"$HOME/Desktop/Second-Brain" \
"$HOME/Desktop/Vault" \
"$HOME/Documents/2ndBrain" \
"$HOME/Documents/Second-Brain"; do
if [ -d "$candidate" ]; then
VAULT="$candidate"
break
fi
done
if [ -z "${VAULT:-}" ]; then
echo "Error: Could not find your 2ndBrain vault."
echo "Run Step 7 first, or set VAULT env var: VAULT=~/path/to/vault cbraintg"
exit 1
fi
cd "$VAULT" && exec claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official "$@"
CBRAINTG_EOF
chmod +x "$HOME/.local/bin/cbraintg"
success "cbraintg command installed to ~/.local/bin/cbraintg"
}
# -----------------------------------------------------------------------------
# Self-test — verify everything actually works
# -----------------------------------------------------------------------------
run_self_test() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} Running Self-Test${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
TEST_PASS=0
TEST_FAIL=0
# Git
if command -v git &>/dev/null; then
success "TEST: git — $(git --version)"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: git — not found"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# Node
if command -v node &>/dev/null; then
NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_MAJOR" -ge 18 ]; then
success "TEST: node $(node -v) — meets v18+ requirement"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: node $(node -v) — too old, need v18+"
TEST_FAIL=$((TEST_FAIL + 1))
fi
else
soft_fail "TEST: node — not found"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# npm
if command -v npm &>/dev/null; then
success "TEST: npm v$(npm -v)"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: npm — not found"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# Claude Code
if command -v claude &>/dev/null; then
success "TEST: claude — $(claude --version 2>/dev/null || echo 'found')"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: claude — not found"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# Shell aliases
ALIAS_PASS=0
ALIAS_TOTAL=5
for alias_name in cskip ctg cc ccr ccc; do
if grep -q "alias ${alias_name}=" "$SHELL_RC" 2>/dev/null; then
ALIAS_PASS=$((ALIAS_PASS + 1))
fi
done
if [ "$ALIAS_PASS" -eq "$ALIAS_TOTAL" ]; then
success "TEST: shell aliases — all $ALIAS_TOTAL configured (cskip, ctg, cc, ccr, ccc)"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: shell aliases — only $ALIAS_PASS/$ALIAS_TOTAL found in $SHELL_RC"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# cbrain command
if [ -x "$HOME/.local/bin/cbrain" ]; then
success "TEST: cbrain command — installed at ~/.local/bin/cbrain"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: cbrain command — not found or not executable"
TEST_FAIL=$((TEST_FAIL + 1))
fi
# cbraintg command
if [ -x "$HOME/.local/bin/cbraintg" ]; then
success "TEST: cbraintg command — installed at ~/.local/bin/cbraintg"
TEST_PASS=$((TEST_PASS + 1))
else
soft_fail "TEST: cbraintg command — not found or not executable"
TEST_FAIL=$((TEST_FAIL + 1))
fi
echo ""
if [ "$TEST_FAIL" -eq 0 ]; then
echo -e " ${GREEN}All $TEST_PASS tests passed.${NC}"
else
echo -e " ${GREEN}$TEST_PASS passed${NC}, ${RED}$TEST_FAIL failed${NC}."
echo -e " ${YELLOW}Scroll up to see what went wrong. You may need to fix these before continuing.${NC}"
fi
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
# -----------------------------------------------------------------------------
# Next steps
# -----------------------------------------------------------------------------
show_next_steps() {
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW} NEXT: Move to Step 2${NC}"
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " 1. Run this command to reload your shell config:"
echo ""
echo -e " ${GREEN}source $SHELL_RC${NC}"
echo ""
echo -e " 2. Close this terminal window and reopen it."
echo ""
echo -e " 3. Verify Claude is working by running:"
echo ""
echo -e " ${GREEN}claude --version${NC}"
echo ""
echo " If you see a version number, you're good to go."
echo -e " You can press ${GREEN}Ctrl+C${NC} to exit, then type ${GREEN}cskip${NC} to continue with auto-approve mode."
echo ""
echo " 4. Set up your Claude account at claude.ai"
echo " (you need a paid subscription, see the README)."
echo ""
echo " 5. Optional: Install Ghostty terminal (see Bonus in the README)."
echo " Or skip it and continue straight to Step 2."
echo ""
echo -e " ${BLUE}Tip:${NC} Press ${GREEN}Shift+Tab${NC} while Claude is running to"
echo " toggle permissions on and off — works in any terminal."
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------
print_summary() {
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} Step 1 Complete — Claude is Ready${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo " Installed:"
if [ "$OS" = "mac" ]; then
echo " Homebrew $(brew --version 2>/dev/null | head -1 || echo '—')"
fi
echo " Git $(git --version 2>/dev/null || echo '—')"
echo " Node.js $(node -v 2>/dev/null || echo '—')"
echo " npm v$(npm -v 2>/dev/null || echo '—')"
echo " Claude Code $(claude --version 2>/dev/null || echo '—')"
echo ""
if [ "$ERRORS" -gt 0 ]; then
echo -e " ${YELLOW}Warnings: $ERRORS non-critical tool(s) failed to install.${NC}"
echo -e " ${YELLOW}Scroll up to see which ones and install them manually.${NC}"
echo ""
fi
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
main() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} Step 1 — Get Claude Running${NC}"
echo -e "${BLUE} 4 tools • macOS + Linux${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " ${YELLOW}Note: This script installs everything automatically, but${NC}"
echo -e " ${YELLOW}the steps AFTER it finishes (Claude login) are${NC}"
echo -e " ${YELLOW}manual. Claude won't be helping in your terminal yet —${NC}"
echo -e " ${YELLOW}that starts after you complete the setup steps below.${NC}"
echo -e " ${YELLOW}It should be smooth, but if something goes wrong, check${NC}"
echo -e " ${YELLOW}the test results at the end of this script.${NC}"
echo ""
detect_os
preflight_checks
# Prompt for sudo password upfront so it doesn't interrupt mid-install
info "Some tools require sudo. You may be prompted for your password."
sudo -v 2>/dev/null || true
update_package_index
install_build_tools
install_homebrew
install_git
install_node
# Ensure base directories exist (tools assume these)
mkdir -p "$HOME/.local/bin"
mkdir -p "$HOME/.config"
mkdir -p "$HOME/.cache"
install_claude_code
run_self_test
print_summary
show_next_steps
}
main "$@"