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.
| 46d6165 | 1 | #!/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 | |
| 43095dc | 15 | # 8. Drop the Claude Code skill bundle into ~/.claude/skills/ |
| 16 | # 9. Print a "you're done" success banner | |
| 46d6165 | 17 | # |
| 18 | # No third-party tools beyond plain curl, jq, git. Idempotent. Safe to re-run. | |
| 19 | # ============================================================================= | |
| 20 | ||
| 21 | set -euo pipefail | |
| 22 | ||
| 23 | # ── pretty printers (match scripts/bootstrap-hetzner.sh style) ─────────────── | |
| 24 | say() { printf "\n\033[1;34m> %s\033[0m\n" "$*"; } | |
| 25 | ok() { printf " \033[32mv\033[0m %s\n" "$*"; } | |
| 26 | warn() { printf " \033[33m!\033[0m %s\n" "$*"; } | |
| 27 | fail() { printf " \033[31mx\033[0m %s\n" "$*"; exit 1; } | |
| 28 | ||
| 29 | # ── 1. Prerequisites ──────────────────────────────────────────────────────── | |
| 43095dc | 30 | say "[1/8] Checking prerequisites" |
| 46d6165 | 31 | MISSING=() |
| 32 | for tool in git curl jq; do | |
| 33 | if ! command -v "$tool" >/dev/null 2>&1; then | |
| 34 | MISSING+=("$tool") | |
| 35 | fi | |
| 36 | done | |
| 37 | if [ "${#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." | |
| 48 | fi | |
| 49 | ok "git, curl, jq present" | |
| 50 | ||
| 51 | # ── 2. Host ───────────────────────────────────────────────────────────────── | |
| 43095dc | 52 | say "[2/8] Resolving Gluecron host" |
| 46d6165 | 53 | HOST="${GLUECRON_HOST:-https://gluecron.com}" |
| 54 | HOST="${HOST%/}" # strip trailing slash | |
| 55 | ok "Using host: $HOST" | |
| 56 | ||
| 57 | # ── 3. Locate Claude Desktop config ──────────────────────────────────────── | |
| 43095dc | 58 | say "[3/8] Locating Claude Desktop config" |
| 46d6165 | 59 | UNAME_S="$(uname -s 2>/dev/null || echo unknown)" |
| 60 | MAC_CONF="$HOME/Library/Application Support/Claude/claude_desktop_config.json" | |
| 61 | LINUX_CONF="$HOME/.config/Claude/claude_desktop_config.json" | |
| 62 | CLAUDE_CONF="" | |
| 63 | ||
| 64 | case "$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 | ;; | |
| 85 | esac | |
| 86 | ||
| 87 | mkdir -p "$(dirname "$CLAUDE_CONF")" | |
| 88 | if [ ! -f "$CLAUDE_CONF" ]; then | |
| 89 | echo '{}' > "$CLAUDE_CONF" | |
| 90 | ok "Created empty $CLAUDE_CONF" | |
| 91 | else | |
| 92 | ok "Found existing $CLAUDE_CONF" | |
| 93 | fi | |
| 94 | ||
| 95 | # ── 4. Sign in ────────────────────────────────────────────────────────────── | |
| 43095dc | 96 | say "[4/8] Signing in to $HOST" |
| 46d6165 | 97 | USERNAME="${GLUECRON_USERNAME:-}" |
| b5dd694 | 98 | PASSWORD="${GLUECRON_PASSWORD:-}" # secrets-ok: read from env var, not a hardcoded credential |
| 46d6165 | 99 | if [ -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 | |
| 105 | fi | |
| 106 | if [ -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" | |
| 115 | fi | |
| 116 | ||
| 117 | COOKIE_JAR="$(mktemp -t gluecron-cookies.XXXXXX)" | |
| 118 | trap 'rm -f "$COOKIE_JAR"' EXIT | |
| 119 | ||
| 120 | LOGIN_BODY="username=$(printf %s "$USERNAME" | jq -sRr @uri)&password=$(printf %s "$PASSWORD" | jq -sRr @uri)" | |
| 121 | LOGIN_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 | ) | |
| 129 | if ! grep -q "session" "$COOKIE_JAR" 2>/dev/null; then | |
| 130 | fail "Login failed (HTTP $LOGIN_STATUS). Check username/password." | |
| 131 | fi | |
| 132 | ok "Signed in as $USERNAME" | |
| 133 | ||
| 134 | # ── 5. Mint PAT ───────────────────────────────────────────────────────────── | |
| 43095dc | 135 | say "[5/8] Minting a fresh PAT" |
| 46d6165 | 136 | SHORT_TS=$(date +%s | tail -c 7) |
| 137 | MINT_BODY=$(jq -nc --arg name "gluecron-install-$SHORT_TS" --arg scope "admin" \ | |
| 138 | '{name: $name, scope: $scope}') | |
| 139 | MINT_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 | ) | |
| 147 | PAT=$(printf %s "$MINT_RESPONSE" | jq -r '.token // empty') | |
| 148 | if [ -z "$PAT" ]; then | |
| 149 | ERR=$(printf %s "$MINT_RESPONSE" | jq -r '.error // .hint // .') | |
| 150 | fail "PAT mint failed: $ERR" | |
| 151 | fi | |
| 152 | PAT_PREFIX=$(printf %s "$PAT" | cut -c1-12) | |
| 153 | ok "Created PAT $PAT_PREFIX..." | |
| 154 | ||
| 155 | # ── 6. Merge MCP server entry into Claude Desktop config ─────────────────── | |
| 43095dc | 156 | say "[6/8] Wiring Claude Desktop -> Gluecron MCP" |
| 46d6165 | 157 | TMP_CONF="$(mktemp -t gluecron-conf.XXXXXX)" |
| 158 | jq --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" | |
| 170 | mv "$TMP_CONF" "$CLAUDE_CONF" | |
| 171 | ok "Updated $CLAUDE_CONF" | |
| 172 | ||
| 173 | # ── 7. Optional repo import ───────────────────────────────────────────────── | |
| 43095dc | 174 | say "[7/8] Repo import (optional)" |
| 46d6165 | 175 | if 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 | |
| 207 | else | |
| 208 | ok "Not inside a git repo — skipping import" | |
| 209 | fi | |
| 210 | ||
| 43095dc | 211 | # ── 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. | |
| 215 | say "[8/8] Installing Claude Code skill bundle (~/.claude/skills/)" | |
| 216 | SKILLS_ROOT="$HOME/.claude/skills" | |
| 217 | mkdir -p "$SKILLS_ROOT/gluecron-pr" "$SKILLS_ROOT/gluecron-issue" "$SKILLS_ROOT/gluecron-review" | |
| 218 | ||
| 219 | cat > "$SKILLS_ROOT/gluecron-pr/SKILL.md" <<'GLUECRON_SKILL_PR_EOF' | |
| 220 | --- | |
| 221 | name: gluecron-pr | |
| 222 | description: 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. | |
| 223 | tools: | |
| 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 | ||
| 233 | When invoked, drive the Gluecron MCP write surface to manage pull | |
| 234 | requests 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` — | |
| 237 | strip the `.git`). Default base branch: | |
| 238 | `git symbolic-ref refs/remotes/origin/HEAD` (fall back to `main`). | |
| 239 | Always read `git diff origin/<base>...HEAD` before opening a PR so the | |
| 240 | title 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. | |
| 242 | GLUECRON_SKILL_PR_EOF | |
| 243 | ||
| 244 | cat > "$SKILLS_ROOT/gluecron-issue/SKILL.md" <<'GLUECRON_SKILL_ISSUE_EOF' | |
| 245 | --- | |
| 246 | name: gluecron-issue | |
| 247 | description: 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. | |
| 248 | tools: | |
| 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 | ||
| 257 | When invoked, drive the Gluecron MCP write surface to manage issues on | |
| 258 | the active Gluecron-hosted repo. Detect owner/repo from | |
| 259 | `git config --get remote.origin.url`. Common ops: file an issue with a | |
| 260 | Markdown body (What happened / Expected / Repro), comment on an existing | |
| 261 | issue, close, reopen, list open issues. Never bulk-close more than 5 | |
| 262 | issues in one tool sequence without re-confirming with the user. | |
| 263 | GLUECRON_SKILL_ISSUE_EOF | |
| 264 | ||
| 265 | cat > "$SKILLS_ROOT/gluecron-review/SKILL.md" <<'GLUECRON_SKILL_REVIEW_EOF' | |
| 266 | --- | |
| 267 | name: gluecron-review | |
| 268 | description: 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. | |
| 269 | tools: | |
| 270 | - gluecron_get_pr | |
| 271 | - gluecron_list_prs | |
| 272 | - gluecron_comment_pr | |
| 273 | - Bash | |
| 274 | --- | |
| 275 | ||
| 276 | When invoked, act as a secondary reviewer on top of Gluecron's built-in | |
| 277 | AI 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 | |
| 280 | origin/<base>...origin/<head>`, post one `gluecron_comment_pr` per | |
| 281 | finding (file + line + suggestion), then a summary comment prefixed | |
| 282 | with `<!-- claude-secondary-review:summary -->` and a verdict line | |
| 283 | ("**Verdict:** approved" or "**Verdict:** changes requested"). Do not | |
| 284 | call `gluecron_merge_pr` from this skill. | |
| 285 | GLUECRON_SKILL_REVIEW_EOF | |
| 286 | ||
| 287 | ok "Wrote $SKILLS_ROOT/gluecron-pr/SKILL.md" | |
| 288 | ok "Wrote $SKILLS_ROOT/gluecron-issue/SKILL.md" | |
| 289 | ok "Wrote $SKILLS_ROOT/gluecron-review/SKILL.md" | |
| 290 | ||
| 46d6165 | 291 | # ── Done ──────────────────────────────────────────────────────────────────── |
| 292 | printf "\n" | |
| 293 | printf "\033[1;32m============================================================\033[0m\n" | |
| 294 | printf "\033[1;32m GLUECRON INSTALL COMPLETE\033[0m\n" | |
| 295 | printf "\033[1;32m============================================================\033[0m\n" | |
| 296 | printf " Host: %s\n" "$HOST" | |
| 297 | printf " PAT prefix: %s...\n" "$PAT_PREFIX" | |
| 298 | printf " MCP config: %s\n" "$CLAUDE_CONF" | |
| 43095dc | 299 | printf " Skills: %s/{gluecron-pr,gluecron-issue,gluecron-review}\n" "$SKILLS_ROOT" |
| 46d6165 | 300 | printf "\n" |
| 301 | printf " v Done. Restart Claude Desktop and try:\n" | |
| 302 | printf " \"Open a PR on this repo with a one-line README fix\"\n" | |
| 303 | printf "\n" | |
| 3b99c5f | 304 | printf " Tip: if you also use Claude Code in this repo, the\n" |
| 305 | printf " .claude/settings.json at the repo root auto-configures\n" | |
| 306 | printf " the Gluecron MCP server. Set GLUECRON_PAT in your shell\n" | |
| 307 | printf " and future Code sessions wire up with no further config.\n" | |
| 308 | printf "\n" |