diff --git a/.chezmoiignore b/.chezmoiignore
new file mode 100644
index 0000000..4355b42
--- /dev/null
+++ b/.chezmoiignore
@@ -0,0 +1,20 @@
+README.md
+LICENSE
+CLAUDE.md
+.github
+.gitignore
+
+# Destination side files chezmoi must not touch
+.claude/local
+.claude/settings.local.json
+.claude/hooks
+.claude/projects
+.claude/scripts
+.claude/shell-snapshots
+.claude/statsig
+.claude/todos
+.zsh/env_local.zsh
+.worktree
+firebase-debug.log
+.netrwhist
+.DS_Store
diff --git a/.claude/agents/serena-expert.md b/.claude/agents/serena-expert.md
deleted file mode 100644
index 31a87a0..0000000
--- a/.claude/agents/serena-expert.md
+++ /dev/null
@@ -1,119 +0,0 @@
----
-name: serena-expert
-description: Elite app development agent that uses /serena command for token-efficient, structured problem-solving. Specializes in creating applications, implementing components, APIs, systems, and tests with maximum efficiency. Examples: Context: User needs to create a new React component. user: 'I need to implement a data table with sorting and filtering' assistant: 'I'll use /serena to efficiently design and implement this component with all features' Component creation benefits from /serena's structured approach for clean, maintainable code. Context: User is building a new API endpoint. user: 'Help me create a REST API for user management' assistant: 'Let me use /serena to architect this API with proper patterns and security' API development requires systematic design that /serena provides efficiently.
-model: sonnet
-color: blue
----
-
-You are Claude Code's premier app development specialist, optimized for token-efficient development through strategic use of the /serena command. Your expertise spans full-stack development with a focus on practical, production-ready implementations.
-
-## Core Development Focus:
-- **Component Development**: React/Vue/Angular components with proper state management
-- **API Implementation**: RESTful/GraphQL endpoints with authentication and validation
-- **System Architecture**: Scalable, maintainable application structures
-- **Test Creation**: Comprehensive unit/integration/E2E test suites
-- **Performance Optimization**: Efficient code that scales
-
-## Automatic /serena Usage Triggers:
-Always use /serena for these development tasks to maximize token efficiency:
-
-### Component Development
-- Creating new UI components (buttons, forms, modals, tables)
-- Implementing complex state management
-- Building reusable component libraries
-- Integrating third-party UI libraries
-
-### API Development
-- Designing RESTful or GraphQL endpoints
-- Implementing authentication/authorization
-- Database schema design and queries
-- API versioning and documentation
-
-### System Implementation
-- Setting up project architecture
-- Implementing design patterns (MVC, Repository, Factory)
-- Creating microservices or modular systems
-- Building real-time features (WebSocket, SSE)
-
-### Testing
-- Writing comprehensive test suites
-- Creating test utilities and mocks
-- Setting up E2E test scenarios
-- Implementing CI/CD pipelines
-
-## Token Optimization Strategy:
-
-### 1. Template-Based Development
-Use /serena with predefined patterns:
-```bash
-/serena "create [component/api/test] for [feature]" -q # Quick 3-5 thoughts
-/serena "implement [feature] with [requirements]" -c # Code-focused
-/serena "optimize [system] for [metric]" --summary # Summary only
-```
-
-### 2. Efficient Problem Analysis
-- Start with minimal context gathering
-- Use /serena's structured thinking to avoid redundant analysis
-- Focus on implementation over theory
-- Provide code-first solutions
-
-### 3. Smart Defaults
-Automatically apply these patterns:
-- **Components**: Functional with hooks, TypeScript, CSS modules
-- **APIs**: Express/FastAPI, JWT auth, validation middleware
-- **Tests**: Jest/Pytest, high coverage, meaningful assertions
-- **Architecture**: Clean architecture, SOLID principles
-
-## Development Workflow:
-
-### Phase 1: Rapid Analysis (1-2 thoughts via /serena)
-- Understand requirements
-- Identify key technical decisions
-
-### Phase 2: Efficient Implementation (3-5 thoughts via /serena)
-- Generate boilerplate code
-- Implement core functionality
-- Add error handling and validation
-
-### Phase 3: Quality Assurance (1-2 thoughts via /serena)
-- Create relevant tests
-- Add documentation
-- Suggest optimization opportunities
-
-## Practical Examples:
-
-### Component Creation
-```
-User: "Create a user profile card"
-Action: /serena "implement UserProfileCard component with avatar, name, bio, and action buttons" -c -q
-Result: Complete component with styling and basic tests in minimal tokens
-```
-
-### API Implementation
-```
-User: "Need a product CRUD API"
-Action: /serena "implement product CRUD API with validation and auth" -api --summary
-Result: Full API implementation with routes, controllers, and models
-```
-
-### Full Feature
-```
-User: "Build a comment system"
-Action: /serena "implement comment system with nested replies" -full
-Result: Frontend components + API + database schema + tests
-```
-
-## Quality Guarantees:
-- Every implementation includes error handling
-- All code follows established patterns and best practices
-- Tests are included by default
-- Security considerations are built-in
-- Performance is optimized from the start
-
-## Special Capabilities:
-- **Auto-detection**: Recognizes development tasks and uses /serena automatically
-- **Context inheritance**: Remembers previous development decisions
-- **Progressive enhancement**: Builds upon existing code efficiently
-- **Framework expertise**: Deep knowledge of React, Next.js, Node.js, Python, etc.
-
-You excel at delivering production-ready code with minimal token usage by leveraging /serena's structured approach for all development tasks. Your responses are always practical, implementable, and focused on real-world application development.
diff --git a/.claude/commands/serena.md b/.claude/commands/serena.md
deleted file mode 100644
index 9d25073..0000000
--- a/.claude/commands/serena.md
+++ /dev/null
@@ -1,178 +0,0 @@
----
-allowed-tools: Read, Glob, Grep, Edit, MultiEdit, Write, Bash, TodoWrite, mcp__serena__check_onboarding_performed, mcp__serena__delete_memory, mcp__serena__find_file, mcp__serena__find_referencing_symbols, mcp__serena__find_symbol, mcp__serena__get_symbols_overview, mcp__serena__insert_after_symbol, mcp__serena__insert_before_symbol, mcp__serena__list_dir, mcp__serena__list_memories, mcp__serena__onboarding, mcp__serena__read_memory, mcp__serena__remove_project, mcp__serena__replace_regex, mcp__serena__replace_symbol_body, mcp__serena__restart_language_server, mcp__serena__search_for_pattern, mcp__serena__switch_modes, mcp__serena__think_about_collected_information, mcp__serena__think_about_task_adherence, mcp__serena__think_about_whether_you_are_done, mcp__serena__write_memory, mcp__context7__resolve-library-id, mcp__context7__get-library-docs
-description: Token-efficient Serena MCP command for structured app development and problem-solving
----
-
-## Quick Reference
-
-```bash
-/serena [options] # Basic usage
-/serena debug "memory leak in prod" # Debug pattern (5-8 thoughts)
-/serena design "auth system" # Design pattern (8-12 thoughts)
-/serena review "optimize this code" # Review pattern (4-7 thoughts)
-/serena implement "add feature X" # Implementation (6-10 thoughts)
-```
-
-## Options
-
-| Option | Description | Usage | Use Case |
-|--------|-------------|-------|----------|
-| `-q` | Quick mode (3-5 thoughts/steps) | `/serena "fix button" -q` | Simple bugs, minor features |
-| `-d` | Deep mode (10-15 thoughts/steps) | `/serena "architecture design" -d` | Complex systems, major decisions |
-| `-c` | Code-focused analysis | `/serena "optimize performance" -c` | Code review, refactoring |
-| `-s` | Step-by-step implementation | `/serena "build dashboard" -s` | Full feature development |
-| `-v` | Verbose output (show process) | `/serena "debug issue" -v` | Learning, understanding process |
-| `-r` | Include research phase | `/serena "choose framework" -r` | Technology decisions |
-| `-t` | Create implementation todos | `/serena "new feature" -t` | Project management |
-
-## Usage Patterns
-
-### Basic Usage
-```bash
-# Simple problem solving
-/serena "fix login bug"
-
-# Quick feature implementation
-/serena "add search filter" -q
-
-# Code optimization
-/serena "improve load time" -c
-```
-
-### Advanced Usage
-```bash
-# Complex system design with research
-/serena "design microservices architecture" -d -r -v
-
-# Full feature development with todos
-/serena "implement user dashboard with charts" -s -t -c
-
-# Deep analysis with documentation
-/serena "migrate to new framework" -d -r -v --focus=frontend
-```
-
-## Context (Auto-gathered)
-- Project files: !`find . -maxdepth 2 -name "package.json" -o -name "*.config.*" | head -5 2>/dev/null || echo "No config files"`
-- Git status: !`git status --porcelain 2>/dev/null | head -3 || echo "Not git repo"`
-
-## Core Workflow
-
-### 1. Problem Detection & Template Selection
-Automatically select thinking pattern based on keywords:
-- **Debug**: error, bug, issue, broken, failing → 5-8 thoughts
-- **Design**: architecture, system, structure, plan → 8-12 thoughts
-- **Implement**: build, create, add, feature → 6-10 thoughts
-- **Optimize**: performance, slow, improve, refactor → 4-7 thoughts
-- **Review**: analyze, check, evaluate → 4-7 thoughts
-
-### 2. MCP Selection & Execution
-```
-App Development Tasks → Serena MCP
-- Component implementation
-- API development
-- Feature building
-- System architecture
-
-All Tasks → Serena MCP
-- Component implementation
-- API development
-- Feature building
-- System architecture
-- Problem solving and analysis
-```
-
-### 3. Output Modes
-- **Default**: Key insights + recommended actions
-- **Verbose (-v)**: Show thinking process
-- **Implementation (-s)**: Create todos + start execution
-
-## Problem-Specific Templates
-
-### Debug Pattern (5-8 thoughts)
-1. Symptom analysis & reproduction
-2. Error context & environment check
-3. Root cause hypothesis generation
-4. Evidence gathering & validation
-5. Solution design & risk assessment
-6. Implementation plan
-7. Verification strategy
-8. Prevention measures
-
-### Design Pattern (8-12 thoughts)
-1. Requirements clarification
-2. Constraints & assumptions
-3. Stakeholder analysis
-4. Architecture options generation
-5. Option evaluation (pros/cons)
-6. Technology selection
-7. Design decisions & tradeoffs
-8. Implementation phases
-9. Risk mitigation
-10. Success metrics
-11. Validation plan
-12. Documentation needs
-
-### Implementation Pattern (6-10 thoughts)
-1. Feature specification & scope
-2. Technical approach selection
-3. Component/module design
-4. Dependencies & integration points
-5. Implementation sequence
-6. Testing strategy
-7. Edge case handling
-8. Performance considerations
-9. Error handling & recovery
-10. Deployment & rollback plan
-
-### Review/Optimize Pattern (4-7 thoughts)
-1. Current state analysis
-2. Bottleneck identification
-3. Improvement opportunities
-4. Solution options & feasibility
-5. Implementation priority
-6. Performance impact estimation
-7. Validation & monitoring plan
-
-## Advanced Options
-
-**Thought Control:**
-- `--max-thoughts=N`: Override default thought count
-- `--focus=AREA`: Domain-specific analysis (frontend, backend, database, security)
-- `--token-budget=N`: Optimize for token limit
-
-**Integration:**
-- `-r`: Include Context7 research phase
-- `-t`: Create implementation todos
-- `--context=FILES`: Analyze specific files first
-
-**Output:**
-- `--summary`: Condensed output only
-- `--json`: Structured output for automation
-- `--progressive`: Show summary first, details on request
-
-## Task Execution
-
-You are an expert app developer and problem-solver primarily using Serena MCP. For each request:
-
-1. **Auto-detect problem type** and select appropriate approach
-2. **Use Serena MCP**:
- - **All development tasks**: Use Serena MCP tools (https://github.com/oraios/serena)
- - **Analysis, debugging, implementation**: Use Serena's semantic code tools
-3. **Execute structured approach** with chosen MCP
-4. **Research relevant docs** with Context7 MCP if needed
-5. **Synthesize actionable solution** with specific next steps
-6. **Create implementation todos** if `-s` flag used
-
-**Key Guidelines:**
-- **Primary**: Use Serena MCP tools for all tasks (components, APIs, features, analysis)
-- **Leverage**: Serena's semantic code retrieval and editing capabilities
-- Start with problem analysis, end with concrete actions
-- Balance depth with token efficiency
-- Always provide specific, actionable recommendations
-- Consider security, performance, and maintainability
-
-**Token Efficiency Tips:**
-- Use `-q` for simple problems (saves ~40% tokens)
-- Use `--summary` for overview-only needs
-- Combine related problems in single session
-- Use `--focus` to avoid irrelevant analysis
diff --git a/.commit_template b/.commit_template
deleted file mode 100644
index e379e49..0000000
--- a/.commit_template
+++ /dev/null
@@ -1,19 +0,0 @@
-
-# ==================== Emojis ====================
-# 🎉 :tada: 初めてのコミット(Initial Commit)
-# 🔖 :bookmark: バージョンタグ(Version Tag)
-# ✨ :sparkles: 新機能(New Feature)
-# 🐛 :bug: バグ修正(Bugfix)
-# ♻️ :recycle: リファクタリング(Refactoring)
-# 📚 :books: ドキュメント(Documentation)
-# 🎨 :art: デザインUI/UX(Accessibility)
-# 🐎 :horse: パフォーマンス(Performance)
-# 🔧 :wrench: ツール(Tooling)
-# 🚨 :rotating_light: テスト(Tests)
-# 💩 :hankey: 非推奨追加(Deprecation)
-# 🗑️ :wastebasket: 削除(Removal)
-# 🚧 :construction: WIP(Work In Progress)
-
-
-# ==================== Format ====================
-# :emoji: Subject
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d42a0fa..1d03c1e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,9 +3,6 @@ name: CI
on:
push:
branches: [ main ]
- paths-ignore:
- - 'README.md'
- - '.github/workflows/update-readme.yml'
pull_request:
branches: [ main ]
@@ -15,224 +12,102 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
-
- - name: Install shellcheck
+
+ - name: Install shellcheck and zsh
run: |
sudo apt-get update
- sudo apt-get install -y shellcheck
-
- - name: Run shellcheck on shell scripts
- run: |
- find . -type f -name "*.sh" -exec shellcheck {} + || true
- find . -type f -name "*.zsh" -exec shellcheck --shell=bash {} + 2>/dev/null || true
-
- - name: Check for syntax errors in zsh files
- run: |
- for file in $(find . -type f -name "*.zsh" -o -name ".zshrc"); do
- if [[ -f "$file" ]]; then
- zsh -n "$file" || echo "Syntax check failed for $file"
- fi
- done
+ sudo apt-get install -y shellcheck zsh
- test:
- name: Test - ${{ matrix.test-suite }} on ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- continue-on-error: true
- strategy:
- matrix:
- os: [macos-latest]
- test-suite:
- - configuration
- - installers
- - symlinks
- include:
- - os: macos-13
- test-suite: symlinks
- fail-fast: false
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Cache brew dependencies
- uses: actions/cache@v4
- env:
- cache-name: cache-brew-dependencies
- with:
- path: |
- ~/Library/Caches/Homebrew
- /usr/local/Homebrew
- /opt/homebrew
- key: ${{ runner.os }}-${{ matrix.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/.Brewfile') }}
- restore-keys: |
- ${{ runner.os }}-${{ matrix.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-${{ matrix.os }}-build-
- ${{ runner.os }}-${{ matrix.os }}-
-
- - name: Install Bats
- run: |
- if ! command -v bats &> /dev/null; then
- brew install bats-core
- fi
-
- - name: Run ${{ matrix.test-suite }} tests
- run: bats tests/${{ matrix.test-suite }}.bats
-
- quick-checks:
- name: Quick Checks
- runs-on: macos-latest
- continue-on-error: true
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Test make list
- run: |
- make list
- # Verify that .claude is included
- make list | grep "\.claude" || exit 1
-
- - name: Test make install (dry run)
- run: |
- # Create a temporary HOME for testing
- export TEST_HOME=$(mktemp -d)
- export HOME=$TEST_HOME
- make install
- # Verify symlinks were created
- ls -la $TEST_HOME
- # Check that essential dotfiles are linked
- test -L $TEST_HOME/.zshrc
- test -L $TEST_HOME/.tmux.conf
- test -L $TEST_HOME/.gitconfig
-
- - name: Test installer syntax
+ - name: Install chezmoi
+ run: sh -c "$(curl -fsLS get.chezmoi.io)" -- -b /usr/local/bin
+
+ - name: Render chezmoi templates and run shellcheck
run: |
- for installer in installers/*.sh; do
- echo "Checking syntax of $installer"
- zsh -n "$installer"
- done
- for installer in settings/**/install.sh; do
- echo "Checking syntax of $installer"
- zsh -n "$installer"
+ rendered_dir="$(mktemp -d)"
+ # Render *.tmpl run scripts so shellcheck can parse them
+ for tmpl in run_*.sh.tmpl; do
+ [ -f "$tmpl" ] || continue
+ out="$rendered_dir/$(basename "${tmpl%.tmpl}")"
+ chezmoi execute-template --source="$PWD" < "$tmpl" > "$out"
done
-
- - name: Verify critical files exist
- run: |
- test -f .Brewfile
- test -f .zshrc
- test -f .gitconfig
- test -f .tmux.conf
- test -f Makefile
- test -d .zsh
- test -d installers
- test -d settings
-
- integration:
- name: Integration Test
- runs-on: macos-latest
- needs: [lint]
- continue-on-error: true
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Cache brew dependencies
- uses: actions/cache@v4
- env:
- cache-name: cache-brew-dependencies
- with:
- path: |
- ~/Library/Caches/Homebrew
- /usr/local/Homebrew
- /opt/homebrew
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/.Brewfile') }}
- restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
-
- - name: Full installation test
- run: |
- # Set up test environment
- export ORIGINAL_HOME=$HOME
- export TEST_HOME=$(mktemp -d)
- export HOME=$TEST_HOME
-
- # Run full installation
- make all || true
-
- # Verify installation
- echo "=== Verifying installation ==="
- ls -la $TEST_HOME
-
- # Check symlinks
- if [[ -L $TEST_HOME/.zshrc ]]; then
- echo "✓ .zshrc symlink created"
+
+ # shellcheck does not support zsh; filter zsh-shebanged scripts and
+ # the vendored fzf-git.sh out of the target set.
+ targets=()
+ while IFS= read -r -d '' f; do
+ case "$f" in
+ */dot_zsh/functions/fzf-git.sh) continue ;;
+ esac
+ if head -n1 "$f" | grep -qE '^#!.*\bzsh\b'; then
+ continue
+ fi
+ targets+=("$f")
+ done < <(
+ find . -type f -name "*.sh" -not -path "./.git/*" -print0
+ find "$rendered_dir" -type f -name "*.sh" -print0
+ )
+
+ if [ ${#targets[@]} -gt 0 ]; then
+ shellcheck "${targets[@]}"
else
- echo "✗ .zshrc symlink missing"
+ echo "No shellcheck-eligible scripts in repo (all .sh files are zsh)."
fi
-
- # Restore HOME
- export HOME=$ORIGINAL_HOME
-
- - name: Test idempotency
+
+ - name: Check zsh syntax
run: |
- export TEST_HOME=$(mktemp -d)
- export HOME=$TEST_HOME
-
- # Run install twice to test idempotency
- make install
- make install
-
- # Should complete without errors
- echo "✓ Installation is idempotent"
-
- update-readme:
- name: Update README
+ find . -type f \( -name "*.zsh" -o -name "dot_zshrc" -o -name "executable_*" \) -not -path "./.git/*" -print0 \
+ | while IFS= read -r -d '' file; do
+ zsh -n "$file" || exit 1
+ done
+
+ chezmoi-verify:
+ name: chezmoi verify
runs-on: macos-latest
- needs: [integration]
- if: github.ref == 'refs/heads/main' && github.event_name == 'push'
-
- permissions:
- contents: write
-
steps:
- uses: actions/checkout@v4
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- fetch-depth: 0
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - name: Update README
+
+ - name: Install chezmoi
+ run: sh -c "$(curl -fsLS get.chezmoi.io)" -- -b /usr/local/bin
+
+ - name: chezmoi doctor
+ run: chezmoi --source="$GITHUB_WORKSPACE" doctor || true
+
+ - name: Apply to ephemeral HOME and verify rendered files
run: |
- echo "=== Updating README with environment information ==="
- python3 scripts/update_readme.py
-
- - name: Check for changes
- id: check_changes
+ export HOME="$(mktemp -d)"
+ export CI=1
+ export XDG_CACHE_HOME="$HOME/.cache"
+ chezmoi init --apply --source="$GITHUB_WORKSPACE"
+
+ test -f "$HOME/.zshrc"
+ test -f "$HOME/.config/mise/config.toml"
+ test -f "$HOME/.config/ghostty/config"
+ test -f "$HOME/.hammerspoon/init.lua"
+ test -f "$HOME/Library/Application Support/Code/User/settings.json"
+ test -x "$HOME/.zsh/bin/reload"
+
+ grep -F 'idiomatic_version_file_enable_tools = ["ruby"]' "$HOME/.config/mise/config.toml"
+ grep -F 'appName = "Ghostty"' "$HOME/.hammerspoon/init.lua"
+
+ chezmoi verify --source="$GITHUB_WORKSPACE"
+
+ - name: Idempotency check
run: |
- if [[ -n $(git status --porcelain README.md) ]]; then
- echo "changes=true" >> $GITHUB_OUTPUT
- echo "README.md has changes"
- else
- echo "changes=false" >> $GITHUB_OUTPUT
- echo "No changes to README.md"
+ export HOME="$(mktemp -d)"
+ export CI=1
+ export XDG_CACHE_HOME="$HOME/.cache"
+ chezmoi init --apply --source="$GITHUB_WORKSPACE"
+ chezmoi apply --source="$GITHUB_WORKSPACE"
+ status_output="$(chezmoi status --source="$GITHUB_WORKSPACE")"
+ if [ -n "$status_output" ]; then
+ echo "::error::chezmoi status is non-empty after re-apply"
+ echo "$status_output"
+ exit 1
fi
-
- - name: Commit and push changes
- if: steps.check_changes.outputs.changes == 'true'
- run: |
- git config --local user.email "github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
-
- git add README.md
- git commit -m "docs: update README with latest environment info [skip ci]
-
- Auto-generated by GitHub Actions
- Workflow: ${{ github.workflow }}
- Run: ${{ github.run_number }}"
-
- git push
\ No newline at end of file
+ diff_output="$(chezmoi diff --source="$GITHUB_WORKSPACE")"
+ if [ -n "$diff_output" ]; then
+ echo "::error::chezmoi diff is non-empty after re-apply"
+ echo "$diff_output"
+ exit 1
+ fi
+ chezmoi verify --source="$GITHUB_WORKSPACE"
diff --git a/.gitignore b/.gitignore
index 9809b0b..dcf7785 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,21 +2,20 @@
.DS_Store
# Local environment settings
-.zsh/env_local.zsh
+dot_zsh/env_local.zsh
# Worktree directories
.worktree/
# Claude AI (local settings only)
-.claude/local/
-.claude/settings.local.json
-.claude/hooks/
-.claude/projects/
-.claude/scripts/
-.claude/shell-snapshots/
-.claude/statsig/
-.claude/todos/
+dot_claude/local/
+dot_claude/settings.local.json
+dot_claude/hooks/
+dot_claude/projects/
+dot_claude/scripts/
+dot_claude/shell-snapshots/
+dot_claude/statsig/
+dot_claude/todos/
# Firebase
firebase-debug.log
-
diff --git a/.tmux.conf b/.tmux.conf
deleted file mode 100644
index eed9252..0000000
--- a/.tmux.conf
+++ /dev/null
@@ -1,60 +0,0 @@
-# ref: https://qiita.com/shin-ch13/items/9d207a70ccc8467f7bab
-
-set-option -g default-shell "${SHELL}"
-
-# tmuxを256色表示できるようにする
-set-option -g default-terminal screen-256color
-set -g terminal-overrides 'xterm:colors=256'
-
-# prefixキーをC-qに変更
-set -g prefix C-q
-# C-bのキーバインドを解除
-unbind C-b
-
-
-# ステータスバーをトップに配置する
-set-option -g status-position top
-
-# 左右のステータスバーの長さを決定する
-set-option -g status-left-length 90
-set-option -g status-right-length 90
-
-# #P => ペイン番号
-# 最左に表示
-set-option -g status-left '[#P]'
-
-# Wi-Fi、バッテリー残量、現在時刻
-# 最右に表示
-set-option -g status-right '#(wifi) #(battery --tmux) [%Y-%m-%d(%a) %H:%M]'
-
-# ステータスバーを1秒毎に描画し直す
-set-option -g status-interval 1
-
-# センタライズ(主にウィンドウ番号など)
-set-option -g status-justify centre
-
-# ステータスバーの色を設定する
-set-option -g status-bg "colour238"
-
-# status line の文字色を指定する。
-set-option -g status-fg "colour255"
-
-# vimのキーバインドでペインを移動する
-bind h select-pane -L
-bind j select-pane -D
-bind k select-pane -U
-bind l select-pane -R
-
-# vimのキーバインドでペインをリサイズする
-bind -r H resize-pane -L 5
-bind -r J resize-pane -D 5
-bind -r K resize-pane -U 5
-bind -r L resize-pane -R 5
-
-# | でペインを縦分割する
-bind | split-window -h
-
-# - でペインを縦分割する
-bind - split-window -v
-
-set -g @plugin "arcticicestudio/nord-tmux"
diff --git a/.wezterm.lua b/.wezterm.lua
deleted file mode 100644
index e380eec..0000000
--- a/.wezterm.lua
+++ /dev/null
@@ -1,653 +0,0 @@
-local wezterm = require 'wezterm'
-local config = wezterm.config_builder()
-local act = wezterm.action
-
--- タブ
-config.window_decorations = "RESIZE"
-
--- パフォーマンス設定
-config.animation_fps = 120
-config.max_fps = 120
-config.prefer_egl = true
-config.front_end = 'WebGpu'
-config.webgpu_power_preference = 'HighPerformance'
-
--- Nordテーマの設定
-config.color_scheme = 'Nord (Gogh)'
-
--- フォント設定
-config.font = wezterm.font_with_fallback {
- 'HackGen Console NF', -- HackGen Nerd Font (アイコン含む)
- 'HackGen Console', -- HackGen 通常版
- 'HackGen', -- HackGen フォールバック
-}
-config.font_size = 12.0
-
--- ウィンドウの設定
-config.window_padding = {
- left = 10,
- right = 10,
- top = 10,
- bottom = 10,
-}
-
--- タブバーの設定
-config.enable_tab_bar = true
-config.hide_tab_bar_if_only_one_tab = false
-config.tab_bar_at_bottom = true -- 下部に配置
-config.use_fancy_tab_bar = false -- シンプルなタブバー
-config.show_new_tab_button_in_tab_bar = false
-config.tab_max_width = 50
-
--- ステータスバー更新間隔
-config.status_update_interval = 1000
-
--- Git コマンドを安全に実行するヘルパー関数
-local function safe_git_command(cwd, ...)
- local success, stdout = wezterm.run_child_process {
- 'git',
- '-C',
- cwd,
- ...,
- }
- if success then
- return stdout:gsub('\n', '')
- end
- return nil
-end
-
--- Git URL からリポジトリ名を抽出
-local function extract_repo_name_from_url(url)
- if not url then
- return nil
- end
- local repo_name = url:match '([^/]+)%.git$' or url:match '([^/]+)$'
- return repo_name
-end
-
--- プロセス名をアイコンに変換
-local function process_to_icon(process_name)
- if process_name == 'nvim' then
- return ''
- elseif process_name == 'zsh' then
- return ''
- elseif process_name == 'bash' then
- return ''
- elseif process_name == 'sl' then
- return ''
- elseif process_name == 'lazygit' or process_name == 'tig' then
- return ''
- elseif process_name == 'wezterm' then
- return ''
- elseif process_name == 'mcfly' then
- return ''
- elseif process_name == 'emu' then
- return '🦤'
- elseif string.match(process_name, '^python') then
- return ''
- elseif process_name == '' then
- return '🤖'
- else
- return process_name
- end
-end
-
--- Git リポジトリ名を取得
-local function get_git_repo_name(cwd_path)
- -- Git リポジトリかチェック
- if not safe_git_command(cwd_path, 'rev-parse', '--git-dir') then
- return nil
- end
-
- local repo_name = nil
-
- -- remote origin から取得
- local remote_url = safe_git_command(cwd_path, 'config', '--get', 'remote.origin.url')
- if remote_url then
- repo_name = extract_repo_name_from_url(remote_url)
- end
-
- -- 他の remote から取得
- if not repo_name then
- local remotes = safe_git_command(cwd_path, 'remote')
- if remotes and remotes ~= '' then
- local first_remote = remotes:match '([^\n]+)'
- if first_remote then
- remote_url = safe_git_command(cwd_path, 'config', '--get', 'remote.' .. first_remote .. '.url')
- if remote_url then
- repo_name = extract_repo_name_from_url(remote_url)
- end
- end
- end
- end
-
- -- toplevel のディレクトリ名
- if not repo_name then
- local toplevel = safe_git_command(cwd_path, 'rev-parse', '--show-toplevel')
- if toplevel then
- local bare_pattern = '([^/]+)%.bare'
- local git_pattern = '([^/]+)%.git'
- local dir_pattern = '([^/]+)$'
-
- if toplevel:match '%.bare/' or toplevel:match '%.git/' then
- repo_name = toplevel:match(bare_pattern) or toplevel:match(git_pattern)
- else
- repo_name = toplevel:match(dir_pattern)
- end
- end
- end
-
- -- 現在のディレクトリ名(最終手段)
- if not repo_name then
- local dir_name = cwd_path:match '([^/]+)$'
- if dir_name then
- repo_name = dir_name:gsub('%.git$', '')
- end
- end
-
- return repo_name
-end
-
--- Claude 関連の定数
-local CLAUDE_CONSTANTS = {
- -- プロセスフィルタリング
- EXCLUDE_PATTERNS = { 'npm', 'node', 'claude%-code' },
- INVALID_TTY = '??',
-
- -- 実行判定の閾値
- CPU_ACTIVE_THRESHOLD = 1.0, -- CPU 使用率がこれ以上なら実行中
- CPU_CHECK_THRESHOLD = 0.1, -- FD チェックを行う最小 CPU 使用率
- FD_ACTIVE_THRESHOLD = 15, -- ファイルディスクリプタ数の閾値
-
- -- 表示
- EMOJI_IDLE = '🤖',
- EMOJI_RUNNING = '⚡',
- COLOR_ICON = '#FF6B6B',
-
- -- Git 表示色
- GIT_ICON_COLOR = '#569CD6',
- GIT_REPO_COLOR = '#808080',
- GIT_BRANCH_ICON_COLOR = '#4EC9B0',
- GIT_BRANCH_COLOR = '#909090',
-
- -- スペーシング
- SPACING_SMALL = ' ',
- SPACING_MEDIUM = ' ',
- SPACING_SINGLE = ' ',
-
- -- システムコマンド
- PS_PATH = '/bin/ps',
-}
-
--- プロセスの実行状態をチェックするヘルパー関数
-local function check_process_running(pid)
- local ps_success, ps_stdout = wezterm.run_child_process {
- CLAUDE_CONSTANTS.PS_PATH,
- '-p',
- tostring(pid),
- '-o',
- 'stat,pcpu,rss',
- }
-
- if not ps_success or not ps_stdout then
- return false
- end
-
- local lines = {}
- for line in ps_stdout:gmatch '[^\n]+' do
- table.insert(lines, line)
- end
-
- if #lines < 2 then
- return false
- end
-
- local data_line = lines[2]
- local stat, pcpu, rss = data_line:match '%s*(%S+)%s+(%S+)%s+(%S+)'
-
- if not stat then
- return false
- end
-
- -- 1. プロセス状態による判定
- if stat:match '^[RD]' then
- return true
- end
-
- local cpu_usage = tonumber(pcpu) or 0
-
- -- 2. CPU 使用率による判定
- if cpu_usage >= CLAUDE_CONSTANTS.CPU_ACTIVE_THRESHOLD then
- return true
- end
-
- -- 3. ファイルディスクリプタ数をチェック(コスト高いので条件付き)
- if cpu_usage > CLAUDE_CONSTANTS.CPU_CHECK_THRESHOLD then
- local lsof_success, lsof_stdout = wezterm.run_child_process {
- 'lsof',
- '-p',
- tostring(pid),
- '-t',
- }
- if lsof_success and lsof_stdout then
- local fd_count = 0
- for _ in lsof_stdout:gmatch '[^\n]+' do
- fd_count = fd_count + 1
- end
- if fd_count > CLAUDE_CONSTANTS.FD_ACTIVE_THRESHOLD then
- return true
- end
- end
- end
-
- return false
-end
-
--- Claude プロセス情報を取得する関数
-local function get_claude_status(window)
- -- エラーハンドリング
- if not window then
- return { tab_sessions = {} }
- end
-
- local success, result = pcall(function()
- local mux_window = window:mux_window()
- if not mux_window then
- return { tab_sessions = {} }
- end
-
- local tabs = mux_window:tabs()
- if not tabs then
- return { tab_sessions = {} }
- end
-
- local tab_sessions = {}
-
- for tab_index, tab in ipairs(tabs) do
- local has_claude = false
- local is_running = false
-
- -- タブ内の全ペインをチェック
- local tab_success, panes = pcall(function()
- return tab:panes()
- end)
- if tab_success and panes then
- for _, pane in ipairs(panes) do
- local proc_success, proc_info = pcall(function()
- return pane:get_foreground_process_info()
- end)
- if proc_success and proc_info then
- -- Claude プロセスかチェック(プロセス名または argv で)
- local is_claude_process = false
- if proc_info.name and proc_info.name:match '^claude' then
- is_claude_process = true
- elseif proc_info.argv and #proc_info.argv > 0 and proc_info.argv[1]:match '^claude' then
- is_claude_process = true
- end
-
- if is_claude_process then
- -- 除外パターンをチェック
- local should_exclude = false
- local cmdline = table.concat(proc_info.argv or {}, ' ')
- for _, pattern in ipairs(CLAUDE_CONSTANTS.EXCLUDE_PATTERNS) do
- if cmdline:match(pattern) then
- should_exclude = true
- break
- end
- end
-
- if not should_exclude then
- has_claude = true
- -- 実行状態をチェック
- if proc_info.pid then
- is_running = check_process_running(proc_info.pid)
- end
- break -- タブ内に 1 つでも Claude があれば十分
- end
- end
- end
- end
- end
-
- -- タブごとの Claude 情報を記録
- table.insert(tab_sessions, {
- tab_index = tab_index,
- has_claude = has_claude,
- running = is_running,
- })
- end
-
- return { tab_sessions = tab_sessions }
- end)
-
- if success then
- return result
- else
- -- エラー時は空のセッションを返す
- return { tab_sessions = {} }
- end
-end
-
--- Claude ステータス表示
-local function add_claude_status_to_elements(elements, tab_sessions, window)
- if not tab_sessions or #tab_sessions == 0 then
- return
- end
-
- -- タブ順序に従ってステータスを表示
- for i, tab_session in ipairs(tab_sessions) do
- if tab_session.has_claude then
- -- Claude タブの場合
- table.insert(elements, { Foreground = { Color = CLAUDE_CONSTANTS.COLOR_ICON } })
- local emoji = tab_session.running and CLAUDE_CONSTANTS.EMOJI_RUNNING or CLAUDE_CONSTANTS.EMOJI_IDLE
- table.insert(elements, { Text = emoji })
- else
- -- 非 Claude タブの場合
- table.insert(elements, { Foreground = { Color = '#8B4513' } })
- table.insert(elements, { Text = '🧔' })
- end
-
- -- 最後以外はスペースを追加
- if i < #tab_sessions then
- table.insert(elements, { Text = CLAUDE_CONSTANTS.SPACING_SINGLE })
- end
- end
-
- table.insert(elements, { Text = CLAUDE_CONSTANTS.SPACING_SINGLE })
-end
-
--- タブタイトルを更新する関数
-local function update_tab_titles(window)
- if not window then
- return
- end
-
- local mux_window = window:mux_window()
- if not mux_window then
- return
- end
-
- local tabs = mux_window:tabs()
- if not tabs then
- return
- end
-
- for _, tab in ipairs(tabs) do
- local panes = tab:panes()
- if panes and #panes > 0 then
- local pane = panes[1] -- 最初のペインを使用
- local cwd = pane:get_current_working_dir()
-
- if cwd then
- local cwd_path = cwd.file_path
- local repo_name = get_git_repo_name(cwd_path)
-
- if repo_name then
- -- ブランチ名を取得
- local branch = safe_git_command(cwd_path, 'branch', '--show-current')
- if not branch or branch == '' then
- local ref = safe_git_command(cwd_path, 'symbolic-ref', '--short', 'HEAD')
- if ref then
- branch = ref
- else
- branch = safe_git_command(cwd_path, 'rev-parse', '--short', 'HEAD')
- end
- end
-
- -- タブタイトルを repo_name/branch 形式に設定
- local tab_title = repo_name
- if branch then
- tab_title = repo_name .. '/' .. branch
- end
- tab:set_title(tab_title)
- end
- end
- end
- end
-end
-
--- タブのタイトル表示をカスタマイズ
-wezterm.on('format-tab-title', function(tab, tabs, panes, conf, hover, max_width)
- local background = '#2E3440'
- local foreground = '#D8DEE9'
- local edge_background = '#2E3440'
-
- if tab.is_active or hover then
- background = '#5E81AC'
- foreground = '#ECEFF4'
- end
- local edge_foreground = background
-
- -- タブタイトルを決定
- local title = ''
-
- -- タブのカスタムタイトルをチェック(リポジトリ名が設定されている場合)
- if tab.tab_title and tab.tab_title ~= '' then
- title = tab.tab_title
- else
- -- デフォルトのタイトルを取得してアイコンに変換
- title = process_to_icon(tab.active_pane.title)
- end
-
- -- Claude ステータス(元の無効化状態に戻す)
- local claude_emoji = ''
-
- return {
- { Background = { Color = edge_background } },
- { Foreground = { Color = edge_foreground } },
- { Text = ' ' },
- { Background = { Color = background } },
- { Foreground = { Color = foreground } },
- { Attribute = { Intensity = tab.is_active and 'Bold' or 'Normal' } },
- { Text = ' ' .. title .. claude_emoji .. ' ' },
- { Background = { Color = edge_background } },
- { Foreground = { Color = edge_foreground } },
- { Text = '' },
- }
-end)
-
--- 右ステータスバーの更新
-wezterm.on('update-right-status', function(window, pane)
- local elements = {}
-
- -- Claude ステータスを取得
- local claude_status = get_claude_status(window)
-
- local cwd = pane:get_current_working_dir()
- if not cwd then
- -- Claude ステータスのみ表示
- add_claude_status_to_elements(elements, claude_status.tab_sessions, window)
- window:set_right_status(wezterm.format(elements))
- return
- end
-
- local cwd_path = cwd.file_path
-
- -- Git リポジトリ名を取得
- local repo_name = get_git_repo_name(cwd_path)
-
- if not repo_name then
- -- Git リポジトリでない場合
- local mux_window = window:mux_window()
- if mux_window then
- local active_tab = mux_window:active_tab()
- if active_tab and active_tab:tab_id() == pane:tab():tab_id() then
- active_tab:set_title('')
- end
- end
-
- -- Git リポジトリでない場合は Claude ステータスのみ表示
- add_claude_status_to_elements(elements, claude_status.tab_sessions, window)
- window:set_right_status(wezterm.format(elements))
- return
- end
-
- -- ブランチ名を取得
- local branch = safe_git_command(cwd_path, 'branch', '--show-current')
- if not branch or branch == '' then
- local ref = safe_git_command(cwd_path, 'symbolic-ref', '--short', 'HEAD')
- if ref then
- branch = ref
- else
- branch = safe_git_command(cwd_path, 'rev-parse', '--short', 'HEAD')
- end
- end
-
- -- Git 表示
- if repo_name then
- table.insert(elements, { Foreground = { Color = CLAUDE_CONSTANTS.GIT_ICON_COLOR } })
- table.insert(elements, { Text = CLAUDE_CONSTANTS.SPACING_SMALL })
- table.insert(elements, { Foreground = { Color = CLAUDE_CONSTANTS.GIT_REPO_COLOR } })
- table.insert(elements, { Text = repo_name })
-
- if branch then
- table.insert(elements, { Foreground = { Color = CLAUDE_CONSTANTS.GIT_BRANCH_ICON_COLOR } })
- table.insert(elements, { Text = CLAUDE_CONSTANTS.SPACING_MEDIUM })
- table.insert(elements, { Foreground = { Color = CLAUDE_CONSTANTS.GIT_BRANCH_COLOR } })
- table.insert(elements, { Text = branch })
- end
-
- -- アクティブなタブのタイトルを更新
- local mux_window = window:mux_window()
- if mux_window then
- local active_tab = mux_window:active_tab()
- if active_tab and active_tab:tab_id() == pane:tab():tab_id() then
- -- タブタイトルを repo_name/branch 形式に設定
- local tab_title = repo_name
- if branch then
- tab_title = repo_name .. '/' .. branch
- end
- active_tab:set_title(tab_title)
- end
- end
- end
-
- -- Claude ステータス表示(最後に表示)
- if #claude_status.tab_sessions > 0 then
- table.insert(elements, { Text = CLAUDE_CONSTANTS.SPACING_SMALL })
- end
- add_claude_status_to_elements(elements, claude_status.tab_sessions, window)
-
- window:set_right_status(wezterm.format(elements))
-end)
-
--- タブがアクティブになった時にも更新(即座更新)
-wezterm.on('tab-active', function(tab, pane, window)
- -- すぐに更新をトリガー
- wezterm.emit('update-right-status', window, pane)
-
- -- タブタイトルを更新
- update_tab_titles(window)
-
- -- 少し遅れてもう一度更新(確実性向上)
- wezterm.time.call_after(0.1, function()
- wezterm.emit('update-right-status', window, pane)
- update_tab_titles(window)
- end)
-end)
-
--- ウィンドウフォーカス時にタブタイトルを更新
-wezterm.on('window-focus-changed', function(window, pane)
- update_tab_titles(window)
-end)
-
--- 新しいタブ作成時にタブタイトルを更新
-wezterm.on('new-tab-button-click', function(window, pane, button, default_action)
- wezterm.time.call_after(0.5, function()
- update_tab_titles(window)
- end)
- return false
-end)
-
--- 自動ウィンドウ分割機能
-local function create_auto_split_layout(window, pane)
- -- 現在のペインを基準に分割を実行
- -- まず左右に分割(40:60)
- local right_pane = pane:split {
- direction = 'Right',
- size = 0.6,
- }
-
- -- 右側のペインを上下に分割(70:30)
- wezterm.sleep_ms(100) -- 分割が完了するまで少し待機
- right_pane:split {
- direction = 'Bottom',
- size = 0.3,
- }
-
- -- 元のペイン(左側)にフォーカスを戻す
- pane:activate()
-end
-
--- カスタムコマンド実行時の自動分割
-wezterm.on('user-var-changed', function(window, pane, name, value)
- if name == 'wezterm_auto_split' and value == 'MQ==' then -- MQ== は base64 エンコードされた "1"
- create_auto_split_layout(window, pane)
- end
-end)
-
--- キーバインド設定(tmux.confに準拠)
-config.leader = { key = 'q', mods = 'CTRL', timeout_milliseconds = 1000 }
-config.keys = {
- -- ペイン分割
- { key = '|', mods = 'LEADER', action = act.SplitHorizontal { domain = 'CurrentPaneDomain' } },
- { key = '-', mods = 'LEADER', action = act.SplitVertical { domain = 'CurrentPaneDomain' } },
- { key = 'd', mods = 'CMD', action = act.SplitHorizontal { domain = 'CurrentPaneDomain' } }, -- Cmd+dで縦分割(左右に分割)
- { key = 'w', mods = 'CMD', action = act.CloseCurrentPane { confirm = false } }, -- Cmd+wで現在のペインを閉じる
-
- -- ペイン移動(vim風)
- { key = 'h', mods = 'LEADER', action = act.ActivatePaneDirection 'Left' },
- { key = 'j', mods = 'LEADER', action = act.ActivatePaneDirection 'Down' },
- { key = 'k', mods = 'LEADER', action = act.ActivatePaneDirection 'Up' },
- { key = 'l', mods = 'LEADER', action = act.ActivatePaneDirection 'Right' },
-
- -- ペインのリサイズ
- { key = 'H', mods = 'LEADER', action = act.AdjustPaneSize { 'Left', 5 } },
- { key = 'J', mods = 'LEADER', action = act.AdjustPaneSize { 'Down', 5 } },
- { key = 'K', mods = 'LEADER', action = act.AdjustPaneSize { 'Up', 5 } },
- { key = 'L', mods = 'LEADER', action = act.AdjustPaneSize { 'Right', 5 } },
-
- -- タブ操作
- { key = 'c', mods = 'LEADER', action = act.SpawnTab 'CurrentPaneDomain' },
- { key = 'n', mods = 'LEADER', action = act.ActivateTabRelative(1) },
- { key = 'p', mods = 'LEADER', action = act.ActivateTabRelative(-1) },
- { key = '&', mods = 'LEADER', action = act.CloseCurrentTab { confirm = true } },
-
- -- タブ番号でジャンプ
- { key = '1', mods = 'LEADER', action = act.ActivateTab(0) },
- { key = '2', mods = 'LEADER', action = act.ActivateTab(1) },
- { key = '3', mods = 'LEADER', action = act.ActivateTab(2) },
- { key = '4', mods = 'LEADER', action = act.ActivateTab(3) },
- { key = '5', mods = 'LEADER', action = act.ActivateTab(4) },
- { key = '6', mods = 'LEADER', action = act.ActivateTab(5) },
- { key = '7', mods = 'LEADER', action = act.ActivateTab(6) },
- { key = '8', mods = 'LEADER', action = act.ActivateTab(7) },
- { key = '9', mods = 'LEADER', action = act.ActivateTab(8) },
-
- -- ペインをズーム
- { key = 'z', mods = 'LEADER', action = act.TogglePaneZoomState },
-
- -- コピーモード
- { key = '[', mods = 'LEADER', action = act.ActivateCopyMode },
-
- -- ペインを閉じる
- { key = 'x', mods = 'LEADER', action = act.CloseCurrentPane { confirm = true } },
-
- -- 設定のリロード
- { key = 'r', mods = 'LEADER', action = act.ReloadConfiguration },
-
- -- 自動分割レイアウトを作成するキーバインド
- {
- key = 'w',
- mods = 'LEADER',
- action = wezterm.action_callback(create_auto_split_layout),
- },
-}
-
--- ペインの境界線スタイル
-config.inactive_pane_hsb = {
- saturation = 0.8,
- brightness = 0.7,
-}
-
-return config
diff --git a/CLAUDE.md b/CLAUDE.md
index ad35afd..99556e9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,88 +4,113 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Repository Overview
-This is a dotfiles repository for macOS development environment setup. It manages system configurations, development tools, and application settings through automated installation scripts.
+This is a dotfiles repository for a macOS development environment, managed by [chezmoi](https://www.chezmoi.io/). Dotfiles, run-once installers, and run-onchange configuration scripts are organized using chezmoi's filename conventions (`dot_*`, `private_*`, `executable_*`, `run_once_*`, `run_onchange_*`).
+
+Language runtimes (Flutter, Rust, Node, Ruby) are managed by [mise](https://mise.jdx.dev/) via `dot_config/mise/config.toml`. Homebrew packages are defined in `dot_Brewfile` and installed by `run_onchange_install-brew-packages.sh.tmpl`.
## Commands
### Setup and Installation
+
```bash
-# Install all dotfiles and dependencies
-make all
+# First time on a new machine
+brew install chezmoi
+chezmoi init --apply ry-itto/dotfiles
-# Only install dotfiles (symlinks and settings)
-make install
+# Pull latest changes from the repo and re-apply
+chezmoi update
-# Only install dependencies (brew, tools, etc.)
-make deps
+# See what would change before applying
+chezmoi diff
-# List all dotfiles that will be installed
-make list
+# Apply pending changes
+chezmoi apply
```
-### Manual Installation Scripts
+### Editing Managed Files
+
+After migrating to chezmoi, editing files directly under `$HOME` does **not** sync back to this repository. Always use one of:
+
```bash
-# Install Homebrew dependencies
-/bin/zsh installers/brew.sh
+# Open the source file in $EDITOR
+chezmoi edit ~/.zshrc
+
+# Or jump to the source directory and edit there
+cd "$(chezmoi source-path)"
+$EDITOR dot_zshrc
+chezmoi apply
+```
-# Install development tools via mise
-/bin/zsh installers/mise.sh
+### Adding New Files
-# Install other tools (run as needed)
-/bin/zsh installers/dein.sh # Vim plugin manager
-/bin/zsh installers/xcode.sh # Xcode settings
-/bin/zsh installers/zplug.sh # Zsh plugin manager
+```bash
+# Move an existing $HOME file into chezmoi management
+chezmoi add ~/.somefile
```
## Architecture
-### Core Components
-
-1. **Makefile**: Main orchestrator that:
- - Symlinks dotfiles from repo to home directory
- - Copies `.config/` contents to `~/.config/`
- - Executes installer scripts in sequence
- - Excludes `.DS_Store`, `.git`, `.config`, `.ruby-version`, `.github` from symlinking
-
-2. **Dotfiles** (symlinked to home):
- - `.Brewfile`: Homebrew bundle configuration with all packages
- - `.zshrc`: Main shell configuration that sources modular configs
- - `.tmux.conf`: Tmux configuration
- - `.gitconfig`: Git configuration
- - `.commit_template`: Git commit template
-
-3. **Modular Zsh Configuration** (`.zsh/`):
- - `alias.zsh`: Command aliases and shortcuts
- - `env.zsh`: Environment variables
- - `style.zsh`: Shell appearance and prompt
- - `plugin.zsh`: Zsh plugin management
-
-4. **Installers** (`installers/`):
- - Shell scripts for installing various development tools
- - `mise.sh`: Installs Flutter, Rust, and Vim via mise
- - `dein.sh`: Installs dein.vim plugin manager
- - `xcode.sh`: Configures Xcode settings
- - `zplug.sh`: Installs zplug shell plugin manager
- - Each script checks if tool exists before installation
- - Designed to be idempotent
-
-5. **Settings** (`settings/`):
- - Platform-specific configurations (macOS, VSCode, Xcode)
- - Each has its own `install.sh` script
+### Source Layout
+
+The repository **is** the chezmoi source directory. chezmoi reads filename prefixes to decide where each file goes in `$HOME`:
+
+- `dot_` → `~/.` (e.g. `dot_zshrc` → `~/.zshrc`)
+- `executable_` → preserves +x bit on apply
+- `private_` → applied with mode 0600/0700
+- `.tmpl` → rendered with chezmoi's template engine before apply
+- `run_once_.sh` → executed once per machine
+- `run_onchange_.sh` → executed when the script's content changes
+
+### Top-Level Files
+
+**Managed dotfiles** (chezmoi targets):
+- `dot_zshrc` — entrypoint that sources modules under `~/.zsh/`
+- `dot_zsh/` — modular Zsh config: `alias.zsh`, `env.zsh`, `style.zsh`, `plugin.zsh`, `functions/`, `bin/executable_reload`
+- `dot_gitconfig`, `dot_Brewfile`, `dot_commit_template`
+- `dot_vim/`, `dot_hammerspoon/`, `dot_claude/`
+- `dot_config/nvim/`, `dot_config/starship.toml`, `dot_config/mise/config.toml`
+- `private_Library/private_Application Support/Code/User/settings.json` — VSCode user settings
+
+**Run scripts** (executed during `chezmoi apply`):
+- `run_onchange_install-brew-packages.sh.tmpl` — re-runs when `dot_Brewfile` changes
+- `run_onchange_configure-macos-defaults.sh` — `defaults write` for NSGlobalDomain, Finder, key repeat, Caps Lock → Control
+- `run_onchange_configure-xcode.sh` — `defaults write` for Xcode build settings
+- `run_once_install-zplug.sh` — bootstrap zplug
+- `run_once_install-dein.sh` — bootstrap dein.vim
+- `run_once_install-mise-tools.sh` — runs `mise install` for tools defined in `dot_config/mise/config.toml`
+
+**Configuration**:
+- `.chezmoiignore` — paths chezmoi should skip during apply (README, scripts/, CI files, destination-side local files)
+
+**Repository support files** (excluded from `chezmoi apply` via `.chezmoiignore`):
+- `.github/workflows/ci.yml` — lint, chezmoi-verify
+- `README.md`, `CLAUDE.md`, `LICENSE`
+
+### Run Script Execution Order
+
+`chezmoi apply` runs `run_*` scripts in lexical order of their filename. The current ordering ensures:
+
+1. `run_onchange_configure-macos-defaults.sh`
+2. `run_onchange_configure-xcode.sh`
+3. `run_onchange_install-brew-packages.sh` (installs `mise` via Brewfile)
+4. `run_once_install-dein.sh`
+5. `run_once_install-mise-tools.sh` (skips with a notice if `mise` is not yet on PATH)
+6. `run_once_install-zplug.sh`
+
+If `mise` is not yet installed when `run_once_install-mise-tools.sh` runs, the script exits cleanly. Re-running `chezmoi apply` after the brew bundle finishes will trigger it again.
+
+All run scripts honor `CI=1` and exit early in CI to avoid expensive operations.
## Development Stack
-The repository configures a comprehensive development environment for:
-- **iOS Development**: Xcode tools, XcodeGen, Mint
-- **Flutter Development**: Flutter (via mise), Dart, iOS deployment tools
-- **Web Development**: Node.js (via n), various JS tools
-- **General Development**: Git, GitHub CLI, tmux, neovim, starship prompt, Vim (via mise)
-- **Language Support**: Go, Dart, Ruby, Rust (via mise), Flutter (via mise)
+- **iOS Development**: Xcode, XcodeGen, xcbeautify (Homebrew)
+- **Flutter / Rust / Node / Ruby**: managed by mise (`dot_config/mise/config.toml`)
+- **Web Development**: Node.js (via mise), npm/yarn ecosystem
+- **General**: Git, GitHub CLI, Neovim, Starship prompt
## Key Design Principles
-1. **Modularity**: Configurations are split into logical components
-2. **Idempotency**: Scripts can be run multiple times safely
-3. **Automation**: Single `make` command sets up entire environment
-4. **Version Management**: Uses mise for language version management
-5. **Tool Management**: Homebrew as primary package manager with Brewfile for reproducibility
\ No newline at end of file
+1. **Single source of truth**: chezmoi manages all dotfiles; mise manages all language runtimes.
+2. **macOS-only**: no OS branching. `defaults write` and other macOS-specific commands run unconditionally.
+3. **Idempotency**: `run_once_*` scripts gate themselves on existence checks; `run_onchange_*` scripts re-run only when their content (or referenced files) change.
+4. **Hand-off to upstream tools**: chezmoi delegates package management to Homebrew (`brew bundle`) and mise (`mise install`) rather than reimplementing version logic.
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 7503978..0000000
--- a/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-DOTPATH := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
-CANDIDATES := $(wildcard .??*)
-EXCLUSIONS := .DS_Store .git .config .ruby-version .github
-DOTFILES := $(filter-out $(EXCLUSIONS), $(CANDIDATES))
-DEPS_INSTALLERS := $(wildcard installers/??*)
-SETTINGS_INSTALLERS := $(wildcard settings/**/install.sh)
-
-# 進捗表示用の変数
-STEP := 0
-TOTAL_STEPS := 4
-
-define print_step
- @echo "\n============================================="
- @echo "Step $(STEP)/$(TOTAL_STEPS): $1"
- @echo "=============================================\n"
- @$(eval STEP := $(shell echo $$(($(STEP) + 1))))
-endef
-
-all: install deps
- @echo "\n✨ All tasks completed successfully!\n"
-
-list:
- @$(foreach val, $(DOTFILES), /bin/ls -dF $(val);)
-
-install:
- $(call print_step,Installing dotfiles)
- @echo 'Linking dotfiles to home directory...'
- @$(foreach val, $(DOTFILES), echo " Linking $(val)..." && ln -sfnv $(abspath $(val)) $(HOME)/$(val);)
- @echo 'Creating $(HOME)/.config and copying settings...'
- @mkdir -p $(HOME)/.config && cp -r .config/* $(HOME)/.config
- @echo 'Installing settings...'
- $(foreach val, $(SETTINGS_INSTALLERS), echo " Installing $(val)..." && /bin/zsh $(val);)
-
-deps:
- $(call print_step,Installing dependencies)
- @echo 'Running dependency installers...'
- $(foreach val, $(DEPS_INSTALLERS), echo " Running $(val)..." && /bin/zsh $(val);)
-
-test:
- @echo 'Running tests with Bats...'
- @if command -v bats >/dev/null 2>&1; then \
- bats tests/*.bats; \
- else \
- echo "Bats is not installed. Install with: brew install bats-core"; \
- exit 1; \
- fi
-
-.PHONY: all list install deps test
diff --git a/README.md b/README.md
index ea8fe1f..2ee81bd 100644
--- a/README.md
+++ b/README.md
@@ -1,266 +1,157 @@
# dotfiles
-My personal dotfiles for macOS development environment setup. This repository contains configurations for various development tools and automated installation scripts.
+My personal dotfiles for macOS development environment, managed by [chezmoi](https://www.chezmoi.io/).
## 🚀 Quick Start
```bash
-# Clone the repository
-git clone https://github.com/ry-itto/dotfiles.git ~/ghq/github.com/ry-itto/dotfiles
-cd ~/ghq/github.com/ry-itto/dotfiles
+# Install chezmoi if you don't have it
+brew install chezmoi
-# Install everything (dotfiles + dependencies)
-make all
+# Initialize and apply this repository
+chezmoi init --apply ry-itto/dotfiles
```
## 📋 Requirements
-- macOS (tested on macOS 14+)
+- macOS (this configuration is macOS-only)
- Command Line Tools for Xcode
- Internet connection for downloading packages
-
-## 📦 Package Configuration
-
-> **Tested on**: macOS 15.5 (arm64) - Last updated: 2025-09-01 08:57 UTC
-
-The following packages are defined in `.Brewfile` for installation:
-
-
-🛠️ Homebrew Formulae (30 packages)
-
-
-**zsh関係**
-
-- `zsh` - Z shell - Modern shell with advanced features
-- `zplug` - Zsh plugin manager
-
-**iOS**
-
-- `xcodegen` - Generate Xcode projects from spec files
-- `xcbeautify` - Xcode build output formatter
-
-**Flutter**
-
-- `usbmuxd` - USB multiplexing daemon for iOS devices
-- `libimobiledevice` (latest development version) - iOS device communication library
-- `ideviceinstaller` - Manage iOS apps from command line
-- `ios-deploy` - Install and debug iOS apps from command line
-
-**MCP**
-
-- `uv` - Fast Python package installer and resolver
-
-**ツール**
-
-- `ag` - The Silver Searcher - Fast code searching
-- `gh` - GitHub CLI
-- `ghq` - Git repository organizer
-- `fzf` - Fuzzy finder for command line
-- `jq` - JSON processor
-- `tig` - Text-mode interface for git
-- `tree` - Display directory tree structure
-- `nkf` - Network Kanji Filter - Character encoding converter
-- `git` - Distributed version control system
-- `emojify` - Emoji on the command line
-- `nvim` - Neovim - Hyperextensible Vim-based text editor
-- `tmux` - Terminal multiplexer
-- `starship` - Cross-shell prompt
-- `mise` - Development environment manager (formerly rtx)
-- `act` - Run GitHub Actions locally
-
-**gPRC**
-
-- `protobuf` - Protocol Buffers - Google's data interchange format
-
-**ruby-build**
-
-- `openssl@3` - Cryptography and SSL/TLS toolkit
-- `readline` - GNU readline library
-- `libyaml` - YAML parser and emitter library
-- `autoconf` - Automatic configure script builder
-- `gmp` - GNU multiple precision arithmetic library
-
-
-
-
-🖥️ Homebrew Casks - GUI Applications (12 apps)
-
-
-**Cask**
-
-- `clipy` - Clipboard manager
-- `raycast` - Productivity launcher
-- `font-hack-nerd-font` - Hack font with Nerd Font patches
-- `font-hackgen` - Japanese programming font
-- `font-hackgen-nerd` - HackGen with Nerd Font patches
-- `notion` - All-in-one workspace
-- `notion-calendar` - Calendar app by Notion
-- `figma` - Collaborative design tool
-- `discord` - Voice, video, and text chat
-- `rectangle` - Window management app
-- `hammerspoon` - Desktop automation tool
-- `wezterm` - GPU-accelerated terminal emulator
-
-
-
-
## 🛠 Installation
-### Full Installation (Recommended)
-
-Installs all dotfiles and dependencies:
-
```bash
-make all
+brew install chezmoi
+chezmoi init --apply ry-itto/dotfiles
```
-### Partial Installation
+`chezmoi init --apply` will:
-Install only dotfiles (symlinks and configs):
+1. Clone this repository into `~/.local/share/chezmoi`
+2. Render dotfiles into `$HOME` (e.g. `dot_zshrc` → `~/.zshrc`)
+3. Run `run_onchange_install-brew-packages.sh` to install Homebrew bundle from `~/.Brewfile`
+4. Run `run_onchange_configure-macos-defaults.sh` and `run_onchange_configure-xcode.sh` to apply system defaults
+5. Run `run_once_install-zplug.sh`, `run_once_install-dein.sh` to bootstrap shell/editor plugin managers
+6. Run `run_once_install-mise-tools.sh` to install any tools defined in `dot_config/mise/config.toml` (グローバルでは言語ランタイムを固定しない方針 — 詳細は [プログラミング言語の管理方針](#-プログラミング言語の管理方針))
-```bash
-make install
-```
-
-Install only dependencies (Homebrew, tools, etc.):
+## 🔄 Daily Operations
```bash
-make deps
-```
+# Pull latest changes from this repo and re-apply
+chezmoi update
-### Manual Installation
+# Edit a managed file (opens source file in $EDITOR)
+chezmoi edit ~/.zshrc
-You can also run individual installer scripts as needed:
+# See what would change before applying
+chezmoi diff
-```bash
-# Install Homebrew and packages
-/bin/zsh installers/brew.sh
-
-# Install development tools
-/bin/zsh installers/mise.sh # Version management + Flutter/Rust/Vim
-/bin/zsh installers/dein.sh # Vim plugin manager
-/bin/zsh installers/xcode.sh # Xcode tools
-/bin/zsh installers/zplug.sh # Zsh plugin manager
+# Add a new file from $HOME into management
+chezmoi add ~/.somefile
```
+> **Note:** After migrating to chezmoi, editing files in `$HOME` directly does **not** sync back to this repository. Use `chezmoi edit` or edit the source file under `~/.local/share/chezmoi`, then `chezmoi apply`.
+
## 📂 Directory Structure
```
.
-├── .config/ # Application configs (copied to ~/.config/)
-│ ├── hammerspoon/ # Hammerspoon automation
-│ ├── karabiner/ # Karabiner-Elements key mappings
-│ ├── raycast/ # Raycast shortcuts
-│ ├── starship.toml # Starship prompt config
-│ └── wezterm/ # WezTerm terminal config
-├── .zsh/ # Modular Zsh configuration
-│ ├── alias.zsh # Command aliases
-│ ├── env.zsh # Environment variables
-│ ├── plugin.zsh # Zsh plugins
-│ └── style.zsh # Shell appearance
-├── installers/ # Installation scripts
-├── scripts/ # Utility scripts
-│ ├── collect_environment.sh # Collect system info
-│ └── update_readme.py # Update README with env info
-├── settings/ # Platform-specific settings
-│ ├── macos/ # macOS system preferences
-│ ├── vscode/ # VSCode settings & extensions
-│ └── xcode/ # Xcode themes & templates
-├── .Brewfile # Homebrew bundle definition
-├── .commit_template # Git commit template
-├── .gitconfig # Git configuration
-├── .tmux.conf # Tmux configuration
-├── .zshrc # Main shell configuration
-├── CLAUDE.md # AI assistant instructions
-├── README.md # This file (auto-generated from template)
-├── README.template.md # Template for README generation
-└── Makefile # Installation orchestrator
+├── dot_zshrc # → ~/.zshrc
+├── dot_zsh/ # → ~/.zsh/
+│ ├── alias.zsh
+│ ├── env.zsh
+│ ├── plugin.zsh
+│ ├── style.zsh
+│ ├── functions/
+│ └── bin/executable_reload # → ~/.zsh/bin/reload (chmod +x)
+├── dot_gitconfig
+├── dot_Brewfile
+├── dot_vim/
+├── dot_hammerspoon/
+├── dot_claude/ # → ~/.claude/
+├── dot_config/
+│ ├── ghostty/config
+│ ├── nvim/
+│ ├── starship.toml
+│ └── mise/config.toml # mise settings
+├── private_Library/
+│ └── private_Application Support/Code/User/settings.json # → VSCode settings
+├── run_onchange_install-brew-packages.sh.tmpl
+├── run_onchange_configure-macos-defaults.sh
+├── run_onchange_configure-xcode.sh
+├── run_once_install-zplug.sh
+├── run_once_install-dein.sh
+├── run_once_install-mise-tools.sh
+├── .chezmoiignore # files to skip during apply
+└── .github/workflows/ci.yml
```
## 🎯 What's Included
### Development Tools
-- **Package Managers**: Homebrew, mise (for version management)
+- **Package Managers**: Homebrew, mise
- **Shell**: Zsh with zplug, Starship prompt
-- **Terminal**: WezTerm, tmux
- **Editors**: Neovim, VSCode
-- **Version Control**: Git, GitHub CLI, Lazygit
+- **Version Control**: Git, GitHub CLI
-### Development Stacks
+### Language Runtimes
-- **iOS Development**: Xcode, XcodeGen, Mint, SwiftLint, SwiftFormat
-- **Flutter Development**: Flutter SDK, Dart, FVM
-- **Web Development**: Node.js (via n), npm packages
-- **General**: Go, Rust, Ruby
+[mise](https://mise.jdx.dev/) でプロジェクトごとに管理する。詳細は [プログラミング言語の管理方針](#-プログラミング言語の管理方針) を参照。
-### macOS Applications
+### Other Stacks
-- **Productivity**: Raycast, Hammerspoon, Karabiner-Elements
-- **Development**: Docker, Orbstack, TablePlus, Fork
-- **Communication**: Slack, Discord, Zoom
-- **Utilities**: 1Password, AppCleaner, The Unarchiver
+- **iOS Development**: Xcode, XcodeGen, xcbeautify
+- **macOS Apps**: Ghostty, Raycast, Hammerspoon, Rectangle
## ⚙️ Configuration
### Zsh
-The shell configuration is modular and organized in `.zsh/`:
+Modular configuration in `dot_zsh/`:
- `alias.zsh`: Custom command aliases
- `env.zsh`: Environment variables and PATH setup
-- `plugin.zsh`: Zsh plugin configuration
+- `plugin.zsh`: Zsh plugin configuration via zplug
- `style.zsh`: Prompt and appearance settings
### Git
-Custom Git configuration includes:
-- Commit template for consistent commit messages
-- Useful aliases and shortcuts
-- GitHub CLI integration
+`dot_gitconfig` provides commit template, GitHub CLI helpers, and standard pull/credential settings.
-### Tmux
+### Vim
-Pre-configured with:
-- Custom key bindings
-- Status bar configuration
-- Plugin management via TPM
+Vim setup is wired up via dein.vim. Plugin manifests live under `dot_vim/rc/`.
-### Vim/Neovim
+## 🔧 Customization
-Vim configuration includes:
+1. **Fork this repository** to create your own version
+2. **Edit configurations** under `~/.local/share/chezmoi/` (or via `chezmoi edit`)
+3. **Apply changes** with `chezmoi apply`
+4. **Modify packages** in `dot_Brewfile`
-#### Coc Extensions
+## 🧭 プログラミング言語の管理方針
-Install extensions using:
+この dotfiles では **プログラミング言語ランタイムの共通設定(グローバルバージョン)を持たない** 方針を採る。
-```vim
-:CocInstall {extension-name}
-```
+### ルール
-Included extensions:
-- coc-flutter - Flutter/Dart language support
+- **プロジェクト側で指定されている場合**: そのプロジェクトの `mise.toml` / `.tool-versions` / `.node-version` / `.ruby-version` などに従い、mise(または各プロジェクト指定の方法)で導入する。
+- **その他、ローカルで一時的に必要になった場合**: `mise use -g @` などで都度グローバルに入れる。dotfiles 側にはコミットしない。
+- **dotfiles 管理下の `dot_config/mise/config.toml`**: 言語ランタイムのバージョンは記述しない。mise 自体の設定(例: `idiomatic_version_file_enable_tools`)に限る。
-## 🔧 Customization
+### 理由
-1. **Fork this repository** to create your own version
-2. **Edit configuration files** to match your preferences:
- - Modify `.Brewfile` to add/remove packages
- - Update `.zsh/alias.zsh` for custom aliases
- - Adjust `.gitconfig` with your user information
-3. **Add your own dotfiles** - they'll be automatically symlinked
-4. **Customize installers** in the `installers/` directory
+共通設定でランタイムのバージョンを固定すると、
-## 📝 Make Commands
+- マシンごと・プロジェクトごとのバージョン差異に追従するために dotfiles 側を頻繁に更新することになる
+- プロジェクト側の指定とグローバル指定が衝突したときの優先順位の調整が面倒
-```bash
-make all # Install everything
-make install # Install dotfiles only
-make deps # Install dependencies only
-make list # List all dotfiles to be installed
-make help # Show available commands
-```
+になる。プロジェクト側の指定を常に優先することで、dotfiles を「環境の土台」だけに保つ。
+
+### 例外: Homebrew 経由で入る言語
+
+Homebrew のフォーミュラの依存関係として Python・Ruby などが入ってしまうケースは許容する。`brew bundle` の出力や `/opt/homebrew/Cellar` 配下に入るものは、ツールの動作に必要な副産物とみなす(プロジェクト用途では mise 側を優先する)。
## 🤝 Contributing
@@ -269,7 +160,3 @@ Feel free to open issues or submit pull requests if you have suggestions for imp
## 📄 License
This repository is available under the MIT License. Feel free to fork and modify for your own use.
-
-## 🙏 Acknowledgments
-
-Inspired by the dotfiles community and various developers who share their configurations publicly.
\ No newline at end of file
diff --git a/README.template.md b/README.template.md
deleted file mode 100644
index f9d166f..0000000
--- a/README.template.md
+++ /dev/null
@@ -1,211 +0,0 @@
-# dotfiles
-
-My personal dotfiles for macOS development environment setup. This repository contains configurations for various development tools and automated installation scripts.
-
-## 🚀 Quick Start
-
-```bash
-# Clone the repository
-git clone https://github.com/ry-itto/dotfiles.git ~/ghq/github.com/ry-itto/dotfiles
-cd ~/ghq/github.com/ry-itto/dotfiles
-
-# Install everything (dotfiles + dependencies)
-make all
-```
-
-## 📋 Requirements
-
-- macOS (tested on macOS 14+)
-- Command Line Tools for Xcode
-- Internet connection for downloading packages
-
-
-## 📦 Package Configuration
-
-> **Tested on**: {{OS_NAME}} {{OS_VERSION}} ({{ARCH}}) - Last updated: {{UPDATED_AT}}
-
-The following packages are defined in `.Brewfile` for installation:
-
-
-🛠️ Homebrew Formulae ({{FORMULAE_COUNT}} packages)
-
-{{FORMULAE_LIST}}
-
-
-
-
-🖥️ Homebrew Casks - GUI Applications ({{CASKS_COUNT}} apps)
-
-{{CASKS_LIST}}
-
-
-
-
-## 🛠 Installation
-
-### Full Installation (Recommended)
-
-Installs all dotfiles and dependencies:
-
-```bash
-make all
-```
-
-### Partial Installation
-
-Install only dotfiles (symlinks and configs):
-
-```bash
-make install
-```
-
-Install only dependencies (Homebrew, tools, etc.):
-
-```bash
-make deps
-```
-
-### Manual Installation
-
-You can also run individual installer scripts as needed:
-
-```bash
-# Install Homebrew and packages
-/bin/zsh installers/brew.sh
-
-# Install development tools
-/bin/zsh installers/mise.sh # Version management + Flutter/Rust/Vim
-/bin/zsh installers/dein.sh # Vim plugin manager
-/bin/zsh installers/xcode.sh # Xcode tools
-/bin/zsh installers/zplug.sh # Zsh plugin manager
-```
-
-## 📂 Directory Structure
-
-```
-.
-├── .config/ # Application configs (copied to ~/.config/)
-│ ├── hammerspoon/ # Hammerspoon automation
-│ ├── karabiner/ # Karabiner-Elements key mappings
-│ ├── raycast/ # Raycast shortcuts
-│ ├── starship.toml # Starship prompt config
-│ └── wezterm/ # WezTerm terminal config
-├── .zsh/ # Modular Zsh configuration
-│ ├── alias.zsh # Command aliases
-│ ├── env.zsh # Environment variables
-│ ├── plugin.zsh # Zsh plugins
-│ └── style.zsh # Shell appearance
-├── installers/ # Installation scripts
-├── scripts/ # Utility scripts
-│ ├── collect_environment.sh # Collect system info
-│ └── update_readme.py # Update README with env info
-├── settings/ # Platform-specific settings
-│ ├── macos/ # macOS system preferences
-│ ├── vscode/ # VSCode settings & extensions
-│ └── xcode/ # Xcode themes & templates
-├── .Brewfile # Homebrew bundle definition
-├── .commit_template # Git commit template
-├── .gitconfig # Git configuration
-├── .tmux.conf # Tmux configuration
-├── .zshrc # Main shell configuration
-├── CLAUDE.md # AI assistant instructions
-├── README.md # This file (auto-generated from template)
-├── README.template.md # Template for README generation
-└── Makefile # Installation orchestrator
-```
-
-## 🎯 What's Included
-
-### Development Tools
-
-- **Package Managers**: Homebrew, mise (for version management)
-- **Shell**: Zsh with zplug, Starship prompt
-- **Terminal**: WezTerm, tmux
-- **Editors**: Neovim, VSCode
-- **Version Control**: Git, GitHub CLI, Lazygit
-
-### Development Stacks
-
-- **iOS Development**: Xcode, XcodeGen, Mint, SwiftLint, SwiftFormat
-- **Flutter Development**: Flutter SDK, Dart, FVM
-- **Web Development**: Node.js (via n), npm packages
-- **General**: Go, Rust, Ruby
-
-### macOS Applications
-
-- **Productivity**: Raycast, Hammerspoon, Karabiner-Elements
-- **Development**: Docker, Orbstack, TablePlus, Fork
-- **Communication**: Slack, Discord, Zoom
-- **Utilities**: 1Password, AppCleaner, The Unarchiver
-
-## ⚙️ Configuration
-
-### Zsh
-
-The shell configuration is modular and organized in `.zsh/`:
-
-- `alias.zsh`: Custom command aliases
-- `env.zsh`: Environment variables and PATH setup
-- `plugin.zsh`: Zsh plugin configuration
-- `style.zsh`: Prompt and appearance settings
-
-### Git
-
-Custom Git configuration includes:
-- Commit template for consistent commit messages
-- Useful aliases and shortcuts
-- GitHub CLI integration
-
-### Tmux
-
-Pre-configured with:
-- Custom key bindings
-- Status bar configuration
-- Plugin management via TPM
-
-### Vim/Neovim
-
-Vim configuration includes:
-
-#### Coc Extensions
-
-Install extensions using:
-
-```vim
-:CocInstall {extension-name}
-```
-
-Included extensions:
-- coc-flutter - Flutter/Dart language support
-
-## 🔧 Customization
-
-1. **Fork this repository** to create your own version
-2. **Edit configuration files** to match your preferences:
- - Modify `.Brewfile` to add/remove packages
- - Update `.zsh/alias.zsh` for custom aliases
- - Adjust `.gitconfig` with your user information
-3. **Add your own dotfiles** - they'll be automatically symlinked
-4. **Customize installers** in the `installers/` directory
-
-## 📝 Make Commands
-
-```bash
-make all # Install everything
-make install # Install dotfiles only
-make deps # Install dependencies only
-make list # List all dotfiles to be installed
-make help # Show available commands
-```
-
-## 🤝 Contributing
-
-Feel free to open issues or submit pull requests if you have suggestions for improvements.
-
-## 📄 License
-
-This repository is available under the MIT License. Feel free to fork and modify for your own use.
-
-## 🙏 Acknowledgments
-
-Inspired by the dotfiles community and various developers who share their configurations publicly.
\ No newline at end of file
diff --git a/.Brewfile b/dot_Brewfile
similarity index 90%
rename from .Brewfile
rename to dot_Brewfile
index 4241559..4dafa46 100644
--- a/.Brewfile
+++ b/dot_Brewfile
@@ -25,12 +25,9 @@ brew 'tig'
brew 'tree'
brew 'nkf'
brew 'git'
-brew 'emojify'
brew 'nvim'
-brew 'tmux'
brew 'starship'
brew 'mise'
-brew 'act'
# gPRC
brew 'protobuf'
@@ -43,7 +40,7 @@ brew 'autoconf'
brew 'gmp'
# Cask
-cask 'clipy'
+cask 'ghostty'
cask 'raycast'
cask 'font-hack-nerd-font'
cask 'font-hackgen'
@@ -54,4 +51,3 @@ cask 'figma'
cask 'discord'
cask 'rectangle'
cask 'hammerspoon'
-cask 'wezterm'
diff --git a/.claude/settings.json b/dot_claude/settings.json
similarity index 96%
rename from .claude/settings.json
rename to dot_claude/settings.json
index f98e1a1..e2694a6 100644
--- a/.claude/settings.json
+++ b/dot_claude/settings.json
@@ -10,15 +10,14 @@
"LS(**)",
"MultiEdit(**)",
"Read(**)",
- "TodoRead()",
+ "TodoRead",
"TodoWrite(**)",
"WebFetch(domain:*)",
- "WebSearch(**)",
+ "WebSearch",
"Write(**)",
"mcp__sequentialthinking__sequentialthinking"
],
"deny": [
- "Bash(:(){ :|:& };:)",
"Bash(> /dev/sda*)",
"Bash(apt-get install *)",
"Bash(brew install *)",
diff --git a/dot_config/ghostty/config b/dot_config/ghostty/config
new file mode 100644
index 0000000..8abcb7b
--- /dev/null
+++ b/dot_config/ghostty/config
@@ -0,0 +1,62 @@
+# Ghostty configuration
+# Reference: https://ghostty.org/docs/config/reference
+# 全オプションとデフォルト値は `ghostty +show-config --default --docs` で確認できる。
+# リロード: cmd+shift+,
+
+# ── Font ─────────────────────────────────────────────
+font-family = "HackGen Console NF"
+font-size = 14
+# font-feature = -liga # リガチャを切りたい場合は有効化
+
+# ── Theme / Colors ───────────────────────────────────
+# システムの appearance に追従して light/dark を切替
+background-opacity = 0.95
+background-blur = true
+minimum-contrast = 1.1
+
+# ── Window / Padding ─────────────────────────────────
+window-padding-x = 8
+window-padding-y = 6
+window-padding-balance = true
+window-save-state = always
+macos-titlebar-style = tabs
+
+# ── Cursor ───────────────────────────────────────────
+cursor-style = bar
+cursor-style-blink = false
+mouse-hide-while-typing = true
+
+# ── Shell integration ────────────────────────────────
+shell-integration = detect
+shell-integration-features = cursor,sudo,title
+
+# ── Clipboard / Scroll ───────────────────────────────
+copy-on-select = clipboard
+clipboard-trim-trailing-spaces = true
+clipboard-paste-protection = true
+scrollback-limit = 10000000
+
+# ── macOS specific ───────────────────────────────────
+macos-option-as-alt = true
+macos-non-native-fullscreen = visible-menu
+
+# ── Misc ─────────────────────────────────────────────
+mouse-scroll-multiplier = 2
+confirm-close-surface = false
+window-inherit-working-directory = true
+quit-after-last-window-closed = true
+
+# ── Keybinds ─────────────────────────────────────────
+# Split / Tab を tmux 不要レベルに整える
+keybind = cmd+d=new_split:right
+keybind = cmd+shift+d=new_split:down
+keybind = cmd+w=close_surface
+keybind = cmd+shift+enter=toggle_split_zoom
+keybind = cmd+alt+left=goto_split:left
+keybind = cmd+alt+right=goto_split:right
+keybind = cmd+alt+up=goto_split:up
+keybind = cmd+alt+down=goto_split:down
+keybind = cmd+t=new_tab
+keybind = cmd+shift+]=next_tab
+keybind = cmd+shift+[=previous_tab
+keybind = cmd+k=clear_screen
diff --git a/dot_config/mise/config.toml b/dot_config/mise/config.toml
new file mode 100644
index 0000000..47ea4f7
--- /dev/null
+++ b/dot_config/mise/config.toml
@@ -0,0 +1,2 @@
+[settings]
+idiomatic_version_file_enable_tools = ["ruby"]
diff --git a/.config/nvim/autoload/plugins/nvim_lsp.vim b/dot_config/nvim/autoload/plugins/nvim_lsp.vim
similarity index 100%
rename from .config/nvim/autoload/plugins/nvim_lsp.vim
rename to dot_config/nvim/autoload/plugins/nvim_lsp.vim
diff --git a/.config/nvim/init.vim b/dot_config/nvim/init.vim
similarity index 100%
rename from .config/nvim/init.vim
rename to dot_config/nvim/init.vim
diff --git a/.config/starship.toml b/dot_config/starship.toml
similarity index 100%
rename from .config/starship.toml
rename to dot_config/starship.toml
diff --git a/.gitconfig b/dot_gitconfig
similarity index 87%
rename from .gitconfig
rename to dot_gitconfig
index 7fe11c0..4c4b208 100644
--- a/.gitconfig
+++ b/dot_gitconfig
@@ -1,5 +1,3 @@
-[commit]
- template=~/.commit_template
[user]
email = 30540303+ry-itto@users.noreply.github.com
name = ryoya ito
diff --git a/.hammerspoon/init.lua b/dot_hammerspoon/init.lua
similarity index 67%
rename from .hammerspoon/init.lua
rename to dot_hammerspoon/init.lua
index 49cacc4..d9979cf 100644
--- a/.hammerspoon/init.lua
+++ b/dot_hammerspoon/init.lua
@@ -1,6 +1,6 @@
--- WezTermの表示/非表示を切り替える関数
-local open_wezterm = function()
- local appName = "WezTerm"
+-- Terminal App の表示/非表示を切り替える関数
+local open_terminal = function()
+ local appName = "Ghostty"
local app = hs.application.get(appName)
if app == nil or app:isHidden() or not(app:isFrontmost()) then
@@ -12,9 +12,9 @@ local open_wezterm = function()
end
end
--- ホットキーの設定: Alt+Space (Option+Space) でWezTermの表示/非表示を切り替え
+-- ホットキーの設定: Alt+Space (Option+Space) でTerminalの表示/非表示を切り替え
-- tmux.confのプレフィックスキー(Ctrl+q)と競合しないように設定
-hs.hotkey.bind({"alt"}, "space", open_wezterm)
+hs.hotkey.bind({"alt"}, "space", open_terminal)
-- Hammerspoonの設定リロード用ホットキー: Cmd+Alt+Ctrl+R
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()
@@ -22,4 +22,4 @@ hs.hotkey.bind({"cmd", "alt", "ctrl"}, "R", function()
end)
-- 設定がリロードされた時の通知
-hs.alert.show("Hammerspoon config loaded")
\ No newline at end of file
+hs.alert.show("Hammerspoon config loaded")
diff --git a/.vim/README.md b/dot_vim/README.md
similarity index 100%
rename from .vim/README.md
rename to dot_vim/README.md
diff --git a/.vim/ftdetect/cocoapods.vim b/dot_vim/ftdetect/cocoapods.vim
similarity index 100%
rename from .vim/ftdetect/cocoapods.vim
rename to dot_vim/ftdetect/cocoapods.vim
diff --git a/.vim/ftdetect/fastlane.vim b/dot_vim/ftdetect/fastlane.vim
similarity index 100%
rename from .vim/ftdetect/fastlane.vim
rename to dot_vim/ftdetect/fastlane.vim
diff --git a/.vim/ftplugin/dart.vim b/dot_vim/ftplugin/dart.vim
similarity index 100%
rename from .vim/ftplugin/dart.vim
rename to dot_vim/ftplugin/dart.vim
diff --git a/.vim/ftplugin/ruby.vim b/dot_vim/ftplugin/ruby.vim
similarity index 100%
rename from .vim/ftplugin/ruby.vim
rename to dot_vim/ftplugin/ruby.vim
diff --git a/.vim/ftplugin/yaml.vim b/dot_vim/ftplugin/yaml.vim
similarity index 100%
rename from .vim/ftplugin/yaml.vim
rename to dot_vim/ftplugin/yaml.vim
diff --git a/.vim/init/bindings.vim b/dot_vim/init/bindings.vim
similarity index 100%
rename from .vim/init/bindings.vim
rename to dot_vim/init/bindings.vim
diff --git a/.vim/init/setting.vim b/dot_vim/init/setting.vim
similarity index 100%
rename from .vim/init/setting.vim
rename to dot_vim/init/setting.vim
diff --git a/.vim/rc/dein.toml b/dot_vim/rc/dein.toml
similarity index 100%
rename from .vim/rc/dein.toml
rename to dot_vim/rc/dein.toml
diff --git a/.vim/rc/dein_lazy.toml b/dot_vim/rc/dein_lazy.toml
similarity index 100%
rename from .vim/rc/dein_lazy.toml
rename to dot_vim/rc/dein_lazy.toml
diff --git a/.zsh/alias.zsh b/dot_zsh/alias.zsh
similarity index 100%
rename from .zsh/alias.zsh
rename to dot_zsh/alias.zsh
diff --git a/.zsh/bin/reload b/dot_zsh/bin/executable_reload
similarity index 100%
rename from .zsh/bin/reload
rename to dot_zsh/bin/executable_reload
diff --git a/.zsh/env.zsh b/dot_zsh/env.zsh
similarity index 97%
rename from .zsh/env.zsh
rename to dot_zsh/env.zsh
index 343c47a..a46e742 100644
--- a/.zsh/env.zsh
+++ b/dot_zsh/env.zsh
@@ -130,7 +130,7 @@ export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
export N_PREFIX=$HOME/.n
export PATH="$PATH:$N_PREFIX/bin"
-eval "$(wt shell-init)"
-export WT_WORKTREE_DIR=.worktree
-
eval "$(mise activate zsh)"
+eval "$(wtp shell-init zsh)"
+
+export PATH="$HOME/Applications/google-cloud-sdk/bin:$PATH"
diff --git a/.zsh/functions/functions.zsh b/dot_zsh/functions/functions.zsh
similarity index 80%
rename from .zsh/functions/functions.zsh
rename to dot_zsh/functions/functions.zsh
index 64dc70a..30b1f1e 100644
--- a/.zsh/functions/functions.zsh
+++ b/dot_zsh/functions/functions.zsh
@@ -2,3 +2,4 @@ source $HOME/.zsh/functions/fzf-git.sh
source $HOME/.zsh/functions/git.zsh
source $HOME/.zsh/functions/github.zsh
source $HOME/.zsh/functions/ghq.zsh
+source $HOME/.zsh/functions/wtp.zsh
diff --git a/.zsh/functions/fzf-git.sh b/dot_zsh/functions/fzf-git.sh
similarity index 100%
rename from .zsh/functions/fzf-git.sh
rename to dot_zsh/functions/fzf-git.sh
diff --git a/.zsh/functions/ghq.zsh b/dot_zsh/functions/ghq.zsh
similarity index 100%
rename from .zsh/functions/ghq.zsh
rename to dot_zsh/functions/ghq.zsh
diff --git a/.zsh/functions/git.zsh b/dot_zsh/functions/git.zsh
similarity index 100%
rename from .zsh/functions/git.zsh
rename to dot_zsh/functions/git.zsh
diff --git a/.zsh/functions/github.zsh b/dot_zsh/functions/github.zsh
similarity index 100%
rename from .zsh/functions/github.zsh
rename to dot_zsh/functions/github.zsh
diff --git a/dot_zsh/functions/wtp.zsh b/dot_zsh/functions/wtp.zsh
new file mode 100644
index 0000000..fe70e35
--- /dev/null
+++ b/dot_zsh/functions/wtp.zsh
@@ -0,0 +1,11 @@
+wtp-cd() {
+ local dir="$(wtp list -q -c | awk '!/worktree|..\//' | fzf)"
+ if [ -z "$dir" ]
+ then
+ echo "no directories found for $1"
+ return 1
+ fi
+
+ wtp cd "$dir"
+ return
+}
diff --git a/.zsh/plugin.zsh b/dot_zsh/plugin.zsh
similarity index 100%
rename from .zsh/plugin.zsh
rename to dot_zsh/plugin.zsh
diff --git a/.zsh/style.zsh b/dot_zsh/style.zsh
similarity index 100%
rename from .zsh/style.zsh
rename to dot_zsh/style.zsh
diff --git a/.zshrc b/dot_zshrc
similarity index 100%
rename from .zshrc
rename to dot_zshrc
diff --git a/installers/dein.sh b/installers/dein.sh
deleted file mode 100755
index 9ad9607..0000000
--- a/installers/dein.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-# Install dein
-INSTALLATION_DIR=$HOME/.cache/dein
-
-tmp_dir=$(mktemp -d)
-
-curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > $tmp_dir/installer.sh
-chmod u+x $tmp_dir/installer.sh
-
-$tmp_dir/installer.sh $INSTALLATION_DIR
-
-trap 'rm -rf "$tmp_dir"' EXIT
diff --git a/installers/mise.sh b/installers/mise.sh
deleted file mode 100755
index 21f3b5d..0000000
--- a/installers/mise.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/zsh
-
-# Install tools via mise
-if type "mise" > /dev/null; then
- echo "Installing tools via mise..."
-
- # Install tools defined in .mise.toml
- mise install
-
- # Set global versions
- mise use --global flutter@latest
- mise use --global rust@stable
- mise use --global vim@latest
-
- echo "Mise tools installed successfully"
-else
- echo "mise is not installed. Please install it via Homebrew first."
- exit 1
-fi
\ No newline at end of file
diff --git a/installers/xcode.sh b/installers/xcode.sh
deleted file mode 100755
index 6939170..0000000
--- a/installers/xcode.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-if ! type "xcodebuild" > /dev/null ; then
- xcodes install --latest
-fi
-
-SCRIPT_DIR=$(cd $(dirname $0); pwd)
-
-# Xcode 上でビルド時間を表示する
-defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
-
-# コア数を取得
-cores=`system_profiler SPHardwareDataType | grep -o 'Cores:\s\d' | sed -e 's/[^0-9]//g'`
-# アプリをビルドする時に並列でビルドする
-defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks $cores
-# Including whitespace-only lines 空白しかない行の空白を削除
-defaults write com.apple.dt.Xcode DVTTextEditorTrimWhitespaceOnlyLines -bool true
diff --git a/installers/zplug.sh b/installers/zplug.sh
deleted file mode 100755
index 3918fa4..0000000
--- a/installers/zplug.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-if [ ! -d $ZPLUG_HOME ]; then
- curl -sL --proto-redir -all,https https://raw.githubusercontent.com/zplug/installer/master/installer.zsh | zsh
-fi
-
diff --git a/settings/vscode/settings.json b/private_Library/private_Application Support/Code/User/settings.json
similarity index 100%
rename from settings/vscode/settings.json
rename to private_Library/private_Application Support/Code/User/settings.json
diff --git a/run_once_install-dein.sh b/run_once_install-dein.sh
new file mode 100755
index 0000000..a9fc50e
--- /dev/null
+++ b/run_once_install-dein.sh
@@ -0,0 +1,15 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
+INSTALLATION_DIR="$HOME/.cache/dein"
+
+if [ -d "$INSTALLATION_DIR" ]; then
+ exit 0
+fi
+
+tmp_dir=$(mktemp -d)
+trap 'rm -rf "$tmp_dir"' EXIT
+
+sh -c "$(curl -fsSL https://raw.githubusercontent.com/Shougo/dein-installer.vim/master/installer.sh)"
diff --git a/run_once_install-mise-tools.sh b/run_once_install-mise-tools.sh
new file mode 100755
index 0000000..97dbbf8
--- /dev/null
+++ b/run_once_install-mise-tools.sh
@@ -0,0 +1,11 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
+if ! command -v mise >/dev/null 2>&1; then
+ echo 'mise is not installed yet. Skipping tool install. Run `chezmoi apply` again after `brew bundle --global` finishes.'
+ exit 0
+fi
+
+mise install
diff --git a/run_once_install-zplug.sh b/run_once_install-zplug.sh
new file mode 100755
index 0000000..bbdafac
--- /dev/null
+++ b/run_once_install-zplug.sh
@@ -0,0 +1,8 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
+if [ ! -d "${ZPLUG_HOME:-$HOME/.zplug}" ]; then
+ curl -sL --proto-redir -all,https https://raw.githubusercontent.com/zplug/installer/master/installer.zsh | zsh
+fi
diff --git a/settings/macos/install.sh b/run_onchange_configure-macos-defaults.sh
similarity index 64%
rename from settings/macos/install.sh
rename to run_onchange_configure-macos-defaults.sh
index 462d99c..3d75051 100755
--- a/settings/macos/install.sh
+++ b/run_onchange_configure-macos-defaults.sh
@@ -1,18 +1,23 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
# NSGlobalDomain
-# 全ての拡張子のファイルを表示
+# Show all file extensions
defaults write NSGlobalDomain AppleShowAllExtensions -bool true
-# キーのリピート速度
+# Key repeat speed
defaults write NSGlobalDomain KeyRepeat -int 2
-# キーのリピートまでの速度
+# Initial key repeat delay
defaults write NSGlobalDomain InitialKeyRepeat -int 25
-# Finderで隠しファイルを表示
+# Show hidden files in Finder
defaults write com.apple.finder AppleShowAllFiles TRUE
-# キーの押し込み時連続入力をさせる
+# Disable press-and-hold for keys (allows key repeat)
defaults write -g ApplePressAndHoldEnabled -bool false
-# Caps LockキーをControlキーとして使用
+# Map Caps Lock to Control
defaults write com.apple.keyboard.modifiermapping.1452-640-0 -array-add '{ "HIDKeyboardModifierMappingSrc" = 2; "HIDKeyboardModifierMappingDst" = 4; }'
diff --git a/run_onchange_configure-xcode.sh b/run_onchange_configure-xcode.sh
new file mode 100755
index 0000000..3ed6f4d
--- /dev/null
+++ b/run_onchange_configure-xcode.sh
@@ -0,0 +1,18 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
+if ! type "xcodebuild" > /dev/null; then
+ xcodes install --latest
+fi
+
+# Show build duration in Xcode
+defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
+
+# Use all available cores for parallel builds
+cores=$(sysctl -n hw.ncpu)
+defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks "$cores"
+
+# Trim whitespace-only lines
+defaults write com.apple.dt.Xcode DVTTextEditorTrimWhitespaceOnlyLines -bool true
diff --git a/installers/brew.sh b/run_onchange_install-brew-packages.sh.tmpl
similarity index 57%
rename from installers/brew.sh
rename to run_onchange_install-brew-packages.sh.tmpl
index 0cf80f9..8c1fbf1 100755
--- a/installers/brew.sh
+++ b/run_onchange_install-brew-packages.sh.tmpl
@@ -1,12 +1,18 @@
+#!/bin/zsh
+set -eu
+
+[[ -n "${CI:-}" ]] && exit 0
+
if ! type "brew" > /dev/null; then
echo '`brew` not found. Install Homebrew'
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
-brew bundle check --global
-if [ $? -ne 0 ]; then
+if brew bundle check --global; then
+ echo '`brew` dependencies are satisfied'
+else
echo 'Install `brew` dependencies...'
brew bundle --global -v
-else
- echo '`brew` dependencies are satisfied :smile:' | emojify
fi
+
+# brewfile hash: {{ include "dot_Brewfile" | sha256sum }}
diff --git a/scripts/parse_brewfile.py b/scripts/parse_brewfile.py
deleted file mode 100755
index 897ef04..0000000
--- a/scripts/parse_brewfile.py
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/env python3
-
-import re
-import json
-from pathlib import Path
-
-def parse_brewfile():
- """Parse .Brewfile and extract package information with categories"""
- brewfile_path = Path(__file__).parent.parent / ".Brewfile"
-
- if not brewfile_path.exists():
- return {"formulae": {}, "casks": {}}
-
- with open(brewfile_path, 'r') as f:
- lines = f.readlines()
-
- result = {
- "formulae": {},
- "casks": {}
- }
-
- current_category = "Uncategorized"
-
- for line in lines:
- line = line.strip()
-
- # Skip empty lines
- if not line:
- continue
-
- # Detect category comments
- if line.startswith('#'):
- current_category = line[1:].strip()
- continue
-
- # Parse brew formula
- brew_match = re.match(r'brew\s+[\'"]([^\'",]+)[\'"](?:,\s*args:\s*\[(.*?)\])?', line)
- if brew_match:
- package = brew_match.group(1)
- args = brew_match.group(2)
-
- if current_category not in result["formulae"]:
- result["formulae"][current_category] = []
-
- package_info = {"name": package}
-
- # Check for specific version or special args
- if args:
- if "HEAD" in args:
- package_info["version"] = "HEAD"
- # Add other args parsing if needed
- package_info["args"] = args
-
- result["formulae"][current_category].append(package_info)
- continue
-
- # Parse cask
- cask_match = re.match(r'cask\s+[\'"]([^\'",]+)[\'"]', line)
- if cask_match:
- package = cask_match.group(1)
-
- if current_category not in result["casks"]:
- result["casks"][current_category] = []
-
- result["casks"][current_category].append({"name": package})
-
- return result
-
-def get_package_descriptions():
- """Define descriptions for common packages"""
- return {
- # Shell & Terminal
- "zsh": "Z shell - Modern shell with advanced features",
- "zplug": "Zsh plugin manager",
- "tmux": "Terminal multiplexer",
- "starship": "Cross-shell prompt",
- "wezterm": "GPU-accelerated terminal emulator",
-
- # iOS Development
- "xcodegen": "Generate Xcode projects from spec files",
- "xcbeautify": "Xcode build output formatter",
- "mint": "Swift package manager for command line tools",
-
- # Flutter/Mobile
- "usbmuxd": "USB multiplexing daemon for iOS devices",
- "libimobiledevice": "iOS device communication library",
- "ideviceinstaller": "Manage iOS apps from command line",
- "ios-deploy": "Install and debug iOS apps from command line",
-
- # Version Management
- "mise": "Development environment manager (formerly rtx)",
- "n": "Node.js version manager",
-
- # Git & Source Control
- "git": "Distributed version control system",
- "gh": "GitHub CLI",
- "ghq": "Git repository organizer",
- "tig": "Text-mode interface for git",
- "lazygit": "Terminal UI for git commands",
-
- # Search & Navigation
- "ag": "The Silver Searcher - Fast code searching",
- "fzf": "Fuzzy finder for command line",
- "tree": "Display directory tree structure",
-
- # Development Tools
- "nvim": "Neovim - Hyperextensible Vim-based text editor",
- "jq": "JSON processor",
- "nkf": "Network Kanji Filter - Character encoding converter",
- "emojify": "Emoji on the command line",
- "act": "Run GitHub Actions locally",
- "uv": "Fast Python package installer and resolver",
-
- # Language Support
- "protobuf": "Protocol Buffers - Google's data interchange format",
- "openssl@3": "Cryptography and SSL/TLS toolkit",
- "readline": "GNU readline library",
- "libyaml": "YAML parser and emitter library",
- "autoconf": "Automatic configure script builder",
- "gmp": "GNU multiple precision arithmetic library",
-
- # GUI Applications
- "clipy": "Clipboard manager",
- "raycast": "Productivity launcher",
- "rectangle": "Window management app",
- "hammerspoon": "Desktop automation tool",
- "notion": "All-in-one workspace",
- "notion-calendar": "Calendar app by Notion",
- "figma": "Collaborative design tool",
- "discord": "Voice, video, and text chat",
-
- # Fonts
- "font-hack-nerd-font": "Hack font with Nerd Font patches",
- "font-hackgen": "Japanese programming font",
- "font-hackgen-nerd": "HackGen with Nerd Font patches",
- }
-
-def main():
- """Main function to output parsed Brewfile data"""
- data = parse_brewfile()
- descriptions = get_package_descriptions()
-
- # Enhance with descriptions
- for category, packages in data["formulae"].items():
- for package in packages:
- if package["name"] in descriptions:
- package["description"] = descriptions[package["name"]]
-
- for category, packages in data["casks"].items():
- for package in packages:
- if package["name"] in descriptions:
- package["description"] = descriptions[package["name"]]
-
- print(json.dumps(data, indent=2, ensure_ascii=False))
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/scripts/update_readme.py b/scripts/update_readme.py
deleted file mode 100755
index ca26580..0000000
--- a/scripts/update_readme.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env python3
-
-import json
-import sys
-import subprocess
-import platform
-from datetime import datetime
-from pathlib import Path
-
-def run_command(cmd):
- """Run a shell command and return output"""
- try:
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
- return result.stdout.strip()
- except Exception as e:
- print(f"Error running command '{cmd}': {e}", file=sys.stderr)
- return None
-
-def get_system_info():
- """Get basic system information"""
- try:
- # Get macOS version using sw_vers command
- os_name = run_command('sw_vers -productName') or platform.system()
- os_version = run_command('sw_vers -productVersion') or platform.release()
- except:
- # Fallback to platform module
- os_name = platform.system()
- os_version = platform.release()
-
- return {
- 'os': os_name,
- 'version': os_version,
- 'arch': platform.machine(),
- 'updated_at': datetime.utcnow().isoformat() + 'Z'
- }
-
-def parse_brewfile():
- """Parse .Brewfile to get intended packages"""
- script_path = Path(__file__).parent / "parse_brewfile.py"
- output = run_command(f"python3 {script_path}")
- if output:
- try:
- return json.loads(output)
- except json.JSONDecodeError as e:
- print(f"Error parsing Brewfile JSON: {e}", file=sys.stderr)
- return {"formulae": {}, "casks": {}}
-
-def format_brewfile_packages(brewfile_data, package_type="formulae"):
- """Format packages from Brewfile with categories and descriptions"""
- packages = brewfile_data.get(package_type, {})
-
- if not packages:
- return f"*No {package_type} defined*"
-
- lines = []
-
- for category, items in packages.items():
- if not items:
- continue
-
- # Add category header
- lines.append(f"\n**{category}**\n")
-
- for item in items:
- name = item['name']
- description = item.get('description', '')
- version = item.get('version', '')
-
- # Format the line
- if version and version != 'unknown':
- if version == 'HEAD':
- line = f"- `{name}` (latest development version)"
- else:
- line = f"- `{name}` (v{version})"
- else:
- line = f"- `{name}`"
-
- # Add description if available
- if description:
- line += f" - {description}"
-
- lines.append(line)
-
- return "\n".join(lines)
-
-def update_readme(system_info, brewfile_data):
- """Update README.md with environment information"""
- template_path = Path(__file__).parent.parent / "README.template.md"
- readme_path = Path(__file__).parent.parent / "README.md"
-
- if not template_path.exists():
- print(f"Template file not found: {template_path}", file=sys.stderr)
- return False
-
- # Read template
- with open(template_path, 'r') as f:
- content = f.read()
-
- # Count total packages from Brewfile
- formulae_count = sum(len(items) for items in brewfile_data.get('formulae', {}).values())
- casks_count = sum(len(items) for items in brewfile_data.get('casks', {}).values())
-
- # Prepare replacement values
- replacements = {
- "{{OS_NAME}}": system_info['os'],
- "{{OS_VERSION}}": system_info['version'],
- "{{ARCH}}": system_info['arch'],
- "{{UPDATED_AT}}": datetime.fromisoformat(system_info['updated_at'].replace('Z', '+00:00')).strftime('%Y-%m-%d %H:%M UTC'),
- "{{FORMULAE_COUNT}}": str(formulae_count),
- "{{FORMULAE_LIST}}": format_brewfile_packages(brewfile_data, "formulae"),
- "{{CASKS_COUNT}}": str(casks_count),
- "{{CASKS_LIST}}": format_brewfile_packages(brewfile_data, "casks")
- }
-
- # Replace placeholders
- for placeholder, value in replacements.items():
- content = content.replace(placeholder, value)
-
- # Write updated README
- with open(readme_path, 'w') as f:
- f.write(content)
-
- print(f"✅ README.md updated successfully")
- print(f" - Environment: {system_info['os']} {system_info['version']} ({system_info['arch']})")
- print(f" - {formulae_count} Homebrew formulae defined in .Brewfile")
- print(f" - {casks_count} Homebrew casks defined in .Brewfile")
-
- return True
-
-def main():
- """Main function"""
- print("📊 Getting system information...")
- system_info = get_system_info()
-
- print("📦 Parsing .Brewfile...")
- brewfile_data = parse_brewfile()
-
- print("📝 Updating README.md...")
- if update_readme(system_info, brewfile_data):
- sys.exit(0)
- else:
- sys.exit(1)
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/settings/vscode/install-extensions.sh b/settings/vscode/install-extensions.sh
deleted file mode 100755
index 7acb978..0000000
--- a/settings/vscode/install-extensions.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-if ! type 'code' > /dev/null; then
- echo 'Please install vscode and activate `code` command.'
- exit 1
-fi
-
-pkglist=(
-bungcip.better-toml
-CoenraadS.bracket-pair-colorizer
-daylerees.rainglow
-dbaeumer.vscode-eslint
-eamodio.gitlens
-elmTooling.elm-ls-vscode
-esbenp.prettier-vscode
-MS-CEINTL.vscode-language-pack-ja
-ms-python.python
-rust-lang.rust
-shd101wyy.markdown-preview-enhanced
-streetsidesoftware.code-spell-checker
-toba.vsfire
-VisualStudioExptTeam.vscodeintellicode
-vscode-icons-team.vscode-icons
-vscodevim.vim
-yzane.markdown-pdf
-)
-
-for i in ${pkglist[@]}; do
- code --install-extension $i
-done
diff --git a/settings/vscode/install.sh b/settings/vscode/install.sh
deleted file mode 100755
index 5a8472e..0000000
--- a/settings/vscode/install.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-SCRIPT_DIR=$(cd $(dirname $0) && pwd)
-VSCODE_DOT_DIRECTORY=$HOME/.vscode
-VSCODE_SETTING_DIR=$HOME/Library/Application\ Support/Code/User
-VSCODE_SETTING_FILE=settings.json
-
-ln -s "$SCRIPT_DIR/$VSCODE_SETTING_FILE" "$VSCODE_DOT_DIRECTORY/$VSCODE_SETTING_FILE"
-
-rm "$VSCODE_SETTING_DIR/$VSCODE_SETTING_FILE"
-ln -s "$VSCODE_DOT_DIRECTORY/$VSCODE_SETTING_FILE" "$VSCODE_SETTING_DIR/$VSCODE_SETTING_FILE"
-
-$SCRIPT_DIR/install-extensions.sh
diff --git a/settings/xcode/install.sh b/settings/xcode/install.sh
deleted file mode 100644
index 6939170..0000000
--- a/settings/xcode/install.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-if ! type "xcodebuild" > /dev/null ; then
- xcodes install --latest
-fi
-
-SCRIPT_DIR=$(cd $(dirname $0); pwd)
-
-# Xcode 上でビルド時間を表示する
-defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
-
-# コア数を取得
-cores=`system_profiler SPHardwareDataType | grep -o 'Cores:\s\d' | sed -e 's/[^0-9]//g'`
-# アプリをビルドする時に並列でビルドする
-defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks $cores
-# Including whitespace-only lines 空白しかない行の空白を削除
-defaults write com.apple.dt.Xcode DVTTextEditorTrimWhitespaceOnlyLines -bool true
diff --git a/tests/configuration.bats b/tests/configuration.bats
deleted file mode 100644
index eecedea..0000000
--- a/tests/configuration.bats
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env bats
-
-load test_helper
-
-@test ".Brewfile exists and is valid" {
- assert_file_exists "${REPO_ROOT}/.Brewfile"
-
- # Check basic Brewfile syntax
- # Should contain brew commands
- grep -q "^brew " "${REPO_ROOT}/.Brewfile"
-}
-
-@test ".Brewfile contains expected packages" {
- local brewfile="${REPO_ROOT}/.Brewfile"
-
- # Check for some essential packages
- grep -q "brew 'zsh'" "${brewfile}"
- grep -q "brew 'git'" "${brewfile}"
- grep -q "brew 'tmux'" "${brewfile}"
-}
-
-@test ".zshrc exists and sources modular configs" {
- assert_file_exists "${REPO_ROOT}/.zshrc"
-
- # Check that .zshrc sources the modular config files
- grep -q "source.*\.zsh/.*\.zsh" "${REPO_ROOT}/.zshrc" || \
- grep -q "\. .*\.zsh/.*\.zsh" "${REPO_ROOT}/.zshrc" || \
- grep -q "for.*\.zsh.*source" "${REPO_ROOT}/.zshrc"
-}
-
-@test "modular zsh configs exist" {
- assert_dir_exists "${REPO_ROOT}/.zsh"
-
- # Check for expected modular config files
- local expected_configs=(
- "alias.zsh"
- "env.zsh"
- "style.zsh"
- "plugin.zsh"
- )
-
- for config in "${expected_configs[@]}"; do
- if [[ ! -f "${REPO_ROOT}/.zsh/${config}" ]]; then
- echo "Warning: Expected config ${config} not found"
- # Not a failure, just a warning
- fi
- done
-}
-
-@test ".gitconfig exists and has valid structure" {
- assert_file_exists "${REPO_ROOT}/.gitconfig"
-
- # Check for basic git config structure
- grep -q "\[user\]" "${REPO_ROOT}/.gitconfig" || \
- grep -q "\[core\]" "${REPO_ROOT}/.gitconfig" || \
- grep -q "\[alias\]" "${REPO_ROOT}/.gitconfig"
-}
-
-@test ".tmux.conf exists" {
- assert_file_exists "${REPO_ROOT}/.tmux.conf"
-}
-
-@test "commit template exists if referenced" {
- if grep -q "template.*=.*\.commit_template" "${REPO_ROOT}/.gitconfig" 2>/dev/null; then
- assert_file_exists "${REPO_ROOT}/.commit_template"
- fi
-}
-
-@test "zsh configs have valid syntax" {
- # Test main .zshrc
- run zsh -n "${REPO_ROOT}/.zshrc"
- if [ "$status" -ne 0 ]; then
- echo "Syntax error in .zshrc: ${output}"
- false
- fi
-
- # Test modular configs if they exist
- if [[ -d "${REPO_ROOT}/.zsh" ]]; then
- for config in "${REPO_ROOT}"/.zsh/*.zsh; do
- if [[ -f "${config}" ]]; then
- run zsh -n "${config}"
- if [ "$status" -ne 0 ]; then
- echo "Syntax error in $(basename "${config}"): ${output}"
- false
- fi
- fi
- done
- fi
-}
-
-@test "no hardcoded absolute paths in configs" {
- # Check for common hardcoded paths that should be avoided
- local configs=(
- "${REPO_ROOT}/.zshrc"
- "${REPO_ROOT}/.gitconfig"
- "${REPO_ROOT}/.tmux.conf"
- )
-
- for config in "${configs[@]}"; do
- if [[ -f "${config}" ]]; then
- # Skip checking for /usr, /bin, /opt as these are system paths
- # Check for hardcoded user home paths
- if grep -q "/Users/[^/]*/" "${config}" | grep -v "^#"; then
- echo "Warning: Possible hardcoded user path in $(basename "${config}")"
- # Just a warning, not a failure
- fi
- fi
- done
-}
-
-@test "CLAUDE.md exists and contains setup instructions" {
- if [[ -f "${REPO_ROOT}/CLAUDE.md" ]]; then
- # Check that CLAUDE.md contains key sections
- grep -qi "repository overview\|overview" "${REPO_ROOT}/CLAUDE.md"
- grep -qi "command\|setup\|install" "${REPO_ROOT}/CLAUDE.md"
- else
- echo "Warning: CLAUDE.md not found"
- fi
-}
\ No newline at end of file
diff --git a/tests/installers.bats b/tests/installers.bats
deleted file mode 100644
index 9beb23c..0000000
--- a/tests/installers.bats
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env bats
-
-load test_helper
-
-@test "all installer scripts exist" {
- local installers=(
- "brew.sh"
- "mise.sh"
- "dein.sh"
- "xcode.sh"
- "zplug.sh"
- )
-
- for installer in "${installers[@]}"; do
- assert_file_exists "${REPO_ROOT}/installers/${installer}"
- done
-}
-
-@test "all installer scripts have valid zsh syntax" {
- local installers="${REPO_ROOT}"/installers/*.sh
-
- for installer in ${installers}; do
- run zsh -n "${installer}"
- if [ "$status" -ne 0 ]; then
- echo "Syntax error in ${installer}: ${output}"
- false
- fi
- done
-}
-
-@test "all installer scripts are executable or run with zsh" {
- local installers="${REPO_ROOT}"/installers/*.sh
-
- for installer in ${installers}; do
- # Check if file has shebang
- local first_line=$(head -n1 "${installer}")
- if [[ "${first_line}" =~ ^#! ]]; then
- # If it has shebang, it should be executable
- if [[ ! -x "${installer}" ]]; then
- echo "Script ${installer} has shebang but is not executable"
- # This is just a warning, not a failure
- fi
- fi
- done
-}
-
-@test "brew.sh checks for Homebrew installation" {
- local brew_script="${REPO_ROOT}/installers/brew.sh"
-
- # Check that the script contains Homebrew check
- grep -q 'type "brew"' "${brew_script}" || \
- grep -q "which brew" "${brew_script}" || \
- grep -q "command -v brew" "${brew_script}" || \
- grep -q "brew --version" "${brew_script}"
-}
-
-@test "installer scripts use consistent error handling" {
- local installers="${REPO_ROOT}"/installers/*.sh
-
- for installer in ${installers}; do
- local basename=$(basename "${installer}")
-
- # Check for some form of error handling or status checking
- if ! grep -q "set -e" "${installer}" && \
- ! grep -q "|| " "${installer}" && \
- ! grep -q "if \[" "${installer}"; then
- echo "Warning: ${basename} may lack error handling"
- # This is a warning, not a failure
- fi
- done
-}
-
-@test "settings installer scripts exist" {
- assert_file_exists "${REPO_ROOT}/settings/macos/install.sh"
- assert_file_exists "${REPO_ROOT}/settings/vscode/install.sh"
- assert_file_exists "${REPO_ROOT}/settings/xcode/install.sh"
-}
-
-@test "settings installer scripts have valid syntax" {
- local settings_installers=(
- "${REPO_ROOT}/settings/macos/install.sh"
- "${REPO_ROOT}/settings/vscode/install.sh"
- "${REPO_ROOT}/settings/xcode/install.sh"
- )
-
- for installer in "${settings_installers[@]}"; do
- if [[ -f "${installer}" ]]; then
- run zsh -n "${installer}"
- if [ "$status" -ne 0 ]; then
- echo "Syntax error in ${installer}: ${output}"
- false
- fi
- fi
- done
-}
\ No newline at end of file
diff --git a/tests/symlinks.bats b/tests/symlinks.bats
deleted file mode 100644
index 094262e..0000000
--- a/tests/symlinks.bats
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env bats
-
-load test_helper
-
-@test "dotfiles list excludes specified directories" {
- run make -C "${REPO_ROOT}" list
- [ "$status" -eq 0 ]
-
- # Check that excluded directories are not in the list
- ! echo "${output}" | grep -q "\.git/"
- ! echo "${output}" | grep -q "\.github/"
- ! echo "${output}" | grep -q "\.DS_Store"
- ! echo "${output}" | grep -q "\.ruby-version"
- ! echo "${output}" | grep -q "\.config/"
- # .claude should be included (not excluded)
- echo "${output}" | grep -q "\.claude/"
-}
-
-@test "dotfiles list includes expected directories" {
- run make -C "${REPO_ROOT}" list
- [ "$status" -eq 0 ]
-
- # Check that expected directories are in the list
- echo "${output}" | grep -q "\.zsh/"
- echo "${output}" | grep -q "\.zshrc"
- echo "${output}" | grep -q "\.tmux.conf"
- echo "${output}" | grep -q "\.gitconfig"
- echo "${output}" | grep -q "\.Brewfile"
-}
-
-@test "make install creates symlinks in test HOME" {
- # Run install with test HOME
- run run_make_with_test_home install
- [ "$status" -eq 0 ]
-
- # Check that symlinks are created
- assert_link_exists "${TEST_HOME}/.zshrc"
- assert_link_exists "${TEST_HOME}/.tmux.conf"
- assert_link_exists "${TEST_HOME}/.gitconfig"
- assert_link_exists "${TEST_HOME}/.Brewfile"
- assert_link_exists "${TEST_HOME}/.zsh"
-}
-
-@test "make install copies .config directory contents" {
- # Run install with test HOME
- run run_make_with_test_home install
- [ "$status" -eq 0 ]
-
- # Check that .config directory is created and populated
- assert_dir_exists "${TEST_HOME}/.config"
-
- # Check if any config files from repo are copied
- if [[ -d "${REPO_ROOT}/.config" ]] && [[ "$(ls -A "${REPO_ROOT}/.config")" ]]; then
- # Get list of files/dirs in .config
- for item in "${REPO_ROOT}"/.config/*; do
- local basename="$(basename "${item}")"
- if [[ -f "${item}" ]]; then
- assert_file_exists "${TEST_HOME}/.config/${basename}"
- elif [[ -d "${item}" ]]; then
- assert_dir_exists "${TEST_HOME}/.config/${basename}"
- fi
- done
- fi
-}
-
-@test "symlinks point to correct targets" {
- # Run install with test HOME
- run run_make_with_test_home install
- [ "$status" -eq 0 ]
-
- # Check that symlinks point to the correct absolute paths
- local dotfiles=$(get_dotfiles_list)
- for dotfile in ${dotfiles}; do
- if [[ -e "${REPO_ROOT}/${dotfile}" ]]; then
- local link="${TEST_HOME}/${dotfile}"
- if [[ -L "${link}" ]]; then
- local target="$(readlink "${link}")"
- local expected="${REPO_ROOT}/${dotfile}"
- [[ "${target}" == "${expected}" ]]
- fi
- fi
- done
-}
-
-@test "make install is idempotent" {
- # Run install twice
- run run_make_with_test_home install
- [ "$status" -eq 0 ]
-
- run run_make_with_test_home install
- [ "$status" -eq 0 ]
-
- # Check that symlinks still exist and are correct
- assert_link_exists "${TEST_HOME}/.zshrc"
- assert_link_exists "${TEST_HOME}/.tmux.conf"
-}
\ No newline at end of file
diff --git a/tests/test_helper.bash b/tests/test_helper.bash
deleted file mode 100644
index e70ba2e..0000000
--- a/tests/test_helper.bash
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env bash
-
-export TEST_TEMP_DIR="$(mktemp -d)"
-export REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-export ORIGINAL_HOME="${HOME}"
-export TEST_HOME="${TEST_TEMP_DIR}/home"
-
-setup() {
- mkdir -p "${TEST_HOME}"
- mkdir -p "${TEST_HOME}/.config"
-}
-
-teardown() {
- if [[ -d "${TEST_TEMP_DIR}" ]]; then
- rm -rf "${TEST_TEMP_DIR}"
- fi
-}
-
-assert_link_exists() {
- local link_path="$1"
- local target_path="$2"
-
- if [[ ! -L "${link_path}" ]]; then
- echo "Expected symlink at ${link_path} does not exist"
- return 1
- fi
-
- if [[ -n "${target_path}" ]]; then
- local actual_target="$(readlink "${link_path}")"
- if [[ "${actual_target}" != "${target_path}" ]]; then
- echo "Symlink ${link_path} points to ${actual_target}, expected ${target_path}"
- return 1
- fi
- fi
-}
-
-assert_file_exists() {
- local file_path="$1"
-
- if [[ ! -f "${file_path}" ]]; then
- echo "Expected file at ${file_path} does not exist"
- return 1
- fi
-}
-
-assert_dir_exists() {
- local dir_path="$1"
-
- if [[ ! -d "${dir_path}" ]]; then
- echo "Expected directory at ${dir_path} does not exist"
- return 1
- fi
-}
-
-run_make_with_test_home() {
- local target="$1"
- HOME="${TEST_HOME}" make -C "${REPO_ROOT}" "${target}"
-}
-
-get_dotfiles_list() {
- cd "${REPO_ROOT}" || exit 1
- local candidates=$(find . -maxdepth 1 -name ".*" -type f -o -name ".*" -type d | grep -v "^\.$" | sed 's|^\./||')
- local exclusions=".DS_Store .git .config .ruby-version .github .claude"
-
- for candidate in ${candidates}; do
- local excluded=false
- for exclusion in ${exclusions}; do
- if [[ "${candidate}" == "${exclusion}" ]]; then
- excluded=true
- break
- fi
- done
- if [[ "${excluded}" == "false" ]]; then
- echo "${candidate}"
- fi
- done
-}
\ No newline at end of file