Merge pull request #381 from outis1one/claude/nvidia-gpu-flux-2-c7y9fg

docs(ai-stack): give exact claude mcp add commands for gitea-mcp
This commit is contained in:
Outis
2026-08-22 00:46:53 -04:00
committed by GitHub
4 changed files with 177 additions and 7 deletions
+2 -1
View File
@@ -190,7 +190,7 @@ a ready-to-copy Caddy config snippet to `~/docker/caddy-snippets/`.
| `media` | `arm`, `audiobookshelf`, `calibre-web`, `emby`, `immich`, `jellyfin`, `lyrion` | | `media` | `arm`, `audiobookshelf`, `calibre-web`, `emby`, `immich`, `jellyfin`, `lyrion` |
| `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` | | `cameras` | `frigate`, `frigate-audio`, `frigate-notify`, `sky-cam` |
| `gaming` | `drum-rhythm-game`, `js99er`, `kyber-launcher`, `kyber-server`, `minecraft`, `wolf`, `wolf-pair` | | `gaming` | `drum-rhythm-game`, `js99er`, `kyber-launcher`, `kyber-server`, `minecraft`, `wolf`, `wolf-pair` |
| `extras` | `kdeconnect`, `silent-send`, `ssh-config`, `ssh-key-import` (import SSH public keys from GitHub/Launchpad, optionally lock down password auth — same step base.sh's required setup runs, re-runnable on its own), `sync-cc` | | `extras` | `kdeconnect`, `silent-send`, `ssh-config`, `ssh-key-import` (import SSH public keys from GitHub/Launchpad, optionally lock down password auth — same step base.sh's required setup runs, re-runnable on its own), `sync-cc`, `claude-cli` (Claude Code CLI — dual-account work/personal setup, model/effort defaults, shared global CLAUDE.md) |
| `backup` | `backup` — complete recovery: entire `~/docker/<service>/` for every service via Kopia (Minecraft: flush+snap, no downtime; others: stop/snap/start for DB consistency), optional offsite mirror (`kopia repository sync-to`), plus `dr_bringup.sh` — unattended restore-everything-and-start for standing up a cold spare box; `borg-backup` — same coverage via Borg (chunk dedup, SSH remote repos, Borgmatic/Vorta compatible); `gaming-backup` — frequent game-save snapshots (Minecraft world data, emulator saves, Steam — no downtime, run hourly) | | `backup` | `backup` — complete recovery: entire `~/docker/<service>/` for every service via Kopia (Minecraft: flush+snap, no downtime; others: stop/snap/start for DB consistency), optional offsite mirror (`kopia repository sync-to`), plus `dr_bringup.sh` — unattended restore-everything-and-start for standing up a cold spare box; `borg-backup` — same coverage via Borg (chunk dedup, SSH remote repos, Borgmatic/Vorta compatible); `gaming-backup` — frequent game-save snapshots (Minecraft world data, emulator saves, Steam — no downtime, run hourly) |
Run `./setup.sh --list` to see descriptions. Run `./setup.sh --list` to see descriptions.
@@ -282,6 +282,7 @@ extras
ssh-config ssh-config
ssh-key-import ssh-key-import
sync-cc sync-cc
claude-cli
backup backup
backup backup
+18 -5
View File
@@ -47,11 +47,24 @@ Two levels, depending on what you need:
an arbitrary self-hosted Gitea on your LAN. an arbitrary self-hosted Gitea on your LAN.
- **PR/issue/CI-level integration (optional).** Reading/commenting on Gitea - **PR/issue/CI-level integration (optional).** Reading/commenting on Gitea
PRs and issues the way a GitHub MCP server does for GitHub needs an MCP PRs and issues the way a GitHub MCP server does for GitHub needs an MCP
server that speaks Gitea's REST API. Gitea's own project publishes one server that speaks Gitea's REST API. Gitea's own project publishes one
(`gitea/gitea-mcp`), authenticated via a personal access token — add it `gitea/gitea-mcp` (gitea.com/gitea/gitea-mcp) — as a binary release, a
with `claude mcp add` pointed at this stack's Gitea instance. Not bundled Docker image (`docker.gitea.com/gitea-mcp-server`), or `go run
by default; this stack's Gitea has no built-in Claude integration out of gitea.com/gitea/gitea-mcp@latest`; it supports both stdio and HTTP
the box. transport. Generate a token first — this stack's Gitea → profile →
Settings → Applications → Generate New Token (repo/api scopes) — then:
```bash
# stdio — simplest, one Claude Code CLI on this box
claude mcp add gitea --env GITEA_HOST=http://localhost:3001 \
--env GITEA_ACCESS_TOKEN=<token> -- gitea-mcp -t stdio
# or HTTP — one server, shared by multiple Claude Code clients
gitea-mcp -t http --port 8090 & # run once, e.g. alongside the stack
claude mcp add gitea http://localhost:8090/mcp \
--header "Authorization: Bearer <token>"
```
Not bundled by default — this stack's Gitea has no built-in Claude
integration out of the box; this is you adding it.
## GPU switcher (small local GPU only) ## GPU switcher (small local GPU only)
One small GPU can't run local chat and local image-gen at once. Swap it: One small GPU can't run local chat and local image-gen at once. Swap it:
+155
View File
@@ -0,0 +1,155 @@
#!/bin/bash
# services/claude-cli.sh — Claude Code CLI: dual-account setup, model/effort
# defaults, and a shared global CLAUDE.md.
#
# Non-Docker (see CLAUDE.md's "Non-Docker services" section). Installs the
# official Claude Code CLI if missing, then wires up:
# - two independent account config directories (work/personal), each its
# own CLAUDE_CONFIG_DIR behind a shell alias, so `claude-work` and
# `claude-personal` are two fully separate logins on one machine
# - one shared, imported global CLAUDE.md (durable personal conventions —
# modular/reuse code, numbered CLI menus with 0=always-exit, verify web
# UI changes with Playwright) that both accounts pull in via `@import`,
# so there's exactly one copy to edit, not two that can drift
# - settings.json defaults applied to both accounts: model pinned to
# claude-sonnet-5, effort level medium, and ENABLE_PROMPT_CACHING_1H=1
# (keeps the 1h prompt-cache lifetime even after usage credits kick in,
# instead of dropping to 5 minutes — see services/ai-stack.md's hybrid
# workflow section for why this pairs with a local-GPU + Claude Code split)
#
# The Anthropic login itself (browser OAuth) can't be scripted — this only
# prepares the directories/aliases/config. Run `claude-work` and
# `claude-personal` once each afterward to actually log each one in.
# Part of the modular post-install system (sourced by setup.sh).
register_service claude-cli extras "Claude Code CLI — dual-account setup (work/personal), model/effort defaults, shared global CLAUDE.md"
install_claude-cli() {
local WORK_DIR="$ACTUAL_HOME/.claude-work"
local PERSONAL_DIR="$ACTUAL_HOME/.claude-personal"
local SHARED_DIR="$ACTUAL_HOME/.claude-shared"
local SHARED_CLAUDE_MD="$SHARED_DIR/CLAUDE.md"
local BASHRC="$ACTUAL_HOME/.bashrc"
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would install the Claude Code CLI (official installer) if missing"
echo "[DRY-RUN] Would create $WORK_DIR and $PERSONAL_DIR config dirs"
echo "[DRY-RUN] Would write $SHARED_CLAUDE_MD (shared conventions) and import it from each account's CLAUDE.md"
echo "[DRY-RUN] Would write settings.json (model=claude-sonnet-5, effortLevel=medium, ENABLE_PROMPT_CACHING_1H=1) into each account dir"
echo "[DRY-RUN] Would add claude-work/claude-personal aliases to $BASHRC (idempotent)"
return 0
fi
if [ -f "$SHARED_CLAUDE_MD" ]; then
local MODE=""
prompt_reinstall_mode MODE
case "$MODE" in
update)
log_info "Refreshing shared CLAUDE.md and settings.json only — account dirs/credentials untouched."
_claude_cli_write_shared_claude_md "$SHARED_CLAUDE_MD"
_claude_cli_write_settings "$WORK_DIR/settings.json"
_claude_cli_write_settings "$PERSONAL_DIR/settings.json"
ensure_docker_dir_ownership "$SHARED_DIR" "$WORK_DIR" "$PERSONAL_DIR"
log_success "claude-cli config refreshed"
return 0
;;
cancel)
log_info "Leaving the existing claude-cli setup as-is."
return 0
;;
fresh) ;; # fall through to the full setup below
esac
fi
# ── Install the CLI itself ──────────────────────────────────────────────
if ! command -v claude >/dev/null 2>&1; then
log_info "Installing Claude Code CLI..."
if curl -fsSL https://claude.ai/install.sh | bash; then
log_success "Claude Code CLI installed"
else
log_error "Claude Code CLI install failed — see https://code.claude.com/docs/en/setup"
return 1
fi
else
log_info "Claude Code CLI already installed ($(command -v claude))"
fi
# ── Account config dirs + shared conventions ────────────────────────────
mkdir -p "$WORK_DIR" "$PERSONAL_DIR" "$SHARED_DIR"
_claude_cli_write_shared_claude_md "$SHARED_CLAUDE_MD"
local _dir
for _dir in "$WORK_DIR" "$PERSONAL_DIR"; do
# @import pulls the shared file in at session start (see Claude
# Code's memory docs) — one canonical copy, not two that can drift.
[ -f "$_dir/CLAUDE.md" ] || printf '@%s\n' "$SHARED_CLAUDE_MD" > "$_dir/CLAUDE.md"
_claude_cli_write_settings "$_dir/settings.json"
done
# ── Shell aliases — idempotent, same append-once pattern base.sh uses
# for tab completion (grep-before-append, chown after) ──────────────────
if [ -f "$BASHRC" ] && ! grep -qF "CLAUDE_CONFIG_DIR=$WORK_DIR" "$BASHRC" 2>/dev/null; then
{
echo ""
echo "# ubuntu-post-install: claude-cli dual-account aliases"
echo "alias claude-work='CLAUDE_CONFIG_DIR=$WORK_DIR claude'"
echo "alias claude-personal='CLAUDE_CONFIG_DIR=$PERSONAL_DIR claude'"
} >> "$BASHRC"
chown "$ACTUAL_USER:$ACTUAL_USER" "$BASHRC" 2>/dev/null || true
log_success "Added claude-work / claude-personal aliases to $BASHRC (new shells, or: source $BASHRC)"
fi
ensure_docker_dir_ownership "$SHARED_DIR" "$WORK_DIR" "$PERSONAL_DIR"
echo ""
log_warning "Login still needs a one-time browser step per account — this only prepared the plumbing:"
echo " claude-work # first run: browser OAuth login for your work account"
echo " claude-personal # first run: browser OAuth login for your personal account"
echo ""
echo " Shared conventions : $SHARED_CLAUDE_MD (edit once, both accounts see it)"
echo " Work config : $WORK_DIR"
echo " Personal config : $PERSONAL_DIR"
echo ""
}
# Writes/refreshes the three keys this service owns via jq (preserves any
# other hand-added settings, e.g. permissions); falls back to a fresh file
# if jq is missing (base.sh installs it, but this service can run standalone)
# or the existing file isn't valid JSON.
_claude_cli_write_settings() {
local dest="$1"
local patch='{"model":"claude-sonnet-5","effortLevel":"medium","env":{"ENABLE_PROMPT_CACHING_1H":"1"}}'
if [ -f "$dest" ] && command -v jq >/dev/null 2>&1; then
local merged
merged="$(jq -s '.[0] * .[1]' "$dest" <(echo "$patch") 2>/dev/null)" \
&& [ -n "$merged" ] \
&& printf '%s\n' "$merged" > "$dest" \
&& return 0
log_warning "$dest wasn't valid JSON — leaving it untouched. Merge manually: $patch"
return 0
fi
[ -f "$dest" ] && return 0
echo "$patch" | (command -v jq >/dev/null 2>&1 && jq . || cat) > "$dest"
}
_claude_cli_write_shared_claude_md() {
cat > "$1" << 'EOF'
# Personal conventions (all projects, both accounts)
## Code reuse
Write shared logic once, in one place. Before adding a new function, check
whether an existing one already does it — extend/parameterize rather than
duplicate.
## CLI menus
Every interactive menu is numbered. `0` is always "exit" / "back" — never
reused for another action, and always present, even on a submenu.
## Verifying web UI changes
After any frontend change, drive it with Playwright before calling it done —
navigate the real page, exercise the changed flow, screenshot if the result
is visual. Don't declare a UI task complete from reading the code alone.
EOF
}
+2 -1
View File
@@ -100,6 +100,7 @@ is_installed() {
kdeconnect) command -v kdeconnect >/dev/null 2>&1 ;; kdeconnect) command -v kdeconnect >/dev/null 2>&1 ;;
silent-send) [ -d "$ACTUAL_HOME/silent-send/.git" ] ;; silent-send) [ -d "$ACTUAL_HOME/silent-send/.git" ] ;;
sync-cc) [ -f "$ACTUAL_HOME/sync-cc/sync_cc.py" ] ;; sync-cc) [ -f "$ACTUAL_HOME/sync-cc/sync_cc.py" ] ;;
claude-cli) [ -f "$ACTUAL_HOME/.claude-shared/CLAUDE.md" ] ;;
sky-cam) [ -d "$ACTUAL_HOME/sky-cam/.git" ] ;; sky-cam) [ -d "$ACTUAL_HOME/sky-cam/.git" ] ;;
sky-cam-frigate) [ -d "$ACTUAL_HOME/sky-cam/.git" ] && [ -f "$ACTUAL_HOME/sky-cam/frigate-retime.sh" ] ;; sky-cam-frigate) [ -d "$ACTUAL_HOME/sky-cam/.git" ] && [ -f "$ACTUAL_HOME/sky-cam/frigate-retime.sh" ] ;;
# Either directory counts: boxes set up before the droplet edition was # Either directory counts: boxes set up before the droplet edition was
@@ -130,7 +131,7 @@ is_installed() {
# is_installed() as 0 or 1. # is_installed() as 0 or 1.
install_count() { install_count() {
case "$1" in case "$1" in
base|glow|crowdsec|security-dashboard|kdeconnect|silent-send|sync-cc|sky-cam|sky-cam-frigate|asterisk|pstn-trunk|sms-inbound|ssh-config|ssh-key-import) base|glow|crowdsec|security-dashboard|kdeconnect|silent-send|sync-cc|claude-cli|sky-cam|sky-cam-frigate|asterisk|pstn-trunk|sms-inbound|ssh-config|ssh-key-import)
is_installed "$1" && echo 1 || echo 0 ;; is_installed "$1" && echo 1 || echo 0 ;;
wordpress) wordpress)
find "$DOCKER_DIR" -mindepth 1 -maxdepth 1 -name 'wordpress-*' -type d 2>/dev/null | wc -l ;; find "$DOCKER_DIR" -mindepth 1 -maxdepth 1 -name 'wordpress-*' -type d 2>/dev/null | wc -l ;;