diff --git a/setup b/setup index fd2981406..aa6b60a5e 100755 --- a/setup +++ b/setup @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# gstack setup — build browser binary + register skills with Claude Code / Codex +# gstack setup — build browser binary + register skills with Claude Code, Codex, Cursor, etc. set -e umask 077 # Restrict new files to owner-only (0o600 files, 0o700 dirs) @@ -22,6 +22,8 @@ CODEX_SKILLS="$HOME/.codex/skills" CODEX_GSTACK="$CODEX_SKILLS/gstack" FACTORY_SKILLS="$HOME/.factory/skills" FACTORY_GSTACK="$FACTORY_SKILLS/gstack" +CURSOR_SKILLS="$HOME/.cursor/skills" +CURSOR_GSTACK="$CURSOR_SKILLS/gstack" IS_WINDOWS=0 case "$(uname -s)" in @@ -35,7 +37,7 @@ SKILL_PREFIX=1 SKILL_PREFIX_FLAG=0 while [ $# -gt 0 ]; do case "$1" in - --host) [ -z "$2" ] && echo "Missing value for --host (expected claude, codex, kiro, or auto)" >&2 && exit 1; HOST="$2"; shift 2 ;; + --host) [ -z "$2" ] && echo "Missing value for --host (expected claude, codex, cursor, factory, kiro, or auto)" >&2 && exit 1; HOST="$2"; shift 2 ;; --host=*) HOST="${1#--host=}"; shift ;; --local) LOCAL_INSTALL=1; shift ;; --prefix) SKILL_PREFIX=1; SKILL_PREFIX_FLAG=1; shift ;; @@ -45,7 +47,7 @@ while [ $# -gt 0 ]; do done case "$HOST" in - claude|codex|kiro|factory|auto) ;; + claude|codex|cursor|factory|kiro|auto) ;; openclaw) echo "" echo "OpenClaw integration uses a different model — OpenClaw spawns Claude Code" @@ -58,7 +60,7 @@ case "$HOST" in echo " 3. See docs/OPENCLAW.md for the full architecture" echo "" exit 0 ;; - *) echo "Unknown --host value: $HOST (expected claude, codex, kiro, factory, openclaw, or auto)" >&2; exit 1 ;; + *) echo "Unknown --host value: $HOST (expected claude, codex, cursor, factory, kiro, openclaw, or auto)" >&2; exit 1 ;; esac # ─── Resolve skill prefix preference ───────────────────────── @@ -117,19 +119,23 @@ INSTALL_CLAUDE=0 INSTALL_CODEX=0 INSTALL_KIRO=0 INSTALL_FACTORY=0 +INSTALL_CURSOR=0 if [ "$HOST" = "auto" ]; then command -v claude >/dev/null 2>&1 && INSTALL_CLAUDE=1 command -v codex >/dev/null 2>&1 && INSTALL_CODEX=1 + command -v cursor >/dev/null 2>&1 && INSTALL_CURSOR=1 command -v kiro-cli >/dev/null 2>&1 && INSTALL_KIRO=1 command -v droid >/dev/null 2>&1 && INSTALL_FACTORY=1 # If none found, default to claude - if [ "$INSTALL_CLAUDE" -eq 0 ] && [ "$INSTALL_CODEX" -eq 0 ] && [ "$INSTALL_KIRO" -eq 0 ] && [ "$INSTALL_FACTORY" -eq 0 ]; then + if [ "$INSTALL_CLAUDE" -eq 0 ] && [ "$INSTALL_CODEX" -eq 0 ] && [ "$INSTALL_CURSOR" -eq 0 ] && [ "$INSTALL_KIRO" -eq 0 ] && [ "$INSTALL_FACTORY" -eq 0 ]; then INSTALL_CLAUDE=1 fi elif [ "$HOST" = "claude" ]; then INSTALL_CLAUDE=1 elif [ "$HOST" = "codex" ]; then INSTALL_CODEX=1 +elif [ "$HOST" = "cursor" ]; then + INSTALL_CURSOR=1 elif [ "$HOST" = "kiro" ]; then INSTALL_KIRO=1 elif [ "$HOST" = "factory" ]; then @@ -236,6 +242,16 @@ if [ "$INSTALL_FACTORY" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then ) fi +# 1d. Generate .cursor/ Cursor skill docs +if [ "$INSTALL_CURSOR" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then + echo "Generating .cursor/ skill docs..." + ( + cd "$SOURCE_GSTACK_DIR" + bun install --frozen-lockfile 2>/dev/null || bun install + bun run gen:skill-docs --host cursor + ) +fi + # 2. Ensure Playwright's Chromium is available if ! ensure_playwright_browser; then echo "Installing Playwright Chromium..." @@ -589,13 +605,87 @@ link_factory_skill_dirs() { fi } +create_cursor_runtime_root() { + local gstack_dir="$1" + local cursor_gstack="$2" + local cursor_dir="$gstack_dir/.cursor/skills" + + if [ -L "$cursor_gstack" ]; then + rm -f "$cursor_gstack" + elif [ -d "$cursor_gstack" ] && [ "$cursor_gstack" != "$gstack_dir" ]; then + rm -rf "$cursor_gstack" + fi + + mkdir -p "$cursor_gstack" "$cursor_gstack/browse" "$cursor_gstack/gstack-upgrade" "$cursor_gstack/review" + + if [ -f "$cursor_dir/gstack/SKILL.md" ]; then + ln -snf "$cursor_dir/gstack/SKILL.md" "$cursor_gstack/SKILL.md" + fi + if [ -d "$gstack_dir/bin" ]; then + ln -snf "$gstack_dir/bin" "$cursor_gstack/bin" + fi + if [ -d "$gstack_dir/browse/dist" ]; then + ln -snf "$gstack_dir/browse/dist" "$cursor_gstack/browse/dist" + fi + if [ -d "$gstack_dir/browse/bin" ]; then + ln -snf "$gstack_dir/browse/bin" "$cursor_gstack/browse/bin" + fi + if [ -f "$cursor_dir/gstack-upgrade/SKILL.md" ]; then + ln -snf "$cursor_dir/gstack-upgrade/SKILL.md" "$cursor_gstack/gstack-upgrade/SKILL.md" + fi + for f in checklist.md design-checklist.md greptile-triage.md TODOS-format.md; do + if [ -f "$gstack_dir/review/$f" ]; then + ln -snf "$gstack_dir/review/$f" "$cursor_gstack/review/$f" + fi + done + if [ -f "$gstack_dir/ETHOS.md" ]; then + ln -snf "$gstack_dir/ETHOS.md" "$cursor_gstack/ETHOS.md" + fi +} + +link_cursor_skill_dirs() { + local gstack_dir="$1" + local skills_dir="$2" + local cursor_dir="$gstack_dir/.cursor/skills" + local linked=() + + if [ ! -d "$cursor_dir" ]; then + echo " Generating .cursor/ skill docs..." + ( cd "$gstack_dir" && bun run gen:skill-docs --host cursor ) + fi + + if [ ! -d "$cursor_dir" ]; then + echo " warning: .cursor/skills/ generation failed — run 'bun run gen:skill-docs --host cursor' manually" >&2 + return 1 + fi + + for skill_dir in "$cursor_dir"/gstack*/; do + if [ -f "$skill_dir/SKILL.md" ]; then + skill_name="$(basename "$skill_dir")" + [ "$skill_name" = "gstack" ] && continue + target="$skills_dir/$skill_name" + if [ -L "$target" ] || [ ! -e "$target" ]; then + ln -snf "$skill_dir" "$target" + linked+=("$skill_name") + fi + fi + done + if [ ${#linked[@]} -gt 0 ]; then + echo " linked skills: ${linked[*]}" + fi +} + # 4. Install for Claude (default) SKILLS_BASENAME="$(basename "$INSTALL_SKILLS_DIR")" SKILLS_PARENT_BASENAME="$(basename "$(dirname "$INSTALL_SKILLS_DIR")")" CODEX_REPO_LOCAL=0 +CURSOR_REPO_LOCAL=0 if [ "$SKILLS_BASENAME" = "skills" ] && [ "$SKILLS_PARENT_BASENAME" = ".agents" ]; then CODEX_REPO_LOCAL=1 fi +if [ "$SKILLS_BASENAME" = "skills" ] && [ "$SKILLS_PARENT_BASENAME" = ".cursor" ]; then + CURSOR_REPO_LOCAL=1 +fi if [ "$INSTALL_CLAUDE" -eq 1 ]; then if [ "$SKILLS_BASENAME" = "skills" ]; then @@ -725,6 +815,25 @@ if [ "$INSTALL_FACTORY" -eq 1 ]; then echo " factory skills: $FACTORY_SKILLS" fi +# 6c. Install for Cursor +if [ "$INSTALL_CURSOR" -eq 1 ]; then + if [ "$CURSOR_REPO_LOCAL" -eq 1 ]; then + CURSOR_SKILLS="$INSTALL_SKILLS_DIR" + CURSOR_GSTACK="$INSTALL_GSTACK_DIR" + fi + mkdir -p "$CURSOR_SKILLS" + + # Skip runtime root creation for repo-local installs — the checkout IS the runtime root. + if [ "$CURSOR_REPO_LOCAL" -eq 0 ]; then + create_cursor_runtime_root "$SOURCE_GSTACK_DIR" "$CURSOR_GSTACK" + fi + link_cursor_skill_dirs "$SOURCE_GSTACK_DIR" "$CURSOR_SKILLS" + + echo "gstack ready (cursor)." + echo " browse: $BROWSE_BIN" + echo " cursor skills: $CURSOR_SKILLS" +fi + # 7. Create .agents/ sidecar symlinks for the real Codex skill target. # The root Codex skill ends up pointing at $SOURCE_GSTACK_DIR/.agents/skills/gstack, # so the runtime assets must live there for both global and repo-local installs.