Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

install.sh

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

install.shBlame308 lines · 2 contributors
46d6165Claude1#!/usr/bin/env bash
2# =============================================================================
3# Gluecron one-command install.
4#
5# curl -sSL https://gluecron.com/install | bash
6#
7# What it does (every step is idempotent + verbose):
8# 1. Verify git, curl, jq are on PATH
9# 2. Resolve Gluecron host (default https://gluecron.com, env override)
10# 3. Locate Claude Desktop config (macOS / Linux / WSL)
11# 4. Sign in (env vars or interactive prompt) and capture session cookie
12# 5. Mint a fresh PAT via POST /api/v2/auth/install-token
13# 6. Merge a 'gluecron' MCP server entry into claude_desktop_config.json
14# 7. If we're inside a GitHub-origin repo, offer to import it
43095dcClaude15# 8. Drop the Claude Code skill bundle into ~/.claude/skills/
16# 9. Print a "you're done" success banner
46d6165Claude17#
18# No third-party tools beyond plain curl, jq, git. Idempotent. Safe to re-run.
19# =============================================================================
20
21set -euo pipefail
22
23# ── pretty printers (match scripts/bootstrap-hetzner.sh style) ───────────────
24say() { printf "\n\033[1;34m> %s\033[0m\n" "$*"; }
25ok() { printf " \033[32mv\033[0m %s\n" "$*"; }
26warn() { printf " \033[33m!\033[0m %s\n" "$*"; }
27fail() { printf " \033[31mx\033[0m %s\n" "$*"; exit 1; }
28
29# ── 1. Prerequisites ────────────────────────────────────────────────────────
43095dcClaude30say "[1/8] Checking prerequisites"
46d6165Claude31MISSING=()
32for tool in git curl jq; do
33 if ! command -v "$tool" >/dev/null 2>&1; then
34 MISSING+=("$tool")
35 fi
36done
37if [ "${#MISSING[@]}" -gt 0 ]; then
38 warn "Missing tools: ${MISSING[*]}"
39 echo " Install them and re-run, e.g.:"
40 for tool in "${MISSING[@]}"; do
41 case "$tool" in
42 jq) echo " macOS: brew install jq | Debian/Ubuntu: sudo apt-get install -y jq" ;;
43 git) echo " macOS: xcode-select --install | Debian/Ubuntu: sudo apt-get install -y git" ;;
44 curl) echo " macOS: (preinstalled) | Debian/Ubuntu: sudo apt-get install -y curl" ;;
45 esac
46 done
47 fail "Please install the missing tools above and re-run."
48fi
49ok "git, curl, jq present"
50
51# ── 2. Host ─────────────────────────────────────────────────────────────────
43095dcClaude52say "[2/8] Resolving Gluecron host"
46d6165Claude53HOST="${GLUECRON_HOST:-https://gluecron.com}"
54HOST="${HOST%/}" # strip trailing slash
55ok "Using host: $HOST"
56
57# ── 3. Locate Claude Desktop config ────────────────────────────────────────
43095dcClaude58say "[3/8] Locating Claude Desktop config"
46d6165Claude59UNAME_S="$(uname -s 2>/dev/null || echo unknown)"
60MAC_CONF="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
61LINUX_CONF="$HOME/.config/Claude/claude_desktop_config.json"
62CLAUDE_CONF=""
63
64case "$UNAME_S" in
65 Darwin*)
66 CLAUDE_CONF="$MAC_CONF"
67 ;;
68 Linux*)
69 # WSL detection
70 if grep -qi microsoft /proc/version 2>/dev/null; then
71 if [ -d "$HOME/.config/Claude" ]; then
72 CLAUDE_CONF="$LINUX_CONF"
73 else
74 CLAUDE_CONF="$LINUX_CONF"
75 warn "WSL detected but no existing Claude config — using $CLAUDE_CONF"
76 fi
77 else
78 CLAUDE_CONF="$LINUX_CONF"
79 fi
80 ;;
81 *)
82 warn "Unknown platform '$UNAME_S' — defaulting to macOS path."
83 CLAUDE_CONF="$MAC_CONF"
84 ;;
85esac
86
87mkdir -p "$(dirname "$CLAUDE_CONF")"
88if [ ! -f "$CLAUDE_CONF" ]; then
89 echo '{}' > "$CLAUDE_CONF"
90 ok "Created empty $CLAUDE_CONF"
91else
92 ok "Found existing $CLAUDE_CONF"
93fi
94
95# ── 4. Sign in ──────────────────────────────────────────────────────────────
43095dcClaude96say "[4/8] Signing in to $HOST"
46d6165Claude97USERNAME="${GLUECRON_USERNAME:-}"
b5dd694Claude98PASSWORD="${GLUECRON_PASSWORD:-}" # secrets-ok: read from env var, not a hardcoded credential
46d6165Claude99if [ -z "$USERNAME" ]; then
100 if [ ! -t 0 ]; then
101 fail "GLUECRON_USERNAME is unset and stdin is not a TTY. Re-run as: GLUECRON_USERNAME=you GLUECRON_PASSWORD=*** curl -sSL $HOST/install | bash"
102 fi
103 printf " Gluecron username: "
104 read -r USERNAME
105fi
106if [ -z "$PASSWORD" ]; then
107 if [ ! -t 0 ]; then
108 fail "GLUECRON_PASSWORD is unset and stdin is not a TTY."
109 fi
110 printf " Gluecron password: "
111 stty -echo
112 read -r PASSWORD
113 stty echo
114 printf "\n"
115fi
116
117COOKIE_JAR="$(mktemp -t gluecron-cookies.XXXXXX)"
118trap 'rm -f "$COOKIE_JAR"' EXIT
119
120LOGIN_BODY="username=$(printf %s "$USERNAME" | jq -sRr @uri)&password=$(printf %s "$PASSWORD" | jq -sRr @uri)"
121LOGIN_STATUS=$(
122 curl -sS -o /dev/null -w "%{http_code}" \
123 -X POST \
124 -H "Content-Type: application/x-www-form-urlencoded" \
125 -c "$COOKIE_JAR" \
126 --data "$LOGIN_BODY" \
127 "$HOST/login" || echo 000
128)
129if ! grep -q "session" "$COOKIE_JAR" 2>/dev/null; then
130 fail "Login failed (HTTP $LOGIN_STATUS). Check username/password."
131fi
132ok "Signed in as $USERNAME"
133
134# ── 5. Mint PAT ─────────────────────────────────────────────────────────────
43095dcClaude135say "[5/8] Minting a fresh PAT"
46d6165Claude136SHORT_TS=$(date +%s | tail -c 7)
137MINT_BODY=$(jq -nc --arg name "gluecron-install-$SHORT_TS" --arg scope "admin" \
138 '{name: $name, scope: $scope}')
139MINT_RESPONSE=$(
140 curl -sS \
141 -X POST \
142 -H "Content-Type: application/json" \
143 -b "$COOKIE_JAR" \
144 --data "$MINT_BODY" \
145 "$HOST/api/v2/auth/install-token"
146)
147PAT=$(printf %s "$MINT_RESPONSE" | jq -r '.token // empty')
148if [ -z "$PAT" ]; then
149 ERR=$(printf %s "$MINT_RESPONSE" | jq -r '.error // .hint // .')
150 fail "PAT mint failed: $ERR"
151fi
152PAT_PREFIX=$(printf %s "$PAT" | cut -c1-12)
153ok "Created PAT $PAT_PREFIX..."
154
155# ── 6. Merge MCP server entry into Claude Desktop config ───────────────────
43095dcClaude156say "[6/8] Wiring Claude Desktop -> Gluecron MCP"
46d6165Claude157TMP_CONF="$(mktemp -t gluecron-conf.XXXXXX)"
158jq --arg url "$HOST/mcp" --arg auth "Bearer $PAT" '
159 . as $root
160 | (.mcpServers // {}) as $servers
161 | $root
162 | .mcpServers = ($servers + {
163 "gluecron": {
164 "transport": "http",
165 "url": $url,
166 "headers": { "Authorization": $auth }
167 }
168 })
169' "$CLAUDE_CONF" > "$TMP_CONF"
170mv "$TMP_CONF" "$CLAUDE_CONF"
171ok "Updated $CLAUDE_CONF"
172
173# ── 7. Optional repo import ─────────────────────────────────────────────────
43095dcClaude174say "[7/8] Repo import (optional)"
46d6165Claude175if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
176 ORIGIN_URL=$(git config --get remote.origin.url || true)
177 if [ -n "$ORIGIN_URL" ] && printf %s "$ORIGIN_URL" | grep -q "github.com"; then
178 if [ -t 0 ]; then
179 printf " Import this repo (%s) to Gluecron? [y/N] " "$ORIGIN_URL"
180 read -r ANSWER
181 else
182 ANSWER="${GLUECRON_IMPORT:-n}"
183 fi
184 case "$ANSWER" in
185 y|Y|yes|YES)
186 IMPORT_BODY="repo_url=$(printf %s "$ORIGIN_URL" | jq -sRr @uri)"
187 IMPORT_STATUS=$(
188 curl -sS -o /dev/null -w "%{http_code}" \
189 -X POST \
190 -H "Authorization: Bearer $PAT" \
191 -H "Content-Type: application/x-www-form-urlencoded" \
192 --data "$IMPORT_BODY" \
193 "$HOST/import/github/repo" || echo 000
194 )
195 case "$IMPORT_STATUS" in
196 2*|3*) ok "Import dispatched (HTTP $IMPORT_STATUS)" ;;
197 *) warn "Import returned HTTP $IMPORT_STATUS — you can re-run from $HOST/import" ;;
198 esac
199 ;;
200 *)
201 ok "Skipped import"
202 ;;
203 esac
204 else
205 ok "No GitHub origin detected — skipping import"
206 fi
207else
208 ok "Not inside a git repo — skipping import"
209fi
210
43095dcClaude211# ── 8. Claude Code skill bundle ─────────────────────────────────────────────
212# Drop the gluecron-pr / gluecron-issue / gluecron-review skills into
213# ~/.claude/skills/ so they appear as slash commands inside Claude Code.
214# Idempotent: each write overwrites the existing file.
215say "[8/8] Installing Claude Code skill bundle (~/.claude/skills/)"
216SKILLS_ROOT="$HOME/.claude/skills"
217mkdir -p "$SKILLS_ROOT/gluecron-pr" "$SKILLS_ROOT/gluecron-issue" "$SKILLS_ROOT/gluecron-review"
218
219cat > "$SKILLS_ROOT/gluecron-pr/SKILL.md" <<'GLUECRON_SKILL_PR_EOF'
220---
221name: gluecron-pr
222description: Open, list, fetch, comment on, merge, or close pull requests on a Gluecron-hosted repository. Use this skill whenever the user references a Gluecron repo (origin URL contains "gluecron.com" or matches the GLUECRON_HOST env var) and asks to "open a PR", "merge", "review", "comment on PR #N", "list open PRs", or "close PR #N" on a repo that is NOT hosted on GitHub.
223tools:
224 - gluecron_create_pr
225 - gluecron_get_pr
226 - gluecron_list_prs
227 - gluecron_comment_pr
228 - gluecron_merge_pr
229 - gluecron_close_pr
230 - Bash
231---
232
233When invoked, drive the Gluecron MCP write surface to manage pull
234requests on the active Gluecron-hosted repo. Detect owner/repo from
235`git config --get remote.origin.url` (shapes:
236`https://<HOST>/<owner>/<repo>.git`, `git@<HOST>:<owner>/<repo>.git` —
237strip the `.git`). Default base branch:
238`git symbolic-ref refs/remotes/origin/HEAD` (fall back to `main`).
239Always read `git diff origin/<base>...HEAD` before opening a PR so the
240title and body match the diff. See the bundled SKILL.md in this repo's
241`.claude/skills/gluecron-pr/` for the full recipe and example prompts.
242GLUECRON_SKILL_PR_EOF
243
244cat > "$SKILLS_ROOT/gluecron-issue/SKILL.md" <<'GLUECRON_SKILL_ISSUE_EOF'
245---
246name: gluecron-issue
247description: Create, list, comment on, close, or reopen issues on a Gluecron-hosted repository. Use this skill whenever the user is on a Gluecron repo (origin URL contains "gluecron.com" or matches the GLUECRON_HOST env var) and asks to "open an issue", "file a bug", "comment on #N", "close #N", or "reopen #N" on a repo that is NOT hosted on GitHub.
248tools:
249 - gluecron_create_issue
250 - gluecron_comment_issue
251 - gluecron_close_issue
252 - gluecron_reopen_issue
253 - gluecron_repo_list_issues
254 - Bash
255---
256
257When invoked, drive the Gluecron MCP write surface to manage issues on
258the active Gluecron-hosted repo. Detect owner/repo from
259`git config --get remote.origin.url`. Common ops: file an issue with a
260Markdown body (What happened / Expected / Repro), comment on an existing
261issue, close, reopen, list open issues. Never bulk-close more than 5
262issues in one tool sequence without re-confirming with the user.
263GLUECRON_SKILL_ISSUE_EOF
264
265cat > "$SKILLS_ROOT/gluecron-review/SKILL.md" <<'GLUECRON_SKILL_REVIEW_EOF'
266---
267name: gluecron-review
268description: Act as a secondary AI code reviewer on a Gluecron-hosted pull request. Use this skill when the user asks Claude to "review PR #N", "give a second-opinion review", or "leave inline comments" on a Gluecron pull request. Complements Gluecron's built-in AI review.
269tools:
270 - gluecron_get_pr
271 - gluecron_list_prs
272 - gluecron_comment_pr
273 - Bash
274---
275
276When invoked, act as a secondary reviewer on top of Gluecron's built-in
277AI review pass (`src/lib/ai-review.ts`, marker
278`<!-- gluecron-ai-review:summary -->`). Flow: fetch the PR via
279`gluecron_get_pr`, fetch the diff locally via `git diff
280origin/<base>...origin/<head>`, post one `gluecron_comment_pr` per
281finding (file + line + suggestion), then a summary comment prefixed
282with `<!-- claude-secondary-review:summary -->` and a verdict line
283("**Verdict:** approved" or "**Verdict:** changes requested"). Do not
284call `gluecron_merge_pr` from this skill.
285GLUECRON_SKILL_REVIEW_EOF
286
287ok "Wrote $SKILLS_ROOT/gluecron-pr/SKILL.md"
288ok "Wrote $SKILLS_ROOT/gluecron-issue/SKILL.md"
289ok "Wrote $SKILLS_ROOT/gluecron-review/SKILL.md"
290
46d6165Claude291# ── Done ────────────────────────────────────────────────────────────────────
292printf "\n"
293printf "\033[1;32m============================================================\033[0m\n"
294printf "\033[1;32m GLUECRON INSTALL COMPLETE\033[0m\n"
295printf "\033[1;32m============================================================\033[0m\n"
296printf " Host: %s\n" "$HOST"
297printf " PAT prefix: %s...\n" "$PAT_PREFIX"
298printf " MCP config: %s\n" "$CLAUDE_CONF"
43095dcClaude299printf " Skills: %s/{gluecron-pr,gluecron-issue,gluecron-review}\n" "$SKILLS_ROOT"
46d6165Claude300printf "\n"
301printf " v Done. Restart Claude Desktop and try:\n"
302printf " \"Open a PR on this repo with a one-line README fix\"\n"
303printf "\n"
3b99c5fTest User304printf " Tip: if you also use Claude Code in this repo, the\n"
305printf " .claude/settings.json at the repo root auto-configures\n"
306printf " the Gluecron MCP server. Set GLUECRON_PAT in your shell\n"
307printf " and future Code sessions wire up with no further config.\n"
308printf "\n"