Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .claude/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ Each entry: **what** changed, **why** it was needed, **files** touched.

---

## 2026-05-25 — v0.5.0 — OpenCode CLI + plugin marketplaces (GSD/gstack/superpowers)

**What**
- `profiles/cli-bundle/05b-opencode.sh` — installs OpenCode via upstream `curl -fsSL https://opencode.ai/install | bash`. Adds `~/.opencode/bin` and `~/.local/bin` to PATH via `~/.bashrc`. Toggle: `INSTALL_OPENCODE`.
- `lib/base-packages.sh` — new `install_bun()` (required by gstack).
- `lib/plugins.sh` — `claude_headless`, `install_claude_plugin`, `install_gstack_for`, `print_manual_install_hint`.
- `profiles/cli-bundle/09-plugins.sh` — orchestrates the three plugins across the bundle.
- `tests/test_plugins.bats` — 4 new unit tests (stubbed `claude` binary).
- `.env.example` — toggles + per-plugin per-CLI subtoggles.

**Plugin coverage matrix**
| Plugin | Claude | Codex | Antigravity | Cursor | OpenCode |
|--------------|----------|------------------|------------------------------|----------------------|----------------------|
| GSD | headless | — | — | — | — |
| gstack | headless | — | — | — | headless (`--host opencode`) |
| superpowers | headless | manual `/plugins`| manual (NOT documented) | manual `/add-plugin` | manual fetch URL |

**Decisions**
- **Antigravity superpowers**: option (b) from prior planning — no blind attempt. Prints manual hint with a Gemini-CLI-pattern guess + warning that upstream docs don't cover Antigravity.
- **gstack runtime**: requires Bun. `install_bun()` lives in `base-packages.sh` (reusable) rather than `plugins.sh`; only invoked when `INSTALL_GSTACK=true`.
- **Headless plugin install for Claude**: uses `claude -p "/plugin install ..." --dangerously-skip-permissions`. Other CLIs lack equivalent flags; we deliberately do not script keystrokes against interactive prompts.
- **gstack targets**: list-based (`GSTACK_TARGETS="claude opencode"`) instead of per-target booleans. Extensible to other supported hosts (Cursor, Codex, Hermes, etc.) without env-var explosion.

**Why**
- The three plugins shape how every CLI session behaves (slash commands, agent roles, workflows). Manual install on every fresh VPS is the worst kind of toil.
- OpenCode joins the bundle because gstack and superpowers both support it and the install path is the same shape as the other CLIs (single curl|bash).

**Files**
- `profiles/cli-bundle/05b-opencode.sh` (new)
- `profiles/cli-bundle/09-plugins.sh` (new)
- `profiles/cli-bundle/install.sh` (calls 05b + 09)
- `profiles/cli-bundle/.env.example` (`INSTALL_OPENCODE`, plugin toggles)
- `profiles/cli-bundle/README.md` (Plugins section)
- `lib/plugins.sh` (new)
- `lib/base-packages.sh` (`install_bun`)
- `tests/test_plugins.bats` (new, 4 tests)

---

## 2026-05-25 — v0.4.0 — Obsidian vault for cli-bundle agents

**What**
Expand Down
8 changes: 8 additions & 0 deletions .claude/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
- [ ] Per-CLI auth verification (`claude --version`, `codex --version`, etc.) gated by toggle.
- [ ] Document tmux session naming convention when running multiple CLIs.

## Plugins (v0.5.0+)

- [ ] **Antigravity + superpowers**: confirm whether `antigravity extensions install <url>` is a real command. Replace manual hint with headless install if confirmed.
- [ ] gstack `./setup` — confirm non-interactive behaviour on fresh box (`--yes`-style flag?). Currently we assume it runs cleanly without prompts.
- [ ] GSD: legacy npm package conflict check (`get-shit-done-cc`, `get-shit-done-redux`) — auto-uninstall in 09-plugins.sh if found.
- [ ] Superpowers OpenCode install is "fetch instructions" — investigate whether OpenCode has a headless prompt API to drive it programmatically.
- [ ] Plugin verification: after install, run a sanity check (e.g. `claude -p "/plugin list"` and grep for the installed names).

## Obsidian vault (v0.4.0+)

- [ ] Workstation-side companion: doc snippet for installing Obsidian app + git pull of the same vault.
Expand Down
19 changes: 19 additions & 0 deletions lib/base-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ install_pnpm() {
pnpm -v
}

# Install Bun (JS runtime; required by gstack plugin).
install_bun() {
if command -v bun >/dev/null 2>&1; then
echo "==> bun $(bun --version) already installed"
return 0
fi
echo "==> Installing bun (bun.sh)"
curl -fsSL https://bun.sh/install | bash
export PATH="$HOME/.bun/bin:$PATH"
if ! grep -q 'BUN_INSTALL' "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" <<'EOF'

# Bun
export BUN_INSTALL="$HOME/.bun"
case ":$PATH:" in *":$BUN_INSTALL/bin:"*) ;; *) export PATH="$BUN_INSTALL/bin:$PATH";; esac
EOF
fi
}

# Install uv (Python package manager used by Hermes).
install_uv() {
if command -v uv >/dev/null 2>&1; then
Expand Down
80 changes: 80 additions & 0 deletions lib/plugins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# ============================================================
# lib/plugins.sh — install Claude Code plugins (and friends) headlessly.
#
# Most plugin markets are interactive (`/plugin install …` typed inside a
# CLI session). Where headless is supported, we drive it via `claude -p`
# with --dangerously-skip-permissions. Elsewhere we print a manual hint
# the operator pastes into the relevant CLI.
# ============================================================

# Run a Claude Code slash command headless and return its exit code.
# $1 = prompt (typically a slash command like `/plugin install foo`)
claude_headless() {
local prompt="$1"
if ! command -v claude >/dev/null 2>&1; then
echo "ERROR: 'claude' not on PATH — install Claude Code first."
return 1
fi
claude -p "$prompt" --dangerously-skip-permissions
}

# Install a Claude Code plugin from a marketplace.
# $1 = marketplace spec (e.g. "jnuyens/gsd-plugin" or "obra/superpowers-marketplace")
# $2 = plugin spec (e.g. "gsd@gsd-plugin" or "superpowers@claude-plugins-official")
install_claude_plugin() {
local marketplace="$1"
local plugin="$2"
echo "==> Claude plugin: marketplace=$marketplace plugin=$plugin"
# Marketplace add is idempotent in Claude Code; install is too (re-runs fine).
claude_headless "/plugin marketplace add $marketplace" || true
claude_headless "/plugin install $plugin"
claude_headless "/reload-plugins" || true
}

# Install gstack into a target CLI's skills dir.
# $1 = host name (claude, opencode, etc.)
install_gstack_for() {
local host="$1"
local target_dir="$HOME/.${host}/skills/gstack"
echo "==> Installing gstack for $host → $target_dir"

if [[ -d "$target_dir/.git" ]]; then
git -C "$target_dir" fetch --depth 1 origin
git -C "$target_dir" reset --hard origin/HEAD
else
git clone --single-branch --depth 1 \
https://github.com/garrytan/gstack.git "$target_dir"
fi

pushd "$target_dir" >/dev/null || return 1
if [[ -x ./setup ]]; then
if [[ "$host" == "claude" ]]; then
./setup
else
./setup --host "$host"
fi
else
echo "WARN: gstack/setup not found or not executable"
fi
popd >/dev/null || return 1
}

# Tell the operator exactly what to type into a non-headless CLI.
print_manual_install_hint() {
local cli="$1"
local instruction="$2"
cat <<EOF

----------------------------------------------------------------
MANUAL STEP REQUIRED — $cli
----------------------------------------------------------------
Open a $cli session, then run:

$instruction

This CLI does not expose a headless plugin-install path; the step
has to be performed interactively.
----------------------------------------------------------------
EOF
}
28 changes: 28 additions & 0 deletions profiles/cli-bundle/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ INSTALL_CLAUDE=true
INSTALL_CODEX=false
INSTALL_ANTIGRAVITY=false
INSTALL_CURSOR=false
INSTALL_OPENCODE=false

# --- Claude Code Auth ---
# Optional. Leave empty to use OAuth via `claude login`. console.anthropic.com
Expand Down Expand Up @@ -94,3 +95,30 @@ OBSIDIAN_VAULT_REPO=
OBSIDIAN_VAULT_BRANCH=main
OBSIDIAN_AUTOSYNC=false
OBSIDIAN_AUTOSYNC_SCHEDULE=*/15 * * * *

# ============================================================
# Plugins (09-plugins.sh)
# Coverage:
# GSD → Claude only
# gstack → any host listed in GSTACK_TARGETS (Claude + OpenCode supported)
# superpowers → per-CLI toggles below
# ============================================================

# Get Shit Done (Claude-only plugin marketplace)
INSTALL_GSD=false

# gstack — virtual engineering team (slash commands + skills)
INSTALL_GSTACK=false
# Space-separated list. Valid: claude opencode
GSTACK_TARGETS=claude

# superpowers — composable agent methodology, multi-CLI
INSTALL_SUPERPOWERS=false
SUPERPOWERS_CLAUDE=true
SUPERPOWERS_CODEX=true
SUPERPOWERS_CURSOR=true
SUPERPOWERS_OPENCODE=true
# Antigravity integration is NOT officially documented.
# When true and INSTALL_ANTIGRAVITY=true, 09-plugins.sh prints a manual hint
# only (option (b) — no blind attempt).
SUPERPOWERS_ANTIGRAVITY=false
41 changes: 41 additions & 0 deletions profiles/cli-bundle/05b-opencode.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# ============================================================
# 05b-opencode.sh — Installs OpenCode CLI.
# Upstream installer: https://opencode.ai/install
# Auth: provider keys per Models.dev integration.
# ============================================================
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"

if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: $ENV_FILE not found."
exit 1
fi

# shellcheck disable=SC1090
set -a; source "$ENV_FILE"; set +a

if [[ "${INSTALL_OPENCODE:-false}" != "true" ]]; then
echo "==> OpenCode install disabled (INSTALL_OPENCODE != true). Skipping."
exit 0
fi

echo "==> Installing OpenCode CLI (upstream installer)"
curl -fsSL https://opencode.ai/install | bash

# Installer typically drops binary in ~/.opencode/bin or ~/.local/bin.
if ! grep -q 'OPENCODE_BIN' "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" <<'EOF'

# OpenCode CLI
export OPENCODE_BIN="$HOME/.opencode/bin"
case ":$PATH:" in *":$OPENCODE_BIN:"*) ;; *) export PATH="$OPENCODE_BIN:$HOME/.local/bin:$PATH";; esac
EOF
fi
export PATH="$HOME/.opencode/bin:$HOME/.local/bin:$PATH"

opencode --version 2>/dev/null || echo " (opencode binary not yet on PATH — run \`source ~/.bashrc\`)"

echo "==> OpenCode CLI installed. Configure with: opencode auth"
98 changes: 98 additions & 0 deletions profiles/cli-bundle/09-plugins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
# ============================================================
# 09-plugins.sh — install GSD, gstack, superpowers across the CLIs.
#
# Coverage matrix:
# GSD → Claude only
# gstack → Claude, OpenCode (and any host listed in GSTACK_TARGETS)
# superpowers → Claude (headless), Codex/Cursor/OpenCode (manual hints),
# Antigravity (option (b) per RECOMMENDATIONS: no attempt,
# just print hint with the warning that it is not officially
# documented).
# ============================================================
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"

if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: $ENV_FILE not found."
exit 1
fi

# shellcheck disable=SC1090
set -a; source "$ENV_FILE"; set +a

# shellcheck source=../../lib/plugins.sh
source "$REPO_ROOT/lib/plugins.sh"
# shellcheck source=../../lib/base-packages.sh
source "$REPO_ROOT/lib/base-packages.sh"

export PATH="$HOME/.npm-global/bin:$HOME/.local/bin:$HOME/.opencode/bin:$HOME/.bun/bin:$PATH"

# --- GSD (Claude only) ----------------------------------------------------
if [[ "${INSTALL_GSD:-false}" == "true" ]]; then
if [[ "${INSTALL_CLAUDE:-true}" != "true" ]]; then
echo "WARN: INSTALL_GSD=true but Claude not installed — skipping."
else
install_claude_plugin "jnuyens/gsd-plugin" "gsd@gsd-plugin"
fi
fi

# --- gstack (Claude + any host in GSTACK_TARGETS) -------------------------
if [[ "${INSTALL_GSTACK:-false}" == "true" ]]; then
install_bun
TARGETS="${GSTACK_TARGETS:-claude}"
for host in $TARGETS; do
case "$host" in
claude)
if [[ "${INSTALL_CLAUDE:-true}" != "true" ]]; then
echo "WARN: gstack target 'claude' requested but INSTALL_CLAUDE != true. Skipping."
continue
fi
;;
opencode)
if [[ "${INSTALL_OPENCODE:-false}" != "true" ]]; then
echo "WARN: gstack target 'opencode' requested but INSTALL_OPENCODE != true. Skipping."
continue
fi
;;
esac
install_gstack_for "$host"
done
fi

# --- superpowers (multi-CLI) ----------------------------------------------
if [[ "${INSTALL_SUPERPOWERS:-false}" == "true" ]]; then

if [[ "${SUPERPOWERS_CLAUDE:-true}" == "true" && "${INSTALL_CLAUDE:-true}" == "true" ]]; then
install_claude_plugin "obra/superpowers-marketplace" "superpowers@superpowers-marketplace"
fi

if [[ "${SUPERPOWERS_CODEX:-true}" == "true" && "${INSTALL_CODEX:-false}" == "true" ]]; then
print_manual_install_hint "Codex CLI" \
"/plugins → search 'superpowers' → Install Plugin"
fi

if [[ "${SUPERPOWERS_CURSOR:-true}" == "true" && "${INSTALL_CURSOR:-false}" == "true" ]]; then
print_manual_install_hint "Cursor agent CLI" \
"/add-plugin superpowers"
fi

if [[ "${SUPERPOWERS_OPENCODE:-true}" == "true" && "${INSTALL_OPENCODE:-false}" == "true" ]]; then
print_manual_install_hint "OpenCode" \
"Tell OpenCode: Fetch and follow instructions from https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/.opencode/INSTALL.md"
fi

if [[ "${SUPERPOWERS_ANTIGRAVITY:-false}" == "true" && "${INSTALL_ANTIGRAVITY:-false}" == "true" ]]; then
# Option (b) from .claude/RECOMMENDATIONS-style decision: no blind attempt.
print_manual_install_hint "Google Antigravity CLI" \
"(NOT OFFICIALLY DOCUMENTED) Try the Gemini-CLI pattern manually: antigravity extensions install https://github.com/obra/superpowers"
echo "WARN: Antigravity superpowers integration is not officially documented."
echo " The hint above mirrors the Gemini CLI pattern — it may or may not work."
fi
fi

echo
echo "==> Plugins step complete."
38 changes: 38 additions & 0 deletions profiles/cli-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,44 @@ crontab -l | grep -v obsidian-vault-sync | crontab -
rm ~/.local/bin/obsidian-vault-sync
```

## Plugins (GSD, gstack, superpowers)

Opt-in plugin install handled by `09-plugins.sh`. Headless onde possível;
hint manual onde não.

| Plugin | Claude | Codex | Antigravity | Cursor | OpenCode |
|--------------|-------------------|-------------------|-----------------------------|-------------------|-------------------|
| GSD | headless | — | — | — | — |
| gstack | headless (`./setup`) | — | — | — | headless (`./setup --host opencode`) |
| superpowers | headless | manual `/plugins` | manual (não documentado) | manual `/add-plugin` | manual fetch URL |

Toggles no `.env`:
```env
INSTALL_GSD=true # Claude only
INSTALL_GSTACK=true
GSTACK_TARGETS="claude opencode" # ou só "claude"
INSTALL_SUPERPOWERS=true
SUPERPOWERS_CLAUDE=true
SUPERPOWERS_CODEX=true
SUPERPOWERS_CURSOR=true
SUPERPOWERS_OPENCODE=true
SUPERPOWERS_ANTIGRAVITY=false # não documentado oficialmente
```

Rodar isolado:
```bash
bash 09-plugins.sh
```

**Bun**: gstack precisa de Bun. `09-plugins.sh` instala via `install_bun()` se
`INSTALL_GSTACK=true`.

**Antigravity superpowers**: docs upstream não cobrem. Toggle imprime hint
manual com palpite (padrão Gemini CLI). Sem tentativa automática.

**Manual hints**: Codex/Cursor/OpenCode imprimem mensagem com comando exato
pra colar dentro da sessão. Sem CLI flag headless documentada.

## Manutenção
```bash
claude mcp list # ver registrados
Expand Down
Loading
Loading