Last active
March 26, 2026 13:26
-
-
Save lastknight/f21271d761a86a5ab2b6a5f2e73256d5 to your computer and use it in GitHub Desktop.
clouded-bootstrap: one-curl Claude Code setup with skills
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # clouded-bootstrap.sh — Setup/migrazione Claude Code su qualsiasi macchina | |
| # Idempotente: funziona su macchina nuova E su macchina esistente da aggiornare. | |
| # Usage: curl -fsSL https://gist.githubusercontent.com/lastknight/f21271d761a86a5ab2b6a5f2e73256d5/raw/clouded-bootstrap.sh | bash | |
| set -euo pipefail | |
| # ── colori ──────────────────────────────────────────────────────────────────── | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m' | |
| log() { echo -e "${GREEN}▸${NC} $*"; } | |
| warn() { echo -e "${YELLOW}⚠${NC} $*"; } | |
| err() { echo -e "${RED}✗${NC} $*" >&2; exit 1; } | |
| ok() { echo -e "${GREEN}✓${NC} $*"; } | |
| REPO="lastknight/claude-skills" | |
| SKILLS_DIR="$HOME/.claude/skills" | |
| REPO_DIR="$HOME/.claude-skills-repo" | |
| HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname)" | |
| CMD_NAME="clouded" | |
| MEMORY_DIR="$HOME/.claude/projects/$(echo "$HOME" | sed 's|/|-|g')/memory" | |
| # rsync exclusions — MUST match sync.md (single source of truth) | |
| RSYNC_EXCLUDES=( | |
| --exclude='.git' --exclude='.DS_Store' --exclude='.gitignore' | |
| --exclude='README.md' --exclude='README.pdf' --exclude='CLAUDE.md' | |
| --exclude='memory/' --exclude='agents/' --exclude='rules/' | |
| --exclude='sessions/' --exclude='hooks/' --exclude='scripts/' | |
| --exclude='config/' --exclude='settings.json' | |
| --exclude='clouded-bootstrap.sh' | |
| ) | |
| # rileva se siamo su server headless (SSH senza display) | |
| HAS_DISPLAY=false | |
| if [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ] || [[ "$(uname)" == "Darwin" ]]; then | |
| HAS_DISPLAY=true | |
| fi | |
| # rileva se siamo root | |
| IS_ROOT=false | |
| [ "$(id -u)" = "0" ] && IS_ROOT=true | |
| echo -e "\n${BOLD}clouded bootstrap${NC} — macchina: ${YELLOW}${HOSTNAME_SHORT}${NC}\n" | |
| # ── 1. prerequisiti base ────────────────────────────────────────────────────── | |
| log "Controllo prerequisiti..." | |
| check_cmd() { | |
| command -v "$1" &>/dev/null && return 0 | |
| return 1 | |
| } | |
| # funzione per installare pacchetti di sistema (apt o brew) | |
| install_pkg() { | |
| if check_cmd apt-get; then | |
| apt-get install -y "$@" 2>/dev/null || warn "Impossibile installare $* (serve root?)" | |
| elif check_cmd brew; then | |
| brew install "$@" | |
| else | |
| warn "Impossibile installare $* — installa manualmente" | |
| fi | |
| } | |
| # claude | |
| if ! check_cmd claude; then | |
| warn "Claude Code non trovato. Provo ad installarlo..." | |
| if check_cmd npm; then | |
| npm install -g @anthropic-ai/claude-code | |
| elif check_cmd brew; then | |
| brew install claude | |
| else | |
| err "npm non trovato. Installa Node.js prima:\n https://nodejs.org\nPoi: npm install -g @anthropic-ai/claude-code" | |
| fi | |
| fi | |
| ok "claude $(claude --version 2>/dev/null | head -1)" | |
| # gh CLI | |
| if ! check_cmd gh; then | |
| warn "GitHub CLI (gh) non trovato. Provo ad installarlo..." | |
| if check_cmd brew; then | |
| brew install gh | |
| elif check_cmd apt-get; then | |
| (type -p curl >/dev/null || apt install curl -y) && \ | |
| curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null && \ | |
| echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \ | |
| apt update && apt install gh -y | |
| else | |
| err "Installa gh manualmente: https://cli.github.com" | |
| fi | |
| fi | |
| ok "gh $(gh --version 2>/dev/null | head -1 | awk '{print $3}')" | |
| # rsync, git, tmux, curl | |
| for tool in rsync git tmux curl; do | |
| if ! check_cmd "$tool"; then | |
| warn "$tool non trovato. Installo..." | |
| install_pkg "$tool" | |
| fi | |
| check_cmd "$tool" && ok "$tool" || warn "$tool non disponibile" | |
| done | |
| # ── 1b. dipendenze Python e system ─────────────────────────────────────────── | |
| log "Installazione dipendenze Python e system tools..." | |
| # poppler-utils (per lettura PDF) | |
| if ! check_cmd pdftotext; then | |
| log "Installo poppler-utils (lettura PDF)..." | |
| install_pkg poppler-utils | |
| fi | |
| check_cmd pdftotext && ok "poppler-utils (pdftotext)" || warn "poppler-utils non installato" | |
| # Python3 packages per md-to-docx e conversioni | |
| if check_cmd python3; then | |
| ok "python3 $(python3 --version 2>&1 | awk '{print $2}')" | |
| # controlla se i pacchetti sono già installati | |
| MISSING_PY="" | |
| python3 -c "import yaml" 2>/dev/null || MISSING_PY="$MISSING_PY python3-yaml" | |
| python3 -c "import markdown" 2>/dev/null || MISSING_PY="$MISSING_PY python3-markdown" | |
| python3 -c "import bs4" 2>/dev/null || MISSING_PY="$MISSING_PY python3-bs4" | |
| python3 -c "import docx" 2>/dev/null || MISSING_PY="$MISSING_PY python3-docx" | |
| if [ -n "$MISSING_PY" ]; then | |
| log "Installo pacchetti Python:$MISSING_PY" | |
| if check_cmd apt-get; then | |
| apt-get install -y $MISSING_PY 2>/dev/null || \ | |
| pip3 install --break-system-packages pyyaml python-docx markdown beautifulsoup4 2>/dev/null || \ | |
| warn "Impossibile installare pacchetti Python (serve root?)" | |
| elif check_cmd pip3; then | |
| pip3 install pyyaml python-docx markdown beautifulsoup4 | |
| elif check_cmd brew; then | |
| pip3 install pyyaml python-docx markdown beautifulsoup4 | |
| fi | |
| fi | |
| # verifica | |
| python3 -c "import yaml, markdown, bs4, docx" 2>/dev/null && ok "Python packages (yaml, markdown, bs4, docx)" || warn "Alcuni pacchetti Python mancanti" | |
| else | |
| warn "python3 non trovato — md-to-docx non funzionerà" | |
| fi | |
| # ── 2. autenticazione gh ────────────────────────────────────────────────────── | |
| log "Verifica autenticazione GitHub..." | |
| if ! gh auth status &>/dev/null; then | |
| warn "Non autenticato con gh. Avvio autenticazione..." | |
| gh auth login | |
| fi | |
| ok "GitHub autenticato" | |
| gh auth setup-git 2>/dev/null || true | |
| # ── 3. skills ───────────────────────────────────────────────────────────────── | |
| log "Download skills da ${REPO}..." | |
| git config --global --add safe.directory "$REPO_DIR" 2>/dev/null || true | |
| if [ -d "$REPO_DIR/.git" ] && [ "$(stat -c '%u' "$REPO_DIR" 2>/dev/null || stat -f '%u' "$REPO_DIR" 2>/dev/null)" = "$(id -u)" ]; then | |
| cd "$REPO_DIR" && git pull --quiet | |
| else | |
| rm -rf "$REPO_DIR" | |
| gh repo clone "$REPO" "$REPO_DIR" -- --quiet | |
| fi | |
| mkdir -p "$SKILLS_DIR" | |
| # ── 3a. CLEANUP — rimuovi directory che non devono stare sotto skills/ ─────── | |
| log "Cleanup directory obsolete..." | |
| CLEANED=0 | |
| # Skill deprecate (migrate a /self nel marzo 2026) | |
| for old_dir in warmup sogna coherence person-decode skills; do | |
| if [ -d "$SKILLS_DIR/$old_dir" ]; then | |
| rm -rf "$SKILLS_DIR/$old_dir" | |
| ok "Rimosso: $old_dir/ (migrato a /self)" | |
| CLEANED=$((CLEANED + 1)) | |
| fi | |
| done | |
| # Directory del repo finite in skills/ per errore (vecchi rsync senza esclusioni) | |
| for stray_dir in agents hooks rules memory sessions scripts config; do | |
| if [ -d "$SKILLS_DIR/$stray_dir" ]; then | |
| rm -rf "$SKILLS_DIR/$stray_dir" | |
| ok "Rimosso: $stray_dir/ (non deve stare in skills/)" | |
| CLEANED=$((CLEANED + 1)) | |
| fi | |
| done | |
| # File del repo finiti in skills/ per errore | |
| for stray_file in clouded-bootstrap.sh settings.json README.pdf README.md; do | |
| if [ -f "$SKILLS_DIR/$stray_file" ]; then | |
| rm -f "$SKILLS_DIR/$stray_file" | |
| ok "Rimosso: $stray_file (non deve stare in skills/)" | |
| CLEANED=$((CLEANED + 1)) | |
| fi | |
| done | |
| [ "$CLEANED" -gt 0 ] && ok "Cleanup: $CLEANED elementi rimossi" || ok "Nessun residuo trovato" | |
| # ── 3b. salva stile attivo se esiste ───────────────────────────────────────── | |
| ACTIVE_STYLE="" | |
| if [ -L "$SKILLS_DIR/ACTIVE_STYLE.md" ]; then | |
| ACTIVE_STYLE="$(readlink "$SKILLS_DIR/ACTIVE_STYLE.md")" | |
| fi | |
| # ── 3c. rsync skills (con esclusioni corrette) ────────────────────────────── | |
| rsync -a --delete "${RSYNC_EXCLUDES[@]}" "$REPO_DIR/" "$SKILLS_DIR/" | |
| ok "Skills sincronizzate" | |
| # ── 3d. agents, rules ──────────────────────────────────────────────────────── | |
| mkdir -p "$HOME/.claude/agents" "$HOME/.claude/rules" | |
| [ -d "$REPO_DIR/agents" ] && rsync -a "$REPO_DIR/agents/" "$HOME/.claude/agents/" | |
| [ -d "$REPO_DIR/rules" ] && rsync -a "$REPO_DIR/rules/" "$HOME/.claude/rules/" | |
| ok "Agents e rules sincronizzati" | |
| # ── 3e. CLAUDE.md globale ──────────────────────────────────────────────────── | |
| [ -f "$REPO_DIR/CLAUDE.md" ] && cp "$REPO_DIR/CLAUDE.md" "$HOME/.claude/CLAUDE.md" | |
| ok "CLAUDE.md aggiornato" | |
| # ── 3f. hooks ──────────────────────────────────────────────────────────────── | |
| if [ -d "$REPO_DIR/hooks" ]; then | |
| mkdir -p "$HOME/.claude/hooks" | |
| cp "$REPO_DIR/hooks/"*.sh "$HOME/.claude/hooks/" 2>/dev/null | |
| chmod +x "$HOME/.claude/hooks/"*.sh 2>/dev/null | |
| ok "Hooks sincronizzati" | |
| fi | |
| # ── 3g. settings.json ─────────────────────────────────────────────────────── | |
| [ -f "$REPO_DIR/settings.json" ] && cp "$REPO_DIR/settings.json" "$HOME/.claude/settings.json" | |
| # ── 3h. ripristina stile ──────────────────────────────────────────────────── | |
| if [ -n "$ACTIVE_STYLE" ]; then | |
| ln -sf "$ACTIVE_STYLE" "$SKILLS_DIR/ACTIVE_STYLE.md" | |
| elif [ -f "$SKILLS_DIR/styles/tf-corporate.md" ]; then | |
| ln -sf "styles/tf-corporate.md" "$SKILLS_DIR/ACTIVE_STYLE.md" | |
| fi | |
| ok "Stile attivo: $(readlink "$SKILLS_DIR/ACTIVE_STYLE.md" 2>/dev/null || echo 'nessuno')" | |
| # ── 3i. memory ─────────────────────────────────────────────────────────────── | |
| log "Sincronizzazione memorie..." | |
| mkdir -p "$MEMORY_DIR" | |
| if [ -d "$REPO_DIR/memory" ]; then | |
| rsync -a "$REPO_DIR/memory/" "$MEMORY_DIR/" | |
| ok "Memorie sincronizzate in $MEMORY_DIR" | |
| else | |
| warn "Nessuna directory memory nel repo — skip" | |
| fi | |
| # ── 3j. copia repo in /tmp per sync futuro ─────────────────────────────────── | |
| if [ ! -d /tmp/claude-skills/.git ]; then | |
| cp -a "$REPO_DIR" /tmp/claude-skills | |
| ok "Repo copiato in /tmp/claude-skills per /self update" | |
| fi | |
| # ── 4. comando 'clouded' ────────────────────────────────────────────────────── | |
| log "Installazione comando '${CMD_NAME}'..." | |
| # sceglie la directory di installazione | |
| if [ -w /usr/local/bin ]; then | |
| INSTALL_DIR="/usr/local/bin" | |
| elif [ -d "$HOME/.local/bin" ]; then | |
| INSTALL_DIR="$HOME/.local/bin" | |
| else | |
| mkdir -p "$HOME/.local/bin" | |
| INSTALL_DIR="$HOME/.local/bin" | |
| fi | |
| CMD_PATH="$INSTALL_DIR/$CMD_NAME" | |
| cat > "$CMD_PATH" << 'ENDOFSCRIPT' | |
| #!/usr/bin/env bash | |
| # clouded — Claude Code con tmux persistente + Telegram | |
| SESSION="clouded" | |
| CLAUDE_CMD="claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official" | |
| if command -v tmux &>/dev/null; then | |
| if tmux has-session -t "$SESSION" 2>/dev/null; then | |
| exec tmux attach-session -t "$SESSION" | |
| else | |
| tmux new-session -d -s "$SESSION" "while true; do $CLAUDE_CMD; echo 'Claude uscito. Riavvio in 2s... (Ctrl+C per fermare)'; sleep 2; done" | |
| exec tmux attach-session -t "$SESSION" | |
| fi | |
| else | |
| exec $CLAUDE_CMD | |
| fi | |
| ENDOFSCRIPT | |
| chmod +x "$CMD_PATH" | |
| ok "Comando installato: $CMD_PATH (con Telegram)" | |
| # ── 5. PATH check ───────────────────────────────────────────────────────────── | |
| if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then | |
| warn "$INSTALL_DIR non è nel PATH." | |
| SHELL_RC="" | |
| case "${SHELL:-}" in | |
| */zsh) SHELL_RC="$HOME/.zshrc" ;; | |
| */bash) SHELL_RC="$HOME/.bashrc" ;; | |
| esac | |
| if [ -n "$SHELL_RC" ] && ! grep -q "$INSTALL_DIR" "$SHELL_RC" 2>/dev/null; then | |
| echo "" >> "$SHELL_RC" | |
| echo "# clouded" >> "$SHELL_RC" | |
| echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$SHELL_RC" | |
| ok "PATH aggiunto a $SHELL_RC" | |
| fi | |
| fi | |
| # ── 5b. scripts operativi ──────────────────────────────────────────────────── | |
| if [ -d "$REPO_DIR/scripts" ]; then | |
| mkdir -p "$INSTALL_DIR" | |
| cp "$REPO_DIR/scripts/"*.sh "$INSTALL_DIR/" 2>/dev/null | |
| chmod +x "$INSTALL_DIR/"*.sh 2>/dev/null | |
| ok "Scripts operativi installati" | |
| fi | |
| # ── 5c. sogna-nightly + cron (restart notturno) ────────────────────────────── | |
| log "Installazione sogna-nightly (consolidamento memoria + restart)..." | |
| NIGHTLY_PATH="$INSTALL_DIR/sogna-nightly.sh" | |
| CLAUDE_CMD_NIGHTLY="claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official" | |
| cat > "$NIGHTLY_PATH" << ENDOFNIGHTLY | |
| #!/usr/bin/env bash | |
| # sogna-nightly — consolida memoria + push + restart clouded | |
| # Cron: 0 23 * * * (23:00 UTC = ~mezzanotte Italia) | |
| LOG="\$HOME/.claude/sogna-nightly.log" | |
| SESSION="clouded" | |
| CLAUDE_CMD="$CLAUDE_CMD_NIGHTLY" | |
| echo "\$(date -Iseconds) — sogna-nightly started" >> "\$LOG" | |
| # 1. Dream + push in una sessione claude dedicata | |
| echo "\$(date -Iseconds) — running /self dream --force + push" >> "\$LOG" | |
| echo -e '/self dream --force\n/self update push' | claude --dangerously-skip-permissions --print 2>&1 | tail -30 >> "\$LOG" | |
| echo "\$(date -Iseconds) — dream + push completed" >> "\$LOG" | |
| # 2. Killa la sessione tmux clouded | |
| if tmux has-session -t "\$SESSION" 2>/dev/null; then | |
| echo "\$(date -Iseconds) — killing tmux session \$SESSION" >> "\$LOG" | |
| tmux kill-session -t "\$SESSION" 2>/dev/null | |
| sleep 3 | |
| fi | |
| # 3. Riavvia clouded con sessione fresca | |
| echo "\$(date -Iseconds) — restarting clouded" >> "\$LOG" | |
| tmux new-session -d -s "\$SESSION" \\ | |
| "while true; do \$CLAUDE_CMD; echo 'Claude uscito. Riavvio in 2s...'; sleep 2; done" | |
| echo "\$(date -Iseconds) — sogna-nightly completed, clouded restarted" >> "\$LOG" | |
| ENDOFNIGHTLY | |
| chmod +x "$NIGHTLY_PATH" | |
| ok "sogna-nightly.sh installato: $NIGHTLY_PATH" | |
| # installa cron (23:00 UTC = ~mezzanotte Italia) | |
| if check_cmd crontab; then | |
| (crontab -l 2>/dev/null | grep -v sogna-nightly; echo "0 23 * * * $NIGHTLY_PATH") | crontab - | |
| ok "Cron installato: ogni giorno alle 23:00 UTC (dream + push + restart clouded)" | |
| else | |
| warn "crontab non disponibile — installa cron manualmente" | |
| fi | |
| # ── 6. MCP: Microsoft 365 Calendar (globale, non npx) ───────────────────────── | |
| echo "" | |
| log "Configurazione Microsoft 365 Calendar..." | |
| # installa globalmente (NON npx — il token si perde con npx) | |
| if ! check_cmd ms-365-mcp-server; then | |
| log "Installo ms-365-mcp-server globalmente..." | |
| npm install -g @softeria/ms-365-mcp-server 2>/dev/null || warn "Impossibile installare ms-365-mcp-server (serve root per npm global?)" | |
| fi | |
| if check_cmd ms-365-mcp-server; then | |
| ok "ms-365-mcp-server installato globalmente" | |
| # chmod per permettere scrittura token dall'utente claude | |
| MS365_DIR="$(npm root -g 2>/dev/null)/@softeria/ms-365-mcp-server" | |
| if [ -d "$MS365_DIR" ] && $IS_ROOT; then | |
| chmod 777 "$MS365_DIR" 2>/dev/null || true | |
| ok "Permessi token directory impostati" | |
| elif [ -d "$MS365_DIR" ]; then | |
| warn "Token directory potrebbe non essere scrivibile (serve chmod 777 da root su $MS365_DIR)" | |
| fi | |
| # autenticazione (device code flow — funziona anche headless) | |
| echo "" | |
| log "Autenticazione Microsoft 365 (device code flow)..." | |
| echo -e "${YELLOW}Verrà mostrato un URL e un codice. Apri l'URL in un browser qualsiasi e inserisci il codice.${NC}" | |
| read -r -p "Procedere? [y/N] " choice | |
| if [[ "$choice" =~ ^[Yy]$ ]]; then | |
| ms-365-mcp-server --login --org-mode && ok "Microsoft 365 autenticato" || warn "Autenticazione saltata" | |
| else | |
| warn "Autenticazione saltata. Quando vuoi: ms-365-mcp-server --login --org-mode" | |
| fi | |
| else | |
| warn "ms-365-mcp-server non installato — calendario non disponibile" | |
| fi | |
| # ── 7. Gmail: gog CLI ───────────────────────────────────────────────────────── | |
| echo "" | |
| log "Configurazione Gmail (gog)..." | |
| if ! check_cmd gog; then | |
| warn "gog non trovato. Provo ad installarlo..." | |
| if check_cmd brew; then | |
| brew install steipete/tap/gogcli | |
| else | |
| GOG_VERSION="0.12.0" | |
| ARCH="$(uname -m)" | |
| OS="$(uname -s | tr '[:upper:]' '[:lower:]')" | |
| case "$ARCH" in | |
| x86_64) ARCH="amd64" ;; | |
| aarch64|arm64) ARCH="arm64" ;; | |
| esac | |
| GOG_URL="https://github.com/steipete/gogcli/releases/download/v${GOG_VERSION}/gogcli_${GOG_VERSION}_${OS}_${ARCH}.tar.gz" | |
| if [ -w /usr/local/bin ]; then | |
| curl -fsSL "$GOG_URL" | tar -xz -C /usr/local/bin gog | |
| chmod +x /usr/local/bin/gog | |
| else | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fsSL "$GOG_URL" | tar -xz -C "$HOME/.local/bin" gog | |
| chmod +x "$HOME/.local/bin/gog" | |
| fi | |
| fi | |
| fi | |
| check_cmd gog && ok "gog $(gog --version 2>/dev/null | head -1)" || warn "gog non installato — Gmail non disponibile" | |
| # registra credenziali e token | |
| if check_cmd gog; then | |
| GOG_CREDS="$REPO_DIR/config/gog-credentials.json" | |
| GOG_TOKEN="$REPO_DIR/config/gog-token.json" | |
| if [ -f "$GOG_CREDS" ]; then | |
| gog auth credentials set "$GOG_CREDS" 2>/dev/null && ok "Credenziali Gmail registrate" | |
| else | |
| warn "gog-credentials.json non trovato — skip" | |
| fi | |
| if [ -f "$GOG_TOKEN" ]; then | |
| export GOG_KEYRING_PASSWORD=clouded | |
| gog auth keyring file 2>/dev/null || true | |
| gog auth tokens import "$GOG_TOKEN" 2>/dev/null && ok "Token Gmail importato" | |
| grep -q GOG_KEYRING_PASSWORD "$HOME/.bashrc" 2>/dev/null || echo 'export GOG_KEYRING_PASSWORD=clouded' >> "$HOME/.bashrc" | |
| else | |
| if $HAS_DISPLAY; then | |
| read -r -p "Autenticarsi con Gmail ora? (richiede browser) [y/N] " choice | |
| [[ "$choice" =~ ^[Yy]$ ]] && gog auth add mf@matteoflora.com && ok "Gmail autenticato" | |
| else | |
| warn "Server headless: esegui 'gog auth tokens import <file>' per autenticare Gmail" | |
| fi | |
| fi | |
| fi | |
| # ── 8. utente 'claude' per SSH (solo se root) ───────────────────────────────── | |
| if $IS_ROOT; then | |
| echo "" | |
| log "Creazione utente 'claude' per SSH..." | |
| if ! id claude &>/dev/null; then | |
| useradd -m -s /bin/bash claude | |
| ok "Utente 'claude' creato" | |
| else | |
| ok "Utente 'claude' già esistente" | |
| fi | |
| # copia le authorized_keys di root | |
| mkdir -p /home/claude/.ssh | |
| if [ -f /root/.ssh/authorized_keys ]; then | |
| cp /root/.ssh/authorized_keys /home/claude/.ssh/authorized_keys | |
| chown -R claude:claude /home/claude/.ssh | |
| chmod 700 /home/claude/.ssh | |
| chmod 600 /home/claude/.ssh/authorized_keys | |
| ok "Chiavi SSH copiate da root" | |
| fi | |
| # installa skills per l'utente claude | |
| CLAUDE_HOME="/home/claude" | |
| mkdir -p "$CLAUDE_HOME/.claude/skills" "$CLAUDE_HOME/.claude/agents" "$CLAUDE_HOME/.claude/rules" | |
| rsync -a --delete "${RSYNC_EXCLUDES[@]}" "$REPO_DIR/" "$CLAUDE_HOME/.claude/skills/" | |
| # cleanup anche per utente claude | |
| for old_dir in warmup sogna coherence person-decode skills agents hooks rules memory sessions scripts config; do | |
| rm -rf "$CLAUDE_HOME/.claude/skills/$old_dir" | |
| done | |
| [ -d "$HOME/.claude/agents" ] && rsync -a "$HOME/.claude/agents/" "$CLAUDE_HOME/.claude/agents/" | |
| [ -d "$HOME/.claude/rules" ] && rsync -a "$HOME/.claude/rules/" "$CLAUDE_HOME/.claude/rules/" | |
| [ -f "$HOME/.claude/CLAUDE.md" ] && cp "$HOME/.claude/CLAUDE.md" "$CLAUDE_HOME/.claude/CLAUDE.md" | |
| [ -f "$HOME/.claude/settings.json" ] && cp "$HOME/.claude/settings.json" "$CLAUDE_HOME/.claude/settings.json" | |
| # hooks per utente claude | |
| if [ -d "$HOME/.claude/hooks" ]; then | |
| mkdir -p "$CLAUDE_HOME/.claude/hooks" | |
| cp "$HOME/.claude/hooks/"*.sh "$CLAUDE_HOME/.claude/hooks/" 2>/dev/null | |
| chmod +x "$CLAUDE_HOME/.claude/hooks/"*.sh 2>/dev/null | |
| fi | |
| # memory per l'utente claude | |
| CLAUDE_MEMORY_DIR="$CLAUDE_HOME/.claude/projects/$(echo "$CLAUDE_HOME" | sed 's|/|-|g')/memory" | |
| mkdir -p "$CLAUDE_MEMORY_DIR" | |
| [ -d "$REPO_DIR/memory" ] && rsync -a "$REPO_DIR/memory/" "$CLAUDE_MEMORY_DIR/" | |
| chown -R claude:claude "$CLAUDE_HOME/.claude" | |
| # GOG_KEYRING_PASSWORD per l'utente claude | |
| grep -q GOG_KEYRING_PASSWORD "$CLAUDE_HOME/.bashrc" 2>/dev/null || echo 'export GOG_KEYRING_PASSWORD=clouded' >> "$CLAUDE_HOME/.bashrc" | |
| warn "Connettiti con: ssh claude@${HOSTNAME_SHORT}" | |
| fi | |
| # ── done ────────────────────────────────────────────────────────────────────── | |
| echo "" | |
| echo -e "${BOLD}${GREEN}Setup completato!${NC}" | |
| echo "" | |
| echo -e " Macchina: ${YELLOW}${HOSTNAME_SHORT}${NC}" | |
| echo -e " Comando: ${BOLD}${CMD_NAME}${NC} (tmux + Telegram)" | |
| echo -e " Skills: $SKILLS_DIR" | |
| echo -e " Memory: $MEMORY_DIR" | |
| echo -e " Agents: $HOME/.claude/agents/" | |
| echo -e " Rules: $HOME/.claude/rules/" | |
| echo -e " CLAUDE.md: $HOME/.claude/CLAUDE.md" | |
| echo -e " Calendar: ms-365-mcp-server (globale)" | |
| echo -e " Gmail: gog CLI (mf@matteoflora.com)" | |
| echo -e " Python: yaml, markdown, bs4, docx" | |
| echo -e " PDF: poppler-utils (pdftotext)" | |
| echo "" | |
| echo -e "Prossimo passo: ${BOLD}${CMD_NAME}${NC} poi ${BOLD}/self dream --force${NC} per consolidare le sessioni" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment