Initial commit

This commit is contained in:
Mikael Johansson 2026-05-20 20:42:39 +02:00
commit c878e5e35f
22 changed files with 1695 additions and 0 deletions

92
.gitignore vendored Normal file
View File

@ -0,0 +1,92 @@
# Dotfiles gitignore
# Comprehensive ignore patterns for development environments
# ================================
# Build artifacts and caches
# ================================
**/build/
!src/**/build/
**/dist/
**/node_modules/
**/__pycache__/
**/*.pyc
**/*.pyo
**/.cache/
# ================================
# IDE and Editor files
# ================================
# Eclipse
.project
.classpath
.metadata/
.settings/
bin/
# IntelliJ IDEA
.idea/
**/*.iml
**/*.iws
# VS Code
.vscode/
**/*.code-workspace
# Vim
**/*.swp
**/*.swo
**/*~
# Emacs
**/*~
**/#*#
**/.#*
# ================================
# OS generated files
# ================================
.DS_Store
.DS_Store?
**/.DS_Store
**/Thumbs.db
**/desktop.ini
# ================================
# Temporary and log files
# ================================
**/*.log
**/*.tmp
**/*.temp
**/*.bak
**/*.backup
**/*.orig
# ================================
# Security and credentials
# ================================
**/.netrc
**/id_rsa
**/id_rsa_*
**/id_rsa.pub
**/known_hosts
**/*.pem
**/*.key
**/*.p12
**/*.pfx
# ================================
# Large binary files
# ================================
**/*.iso
**/*.dmg
**/*.exe
**/*.msi
**/*.deb
**/*.rpm
# ================================
# Backup files created by scripts
# ================================
.dotfiles-backup-*/
**/*.backup
.gitignore.backup

53
AGENTS.md Normal file
View File

@ -0,0 +1,53 @@
# dotfiles repo agent guide
This repo is a collection of personal config files deployed via GNU Stow.
## How it works
- Each subdirectory is a **stow package**: `bash`, `zsh`, `git`, `ghostty`, `starship`, `tmux`, `lazygit`, `zed`, `opencode`.
- Directory structure mirrors `$HOME` (e.g. `git/.gitconfig``~/.gitconfig`).
## Commands
```bash
# Deploy all packages (auto-installs stow via apt if missing)
./setup.sh
# Selective install
./setup.sh --only=git,zsh
# Preview without changes
./setup.sh --dry-run
# Remove symlinks
./uninstall.sh
./uninstall.sh --only=git # selective removal
```
All scripts accept `--verbose`, `--quiet`, `--dry-run`.
## Adding a new package
```bash
mkdir <name>
cp ~/.<configfile> <name>/
./setup.sh --only=<name>
```
## Toolchain auto-installed by setup.sh
`setup.sh` installs these tools to `~/.local/bin` if not already present — do not add manual install steps for them: **zinit** (zsh plugin manager), **zoxide** (smart cd), **fzf**, **bat**, **eza**, **delta**, **tmux**, **lazygit**, **ripgrep**.
## Theming convention
All tools are themed with **Gruvbox Dark Hard**. When adding config for a new tool, apply Gruvbox colors to keep the environment cohesive.
## Important constraints
- Default shell is **zsh** (`bash/.bashrc` does NOT exec zsh — set your default shell with `chsh`). Uses **zinit** as plugin manager (turbo mode for async loading).
- `.bashrc` sources `~/.bash_aliases`; `.zshrc` sources `~/.zsh_aliases`.
- **zoxide** replaces `cd` for directory jumping (frecency-based, auto-tracked via chpwd hook). `j` alias for `zoxide cd`.
- **Sensitive files are gitignored**: `.netrc`, SSH keys (`id_rsa*`, `known_hosts`), gradle caches.
- The `opencode/` package stows OpenCode's own config (`~/.config/opencode/`). Do not treat it as a repo instruction file — that file is `AGENTS.md` at the repo root.
- No build, test, lint, or typecheck steps — this is pure config.
- Backups of pre-existing real files are created at `~/.dotfiles-backup-YYYYMMDD-HHMMSS` before stowing.

55
README.md Normal file
View File

@ -0,0 +1,55 @@
# Dotfiles
Personal dotfiles managed with GNU Stow. Themed with **Gruvbox Dark Hard**.
## Packages
| Package | Description |
|---------|-------------|
| `bash` | Bash configuration |
| `zsh` | Zsh configuration with zinit plugin manager |
| `git` | Git configuration with delta pager |
| `ghostty` | Ghostty terminal config |
| `starship` | Cross-shell prompt |
| `tmux` | Terminal multiplexer config |
| `lazygit` | Git TUI config |
| `zed` | Zed editor config |
| `opencode` | OpenCode AI config |
## Quick Start
```bash
# Deploy all packages
./setup.sh
# Selective install
./setup.sh --only=git,zsh
# Preview without changes
./setup.sh --dry-run
# Remove symlinks
./uninstall.sh
./uninstall.sh --only=git
```
## Toolchain
`setup.sh` auto-installs these tools to `~/.local/bin` if missing:
- **zinit** (zsh plugin manager)
- **zoxide** (smart cd)
- **fzf** (fuzzy finder)
- **bat** (syntax-highlighted cat)
- **eza** (modern ls)
- **delta** (better git diff)
- **tmux** (terminal multiplexer)
- **lazygit** (git TUI)
- **ripgrep** (fast grep)
## Theming
All tools are themed with **Gruvbox Dark Hard** for a cohesive appearance.
## SSH & Netrc
SSH config and `.netrc` are **not** included in this repo. Manage those separately for security.

132
bash/.bashrc Normal file
View File

@ -0,0 +1,132 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
export PATH=/home/mikjoh/.local/bin:/usr/local/go/bin:$PATH
. "$HOME/.atuin/bin/env"
[[ -f ~/.bash-preexec.sh ]] && source ~/.bash-preexec.sh
eval "$(atuin init bash)"
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
# bun
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"

View File

@ -0,0 +1,8 @@
theme = GruvboxDarkHard
font-family = JetBrainsMono Nerd Font
font-size = 10
window-padding-x = 8
window-padding-y = 8
scrollback-limit = 10000
shell-integration = zsh

66
git/.gitconfig Normal file
View File

@ -0,0 +1,66 @@
# This is Git's per-user configuration file.
[user]
name = Mikael Johansson
email = mikael@thematrix.se
[core]
editor = nano
pager = delta
[interactive]
diffFilter = delta --color-only
[delta]
side-by-side = true
line-numbers = true
navigate = true
syntax-theme = "Gruvbox Dark"
plus-color = "#b8bb26"
minus-color = "#fb4934"
[init]
defaultBranch = main
[safe]
directory = /tank/docker
[push]
default = simple
autoSetupRemote = true
[pull]
rebase = true
[fetch]
prune = true
[rebase]
autoSquash = true
autoStash = true
[rerere]
enabled = true
[diff]
colorMoved = default
[branch]
sort = -committerdate
[alias]
st = status --short --branch
ci = commit
co = checkout
br = branch
lg = log --graph --pretty=format:'%C(auto)%h%d %s%n %C(yellow)(%ar) by %an%Creset%n %C(white)%Creset' --all
ll = lg --stat
ls = lg --simplify-by-decoration
undo = reset HEAD~1 --mixed
amend = commit --amend --no-edit
wip = commit -m "WIP"
unstage = reset HEAD --
stage = add
last = log -1 HEAD --stat
diffs = diff --stat
fixup = commit --fixup
aliases = config --get-regexp alias

View File

@ -0,0 +1,23 @@
gui:
theme:
lightTheme: false
activeBorderColor:
- '#d3869b'
- bold
inactiveBorderColor:
- '#7c6f64'
searchingActiveBorderColor:
- '#fabd2f'
- bold
optionsTextColor:
- '#83a598'
selectedLineBgColor:
- '#3c3836'
cherryPickedCommitBgColor:
- '#d3869b'
cherryPickedCommitFgColor:
- '#1d2021'
unstagedChangesColor:
- '#fb4934'
defaultFgColor:
- '#ebdbb2'

View File

@ -0,0 +1,9 @@
# Claude-Mem Memory Context
<claude-mem-context>
# Memory Context from Past Sessions
*No context yet. Complete your first session and context will appear here.*
Use claude-mem search tools for manual memory queries.
</claude-mem-context>

View File

@ -0,0 +1,20 @@
---
description: High-level system design and architectural analysis.
mode: primary
model: ollama-local/qwen3.5:9b #anthropic/claude-3-5-sonnet
temperature: 0.2
tools:
write: true
edit: true
bash: true
permission:
edit: allow
bash:
"*": ask
"git status": allow
---
# System Architect Instructions
You are an expert software architect.
- Prioritize modularity and scalability in all suggestions.
- When proposing changes, always check existing architectural patterns in the codebase.
- Use the `bash` tool to verify dependency trees before suggesting new libraries.

View File

@ -0,0 +1,76 @@
---
description: AI assistant specialized in creating and maintaining API documentation, README files, and technical guides for the x-call project
mode: primary
model: "github-copilot/claude-sonnet-4.6"
temperature: 0.1
tools:
write: true
edit: true
bash: true
permission:
edit: allow
bash:
"*": ask
"git status": allow
---
You are an expert technical writer for this project.
## Persona
- You specialize in writing clear, developer-friendly documentation for APIs, SDKs, and internal services
- You understand the x-call codebase architecture and translate complex features into accessible guides
- Your output: API documentation, architecture diagrams, setup guides, and troubleshooting docs that developers can quickly understand and implement
## Project knowledge
- **Tech Stack:** Kotlin, Java, Gradle, Android, Markdown (for documentation), Mermaid (for creating diagrams)
- **Repository:** x-call (BMW Group internal telematics application)
- **File Structure:**
- `app/` Main Android application source code
- `app/src/main/` Core application code (Kotlin/Java)
- `app/src/test/` Unit tests
- `app/src/androidTest/` Android instrumentation tests
- `connection/` Connection modules (interfaces, joynr, mcp)
- `core/` Core library modules
- `remotediagnosis/` Remote diagnosis functionality
- `docs/` Project documentation
## Tools you can use
- **Build:** `./gradlew build` (compiles Kotlin/Java, runs tests)
- **Test:** `./gradlew test` (runs unit tests)
- **Android Test:** `./gradlew connectedAndroidTest` (runs instrumentation tests)
- **Lint:** `./gradlew lint` (checks code quality)
- **Clean:** `./gradlew clean` (removes build artifacts)
## Standards
Follow these rules for all documentation you write:
**Documentation style:**
- Use clear, concise language avoiding jargon where possible
- Include code examples with proper syntax highlighting
- Structure docs with headings, lists, and callout boxes
- Keep API docs consistent with project conventions
- Reference existing documentation in `docs/` and `README.md`
**Code examples in docs:**
```kotlin
// ✅ Good - clear intent, proper error handling
fun getUserById(id: String): User {
if (id.isBlank()) throw IllegalArgumentException("User ID required")
return userRepository.findById(id) ?: throw NotFoundException()
}
// ❌ Bad - unclear, missing validation
fun getUser(x) {
return userRepository.find(x)
}
```
**Naming conventions:**
- Functions: camelCase (`getUserData`, `fetchTelemetry`)
- Classes: PascalCase (`UserService`, `TelemetryController`)
- Constants: UPPER_SNAKE_CASE (`API_KEY`, `MAX_RETRIES`)
## Boundaries
- ✅ **Always:** Write to `docs/`, follow markdown best practices, keep docs in sync with code changes, reference CHANGELOG files
- ⚠️ **Ask first:** Major documentation restructuring, adding new doc sections, API contract changes, modifying README.md
- 🚫 **Never:** Commit internal URLs, remove existing documentation without approval, modify source code logic, edit `build/`

View File

@ -0,0 +1,25 @@
---
description: Reviews code for quality and best practices
mode: subagent
model: ollama-local/qwen3.5:9b #openai/gpt-5.3-codex
temperature: 0.1
tools:
write: false
edit: false
bash: true
permission:
bash:
"git diff": allow
"grep *": allow
"*": deny
---
# Code Reviewer Instructions
You are a meticulous code reviewer agent that provides constructive feedback on code changes.
- Code quality and best practices
- Potential bugs and edge cases
- Performance issues
- Security considerations and vunerabilities
- Logic errors and code readability
- Do NOT make direct edits to files.
- Provide feedback in a structured bulleted list.

View File

@ -0,0 +1,18 @@
{
"$schema": "https://opencode.ai/config.json",
"plugin": [],
"provider": {
"ollama-local": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama Local",
"options": {
"baseURL": "http://localhost:11434/v1"
},
"models": {
"qwen3.5:9b": {
"name": "qwen3.5:9b"
}
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
---
name: python-runner
description: |-
Expert in creating, documenting, and executing standalone Python 3 scripts.
Use this skill when the user asks to "automate a task," "process a CSV," or "run a local script."
Examples:
- user: "Write a script to clean this data" -> Create script with logging and error handling.
- user: "How do I run this?" -> Provide the exact 'python3 main.py' command.
---
# Python Runner Instructions
When this skill is active, follow these rules:
- **Environment**: Always assume Python 3.10+ is available.
- **Documentation**: Every script must include a docstring at the top explaining its purpose.
- **Execution**: Provide the terminal command to run the script immediately after writing the code.
- **Safety**: Do not use `os.system()` for user-provided strings; use `subprocess` instead.

View File

@ -0,0 +1,4 @@
{
"$schema": "https://opencode.ai/tui.json",
"theme": "dracula"
}

438
setup.sh Executable file
View File

@ -0,0 +1,438 @@
#!/bin/bash
# Dotfiles Setup Script
# Description: Install dotfiles using GNU Stow with backup and selective installation
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKUP_DIR="$HOME/.dotfiles-backup-$(date +%Y%m%d-%H%M%S)"
AVAILABLE_PACKAGES=(bash zsh git ghostty starship tmux lazygit zed opencode)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
show_help() {
cat << EOF
Dotfiles Setup Script
USAGE:
$0 [OPTIONS]
OPTIONS:
-h, --help Show this help message
-d, --dry-run Show what would be done without making changes
-v, --verbose Enable verbose output
-q, --quiet Suppress non-essential output
--only=PACKAGES Install only specified packages (comma-separated)
Available: ${AVAILABLE_PACKAGES[*]}
--backup-dir=DIR Custom backup directory (default: ~/.dotfiles-backup-TIMESTAMP)
EXAMPLES:
$0 # Install all configurations
$0 --only=git,zsh # Install only git and zsh configurations
$0 --dry-run # Preview what would be installed
$0 --verbose # Install with detailed output
AVAILABLE PACKAGES:
EOF
for package in "${AVAILABLE_PACKAGES[@]}"; do
echo " - $package"
done
}
check_dependencies() {
log_info "Checking dependencies..."
if ! command -v stow &> /dev/null; then
log_error "GNU Stow is not installed"
log_info "Installing GNU Stow..."
if command -v apt-get &> /dev/null; then
sudo apt-get update && sudo apt-get install -y stow
else
log_error "Cannot install stow automatically. Please install it manually."
exit 1
fi
fi
log_success "All dependencies satisfied"
}
install_toolchain() {
local bin_dir="$HOME/.local/bin"
mkdir -p "$bin_dir"
# Install zinit (zsh plugin manager) if missing
if [[ ! -f "$HOME/.local/share/zinit/zinit.git/zinit.zsh" ]]; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install zinit"
else
log_info "Installing zinit..."
command mkdir -p "$HOME/.local/share/zinit" && command chmod g-rwX "$HOME/.local/share/zinit"
command git clone https://github.com/zdharma-continuum/zinit "$HOME/.local/share/zinit/zinit.git"
log_success "zinit installed"
fi
fi
# Install zoxide (smart cd) if missing
if ! command -v zoxide &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install zoxide"
else
log_info "Installing zoxide..."
curl -sS https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | bash
log_success "zoxide installed"
fi
fi
# Install fzf (fuzzy finder) if missing
if ! command -v fzf &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install fzf"
else
log_info "Installing fzf..."
if [[ -d "$HOME/.fzf" ]]; then
"$HOME/.fzf/install" --bin
else
git clone --depth 1 https://github.com/junegunn/fzf.git "$HOME/.fzf"
"$HOME/.fzf/install" --bin
fi
ln -sf "$HOME/.fzf/bin/fzf" "$bin_dir/fzf"
log_success "fzf installed"
fi
fi
# Install bat (syntax-highlighted cat) if missing
if ! command -v bat &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install bat"
else
log_info "Installing bat..."
local bat_ver
bat_ver=$(curl -s https://api.github.com/repos/sharkdp/bat/releases/latest | grep -oP '"tag_name": "\K[^"]+')
curl -sL "https://github.com/sharkdp/bat/releases/download/${bat_ver}/bat-${bat_ver}-x86_64-unknown-linux-gnu.tar.gz" -o /tmp/bat.tar.gz
tar xzf /tmp/bat.tar.gz -C /tmp/
cp "/tmp/bat-${bat_ver}-x86_64-unknown-linux-gnu/bat" "$bin_dir/"
log_success "bat installed"
fi
fi
# Install eza (modern ls) if missing
if ! command -v eza &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install eza"
else
log_info "Installing eza..."
curl -sL "https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz" -o /tmp/eza.tar.gz
tar xzf /tmp/eza.tar.gz -C /tmp/
cp /tmp/eza "$bin_dir/"
log_success "eza installed"
fi
fi
# Install delta (better git diff) if missing
if ! command -v delta &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install delta"
else
log_info "Installing delta..."
local delta_ver
delta_ver=$(curl -s https://api.github.com/repos/dandavison/delta/releases/latest | grep -oP '"tag_name": "\K[^"]+')
curl -sL "https://github.com/dandavison/delta/releases/download/${delta_ver}/delta-${delta_ver}-x86_64-unknown-linux-gnu.tar.gz" -o /tmp/delta.tar.gz
tar xzf /tmp/delta.tar.gz -C /tmp/
cp "/tmp/delta-${delta_ver}-x86_64-unknown-linux-gnu/delta" "$bin_dir/"
log_success "delta installed"
fi
fi
# Install tmux (terminal multiplexer) if missing
if ! command -v tmux &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install tmux"
else
log_info "Installing tmux..."
if command -v apt-get &> /dev/null; then
sudo apt-get install -y tmux 2>/dev/null || true
fi
if ! command -v tmux &> /dev/null; then
curl -sL "https://github.com/nelsonenzo/tmux-appimage/releases/download/3.5a/tmux.appimage" -o "$bin_dir/tmux"
chmod +x "$bin_dir/tmux"
fi
if infocmp xterm-ghostty &>/dev/null; then
mkdir -p "$HOME/.terminfo"
infocmp -x xterm-ghostty | tic -o "$HOME/.terminfo" -x - &>/dev/null || true
fi
log_success "tmux installed"
fi
fi
# Install lazygit (TUI for git) if missing
if ! command -v lazygit &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install lazygit"
else
log_info "Installing lazygit..."
local lg_ver
lg_ver=$(curl -s https://api.github.com/repos/jesseduffield/lazygit/releases/latest | grep -oP '"tag_name": "\K[^"]+')
curl -sL "https://github.com/jesseduffield/lazygit/releases/download/${lg_ver}/lazygit_${lg_ver#v}_Linux_x86_64.tar.gz" -o /tmp/lazygit.tar.gz
tar xzf /tmp/lazygit.tar.gz -C /tmp/
cp /tmp/lazygit "$bin_dir/"
log_success "lazygit installed"
fi
fi
# Install ripgrep (fast grep alternative) if missing
if ! command -v rg &> /dev/null; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would install ripgrep"
else
log_info "Installing ripgrep..."
local rg_ver
rg_ver=$(curl -s https://api.github.com/repos/BurntSushi/ripgrep/releases/latest | grep -oP '"tag_name": "\K[^"]+')
curl -sL "https://github.com/BurntSushi/ripgrep/releases/download/${rg_ver}/ripgrep-${rg_ver}-x86_64-unknown-linux-musl.tar.gz" -o /tmp/ripgrep.tar.gz
tar xzf /tmp/ripgrep.tar.gz -C /tmp/
cp "/tmp/ripgrep-${rg_ver}-x86_64-unknown-linux-musl/rg" "$bin_dir/"
log_success "ripgrep installed"
fi
fi
}
declare -A BACKUP_PATTERNS
BACKUP_PATTERNS[ssh]=".ssh/config .ssh/known_hosts"
should_backup_file() {
local package=$1
local relative_path=$2
if [[ -n "${BACKUP_PATTERNS[$package]:-}" ]]; then
local patterns="${BACKUP_PATTERNS[$package]}"
for pattern in $patterns; do
if [[ "$relative_path" == "$pattern" ]]; then
return 0
fi
done
return 1
fi
case "$relative_path" in
*.gradle/caches/* | *.gradle/daemon/* | *.gradle/wrapper/* | \
*.gradle/.tmp/* | *.gradle/native/* | *.gradle/jdks/* | \
*.gradle/notifications/* | *.gradle/workers/* | \
*.gradle/kotlin-profile/* | *.gradle/android/* | \
*.gradle/*.lock | *.cache/* | */.DS_Store | \
*/node_modules/* | */build/* | */dist/* | \
*/__pycache__/* | *.pyc | *.pyo | \
*.log | *.tmp | *.temp)
return 1
;;
*)
return 0
;;
esac
}
backup_existing() {
local package=$1
local files_backed_up=0
local files_skipped=0
if [[ "$VERBOSE" == "true" ]]; then
log_info "Checking for existing files for package: $package"
fi
while IFS= read -r -d '' file; do
local relative_path="${file#$SCRIPT_DIR/$package/}"
local target_path="$HOME/$relative_path"
if [[ -e "$target_path" ]] && [[ ! -L "$target_path" ]]; then
if should_backup_file "$package" "$relative_path"; then
if [[ "$files_backed_up" -eq 0 ]]; then
mkdir -p "$BACKUP_DIR"
log_info "Backing up existing files to $BACKUP_DIR"
fi
local backup_path="$BACKUP_DIR/$relative_path"
mkdir -p "$(dirname "$backup_path")"
cp -r "$target_path" "$backup_path"
if [[ "$VERBOSE" == "true" ]]; then
log_info "Backed up: $target_path -> $backup_path"
fi
((files_backed_up++))
else
if [[ "$VERBOSE" == "true" ]]; then
log_info "Skipped backup (generated/cache): $target_path"
fi
((files_skipped++))
fi
fi
done < <(find "$SCRIPT_DIR/$package" -type f -print0 2>/dev/null || true)
if [[ "$files_backed_up" -gt 0 ]]; then
log_success "Backed up $files_backed_up files for $package"
fi
if [[ "$files_skipped" -gt 0 ]] && [[ "$VERBOSE" == "true" ]]; then
log_info "Skipped $files_skipped generated/cache files"
fi
}
clean_generated_files() {
local package=$1
log_info "Cleaning generated files for $package..."
case "$package" in
ssh)
find "$SCRIPT_DIR/$package/.ssh" -name "*.backup" -o -name "*.tmp" -o -name "*.old" \
-exec rm -f {} + 2>/dev/null || true
;;
*)
find "$SCRIPT_DIR/$package" -name "*.log" -o -name "*.tmp" -o -name "*.backup" \
-exec rm -f {} + 2>/dev/null || true
;;
esac
}
install_package() {
local package=$1
if [[ ! -d "$SCRIPT_DIR/$package" ]]; then
log_error "Package '$package' not found in $SCRIPT_DIR"
return 1
fi
log_info "Installing $package configuration..."
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would clean generated files for $package"
log_info "[DRY RUN] Would run: stow $package"
log_info "[DRY RUN] Would link files from $SCRIPT_DIR/$package to $HOME"
return 0
fi
clean_generated_files "$package"
backup_existing "$package"
if stow "$package"; then
log_success "Successfully installed $package"
else
log_error "Failed to install $package"
return 1
fi
}
# Parse command line arguments
DRY_RUN=false
VERBOSE=false
QUIET=false
SELECTED_PACKAGES=()
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-q|--quiet)
QUIET=true
shift
;;
--only=*)
IFS=',' read -ra SELECTED_PACKAGES <<< "${1#*=}"
shift
;;
--backup-dir=*)
BACKUP_DIR="${1#*=}"
shift
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
if [[ ${#SELECTED_PACKAGES[@]} -eq 0 ]]; then
PACKAGES_TO_INSTALL=("${AVAILABLE_PACKAGES[@]}")
else
PACKAGES_TO_INSTALL=("${SELECTED_PACKAGES[@]}")
for package in "${PACKAGES_TO_INSTALL[@]}"; do
if [[ ! " ${AVAILABLE_PACKAGES[*]} " =~ " ${package} " ]]; then
log_error "Unknown package: $package"
log_info "Available packages: ${AVAILABLE_PACKAGES[*]}"
exit 1
fi
done
fi
if [[ "$QUIET" == "true" ]]; then
exec > /dev/null 2>&1
fi
main() {
log_info "Starting dotfiles installation..."
if [[ "$DRY_RUN" == "true" ]]; then
log_info "DRY RUN MODE - No changes will be made"
fi
cd "$SCRIPT_DIR"
check_dependencies
install_toolchain
local failed_packages=()
for package in "${PACKAGES_TO_INSTALL[@]}"; do
if ! install_package "$package"; then
failed_packages+=("$package")
fi
done
echo
if [[ ${#failed_packages[@]} -eq 0 ]]; then
log_success "All packages installed successfully!"
if [[ -d "$BACKUP_DIR" ]]; then
log_info "Backup created at: $BACKUP_DIR"
fi
else
log_error "Some packages failed to install: ${failed_packages[*]}"
exit 1
fi
}
main

View File

@ -0,0 +1,148 @@
"$schema" = 'https://starship.rs/config-schema.json'
format = """
[](#d3869b)\
$username\
[](bg:#fb4934 fg:#d3869b)\
$directory\
[](fg:#fb4934 bg:#fe8019)\
$git_branch\
$git_status\
[](fg:#fe8019 bg:#8ec07c)\
$c\
$elixir\
$elm\
$golang\
$haskell\
$java\
$julia\
$maven\
$nodejs\
$bun\
$nim\
$rust\
$scala\
[](fg:#8ec07c bg:#b8bb26)\
$docker_context\
[](fg:#b8bb26 bg:#665c54)\
$time\
[ ](fg:#665c54)\
$cmd_duration\
"""
[username]
show_always = true
style_user = "fg:#1d2021 bg:#d3869b"
style_root = "fg:#1d2021 bg:#d3869b"
format = '[$user ]($style)'
disabled = false
[os]
style = "fg:#1d2021 bg:#d3869b"
disabled = true
[directory]
style = "fg:#1d2021 bg:#fb4934"
format = "[ $path ]($style)"
truncation_length = 3
truncation_symbol = "…/"
[directory.substitutions]
"Documents" = "󰈙 "
"Downloads" = " "
"Music" = " "
"Pictures" = " "
[c]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[cpp]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[docker_context]
symbol = " "
style = "fg:#1d2021 bg:#b8bb26"
format = '[ $symbol $context ]($style)'
[elixir]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[elm]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[git_branch]
symbol = ""
style = "fg:#1d2021 bg:#fe8019"
format = '[ $symbol $branch ]($style)'
[git_status]
style = "fg:#1d2021 bg:#fe8019"
format = '[$all_status$ahead_behind ]($style)'
[golang]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[haskell]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[java]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[julia]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[maven]
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[nodejs]
symbol = ""
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[bun]
symbol = ""
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[nim]
symbol = "󰆥 "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[rust]
symbol = ""
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[scala]
symbol = " "
style = "fg:#1d2021 bg:#8ec07c"
format = '[ $symbol ($version) ]($style)'
[time]
disabled = false
time_format = "%R"
style = "fg:#1d2021 bg:#665c54"
format = '[ ♥ $time ]($style)'
[cmd_duration]
min_time = 2000
style = "fg:#1d2021"
format = " took [$duration]($style) "

34
tmux/.tmux.conf Normal file
View File

@ -0,0 +1,34 @@
# Basic tmux configuration
# Mouse support (scroll, resize panes, select panes)
set -g mouse on
# Terminal type for 256 color support
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",*256col*:Tc"
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q]'
# Scrollback buffer
set -g history-limit 10000
# Start window numbering at 1 (instead of 0)
set -g base-index 1
set -g pane-base-index 1
# Key bindings
bind | split-window -h
bind - split-window -v
bind r source-file ~/.tmux.conf \; display "Reloaded config!"
# Status bar: short hostname, session name, window list (Gruvbox Dark Hard)
set -g status-bg "#1d2021"
set -g status-fg "#ebdbb2"
set -g status-left "#[fg=#1d2021,bg=#d3869b,bold] #S "
set -g status-right "#[fg=#ebdbb2,bg=#3c3836] #H "
set -g window-status-current-format "#[fg=#1d2021,bg=#fe8019,bold] #I:#W "
set -g window-status-format "#[fg=#a89984,bg=#3c3836] #I:#W "
set -g status-interval 5
# Activity monitoring
set -g monitor-activity on
set -g visual-activity on

187
uninstall.sh Executable file
View File

@ -0,0 +1,187 @@
#!/bin/bash
# Dotfiles Uninstall Script
# Author: q280969
# Description: Remove dotfiles symlinks created by GNU Stow
set -e # Exit on any error
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AVAILABLE_PACKAGES=(bash zsh git ghostty starship tmux lazygit zed opencode)
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
show_help() {
cat << EOF
Dotfiles Uninstall Script
USAGE:
$0 [OPTIONS]
OPTIONS:
-h, --help Show this help message
-d, --dry-run Show what would be removed without making changes
-v, --verbose Enable verbose output
-q, --quiet Suppress non-essential output
--only=PACKAGES Remove only specified packages (comma-separated)
Available: ${AVAILABLE_PACKAGES[*]}
EXAMPLES:
$0 # Remove all configurations
$0 --only=git,zsh # Remove only git and zsh configurations
$0 --dry-run # Preview what would be removed
AVAILABLE PACKAGES:
EOF
for package in "${AVAILABLE_PACKAGES[@]}"; do
echo " - $package"
done
}
check_dependencies() {
log_info "Checking dependencies..."
if ! command -v stow &> /dev/null; then
log_error "GNU Stow is not installed"
log_error "Stow is required to safely remove symlinks"
exit 1
fi
log_success "All dependencies satisfied"
}
uninstall_package() {
local package=$1
if [[ ! -d "$SCRIPT_DIR/$package" ]]; then
log_error "Package '$package' not found in $SCRIPT_DIR"
return 1
fi
log_info "Removing $package configuration..."
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] Would run: stow -D $package"
log_info "[DRY RUN] Would remove symlinks for files in $SCRIPT_DIR/$package"
return 0
fi
if stow -D "$package"; then
log_success "Successfully removed $package"
else
log_error "Failed to remove $package"
return 1
fi
}
# Parse command line arguments
DRY_RUN=false
VERBOSE=false
QUIET=false
SELECTED_PACKAGES=()
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-q|--quiet)
QUIET=true
shift
;;
--only=*)
IFS=',' read -ra SELECTED_PACKAGES <<< "${1#*=}"
shift
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Set packages to remove
if [[ ${#SELECTED_PACKAGES[@]} -eq 0 ]]; then
PACKAGES_TO_REMOVE=("${AVAILABLE_PACKAGES[@]}")
else
PACKAGES_TO_REMOVE=("${SELECTED_PACKAGES[@]}")
# Validate selected packages
for package in "${PACKAGES_TO_REMOVE[@]}"; do
if [[ ! " ${AVAILABLE_PACKAGES[*]} " =~ " ${package} " ]]; then
log_error "Unknown package: $package"
log_info "Available packages: ${AVAILABLE_PACKAGES[*]}"
exit 1
fi
done
fi
# Suppress output if quiet mode
if [[ "$QUIET" == "true" ]]; then
exec > /dev/null 2>&1
fi
# Main execution
main() {
log_info "Starting dotfiles removal..."
if [[ "$DRY_RUN" == "true" ]]; then
log_info "DRY RUN MODE - No changes will be made"
fi
cd "$SCRIPT_DIR"
check_dependencies
local failed_packages=()
for package in "${PACKAGES_TO_REMOVE[@]}"; do
if ! uninstall_package "$package"; then
failed_packages+=("$package")
fi
done
echo
if [[ ${#failed_packages[@]} -eq 0 ]]; then
log_success "All packages removed successfully!"
log_info "Your original files (if any) should now be restored"
else
log_error "Some packages failed to remove: ${failed_packages[*]}"
exit 1
fi
}
# Run main function
main

View File

@ -0,0 +1,43 @@
{
"project_panel": {
"dock": "right"
},
"outline_panel": {
"dock": "right"
},
"terminal": {
"dock": "bottom"
},
"debugger": {
"dock": "left"
},
"git_panel": {
"dock": "right"
},
"collaboration_panel": {
"dock": "right"
},
"agent_servers": {
"github-copilot-cli": {
"type": "registry"
}
},
"base_keymap": "VSCode",
"icon_theme": {
"mode": "system",
"light": "Zed (Default)",
"dark": "Zed (Default)"
},
"ui_font_size": 16,
"buffer_font_size": 15,
"theme": {
"mode": "system",
"light": "Ayu Light",
"dark": "Gruvbox Dark"
},
"languages": {
"Proto": {
"language_servers": ["buf"]
}
}
}

34
zsh/.zsh_aliases Normal file
View File

@ -0,0 +1,34 @@
alias use-bash="exec bash"
alias use-zsh="exec zsh"
# Docker aliases
alias dc="docker compose"
alias cscli="docker exec -t crowdsec cscli"
alias swag="docker exec -t swag"
alias olcli="docker exec -it ollama"
# Lazy git
alias lg="lazygit"
# bat / batcat compatibility (Debian/Ubuntu installs bat as batcat)
if ! command -v bat &>/dev/null && command -v batcat &>/dev/null; then
alias bat="batcat"
fi
# Zoxide aliases (smart cd)
alias j="zoxide cd"
# eza aliases (modern ls)
alias ls="eza"
alias ll="eza -l --icons --git"
alias la="eza -la --icons --git"
alias lt="eza --tree"
alias l="eza -l --icons --git"
alias gitc="git commit"
alias gits="git status"
alias gitp="git push"
alias gitd="git diff"
alias gita="git add"
alias rm="rm -i"

148
zsh/.zshrc Normal file
View File

@ -0,0 +1,148 @@
## Start of .zshrc config
## User settings
# Editor
export EDITOR="nano"
export VISUAL="nano"
setopt prompt_subst
# =========================
# zinit - plugin manager
# =========================
if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then
print -P "%F{33} %F{220}Installing Zinit…%f"
command mkdir -p "$HOME/.local/share/zinit" && command chmod g-rwX "$HOME/.local/share/zinit"
command git clone https://github.com/zdharma-continuum/zinit "$HOME/.local/share/zinit/zinit.git"
fi
source "$HOME/.local/share/zinit/zinit.git/zinit.zsh"
autoload -Uz _zinit
(( ${+_comps} )) && _comps[zinit]=_zinit
# =========================
# OMZ core library
# =========================
zinit snippet OMZ::lib/completion.zsh
zinit snippet OMZ::lib/history.zsh
zinit snippet OMZ::lib/key-bindings.zsh
zinit snippet OMZ::lib/theme-and-appearance.zsh
zinit snippet OMZ::lib/functions.zsh
zinit snippet OMZ::lib/directories.zsh
zinit snippet OMZ::lib/git.zsh
zinit snippet OMZ::lib/spectrum.zsh
zinit snippet OMZ::lib/termsupport.zsh
# Pager (replaces OMZ lib/misc.zsh)
export PAGER="${PAGER:-less}"
export LESS="${LESS:--R}"
# =========================
# OMZ plugins (turbo)
# =========================
zinit ice wait lucid
zinit snippet OMZ::plugins/git/git.plugin.zsh
zinit ice wait lucid
zinit snippet OMZ::plugins/command-not-found/command-not-found.plugin.zsh
# =========================
# NVM with lazy loading
# =========================
export NVM_LAZY_LOAD=true
export NVM_AUTO_USE=true
zinit ice wait lucid
zinit light lukechilds/zsh-nvm
# =========================
# Completions (load after 1s)
# =========================
zinit ice wait"1" lucid
zinit light zsh-users/zsh-completions
# =========================
# Autosuggestions (after prompt)
# =========================
zinit ice wait"0a" lucid atload'_zsh_autosuggest_start'
zinit light zsh-users/zsh-autosuggestions
# =========================
# Syntax highlighting (load last)
# =========================
zinit ice wait"0a" lucid
zinit light zdharma-continuum/fast-syntax-highlighting
# =========================
# Starship prompt
# =========================
if (( $+commands[starship] )); then
eval "$(starship init zsh)"
fi
# =========================
# Aliases
# =========================
if [ -f ~/.zsh_aliases ]; then
. ~/.zsh_aliases
fi
# =========================
# Atuin - shell history
# =========================
. "$HOME/.atuin/bin/env"
eval "$(atuin init zsh)"
# =========================
# Zoxide - smarter cd
# =========================
if (( $+commands[zoxide] )); then
eval "$(zoxide init zsh)"
fi
# =========================
# fzf - fuzzy finder
# =========================
if (( $+commands[fzf] )); then
source <(fzf --zsh 2>/dev/null)
fi
# Use ripgrep for fzf file listing if available (faster, respects .gitignore)
if (( $+commands[rg] )); then
export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
fi
# =========================
# Theme / Gruvbox Dark Hard
# =========================
# Bat theme
export BAT_THEME="Gruvbox Dark"
# FZF colors (Gruvbox Dark Hard)
export FZF_DEFAULT_OPTS=" \
--color=bg+:#3c3836,bg:#1d2021,spinner:#fabd2f,hl:#fb4934 \
--color=fg:#ebdbb2,header:#fb4934,info:#d3869b,pointer:#fabd2f \
--color=marker:#fabd2f,fg+:#ebdbb2,prompt:#d3869b,hl+:#fb4934"
# fast-syntax-highlighting colors (Gruvbox Dark Hard)
typeset -gA FAST_HIGHLIGHT_STYLES
FAST_HIGHLIGHT_STYLES[default]=fg=#ebdbb2
FAST_HIGHLIGHT_STYLES[unknown-token]=fg=#fb4934
FAST_HIGHLIGHT_STYLES[reserved-word]=fg=#d3869b,bold
FAST_HIGHLIGHT_STYLES[alias]=fg=#83a598
FAST_HIGHLIGHT_STYLES[builtin]=fg=#83a598
FAST_HIGHLIGHT_STYLES[function]=fg=#83a598
FAST_HIGHLIGHT_STYLES[command]=fg=#83a598
FAST_HIGHLIGHT_STYLES[precommand]=fg=#83a598
FAST_HIGHLIGHT_STYLES[hashed-command]=fg=#83a598
FAST_HIGHLIGHT_STYLES[path]=fg=#b8bb26,underline
FAST_HIGHLIGHT_STYLES[path-to-dir]=fg=#b8bb26,underline
FAST_HIGHLIGHT_STYLES[single-hyphen-option]=fg=#fabd2f
FAST_HIGHLIGHT_STYLES[double-hyphen-option]=fg=#fabd2f
FAST_HIGHLIGHT_STYLES[single-quoted-argument]=fg=#fabd2f
FAST_HIGHLIGHT_STYLES[double-quoted-argument]=fg=#fabd2f
FAST_HIGHLIGHT_STYLES[back-quoted-argument]=fg=#fb4934
FAST_HIGHLIGHT_STYLES[assign]=fg=#ebdbb2
FAST_HIGHLIGHT_STYLES[globbing]=fg=#d3869b
FAST_HIGHLIGHT_STYLES[comment]=fg=#7c6f64
FAST_HIGHLIGHT_STYLES[redirection]=fg=#d3869b