diff --git a/user-toolkits/alapenna-container/Dockerfile b/user-toolkits/alapenna-container/Dockerfile new file mode 100644 index 0000000..2d028e4 --- /dev/null +++ b/user-toolkits/alapenna-container/Dockerfile @@ -0,0 +1,334 @@ +FROM portainer/dev-toolkit:2025.12 + +ENV HOME=/root +ENV TERM=xterm-256color + +# Detect architecture for binary downloads +ARG TARGETARCH + +# ============================================ +# System packages (combined, with cleanup) +# ============================================ +RUN apt-get update && apt-get install -y --no-install-recommends \ + zsh \ + wget \ + curl \ + git \ + file \ + fzf \ + ripgrep \ + fd-find \ + bat \ + jq \ + lsof \ + unzip \ + && rm -rf /var/lib/apt/lists/* \ + && ln -sf $(which fdfind) /usr/local/bin/fd \ + && ln -sf $(which batcat) /usr/local/bin/bat + +# ============================================ +# zsh plugins (no oh-my-zsh) + starship prompt +# ============================================ +RUN mkdir -p /root/.zsh \ + && git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions /root/.zsh/zsh-autosuggestions \ + && git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting /root/.zsh/zsh-syntax-highlighting \ + && git clone --depth=1 https://github.com/zsh-users/zsh-history-substring-search /root/.zsh/zsh-history-substring-search \ + && rm -rf /root/.zsh/*/.git \ + && curl -sS https://starship.rs/install.sh | sh -s -- -y + +# Starship config (two-line minimal) +RUN mkdir -p /root/.config && cat > /root/.config/starship.toml <<'EOF' +format = """ +$directory\ +$git_branch\ +$git_status\ +$line_break\ +$character""" + +[directory] +style = "cyan bold" +truncation_length = 5 +truncate_to_repo = false + +[git_branch] +style = "purple bold" +format = "[│ $symbol$branch]($style)" +symbol = "" + +[git_status] +style = "red bold" +format = "[$all_status$ahead_behind]($style)" +conflicted = "=" +ahead = "⇡${count}" +behind = "⇣${count}" +diverged = "⇕" +untracked = "?" +stashed = "" +modified = "!" +staged = "+" +renamed = "»" +deleted = "✘" + +[character] +success_symbol = "[>](green bold)" +error_symbol = "[>](red bold)" +EOF + +# ============================================ +# fresh (terminal editor with LSP) +# ============================================ +RUN FRESH_VERSION=$(curl -s "https://api.github.com/repos/sinelaw/fresh/releases/latest" | grep -Po '"tag_name": "v\K[^"]*') \ + && ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \ + && curl -Lo fresh.deb "https://github.com/sinelaw/fresh/releases/download/v${FRESH_VERSION}/fresh-editor_${FRESH_VERSION}-1_${ARCH}.deb" \ + && dpkg -i fresh.deb \ + && rm fresh.deb \ + && npm install -g typescript-language-server typescript \ + && npm cache clean --force \ + && mkdir -p /root/.config/fresh && cat > /root/.config/fresh/config.json <<'EOF' +{ + "editor": { + "tab_size": 2 + } +} +EOF + +# ============================================ +# CLI tools (lazygit, delta, yazi, zoxide, eza) +# ============================================ +RUN LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep -Po '"tag_name": "v\K[^"]*') \ + && ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "x86_64") \ + && curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${LAZYGIT_VERSION}_Linux_${ARCH}.tar.gz" \ + && tar xf lazygit.tar.gz lazygit \ + && install lazygit /usr/local/bin \ + && rm lazygit.tar.gz lazygit + +RUN DELTA_VERSION=$(curl -s "https://api.github.com/repos/dandavison/delta/releases/latest" | grep -Po '"tag_name": "\K[^"]*') \ + && ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "arm64" || echo "amd64") \ + && curl -Lo delta.deb "https://github.com/dandavison/delta/releases/latest/download/git-delta_${DELTA_VERSION}_${ARCH}.deb" \ + && dpkg -i delta.deb \ + && rm delta.deb + +RUN ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x86_64") \ + && curl -Lo yazi.zip "https://github.com/sxyazi/yazi/releases/latest/download/yazi-${ARCH}-unknown-linux-gnu.zip" \ + && unzip yazi.zip \ + && mv yazi-${ARCH}-unknown-linux-gnu/yazi /usr/local/bin/ \ + && mv yazi-${ARCH}-unknown-linux-gnu/ya /usr/local/bin/ \ + && rm -rf yazi.zip yazi-*-unknown-linux-gnu + +RUN curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash + +RUN ARCH=$([ "$TARGETARCH" = "arm64" ] && echo "aarch64" || echo "x86_64") \ + && curl -Lo eza.tar.gz "https://github.com/eza-community/eza/releases/latest/download/eza_${ARCH}-unknown-linux-gnu.tar.gz" \ + && tar xf eza.tar.gz \ + && mv eza /usr/local/bin/ \ + && rm eza.tar.gz + +# ============================================ +# GitHub CLI (gh) +# ============================================ +RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ + && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ + && 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-get update \ + && apt-get install -y --no-install-recommends gh \ + && rm -rf /var/lib/apt/lists/* + +# ============================================ +# glow (markdown reader) +# ============================================ +RUN go install github.com/charmbracelet/glow@latest \ + && go clean -cache -modcache + +# ============================================ +# Claude Code + Plannotator CLI +# ============================================ +RUN curl -fsSL https://claude.ai/install.sh | bash \ + && curl -fsSL https://plannotator.ai/install.sh | bash + +# ============================================ +# Claude Code plugins (via CLI) +# ============================================ +ENV PATH="/root/.local/bin:${PATH}" +RUN claude plugin marketplace add jarrodwatts/claude-hud \ + && claude plugin marketplace add backnotprop/plannotator \ + && claude plugin marketplace add anthropics/claude-plugins-official \ + && claude plugin install claude-hud \ + && claude plugin install plannotator@plannotator \ + && claude plugin install typescript-lsp@claude-plugins-official \ + && claude plugin install gopls-lsp@claude-plugins-official + +# Claude Code config files +RUN mkdir -p /root/.claude/plugins/claude-hud \ + && cat > /root/.claude/plugins/claude-hud/config.json <<'EOF' +{ + "layout": "default", + "display": { + "showModel": true, + "showContextBar": true, + "showTools": false, + "showAgents": true, + "showTodos": true, + "showConfigCounts": false, + "showTokenBreakdown": true, + "showUsage": true, + "showDuration": false + }, + "gitStatus": { + "enabled": true, + "showDirty": true, + "showAheadBehind": true + } +} +EOF + +RUN cat > /root/.claude/settings.json <<'EOF' +{ + "statusLine": { + "type": "command", + "command": "node \"$(ls -td ~/.claude/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | head -1)dist/index.js\"" + }, + "enabledPlugins": { + "claude-hud@claude-hud": true, + "plannotator@plannotator": true + }, + "alwaysThinkingEnabled": true, + "permissions": { + "deny": [ + "Read(**/.env*)", + "Read(~/.ssh/**)" + ] + } +} +EOF + +# ============================================ +# Git config +# ============================================ +RUN git config --global core.pager delta \ + && git config --global interactive.diffFilter "delta --color-only" \ + && git config --global delta.navigate true \ + && git config --global delta.side-by-side true \ + && git config --global delta.line-numbers true \ + && git config --global merge.conflictstyle diff3 \ + && git config --global diff.colorMoved default \ + && git config --global push.autoSetupRemote true + +# ============================================ +# Shell configuration (.zshrc) +# ============================================ +RUN cat > /root/.zshrc <<'EOF' +# ---------------------------------------- +# Zsh plugins (no oh-my-zsh framework) +# ---------------------------------------- +source ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh +source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh +source ~/.zsh/zsh-history-substring-search/zsh-history-substring-search.zsh + +# ---------------------------------------- +# History configuration +# ---------------------------------------- +HISTFILE=~/.zsh_history +HISTSIZE=50000 +SAVEHIST=50000 +setopt HIST_IGNORE_ALL_DUPS # Remove older duplicate entries +setopt HIST_FIND_NO_DUPS # Don't show duplicates when searching +setopt SHARE_HISTORY # Share history across terminals +setopt INC_APPEND_HISTORY # Add commands immediately + +# ---------------------------------------- +# Shell options +# ---------------------------------------- +setopt AUTO_CD # Type directory name to cd into it +setopt AUTO_PUSHD # cd pushes to directory stack +setopt PUSHD_IGNORE_DUPS # Don't push duplicates +setopt CORRECT # Spelling correction for commands + +# ---------------------------------------- +# Key bindings +# ---------------------------------------- +# Up/down arrow searches history with current prefix +bindkey '^[[A' history-substring-search-up +bindkey '^[[B' history-substring-search-down + +# fzf keybindings (Ctrl+R history, Ctrl+T files, Alt+C cd) +source /usr/share/doc/fzf/examples/key-bindings.zsh + +# ---------------------------------------- +# Environment +# ---------------------------------------- +export PATH="$HOME/.local/bin:$PATH" +export EDITOR="fresh" +export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border' + +# Plannotator (devcontainer mode on port 8999) +export PLANNOTATOR_REMOTE=1 +export PLANNOTATOR_PORT=8999 + +# ---------------------------------------- +# Tool integrations +# ---------------------------------------- +eval "$(zoxide init zsh)" +eval "$(starship init zsh)" + +# ---------------------------------------- +# Aliases +# ---------------------------------------- +alias lg="lazygit" +alias y="yazi" +alias f="fresh" +alias e='file=$(fd --type f --hidden --no-ignore --follow --exclude .git | fzf) && [ -n "$file" ] && fresh "$file"' +alias yf='d=$(fd --type d --hidden --no-ignore --follow --exclude .git | fzf) && [ -n "$d" ] && yazi "$d"' +alias cat="bat --paging=never" +alias l="eza -la --icons" +alias br='fzf --preview "bat --color=always {}"' +alias gp="git push" + +dir() { eza --tree --level="${1:-1}" --icons; } + +# ---------------------------------------- +# Help command for aliases +# ---------------------------------------- +ha() { + echo " + Navigation & Files + l List files (eza -la --icons) + dir [N] Tree view, depth N (default: 1) + br Browse files with preview (fzf + bat) + e Fuzzy find + open in editor + y Yazi file manager + yf Fuzzy find folder + open in yazi + + Tools + lg Lazygit + f Fresh editor + cat Bat (syntax highlighting) + + Keybindings + Ctrl+R Fuzzy search command history + Ctrl+T Fuzzy find file, insert path + Alt+C Fuzzy find directory and cd + Up/Down Search history with current prefix + + Productivity + ccm Claude commit message +" +} + +EOF + +# ============================================ +# Custom scripts +# ============================================ +COPY scripts/ccm /usr/local/bin/ccm +COPY scripts/entrypoint.sh /entrypoint.sh +RUN chmod +x /usr/local/bin/ccm /entrypoint.sh + +# ============================================ +# Set zsh as default shell +# ============================================ +RUN chsh -s $(which zsh) + +WORKDIR /workspace +SHELL ["/bin/zsh", "-c"] + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/user-toolkits/alapenna-container/README.md b/user-toolkits/alapenna-container/README.md new file mode 100644 index 0000000..5f480e4 --- /dev/null +++ b/user-toolkits/alapenna-container/README.md @@ -0,0 +1,147 @@ +# alapenna-container toolkit + +A development environment using Apple's native container CLI on macOS 26+. + +## Why Apple Container? + +| Feature | Docker/OrbStack | Apple Container | +|---------|-----------------|-----------------| +| Architecture | Shared Linux VM | VM-per-container | +| Isolation | Shared kernel | Full VM isolation | +| Host mounts | OrbStack auto-mounts /Users | Explicit only | +| Startup | ~2 sec | ~0.7 sec | +| Maintained by | Docker Inc / OrbStack | Apple | + +Each container runs in its own lightweight VM, providing true isolation without shared attack surfaces. + +## Prerequisites + +- macOS 26 (Tahoe) or later +- Apple Silicon Mac + +## Installing Apple Container CLI + +Follow the official installation guide: [apple/container - Install or Upgrade](https://github.com/apple/container?tab=readme-ov-file#install-or-upgrade) + +Verify installation: + +```bash +container --version +container system status +``` + +## Quick Start + +```bash +# 1. Build the image +cd user-toolkits/alapenna-container +devbox-apple build + +# 2. Start and enter the container +devbox-apple + +# 3. You're in! Multiple terminals can connect +``` + +## Install devbox-apple script + +```bash +cp devbox-apple ~/.local/bin/ +chmod +x ~/.local/bin/devbox-apple +``` + +## Commands + +| Command | Description | +|---------|-------------| +| `devbox-apple` | Enter container (creates if needed) | +| `devbox-apple stop` | Stop the container | +| `devbox-apple destroy` | Remove the container | +| `devbox-apple status` | Show container/image status | +| `devbox-apple build` | Build the image from Dockerfile | +| `devbox-apple logs` | Show container logs | + +## Directory Mounts + +| Host | Container | Mode | +|------|-----------|------| +| `~/workspaces/toolkit-workspace` | `/workspace` | read-write | +| `~/.ssh` | `/root/.ssh` | copied | +| `~/.gnupg` | `/root/.gnupg` | copied | +| `~/tmp/dev-toolkit` | `/share-tmp` | read-write | + +Note: SSH and GPG directories are copied into the container on first creation, not live-mounted from the host. + +Edit `devbox-apple` script to customize mount paths. + +## Included Tools + +- **Languages**: Go, Node.js, Yarn (from base image) +- **Git**: lazygit, delta, gh +- **Terminal**: zsh, starship, fzf, ripgrep, fd, bat, eza +- **Files**: yazi, zoxide, glow +- **Editor**: fresh +- **AI**: Claude Code with plugins (claude-hud, plannotator) +- **Scripts**: ccm (Claude commit message generator) + +## Port Forwarding + +| Port | Service | +|------|---------| +| 6443 | Kubernetes API | +| 8999 | Plannotator | +| 9000 | Portainer HTTP | +| 9443 | Portainer HTTPS | + +## Isolation Benefits + +Unlike OrbStack which [auto-mounts /Users and /Volumes](https://github.com/orbstack/orbstack/issues/169), Apple container only mounts what you explicitly configure. This makes it suitable for running untrusted code (like AI agents) in a sandboxed environment. + +## Known Limitations + +- **No snapshots yet**: The `container` CLI doesn't expose VM snapshot/restore (though Virtualization.framework supports it) +- **Pre-1.0**: API may change between versions +- **Image unpacking**: Can be slow for large images + +## Future Enhancements + +Potential wrapper features to build: +- Snapshot/restore via Swift (using Virtualization.framework APIs) +- Multiple named containers +- Container profiles (different resource allocations) + +## Troubleshooting + +### Container won't start + +```bash +# Check system status +container system status + +# View logs +container system logs +``` + +### Image not found + +```bash +# Build locally +devbox-apple build + +# Or pull from registry (if published) +container image pull your-registry/devbox:latest +``` + +### Permission errors on mounts + +Ensure the mount source directories exist and are readable: + +```bash +mkdir -p ~/workspaces/toolkit-workspace ~/tmp/dev-toolkit +``` + +## References + +- [apple/container](https://github.com/apple/container) - CLI tool +- [apple/containerization](https://github.com/apple/containerization) - Swift framework +- [Virtualization.framework](https://developer.apple.com/documentation/virtualization) - Apple docs diff --git a/user-toolkits/alapenna-container/devbox-apple b/user-toolkits/alapenna-container/devbox-apple new file mode 100755 index 0000000..1d8ce30 --- /dev/null +++ b/user-toolkits/alapenna-container/devbox-apple @@ -0,0 +1,235 @@ +#!/usr/bin/env zsh +# devbox-apple - Container lifecycle manager using Apple container CLI +# +# Requires: macOS 26+ with Apple container CLI +# +# Install: +# cp devbox-apple ~/.local/bin/ +# chmod +x ~/.local/bin/devbox-apple +# +# Usage: +# devbox-apple # Enter the container (creates/starts if needed) +# devbox-apple stop # Stop the container +# devbox-apple destroy # Remove container +# devbox-apple status # Show container status +# devbox-apple build # Build the image +# devbox-apple logs # Show container logs + +set -e + +NAME="devbox-apple" +IMAGE="devbox-apple:latest" + +# Paths (adjust to your setup) +WORKSPACE_DIR="$HOME/workspaces/toolkit-workspace" +SHARE_TMP="$HOME/tmp/dev-toolkit" + +# Source directories for initial copy (not mounted) +SSH_DIR="$HOME/.ssh" +GNUPG_DIR="$HOME/.gnupg" + +check_container_cli() { + if ! command -v container &>/dev/null; then + echo "Apple container CLI not found." + echo "Requires macOS 26+ with container CLI installed." + echo "See: https://github.com/apple/container" + exit 1 + fi +} + +# Copy SSH and GPG keys into container (one-time setup) +setup_credentials() { + echo "Copying SSH keys..." + # Create directories + container exec "$NAME" mkdir -p /root/.ssh /root/.gnupg + + # Copy SSH files (if they exist) + if [[ -d "$SSH_DIR" ]]; then + for f in "$SSH_DIR"/*; do + [[ -f "$f" ]] && cat "$f" | container exec -i "$NAME" tee "/root/.ssh/$(basename "$f")" > /dev/null + done + container exec "$NAME" chmod 700 /root/.ssh + container exec "$NAME" chmod 600 /root/.ssh/* 2>/dev/null || true + fi + + # Copy GPG files (if they exist) + if [[ -d "$GNUPG_DIR" ]]; then + echo "Copying GPG keys..." + for f in "$GNUPG_DIR"/*; do + [[ -f "$f" ]] && cat "$f" | container exec -i "$NAME" tee "/root/.gnupg/$(basename "$f")" > /dev/null + done + container exec "$NAME" chmod 700 /root/.gnupg + container exec "$NAME" chmod 600 /root/.gnupg/* 2>/dev/null || true + fi + + echo "Credentials copied." +} + +cmd_enter() { + check_container_cli + + # Check if container exists and is running + if container list 2>/dev/null | grep -q "^$NAME"; then + # Container exists, exec into it + exec container exec -ti "$NAME" /bin/zsh -l + fi + + # Container doesn't exist, create and start it + echo "Creating container..." + + # Ensure directories exist + mkdir -p "$WORKSPACE_DIR" "$SHARE_TMP" + + # Start container in detached mode + # Note: Apple container uses FIXED allocation per VM (not shared like OrbStack) + # Default is 1GB RAM / 4 CPUs - too low for dev work, so we override. + container run \ + --name "$NAME" \ + --detach \ + --memory 8g \ + --cpus 4 \ + --publish 6443-9999:6443-9999 \ + --volume "$WORKSPACE_DIR:/workspace" \ + --volume "$SHARE_TMP:/share-tmp" \ + --volume "/var/run/docker.sock:/var/run/docker.sock" \ + --ssh \ + "$IMAGE" + + # Wait for container to be ready + sleep 2 + + # Copy credentials on first creation + setup_credentials + + # Enter the container + exec container exec -ti "$NAME" /bin/zsh -l +} + +cmd_stop() { + check_container_cli + + if container list 2>/dev/null | grep -q "^$NAME"; then + echo "Stopping container..." + container stop "$NAME" + echo "Container stopped." + else + echo "Container not running." + fi +} + +cmd_destroy() { + check_container_cli + + if container list --all 2>/dev/null | grep -q "^$NAME"; then + echo "Removing container..." + container delete "$NAME" + echo "Container removed." + else + echo "Container does not exist." + fi +} + +cmd_status() { + check_container_cli + + echo "Container CLI: installed" + echo "" + + if container list 2>/dev/null | grep -q "^$NAME"; then + echo "Container: running" + container inspect "$NAME" 2>/dev/null | head -20 + elif container list --all 2>/dev/null | grep -q "^$NAME"; then + echo "Container: stopped" + else + echo "Container: not created" + fi + + echo "" + echo "Image:" + container image list 2>/dev/null | grep "$IMAGE" || echo " (not built)" +} + +cmd_build() { + check_container_cli + + echo "Building image..." + + # Find the Dockerfile location + SCRIPT_DIR="$(dirname "$0")" + if [[ -f "$SCRIPT_DIR/Dockerfile" ]]; then + BUILD_DIR="$SCRIPT_DIR" + elif [[ -f "./Dockerfile" ]]; then + BUILD_DIR="." + else + echo "Dockerfile not found. Run from the toolkit directory." + exit 1 + fi + + container build --tag "$IMAGE" "$BUILD_DIR" + echo "" + echo "Image built: $IMAGE" +} + +cmd_logs() { + check_container_cli + + if container list --all 2>/dev/null | grep -q "^$NAME"; then + container logs "$NAME" + else + echo "Container does not exist." + fi +} + +cmd_help() { + echo "devbox-apple - Container lifecycle manager (Apple container)" + echo "" + echo "Usage: devbox-apple [command]" + echo "" + echo "Commands:" + echo " (none) Enter the container (creates if needed)" + echo " stop Stop the container" + echo " destroy Remove the container" + echo " status Show container and image status" + echo " build Build the container image" + echo " logs Show container logs" + echo " help Show this help" + echo "" + echo "Mounted:" + echo " $WORKSPACE_DIR -> /workspace" + echo " $SHARE_TMP -> /share-tmp" + echo " /var/run/docker.sock (for docker CLI -> OrbStack)" + echo "" + echo "Ports:" + echo " 6443-9999 (mapped 1:1)" + echo "" + echo "Note: SSH/GPG keys are copied on first creation (not mounted)" +} + +case "${1:-}" in + ""|enter) + cmd_enter + ;; + stop) + cmd_stop + ;; + destroy) + cmd_destroy + ;; + status) + cmd_status + ;; + build) + cmd_build + ;; + logs) + cmd_logs + ;; + help|--help|-h) + cmd_help + ;; + *) + echo "Unknown command: $1" + cmd_help + exit 1 + ;; +esac diff --git a/user-toolkits/alapenna-container/scripts/ccm b/user-toolkits/alapenna-container/scripts/ccm new file mode 100755 index 0000000..c7572b3 --- /dev/null +++ b/user-toolkits/alapenna-container/scripts/ccm @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# ccm - Claude commit message generator +# Stages all changes, then generates a commit message using Claude + +set -e + +# Check for claude CLI +if ! command -v claude &>/dev/null; then + echo "Error: claude CLI not found. Install from https://claude.ai/code" + exit 1 +fi + +# Max diff size in bytes before falling back to manual input +MAX_DIFF_SIZE=${CCM_MAX_DIFF_SIZE:-300000} + +# Check for any changes (staged or unstaged) +if [[ -z "$(git status --porcelain)" ]]; then + echo "No changes to commit." + exit 1 +fi + +# Stage everything +git add -A + +diff=$(git diff --cached) +diff_size=${#diff} + +prompt='Write a commit message for this diff following this exact format: + + + +- +- +- ... + +Example: +Update UI design spec with Step 6 implementation + +- Rename "Review & Deploy" to "Review & Install" throughout +- Document actual implementation details +- Add slide-out drawer specification + +Rules: +- Subject line: imperative mood ("Add X" not "Added X"), max 72 chars +- Body: 3-8 bullet points, each starting with a verb (Add, Fix, Update, Remove, Refactor, Extract, etc.) +- No trailing periods on bullets +- Output ONLY the message, no markdown fences or extra text' + +# Check if diff exceeds size limit +if [[ $diff_size -gt $MAX_DIFF_SIZE ]]; then + echo "Diff too large for Claude ($(numfmt --to=iec $diff_size) > $(numfmt --to=iec $MAX_DIFF_SIZE))" + echo "" + git diff --cached --stat + echo "" + read -p "Enter commit message (or 'q' to abort): " manual_msg + if [[ "$manual_msg" == "q" || -z "$manual_msg" ]]; then + echo "Aborted (changes remain staged)." + exit 0 + fi + msg="$manual_msg" + echo "" + read -p "Commit? [Y/n/e/u] " choice +else + msg=$(echo "$diff" | claude -p "$prompt") + echo -e "\nSuggested:\n$msg\n" + read -p "Commit? [Y/n/e/u] " choice +fi + +prompt_push() { + read -p "Push? [Y/n] " push_choice + case "${push_choice:-y}" in + n|N) echo "Skipped push." ;; + *) git push ;; + esac +} + +case "${choice:-y}" in + n|N) echo "Aborted (changes remain staged)." ;; + e|E) git commit -e -m "$msg" && prompt_push ;; + u|U) git reset HEAD >/dev/null; echo "Unstaged all changes." ;; + *) git commit -m "$msg" && prompt_push ;; +esac diff --git a/user-toolkits/alapenna-container/scripts/entrypoint.sh b/user-toolkits/alapenna-container/scripts/entrypoint.sh new file mode 100755 index 0000000..5cb7206 --- /dev/null +++ b/user-toolkits/alapenna-container/scripts/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Container entrypoint - runs on every container start + +# Clean tmp files older than 8 hours to prevent disk bloat from builds +find /tmp -mindepth 1 -mmin +480 -exec rm -rf {} \; 2>/dev/null || true + +# Keep container running +exec sleep infinity diff --git a/user-toolkits/alapenna-ghostty/Dockerfile b/user-toolkits/alapenna-ghostty/Dockerfile index 4a38ab0..85dda02 100644 --- a/user-toolkits/alapenna-ghostty/Dockerfile +++ b/user-toolkits/alapenna-ghostty/Dockerfile @@ -191,7 +191,13 @@ RUN cat > /root/.claude/settings.json <<'EOF' "claude-hud@claude-hud": true, "plannotator@plannotator": true }, - "alwaysThinkingEnabled": true + "alwaysThinkingEnabled": true, + "permissions": { + "deny": [ + "Read(**/.env*)", + "Read(~/.ssh/**)" + ] + } } EOF @@ -205,8 +211,7 @@ RUN git config --global core.pager delta \ && git config --global delta.line-numbers true \ && git config --global merge.conflictstyle diff3 \ && git config --global diff.colorMoved default \ - && git config --global push.autoSetupRemote true \ - && mkdir -p /root/.config/git && echo ".todo.md" >> /root/.config/git/ignore + && git config --global push.autoSetupRemote true # ============================================ # Shell configuration (.zshrc) @@ -305,24 +310,10 @@ ha() { Up/Down Search history with current prefix Productivity - todo View .todo.md - todo -e Edit .todo.md ccm Claude commit message " } -# ---------------------------------------- -# Local todo management (git-ignored) -# ---------------------------------------- -todo() { - if [[ "$1" == "-e" ]]; then - fresh .todo.md - elif [[ -f .todo.md ]]; then - glow .todo.md - else - echo "No .todo.md found. Use 'todo -e' to create one." - fi -} EOF # ============================================