Skip to content

Instantly share code, notes, and snippets.

@tobiashochguertel
Last active April 29, 2026 15:15
Show Gist options
  • Select an option

  • Save tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3 to your computer and use it in GitHub Desktop.

Select an option

Save tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3 to your computer and use it in GitHub Desktop.
ide-setup: Multi-developer IDE settings management tool with chezmoi, lefthook, and Taskfile integration
# ide-settings-mgmt
Multi-developer IDE settings management tool
A PEP 723 uv inline Python script for managing IDE settings with:
β€’ Per-developer profiles stored in ~/.ide-settings-mgmt/ with chezmoi
β€’ Repository initialization with .ide-settings-mgmt/ structure
β€’ Lefthook integration for git hooks automation
β€’ Taskfile integration for IDE management tasks
β€’ Support for VS Code, JetBrains, and NeoVim
Install: curl -fsSL https://gist.github.com/tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3/raw/install.sh | bash
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
venv/
ENV/
env/
.venv
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Testing
.pytest_cache/
.coverage
htmlcov/
# uv
.uv/
uv.lock
# macOS
.DS_Store
.AppleDouble
.LSOverride
# Temporary files
*.tmp
*.bak

IDE Settings Management

Multi-developer IDE settings management with per-developer profiles, git hooks automation, and Taskfile integration.

Overview

ide-settings-mgmt is a PEP 723 uv inline Python script that helps teams manage IDE settings across multiple developers without conflicts. Each developer maintains their own profile, and repositories contain the structure for IDE settings management.

Features

  • Per-Developer Profiles: Store your IDE settings in ~/.ide-settings-mgmt/ managed by chezmoi
  • Repository Structure: Initialize any repository with .ide-settings-mgmt/ for team collaboration
  • Git Hooks Automation: Lefthook integration for automated IDE settings validation
  • Taskfile Integration: Per-repository tasks for IDE management
  • Multi-IDE Support: VS Code, JetBrains, and NeoVim
  • Zero Configuration: Works out of the box with sensible defaults

Installation

Quick Install

curl -fsSL https://gist.github.com/tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3/raw/install.sh | bash

Manual Install

# Clone the gist
git clone https://gist.github.com/tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3.git ~/.local/share/ide-settings-mgmt

# Create symlink
ln -s ~/.local/share/ide-settings-mgmt/ide-settings-mgmt.py ~/.local/bin/ide-settings-mgmt
chmod +x ~/.local/bin/ide-settings-mgmt

# Verify installation
ide-settings-mgmt status

Requirements

  • Python 3.10+
  • uv (Python package manager)
  • chezmoi (dotfile manager)
  • lefthook (git hooks manager)
  • Task (task runner)

Install dependencies:

# macOS
brew install chezmoi lefthook go-task

# Linux
curl -sfL https://git.io/chezmoi | sh
curl -fsSL https://raw.githubusercontent.com/evilmartians/lefthook/install/install.sh | bash

Quick Start

1. Create Your Profile

# Create a profile for VS Code
ide-settings-mgmt profile create --name my-default --ide vscode

# Edit your profile
ide-settings-mgmt profile edit --name my-default

This creates a template profile at ~/.ide-settings-mgmt-mgmt/chezmoi/my-default/.vscode/ with default settings.

2. Initialize a Repository

New Project

cd your-project
ide-settings-mgmt init

Existing Project (Migration)

If you have an existing repository with IDE settings:

cd your-existing-project
ide-settings-mgmt migrate

This will:

  • Detect your existing IDE settings
  • Create a profile from those settings
  • Set up the .ide-settings-mgmt/ structure
  • Preserve your current configuration
  • Automatically adjust .gitignore to track IDE settings
  • Check other tool-specific ignore files (.prettierignore, .biomeignore, .dockerignore, etc.)

This creates:

  • .ide-settings-mgmt/ directory
  • lefthook.yml for git hooks
  • tasks.yml for Taskfile integration
  • profiles/ for shared team profiles (optional)

3. Apply Your Profile

# Apply your default profile
ide-settings-mgmt apply

# Or specify a profile
ide-settings-mgmt apply --profile my-default --ide vscode

Usage

Profile Management

# List all profiles
ide-settings-mgmt profile list

# Create a new profile
ide-settings-mgmt profile create --name work --ide vscode

# Edit a profile
ide-settings-mgmt profile edit --name work

# Delete a profile
ide-settings-mgmt profile delete --name work

Repository Management

# Initialize IDE settings in a repository (new project)
ide-settings-mgmt init [--ide vscode] [--force]

# Migrate existing IDE settings to multi-developer workflow
ide-settings-mgmt migrate [--profile name] [--ide vscode] [--force]

# Apply your profile to current repository
ide-settings-mgmt apply [--profile name] [--ide vscode]

# Sync current settings to your profile
ide-settings-mgmt sync

# Check setup status
ide-settings-mgmt status

Task Integration

After installation, use Task commands:

# Show IDE status
task ide-settings-mgmt:status
# or use the alias:
task ide:status

# Show help
task ide-settings-mgmt:help
# or use the alias:
task ide:help

# Update tool
task ide-settings-mgmt:update
# or use the alias:
task ide:update

# Remove tool
task ide-settings-mgmt:remove
# or use the alias:
task ide:remove

# Show information
task ide-settings-mgmt:info
# or use the alias:
task ide:info

Upgrading

To upgrade to the latest version:

# Via Task
task ide-settings-mgmt:update
# or use the alias:
task ide:update

# Or manually
cd ~/.local/share/ide-settings-mgmt
git pull

Ignore File Handling

The tool automatically handles .gitignore and other tool-specific ignore files:

.gitignore Adjustments

For Migration (ide-settings-mgmt migrate):

  • Removes IDE directory from .gitignore (e.g., .vscode/) to enable tracking
  • Adds .ide-settings-mgmt/ to .gitignore to exclude user-specific content

For New Projects (ide-settings-mgmt init):

  • Adds .ide-settings-mgmt/ to .gitignore to exclude user-specific content

Tool-Specific Ignore Files

The tool checks these files and adds IDE-specific comments:

  • .prettierignore
  • .biomeignore
  • .dockerignore
  • .eslintignore
  • .stylelintignore
  • .gitattributes

This ensures that your tooling configurations are aware of IDE-specific patterns.

Architecture

Profile Storage

Profiles are stored in ~/.ide-settings-mgmt/chezmoi/ and managed by chezmoi:

~/.ide-settings-mgmt/chezmoi/
β”œβ”€β”€ my-default/
β”‚   β”œβ”€β”€ .vscode/
β”‚   β”‚   β”œβ”€β”€ settings.json
β”‚   β”‚   └── extensions.json
β”‚   └── .idea/
β”‚       └── misc.xml
└── work/
    └── .vscode/
        └── settings.json

Repository Structure

Each repository gets an .ide-settings-mgmt/ directory:

.my-project/
β”œβ”€β”€ .ide-settings-mgmt/
β”‚   β”œβ”€β”€ profiles/          # Shared team profiles (optional)
β”‚   β”œβ”€β”€ hooks/             # Custom git hooks (optional)
β”‚   β”œβ”€β”€ lefthook.yml       # Lefthook configuration
β”‚   β”œβ”€β”€ tasks.yml          # Taskfile tasks
β”‚   └── README.md          # Documentation
β”œβ”€β”€ .vscode/               # IDE-specific settings (applied from profile)
└── .git/

Git Hooks

Lefthook runs automated checks:

  • pre-commit: Check IDE settings consistency
  • pre-push: Validate IDE settings format

Multi-Developer Workflow

  1. Developer A creates profile dev-a-profile with their VS Code settings
  2. Developer B creates profile dev-b-profile with their JetBrains settings
  3. Repository is initialized with ide-settings-mgmt init
  4. Developer A runs ide-settings-mgmt apply --profile dev-a-profile
  5. Developer B runs ide-settings-mgmt apply --profile dev-b-profile
  6. Both developers work with their preferred settings without conflicts

Supported IDEs

VS Code

  • Settings: .vscode/settings.json
  • Extensions: .vscode/extensions.json

JetBrains

  • Settings: .idea/misc.xml
  • Keymaps: .idea/keymap.xml

NeoVim

  • Config: .nvim/init.lua
  • Plugins: .nvim/lua/plugins/

Advanced Usage

Custom Profile Templates

Edit your profile directly:

ide-settings-mgmt profile edit --name my-default

Profiles are stored at ~/.ide-settings-mgmt/chezmoi/<profile-name>/. You can add any files your IDE needs.

Team Shared Profiles

Add shared profiles to the repository's .ide-settings-mgmt/profiles/ directory:

# Add team profile to repo
cp -r ~/.ide-settings-mgmt/chezmoi/team-standard .ide-settings-mgmt/profiles/
git add .ide-settings-mgmt/profiles/team-standard
git commit -m "Add team IDE profile"

Other developers can then copy and customize:

cp -r .ide-settings-mgmt/profiles/team-standard ~/.ide-settings-mgmt/chezmoi/my-custom
ide-settings-mgmt profile edit --name my-custom

Custom Git Hooks

Add custom hooks to .ide-settings-mgmt/hooks/:

# Add custom pre-commit hook
cat > .ide-settings-mgmt/hooks/pre-commit/check-formatting.sh <<'EOF'
#!/bin/sh
# Custom formatting check
echo "Running custom formatting check..."
EOF
chmod +x .ide-settings-mgmt/hooks/pre-commit/check-formatting.sh

Troubleshooting

chezmoi not installed

# macOS
brew install chezmoi

# Linux
curl -sfL https://git.io/chezmoi | sh

uv not installed

curl -LsSf https://astral.sh/uv/install.sh | sh

Profile not found

Check your profiles:

ide-settings-mgmt profile list

If empty, create one:

ide-settings-mgmt profile create --name my-default --ide vscode

Git hooks not running

Ensure lefthook is installed:

# macOS
brew install lefthook

# Install hooks in repository
lefthook install

Development

Running from Source

# Clone the gist
git clone https://gist.github.com/tobiashochguertel/5ff956a2cce1d59c1175d818837ecaa3.git ide-settings-mgmt
cd ide-settings-mgmt

# Run directly with uv
uv run ide_setup.py --help

Editing

Edit ide_setup.py and test:

uv run ide_setup.py status

Testing

The tool includes a comprehensive test suite as a UV_SCRIPT:

# Run all tests
uv run test_ide_setup.py

# Run only unit tests
uv run test_ide_setup.py unit

# Run only integration tests (not yet implemented)
uv run test_ide_setup.py integration

# Run only e2e tests (not yet implemented)
uv run test_ide_setup.py e2e

The test suite includes:

  • Unit tests: Test core functions (constants, template rendering, IDE detection, ignore file handling)
  • Integration tests: Test command integration (planned)
  • E2E tests: Test complete workflows (planned)

Template System

The tool uses Jinja2 templates for generating configuration files:

lefthook.yml.j2       # Lefthook configuration
repo_taskfile.yml.j2  # Repository Taskfile
readme.md.j2          # .ide-settings-mgmt README

Note: Templates are in the root directory with .j2 extension for Gist compatibility (Gist doesn't support subdirectories).

To modify templates:

  1. Edit the template file (e.g., lefthook.yml.j2)
  2. Update context variables in ide_setup.py if needed
  3. Test by running the relevant command
  4. Commit and push to update the gist

Updating the Gist

# Switch to SSH for pushing
git remote set-url origin git@gist.github.com:5ff956a2cce1d59c1175d818837ecaa3.git

# Make changes
git add -A
git commit -m "update"
git push origin HEAD

License

MIT License - Feel free to use and modify for your needs.

Contributing

This is a personal tool, but suggestions and improvements are welcome!

Related Tools


Version: 1.0.0
Last Updated: 2026-04-29

# IDE Settings
This directory contains IDE settings for this repository, managed by [ide-settings-mgmt](https://gist.github.com/{{ github_user }}/{{ gist_id }}).
## Profile
**Profile**: `{{ profile_name }}`
**IDE**: `{{ ide_name }}`
## Structure
- `settings/` - IDE-specific configuration files
- `lefthook.yml` - Git hooks configuration
- `Taskfile.yml` - Repository-specific IDE tasks
## Commands
```bash
# Show IDE settings status
task ide-settings-mgmt:status
# or use the alias:
task ide:status
# Check IDE settings consistency
task ide-settings-mgmt:check
# or use the alias:
task ide:check
# Validate IDE settings
task ide-settings-mgmt:validate
# or use the alias:
task ide:validate
# Sync repository settings back to profile
task ide-settings-mgmt:sync
# or use the alias:
task ide:sync
```
## Management
This directory is managed by ide-settings-mgmt. Manual edits may be overwritten when syncing profiles.
For profile management, use:
```bash
# List available profiles
ide-settings-mgmt profile list
# Apply a different profile
ide-settings-mgmt apply --profile <profile-name>
```
## Git Hooks
Git hooks are automatically configured via lefthook to validate IDE settings before commits and pushes.

AI Agent Development Instructions

Purpose: Guide AI agents in developing and maintaining the ide-settings-mgmt tool.

Overview

This document provides instructions for AI agents working on the ide-settings-mgmt tool, a PEP 723 uv inline Python script for multi-developer IDE settings management.

Core Principles

1. PEP 723 Compliance

❌ NEVER:

  • Use external requirements.txt or setup.py
  • Require complex installation steps
  • Depend on system packages

βœ… ALWAYS:

  • Use PEP 723 inline metadata in script header
  • Declare all dependencies in # /// script section
  • Make script executable with #!/usr/bin/env -S uv run --script

Template:

#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
#   "typer>=0.12.0",
#   "rich>=13.0.0",
# ]
# ///

2. Error Handling

❌ NEVER:

  • Let exceptions propagate without context
  • Use generic error messages
  • Crash without helpful output

βœ… ALWAYS:

  • Use custom IDESetupError for domain errors
  • Provide rich console output with colors
  • Suggest remediation steps

Pattern:

class IDESetupError(Exception):
    """Base exception for IDE setup operations."""
    pass

try:
    run_command(cmd)
except subprocess.CalledProcessError as e:
    console.print(f"[red]Command failed: {' '.join(cmd)}[/red]")
    raise IDESetupError(f"Command failed: {e}") from e

3. File Operations

❌ NEVER:

  • Assume directories exist
  • Overwrite files without confirmation
  • Use hard-coded paths

βœ… ALWAYS:

  • Use pathlib.Path for path operations
  • Create directories with parents=True, exist_ok=True
  • Use constants for all paths

Constants:

IDE_SETTINGS_MGMT_DIR = Path.home() / ".ide-settings-mgmt"
REPO_IDE_DIR = Path(".ide-settings-mgmt")
CHEZMOI_SOURCE_DIR = IDE_SETTINGS_MGMT_DIR / "chezmoi"

4. Command Execution

❌ NEVER:

  • Use os.system() or subprocess.call()
  • Ignore command output
  • Run commands without error handling

βœ… ALWAYS:

  • Use subprocess.run() with capture_output=True
  • Check return codes when needed
  • Provide rich error output

Pattern:

def run_command(cmd: List[str], check: bool = True) -> subprocess.CompletedProcess:
    """Run a shell command with rich error handling."""
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, check=check)
        return result
    except subprocess.CalledProcessError as e:
        console.print(f"[red]Command failed: {' '.join(cmd)}[/red]")
        raise IDESetupError(f"Command failed: {e}") from e

5. User Interaction

❌ NEVER:

  • Use input() for critical decisions
  • Skip confirmation for destructive operations
  • Hide important information

βœ… ALWAYS:

  • Use typer prompts for user input
  • Use typer.Confirm for destructive operations
  • Provide rich console output with progress

Pattern:

if typer.confirm(f"Delete profile '{name}'?"):
    shutil.rmtree(profile_path)
    console.print(f"[green]βœ“ Deleted profile '{name}'[/green]")

Architecture

Main Components

  1. Profile Management: Create, list, edit, delete profiles in ~/.ide-settings-mgmt/
  2. Repository Initialization: Create .ide-settings-mgmt/ structure in repos
  3. Profile Application: Copy profile settings to repository
  4. Settings Synchronization: Sync repo settings back to profile
  5. Status Reporting: Show current setup status
  6. Ignore File Management: Adjust .gitignore and tool-specific ignore files

IDE Support

Add new IDEs by extending SUPPORTED_IDES:

SUPPORTED_IDES = {
    "vscode": {
        "config_dir": ".vscode",
        "settings_file": "settings.json",
        "extensions_file": "extensions.json",
        "gitignore_pattern": ".vscode/"
    },
    "new_ide": {
        "config_dir": ".new_ide",
        "settings_file": "config.json",
        "gitignore_pattern": ".new_ide/"
    }
}

Integration Points

  1. chezmoi: Profile storage and management
  2. lefthook: Git hooks automation
  3. Taskfile: Per-repository task integration
  4. uv: Script execution and dependency management

Development Workflow

Adding New Features

  1. Update dependencies in PEP 723 header
  2. Add new commands using typer decorators
  3. Update constants if new paths needed
  4. Add error handling for new operations
  5. Update documentation (README.md, CHANGELOG.md)
  6. Test locally with uv run ide_setup.py

Modifying Existing Features

  1. Maintain backward compatibility when possible
  2. Update AGENTS.md if patterns change
  3. Update CHANGELOG.md with notable changes
  4. Test all affected commands
  5. Update README.md if user-facing changes

Bug Fixes

  1. Add error handling for edge cases
  2. Improve error messages with context
  3. Add validation for user input
  4. Update CHANGELOG.md with fix details
  5. Test fix against the original issue

Testing

Manual Testing

# Test profile creation
uv run ide_setup.py profile create --name test --ide vscode

# Test repository initialization
cd /tmp/test-repo
uv run ../ide_setup.py init

# Test profile application
uv run ../ide_setup.py apply --profile test

# Test status
uv run ../ide_setup.py status

Integration Testing

Test the full installation workflow:

# Test install script
./install.sh

# Verify installation
ide-settings-mgmt status

# Test Task integration
task ide:status

Common Tasks

Adding a New IDE

  1. Add to SUPPORTED_IDES constant
  2. Update detect_ide() function
  3. Add template creation logic in create_profile_template()
  4. Update documentation in README.md
  5. Test creation and application

Adding a New Profile Operation

  1. Add new action to profile() command
  2. Implement logic with error handling
  3. Add user confirmation if destructive
  4. Update help text
  5. Test thoroughly

Modifying Git Hooks

  1. Update lefthook.yml template in init() command
  2. Add corresponding task in tasks.yml template
  3. Update documentation
  4. Test hook execution

Adding Support for New Ignore Files

  1. Add file to TOOL_IGNORE_FILES constant
  2. Update adjust_ignore_files() function if special handling needed
  3. Add documentation to README.md
  4. Test with existing repositories

Testing

The tool includes a comprehensive test suite in test_ide_setup.py:

# Run all tests
uv run test_ide_setup.py

# Run specific test types
uv run test_ide_setup.py unit
uv run test_ide_setup.py integration
uv run test_ide_setup.py e2e

When adding new features:

  1. Add corresponding unit tests for new functions
  2. Add integration tests for new commands
  3. Run full test suite before committing
  4. Ensure all tests pass

Modifying Templates

Templates are stored in the root directory with .j2 extension and use Jinja2 syntax:

  1. Edit the template file (e.g., lefthook.yml.j2)
  2. Update context variables in ide_setup.py if needed
  3. Test template rendering by running the relevant command
  4. Update documentation if template structure changes

Template Context Variables:

  • gist_id: The GitHub Gist ID
  • github_user: GitHub username
  • profile_name: Profile name (for repository Taskfiles)
  • ide_name: IDE type (vscode, jetbrains, neovim)

Example Template Usage:

# In ide_setup.py
render_template(
    "repo_taskfile.yml.j2",
    gist_id=GIST_ID,
    github_user=GITHUB_USER,
    profile_name=profile_name
)

File Structure

ide-settings-mgmt-gist/
β”œβ”€β”€ #ide-settings-mgmt-GistSummary    ← Discovery file (ASCII 35)
β”œβ”€β”€ .gitignore                ← Python/shell gitignore
β”œβ”€β”€ AGENTS.md                 ← This file
β”œβ”€β”€ CHANGELOG.md              ← Version history
β”œβ”€β”€ README.md                 ← User documentation
β”œβ”€β”€ Taskfile.ide.yml          ← Lifecycle tasks
β”œβ”€β”€ ide_setup.py              ← Main tool (PEP 723)
β”œβ”€β”€ install.sh                ← One-liner installer
β”œβ”€β”€ test_ide_setup.py         ← Test suite (UV_SCRIPT)
β”œβ”€β”€ lefthook.yml.j2           ← Lefthook configuration template
β”œβ”€β”€ repo_taskfile.yml.j2      ← Repository Taskfile template
└── readme.md.j2              ← .ide-settings README template

Note: Template files use .j2 extension and are in the root directory for Gist compatibility (Gist doesn't support subdirectories).

Installation: The installer clones the entire gist to ~/.local/share/ide-settings-mgmt/ and creates a symlink to ~/.local/bin/ide-settings-mgmt. This ensures all files (including templates) are available and makes upgrades easy via git pull.

Gist Maintenance

Updating the Gist

# Switch to SSH for pushing
git remote set-url origin git@gist.github.com:5ff956a2cce1d59c1175d818837ecaa3.git

# Make changes
git add -A
git commit -m "feat: add new feature"

# Push
git push origin HEAD

Updating Gist ID

When creating the gist, update:

  1. install.sh: Set GIST_ID variable to actual gist ID
  2. Taskfile.ide.yml: Set GIST_ID variable to actual gist ID
  3. #ide-settings-mgmt-GistSummary: Update install command with actual user and gist ID
  4. Update all documentation files with correct URLs

Related Guides

Constraints

  • Single File: Keep main tool in single Python file
  • No External Config: All config in script or generated files
  • PEP 723 Only: No external dependency files
  • Rich Output: Use rich for all console output
  • Error Recovery: Provide helpful error messages and recovery steps
  • Template-Based: Use Jinja2 templates for all generated configuration files

Last Updated: 2026-04-29
Version: 1.0.0

Changelog

All notable changes to ide-settings-mgmt will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Changed

  • Complete rebranding: Renamed tool from ide-setup to ide-settings-mgmt
  • Brand name: Changed to "IDE Settings Management"
  • CLI command: Changed from ide-setup to ide-settings-mgmt
  • Environment variables: Changed from IDE_* to IDE_SETTINGS_MGMT_* prefix
  • Directory structure: Changed from .ide-profiles/ and .ide-settings/ to single .ide-settings-mgmt/
  • Task namespace: Changed to ide-settings-mgmt: with ide: as alias
  • File naming: Changed to kebab-case matching CLI name (ide-settings-mgmt.py)
  • No backward compatibility: Complete break from old naming conventions
  • Added: NAMING_CONVENTIONS.md documentation for consistent naming

Added

  • migrate command for migrating existing IDE settings to multi-developer workflow
  • Automatic profile creation from existing repository settings
  • Repository name-based profile naming for migrations
  • Automatic .gitignore adjustment for both init and migrate operations
  • Tool-specific ignore file handling (.prettierignore, .biomeignore, .dockerignore, etc.)
  • IDE-specific comments in tool ignore files
  • Jinja2 template system for configuration file generation
  • Template files with .j2 extension in root directory (lefthook.yml.j2, repo_taskfile.yml.j2, readme.md.j2)
  • Comprehensive test suite as UV_SCRIPT (test_ide_setup.py) with unit, integration, and e2e test support

Changed

  • Refactored from embedded string templates to Jinja2 template files
  • Improved maintainability of configuration file generation
  • Added template context variables for dynamic configuration
  • Templates in root directory for Gist compatibility (Gist doesn't support subdirectories)
  • Installer now clones entire gist to ensure all files (including templates) are available
  • Upgrades now via git pull instead of re-downloading individual files
  • Installation directory changed from single file to cloned repository at ~/.local/share/ide-settings-mgmt/

1.0.0 - 2026-04-29

Added

  • Initial release of ide-settings-mgmt tool
  • PEP 723 uv inline Python script for zero-dependency execution
  • Profile management with chezmoi integration
  • Repository initialization with .ide-settings-mgmt/ structure
  • Lefthook integration for git hooks automation
  • Taskfile integration for per-repository IDE management tasks
  • Support for VS Code, JetBrains, and NeoVim
  • Multi-developer workflow support
  • One-liner installation via curl
  • Auto-integration with global Taskfile orchestrator
  • Profile creation, listing, editing, and deletion
  • IDE detection and auto-configuration
  • Settings synchronization between repos and profiles
  • Comprehensive documentation and examples

Infrastructure

  • GitHub Gist distribution following Gist Guide pattern
  • Automated installation script with Taskfile integration
  • Lifecycle management tasks (update, remove, status, info)
  • Discoverability via #ide-settings-mgmt-GistSummary file
  • Complete documentation (README, CHANGELOG, AGENTS.md)
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
# "typer>=0.12.0",
# "rich>=13.0.0",
# "pydantic>=2.0.0",
# "loguru>=0.7.0",
# "jinja2>=3.1.0",
# ]
# ///
"""
IDE Settings Management - Multi-developer IDE settings management
A tool for managing IDE settings across teams with per-developer profiles,
git hooks automation via lefthook, and Taskfile integration.
Usage:
./ide-settings-mgmt.py [command] [options]
"""
import os
import shutil
import subprocess
import json
from pathlib import Path
from typing import Optional, List
import typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich import print as rprint
from loguru import logger
from jinja2 import Environment, FileSystemLoader
console = Console()
app = typer.Typer(
help="IDE Settings Management - Multi-developer IDE settings management",
add_completion=False
)
# Constants
IDE_SETTINGS_MGMT_DIR = Path.home() / ".ide-settings-mgmt"
REPO_IDE_DIR = Path(".ide-settings-mgmt")
CHEZMOI_SOURCE_DIR = IDE_SETTINGS_MGMT_DIR / "chezmoi"
SUPPORTED_IDES = {
"vscode": {
"config_dir": ".vscode",
"settings_file": "settings.json",
"extensions_file": "extensions.json",
"gitignore_pattern": ".vscode/"
},
"jetbrains": {
"config_dir": ".idea",
"settings_file": "misc.xml",
"keymap_file": "keymap.xml",
"gitignore_pattern": ".idea/"
},
"neovim": {
"config_dir": ".nvim",
"init_file": "init.lua",
"gitignore_pattern": ".nvim/"
}
}
# Tool-specific ignore files that might need IDE-specific patterns
TOOL_IGNORE_FILES = [
".prettierignore",
".biomeignore",
".dockerignore",
".eslintignore",
".stylelintignore",
".gitattributes"
]
# Gist configuration
IDE_SETTINGS_MGMT_GIST_ID = "5ff956a2cce1d59c1175d818837ecaa3"
IDE_SETTINGS_MGMT_GITHUB_USER = "tobiashochguertel"
# Jinja2 templates directory (templates are in root directory for Gist compatibility)
# Resolve symlinks to get the actual clone directory
SCRIPT_PATH = Path(__file__).resolve()
TEMPLATES_DIR = SCRIPT_PATH.parent
# Jinja2 environment for template rendering
env = Environment(loader=FileSystemLoader(TEMPLATES_DIR))
def render_template(template_name: str, **context) -> str:
"""Render a Jinja2 template with the given context.
Args:
template_name: Name of the template file
**context: Variables to pass to the template
Returns:
Rendered template as string
"""
try:
template = env.get_template(template_name)
return template.render(**context)
except Exception as e:
logger.error(f"Failed to render template {template_name}: {e}")
raise IDESetupError(f"Template rendering failed: {e}") from e
class IDESetupError(Exception):
"""Base exception for IDE setup operations."""
pass
def run_command(cmd: List[str], check: bool = True) -> subprocess.CompletedProcess:
"""Run a shell command with rich error handling."""
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=check
)
return result
except subprocess.CalledProcessError as e:
console.print(f"[red]Command failed: {' '.join(cmd)}[/red]")
console.print(f"[red]Error: {e.stderr}[/red]")
raise IDESetupError(f"Command failed: {e}") from e
def adjust_ignore_files(repo_path: Path, ide: str, operation: str = "init") -> None:
"""Adjust .gitignore and other tool-specific ignore files for IDE settings.
Args:
repo_path: Path to the repository
ide: IDE type (vscode, jetbrains, neovim)
operation: Either 'init' (new setup) or 'migrate' (existing settings)
"""
ide_config = SUPPORTED_IDES[ide]
ide_pattern = ide_config["gitignore_pattern"]
logger.debug(f"Adjusting ignore files for {operation} operation...")
# Handle .gitignore
gitignore_path = repo_path / ".gitignore"
if gitignore_path.exists():
gitignore_content = gitignore_path.read_text()
gitignore_lines = gitignore_content.split('\n')
if operation == "migrate":
# For migration, remove IDE directory from .gitignore if present
# since we want to track the settings now
if ide_pattern in gitignore_lines:
console.print(f"[yellow]Found {ide_pattern} in .gitignore[/yellow]")
console.print(f"[dim]Removing {ide_pattern} from .gitignore to track IDE settings[/dim]")
# Remove the pattern
gitignore_lines = [line for line in gitignore_lines if line.strip() != ide_pattern]
gitignore_path.write_text('\n'.join(gitignore_lines))
console.print(f"[green]βœ“ Removed {ide_pattern} from .gitignore[/green]")
else:
console.print(f"[dim]βœ“ {ide_pattern} not in .gitignore (good for tracking)[/dim]")
# Add .ide-settings-mgmt/ to .gitignore (we track structure, not user-specific content)
ide_settings_pattern = ".ide-settings-mgmt/"
if ide_settings_pattern not in gitignore_lines:
console.print(f"[dim]Adding {ide_settings_pattern} to .gitignore[/dim]")
with open(gitignore_path, 'a') as f:
f.write(f"\n# IDE settings management\n{ide_settings_pattern}\n")
console.print(f"[green]βœ“ Added {ide_settings_pattern} to .gitignore[/green]")
# Handle other tool-specific ignore files
for ignore_file in TOOL_IGNORE_FILES:
ignore_path = repo_path / ignore_file
if not ignore_path.exists():
continue
console.print(f"[dim]Checking {ignore_file}...[/dim]")
ignore_content = ignore_path.read_text()
# Add IDE-specific comments if not present
ide_comment = f"# IDE-specific patterns for {ide}"
if ide_comment not in ignore_content:
with open(ignore_path, 'a') as f:
f.write(f"\n{ide_comment}\n")
console.print(f"[green]βœ“ Added IDE comment to {ignore_file}[/green]")
def detect_ide() -> Optional[str]:
"""Detect which IDE is likely being used based on project files."""
cwd = Path.cwd()
# Check for VS Code
if (cwd / ".vscode").exists():
return "vscode"
# Check for JetBrains
if (cwd / ".idea").exists():
return "jetbrains"
# Check for NeoVim
if (cwd / ".nvim").exists() or (cwd / "init.lua").exists():
return "neovim"
return None
def check_chezmoi_installed() -> bool:
"""Check if chezmoi is installed."""
try:
run_command(["chezmoi", "--version"], check=False)
return True
except (IDESetupError, FileNotFoundError):
return False
def init_chezmoi() -> None:
"""Initialize chezmoi for managing IDE profiles."""
logger.info("Initializing chezmoi for IDE profiles...")
if not check_chezmoi_installed():
logger.warning("chezmoi is not installed. Installing...")
console.print("[dim]Please install chezmoi first:[/dim]")
console.print("[dim] brew install chezmoi # macOS[/dim]")
console.print("[dim] curl -sfL https://git.io/chezmoi | sh # Linux[/dim]")
raise IDESetupError("chezmoi is required but not installed")
# Create chezmoi source directory if it doesn't exist
CHEZMOI_SOURCE_DIR.mkdir(parents=True, exist_ok=True)
# Initialize chezmoi if not already initialized
chezmoi_config = Path.home() / ".local" / "share" / "chezmoi"
if not chezmoi_config.exists():
logger.debug("Initializing chezmoi...")
run_command(["chezmoi", "init", "--source", str(CHEZMOI_SOURCE_DIR)])
logger.success("chezmoi initialized for IDE profiles")
def create_profile_template(ide_name: str, profile_name: str) -> Path:
"""Create a template profile for an IDE."""
ide_config = SUPPORTED_IDES.get(ide_name)
if not ide_config:
raise IDESetupError(f"Unsupported IDE: {ide_name}")
profile_dir = CHEZMOI_SOURCE_DIR / profile_name / ide_config["config_dir"]
profile_dir.mkdir(parents=True, exist_ok=True)
# Create template settings file
settings_file = profile_dir / ide_config.get("settings_file", "settings.json")
if not settings_file.exists():
if ide_name == "vscode":
settings_file.write_text(json.dumps({
"editor.formatOnSave": True,
"editor.tabSize": 2,
"files.exclude": {
"**/__pycache__": True,
"**/.git": True
}
}, indent=2))
elif ide_name == "neovim":
settings_file.write_text("""
-- NeoVim configuration
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
""")
console.print(f"[green]βœ“ Created profile template: {profile_name}/{ide_name}[/green]")
logger.info(f"Created profile template: {profile_name}/{ide_name}")
return profile_dir
@app.command()
def init(
repo_path: Path = typer.Argument(
Path.cwd(),
help="Path to the repository to initialize"
),
ide: Optional[str] = typer.Option(
None,
"--ide",
help="IDE to configure (auto-detected if not specified)"
),
force: bool = typer.Option(
False,
"--force",
help="Overwrite existing .ide-settings-mgmt directory"
)
) -> None:
"""Initialize IDE settings structure in a repository."""
console.print(Panel.fit("[bold blue]IDE Settings Management - Initialize Repository[/bold blue]"))
# Detect IDE if not specified
if not ide:
ide = detect_ide()
if ide:
console.print(f"[dim]Detected IDE: {ide}[/dim]")
else:
console.print("[yellow]No IDE detected. Specify --ide or supported IDEs: vscode, jetbrains, neovim[/yellow]")
raise typer.Exit(code=1)
if ide not in SUPPORTED_IDES:
console.print(f"[red]Unsupported IDE: {ide}[/red]")
console.print(f"[dim]Supported IDEs: {', '.join(SUPPORTED_IDES.keys())}[/dim]")
raise typer.Exit(code=1)
# Check if repo is a git repository
if not (repo_path / ".git").exists():
console.print("[yellow]Not a git repository. Skipping git hooks setup.[/yellow]")
# Create .ide-settings-mgmt directory
ide_settings_dir = repo_path / REPO_IDE_DIR
if ide_settings_dir.exists() and not force:
console.print(f"[yellow].ide-settings-mgmt already exists. Use --force to overwrite.[/yellow]")
raise typer.Exit(code=1)
ide_settings_dir.mkdir(parents=True, exist_ok=True)
# Create directory structure
(ide_settings_dir / "profiles").mkdir(exist_ok=True)
(ide_settings_dir / "hooks").mkdir(exist_ok=True)
# Create lefthook configuration
lefthook_config = ide_settings_dir / "lefthook.yml"
lefthook_config.write_text(render_template("lefthook.yml.j2"))
# Create repository-specific Taskfile
repo_taskfile = ide_settings_dir / "Taskfile.yml"
if not repo_taskfile.exists():
repo_taskfile.write_text("""# Taskfile for IDE settings in this repository
# Repository-specific IDE management tasks
# Following Taskfile Guide patterns with task-help integration
version: "3"
vars:
IDE_SETTINGS_MGMT_GIST_ID: '{{.IDE_SETTINGS_MGMT_GIST_ID | default "5ff956a2cce1d59c1175d818837ecaa3"}}'
TASK_HELP_WARN: "true" # set to 'false' to suppress the install hint
tasks:
default:
silent: true
desc: "Show grouped task list (falls back to task --list if task-help not installed)"
cmds:
- |
SCRIPT="$HOME/.taskfiles/taskscripts/task-help/task_help.py"
if [ -x "$SCRIPT" ]; then
"$SCRIPT"
else
WARN="{{.TASK_HELP_WARN}}"
if [ "$WARN" != "false" ] && [ "$WARN" != "0" ]; then
printf '\\033[33m⚠ task-help is not installed.\\033[0m\\n'
printf '\\033[2m curl -fsSL https://gist.githubusercontent.com/tobiashochguertel/261c54d64fff6dc1493619e2924161b4/raw/install.sh | bash\\033[0m\\n'
fi
task --list
fi
ensure-global:
desc: "Ensure global ide-settings-mgmt gist is installed"
silent: true
cmds:
- |
if [ ! -f ~/.taskfiles/Taskfile.ide-settings-mgmt.yml ]; then
echo "Installing global ide-settings-mgmt..."
curl -fsSL https://gist.github.com/tobiashochguertel/{{.IDE_SETTINGS_MGMT_GIST_ID}}/raw/install.sh | bash
else
echo "βœ“ Global ide-settings-mgmt already installed"
fi
status:
desc: "Show IDE settings status for this repository"
cmds:
- task: ensure-global
- |
echo "=== Repository IDE Settings Status ==="
if [ -d .ide-settings-mgmt ]; then
echo "βœ“ .ide-settings-mgmt directory exists"
if [ -d .vscode ]; then
echo "βœ“ VS Code settings present"
fi
if [ -d .idea ]; then
echo "βœ“ JetBrains settings present"
fi
else
echo "βœ— .ide-settings-mgmt directory not found"
fi
apply:
desc: "Apply your IDE profile to this repository"
cmds:
- task: ensure-global
- ide-settings-mgmt apply
sync:
desc: "Sync current IDE settings with your profile"
cmds:
- task: ensure-global
- ide-settings-mgmt sync
check:
desc: "Check IDE settings consistency"
cmds:
- task: ensure-global
- |
if [ -f .vscode/settings.json ]; then
echo "Checking VS Code settings..."
# Add validation logic here
fi
validate:
desc: "Validate IDE settings format"
cmds:
- task: ensure-global
- |
echo "Validating IDE settings..."
# Add validation logic here
""")
console.print("[green]βœ“ Created repository Taskfile[/green]")
# Include in root Taskfile.yml if it exists
root_taskfile = repo_path / "Taskfile.yml"
if root_taskfile.exists():
console.print("[dim]Checking root Taskfile.yml for include...[/dim]")
root_taskfile_content = root_taskfile.read_text()
# Check if already included
if "ide-settings-mgmt" in root_taskfile_content:
console.print("[dim]βœ“ Already included in root Taskfile.yml[/dim]")
else:
console.print("[dim]Adding include to root Taskfile.yml...[/dim]")
# Add include at the end of includes section
with open(root_taskfile, 'a') as f:
f.write("\n # ── ide-settings-mgmt β€” Repository IDE settings management ─────────────────\n")
f.write(" ide-settings-mgmt:\n")
f.write(" taskfile: .ide-settings-mgmt/Taskfile.yml\n")
f.write(" optional: true\n")
console.print("[green]βœ“ Added include to root Taskfile.yml[/green]")
# Create README for .ide-settings-mgmt
readme_file = ide_settings_dir / "README.md"
readme_file.write_text(render_template(
"readme.md.j2",
gist_id=IDE_SETTINGS_MGMT_GIST_ID,
github_user=IDE_SETTINGS_MGMT_GITHUB_USER,
profile_name="default",
ide_name=ide
))
# Adjust .gitignore and other ignore files
adjust_ignore_files(repo_path, ide, operation="init")
console.print(f"[green]βœ“ Initialized IDE settings in {repo_path}[/green]")
console.print(f"[dim]Created .ide-settings-mgmt/ with lefthook and Taskfile integration[/dim]")
console.print("[dim]Next steps:[/dim]")
console.print("[dim] 1. Create your profile: ide-settings-mgmt profile create --ide vscode my-default[/dim]")
console.print("[dim] 2. Apply profile to repo: ide-settings-mgmt apply[/dim]")
@app.command()
def migrate(
repo_path: Path = typer.Argument(
Path.cwd(),
help="Path to the repository to migrate"
),
profile_name: Optional[str] = typer.Option(
None,
"--profile",
help="Profile name to create (default: repo-name)"
),
ide: Optional[str] = typer.Option(
None,
"--ide",
help="IDE type (auto-detected if not specified)"
),
force: bool = typer.Option(
False,
"--force",
help="Overwrite existing .ide-settings-mgmt directory"
)
) -> None:
"""Migrate existing IDE settings to multi-developer workflow."""
console.print(Panel.fit("[bold blue]IDE Settings Management - Migrate Existing Settings[/bold blue]"))
# Detect IDE if not specified
if not ide:
ide = detect_ide()
if ide:
console.print(f"[dim]Detected IDE: {ide}[/dim]")
else:
console.print("[yellow]No IDE detected. Specify --ide or supported IDEs: vscode, jetbrains, neovim[/yellow]")
raise typer.Exit(code=1)
if ide not in SUPPORTED_IDES:
console.print(f"[red]Unsupported IDE: {ide}[/red]")
console.print(f"[dim]Supported IDEs: {', '.join(SUPPORTED_IDES.keys())}[/dim]")
raise typer.Exit(code=1)
# Check if repo is a git repository
if not (repo_path / ".git").exists():
console.print("[yellow]Not a git repository. Migration requires git.[/yellow]")
raise typer.Exit(code=1)
# Check for existing IDE settings
ide_config = SUPPORTED_IDES[ide]
existing_settings = repo_path / ide_config["config_dir"]
if not existing_settings.exists():
console.print(f"[yellow]No existing {ide} settings found in {repo_path}[/yellow]")
console.print(f"[dim]Expected: {ide_config['config_dir']}[/dim]")
console.print("[dim]Use 'ide-settings-mgmt init' instead for new setup[/dim]")
raise typer.Exit(code=1)
console.print(f"[green]βœ“ Found existing {ide} settings[/green]")
logger.info(f"Found existing {ide} settings in {repo_path}")
# Generate profile name if not provided
if not profile_name:
profile_name = repo_path.name.replace("-", "_").replace(" ", "_")
console.print(f"[dim]Using profile name: {profile_name}[/dim]")
# Initialize chezmoi if needed
init_chezmoi()
# Create profile from existing settings
profile_dir = CHEZMOI_SOURCE_DIR / profile_name / ide_config["config_dir"]
profile_dir.mkdir(parents=True, exist_ok=True)
console.print(f"[dim]Migrating settings to profile: {profile_name}[/dim]")
# Copy existing settings to profile
for item in existing_settings.iterdir():
if item.is_file():
shutil.copy2(item, profile_dir / item.name)
console.print(f"[green]βœ“ Migrated {item.name}[/green]")
# Initialize .ide-settings-mgmt structure
ide_settings_dir = repo_path / REPO_IDE_DIR
if ide_settings_dir.exists() and not force:
console.print(f"[yellow].ide-settings-mgmt already exists. Use --force to overwrite.[/yellow]")
if not typer.confirm("Continue with existing .ide-settings-mgmt?"):
raise typer.Exit(code=1)
else:
ide_settings_dir.mkdir(parents=True, exist_ok=True)
# Create directory structure
(ide_settings_dir / "profiles").mkdir(exist_ok=True)
(ide_settings_dir / "hooks").mkdir(exist_ok=True)
# Create lefthook configuration
lefthook_config = ide_settings_dir / "lefthook.yml"
if not lefthook_config.exists():
lefthook_config.write_text(render_template("lefthook.yml.j2"))
console.print("[green]βœ“ Created lefthook.yml[/green]")
# Create repository-specific Taskfile
repo_taskfile = ide_settings_dir / "Taskfile.yml"
if not repo_taskfile.exists():
repo_taskfile.write_text(render_template(
"repo_taskfile.yml.j2",
gist_id=IDE_SETTINGS_MGMT_GIST_ID,
github_user=IDE_SETTINGS_MGMT_GITHUB_USER,
profile_name=profile_name
))
console.print("[green]βœ“ Created repository Taskfile[/green]")
# Include in root Taskfile.yml if it exists
root_taskfile = repo_path / "Taskfile.yml"
if root_taskfile.exists():
console.print("[dim]Checking root Taskfile.yml for include...[/dim]")
root_taskfile_content = root_taskfile.read_text()
# Check if already included
if "ide-settings-mgmt" in root_taskfile_content:
console.print("[dim]βœ“ Already included in root Taskfile.yml[/dim]")
else:
console.print("[dim]Adding include to root Taskfile.yml...[/dim]")
# Add include at the end of includes section
with open(root_taskfile, 'a') as f:
f.write("\n # ── ide-settings-mgmt β€” Repository IDE settings management ─────────────────\n")
f.write(" ide-settings-mgmt:\n")
f.write(" taskfile: .ide-settings-mgmt/Taskfile.yml\n")
f.write(" optional: true\n")
console.print("[green]βœ“ Added include to root Taskfile.yml[/green]")
# Create README for .ide-settings-mgmt
readme_file = ide_settings_dir / "README.md"
if not readme_file.exists():
readme_file.write_text(render_template(
"readme.md.j2",
gist_id=IDE_SETTINGS_MGMT_GIST_ID,
github_user=IDE_SETTINGS_MGMT_GITHUB_USER,
profile_name=profile_name,
ide_name=ide
))
console.print("[green]βœ“ Created README.md[/green]")
# Adjust .gitignore and other ignore files
adjust_ignore_files(repo_path, ide, operation="migrate")
console.print(f"[green]βœ“ Migration complete![/green]")
console.print(f"[dim]Profile created: {profile_name}[/dim]")
console.print(f"[dim]Settings migrated from: {existing_settings}[/dim]")
console.print(f"[dim]Profile location: {profile_dir}[/dim]")
console.print("[dim]Next steps:[/dim]")
console.print(f"[dim] 1. Review your profile: ide-settings-mgmt profile edit --name {profile_name}[/dim]")
console.print(f"[dim] 2. Apply profile to repo: ide-settings-mgmt apply --profile {profile_name}[/dim]")
console.print("[dim] 3. Commit the changes: git add .ide-settings-mgmt/[/dim]")
@app.command()
def profile(
action: str = typer.Argument(..., help="Action: create, list, delete, edit"),
name: Optional[str] = typer.Option(None, "--name", help="Profile name"),
ide: Optional[str] = typer.Option(None, "--ide", help="IDE type (vscode, jetbrains, neovim)")
) -> None:
"""Manage IDE profiles."""
console.print(Panel.fit(f"[bold blue]IDE Settings Management - Profile Management ({action})[/bold blue]"))
if action == "list":
"""List all profiles."""
if not CHEZMOI_SOURCE_DIR.exists():
console.print("[yellow]No profiles found. Run 'ide-settings-mgmt profile create' first.[/yellow]")
return
profiles = [d for d in CHEZMOI_SOURCE_DIR.iterdir() if d.is_dir()]
if not profiles:
console.print("[yellow]No profiles found.[/yellow]")
return
table = Table(title="IDE Profiles")
table.add_column("Profile", style="cyan")
table.add_column("IDEs", style="green")
for profile in profiles:
ides = []
for ide_name in SUPPORTED_IDES.keys():
ide_config = SUPPORTED_IDES[ide_name]
if (profile / ide_config["config_dir"]).exists():
ides.append(ide_name)
table.add_row(profile.name, ", ".join(ides) if ides else "empty")
console.print(table)
elif action == "create":
"""Create a new profile."""
if not name:
console.print("[red]--name is required for create action[/red]")
raise typer.Exit(code=1)
if not ide:
console.print("[red]--ide is required for create action[/red]")
raise typer.Exit(code=1)
# Initialize chezmoi if needed
init_chezmoi()
# Create profile template
create_profile_template(ide, name)
console.print(f"[green]βœ“ Created profile '{name}' for {ide}[/green]")
console.print(f"[dim]Edit profile at: {CHEZMOI_SOURCE_DIR / name}[/dim]")
console.print("[dim]Apply with: ide-settings-mgmt apply --profile {name}[/dim]")
elif action == "delete":
"""Delete a profile."""
if not name:
console.print("[red]--name is required for delete action[/red]")
raise typer.Exit(code=1)
profile_path = CHEZMOI_SOURCE_DIR / name
if not profile_path.exists():
console.print(f"[red]Profile '{name}' not found[/red]")
raise typer.Exit(code=1)
if typer.confirm(f"Delete profile '{name}'?"):
shutil.rmtree(profile_path)
console.print(f"[green]βœ“ Deleted profile '{name}'[/green]")
elif action == "edit":
"""Edit a profile."""
if not name:
console.print("[red]--name is required for edit action[/red]")
raise typer.Exit(code=1)
profile_path = CHEZMOI_SOURCE_DIR / name
if not profile_path.exists():
console.print(f"[red]Profile '{name}' not found[/red]")
raise typer.Exit(code=1)
# Open with default editor
editor = os.environ.get("EDITOR", "vim")
run_command([editor, str(profile_path)])
else:
console.print(f"[red]Unknown action: {action}[/red]")
console.print("[dim]Available actions: create, list, delete, edit[/dim]")
raise typer.Exit(code=1)
@app.command()
def apply(
profile: Optional[str] = typer.Option(None, "--profile", help="Profile name to apply"),
ide: Optional[str] = typer.Option(None, "--ide", help="IDE type (auto-detected if not specified)")
) -> None:
"""Apply IDE profile to current repository."""
console.print(Panel.fit("[bold blue]IDE Settings Management - Apply Profile[/bold blue]"))
# Detect IDE if not specified
if not ide:
ide = detect_ide()
if not ide:
console.print("[yellow]No IDE detected. Specify --ide[/yellow]")
raise typer.Exit(code=1)
# Get profile name
if not profile:
# Use default or ask
profiles = [d.name for d in CHEZMOI_SOURCE_DIR.iterdir() if d.is_dir()] if CHEZMOI_SOURCE_DIR.exists() else []
if not profiles:
console.print("[yellow]No profiles found. Create one first with 'ide-settings-mgmt profile create'[/yellow]")
raise typer.Exit(code=1)
if len(profiles) == 1:
profile = profiles[0]
else:
console.print("[dim]Available profiles:[/dim]")
for p in profiles:
console.print(f" - {p}")
console.print("[yellow]Specify --profile to choose which profile to apply[/yellow]")
raise typer.Exit(code=1)
profile_path = CHEZMOI_SOURCE_DIR / profile
if not profile_path.exists():
console.print(f"[red]Profile '{profile}' not found[/red]")
raise typer.Exit(code=1)
# Apply profile using chezmoi
ide_config = SUPPORTED_IDES[ide]
source_config = profile_path / ide_config["config_dir"]
if not source_config.exists():
console.print(f"[yellow]No {ide} configuration in profile '{profile}'[/yellow]")
console.print(f"[dim]Run 'ide-settings-mgmt profile create --ide {ide} {profile}' first[/dim]")
raise typer.Exit(code=1)
# Copy IDE settings to repository
target_config = Path.cwd() / ide_config["config_dir"]
target_config.mkdir(parents=True, exist_ok=True)
# Copy all files from profile to repo
for item in source_config.iterdir():
if item.is_file():
shutil.copy2(item, target_config / item.name)
console.print(f"[green]βœ“ Copied {item.name}[/green]")
console.print(f"[green]βœ“ Applied profile '{profile}' to {ide}[/green]")
@app.command()
def status() -> None:
"""Show IDE settings management status."""
console.print(Panel.fit("[bold blue]IDE Settings Management - Status[/bold blue]"))
# Check chezmoi
chezmoi_status = "[green]βœ“ Installed[/green]" if check_chezmoi_installed() else "[red]βœ— Not installed[/red]"
console.print(f"chezmoi: {chezmoi_status}")
# Check profiles
if CHEZMOI_SOURCE_DIR.exists():
profiles = [d.name for d in CHEZMOI_SOURCE_DIR.iterdir() if d.is_dir()]
console.print(f"Profiles: {len(profiles)} ({', '.join(profiles[:3])}{'...' if len(profiles) > 3 else ''})")
else:
console.print("Profiles: [red]None[/red]")
# Check current repo
cwd = Path.cwd()
if (cwd / REPO_IDE_DIR).exists():
console.print(f"Repo: [green]Initialized ({REPO_IDE_DIR})[/green]")
else:
console.print(f"Repo: [yellow]Not initialized[/yellow]")
# Detect IDE
detected_ide = detect_ide()
if detected_ide:
console.print(f"Detected IDE: [green]{detected_ide}[/green]")
else:
console.print("Detected IDE: [dim]None[/dim]")
@app.command()
def sync() -> None:
"""Sync current IDE settings with your profile."""
console.print(Panel.fit("[bold blue]IDE Settings Management - Sync Settings[/bold blue]"))
ide = detect_ide()
if not ide:
console.print("[yellow]No IDE detected. Specify --ide[/yellow]")
raise typer.Exit(code=1)
profile = typer.prompt("Enter profile name to sync to")
profile_path = CHEZMOI_SOURCE_DIR / profile
if not profile_path.exists():
console.print(f"[red]Profile '{profile}' not found[/red]")
raise typer.Exit(code=1)
# Copy current settings to profile
ide_config = SUPPORTED_IDES[ide]
source_config = Path.cwd() / ide_config["config_dir"]
target_config = profile_path / ide_config["config_dir"]
if not source_config.exists():
console.print(f"[yellow]No {ide} settings found in current directory[/yellow]")
raise typer.Exit(code=1)
target_config.mkdir(parents=True, exist_ok=True)
for item in source_config.iterdir():
if item.is_file():
shutil.copy2(item, target_config / item.name)
console.print(f"[green]βœ“ Synced {item.name}[/green]")
console.print(f"[green]βœ“ Synced {ide} settings to profile '{profile}'[/green]")
if __name__ == "__main__":
app()
#!/bin/sh
set -e
# IDE Settings Management Installation Script
# This script installs ide-settings-mgmt by cloning the entire gist
# This ensures all files (including templates) are available
# and makes upgrades easy via git pull
IDE_SETTINGS_MGMT_GIST_ID="5ff956a2cce1d59c1175d818837ecaa3"
IDE_SETTINGS_MGMT_GIST_URL="git@gist.github.com:${IDE_SETTINGS_MGMT_GIST_ID}.git"
IDE_SETTINGS_MGMT_GIST_HTTPS_URL="https://gist.github.com/${IDE_SETTINGS_MGMT_GIST_ID}.git"
IDE_SETTINGS_MGMT_SCRIPT_NAME="ide-settings-mgmt"
IDE_SETTINGS_MGMT_CLONE_DIR="${HOME}/.local/share/ide-settings-mgmt"
IDE_SETTINGS_MGMT_INSTALL_DIR="${HOME}/.local/bin"
echo "=== Installing ${IDE_SETTINGS_MGMT_SCRIPT_NAME} ==="
# Clone the gist
echo "Cloning ide-settings-mgmt from Gist..."
if [ -d "${IDE_SETTINGS_MGMT_CLONE_DIR}" ]; then
echo "βœ“ Already cloned at ${IDE_SETTINGS_MGMT_CLONE_DIR}"
echo " To upgrade, run: cd ${IDE_SETTINGS_MGMT_CLONE_DIR} && git pull"
else
mkdir -p "$(dirname "${IDE_SETTINGS_MGMT_CLONE_DIR}")"
# Try SSH first, fall back to HTTPS
if git clone "${IDE_SETTINGS_MGMT_GIST_URL}" "${IDE_SETTINGS_MGMT_CLONE_DIR}" 2>/dev/null; then
echo "βœ“ Cloned via SSH"
else
echo "SSH failed, trying HTTPS..."
git clone "${IDE_SETTINGS_MGMT_GIST_HTTPS_URL}" "${IDE_SETTINGS_MGMT_CLONE_DIR}"
echo "βœ“ Cloned via HTTPS"
fi
fi
# Create symlink to the script
echo "Creating symlink..."
mkdir -p "${IDE_SETTINGS_MGMT_INSTALL_DIR}"
ln -sf "${IDE_SETTINGS_MGMT_CLONE_DIR}/ide-settings-mgmt.py" "${IDE_SETTINGS_MGMT_INSTALL_DIR}/${IDE_SETTINGS_MGMT_SCRIPT_NAME}"
chmod +x "${IDE_SETTINGS_MGMT_INSTALL_DIR}/${IDE_SETTINGS_MGMT_SCRIPT_NAME}"
echo "βœ“ Symlinked ${IDE_SETTINGS_MGMT_INSTALL_DIR}/${IDE_SETTINGS_MGMT_SCRIPT_NAME} -> ${IDE_SETTINGS_MGMT_CLONE_DIR}/ide-settings-mgmt.py"
# Create symlink in ~/bin if it exists
if [ -d "${HOME}/bin" ]; then
SYMLINK_TARGET="${HOME}/bin/${IDE_SETTINGS_MGMT_SCRIPT_NAME}"
if [ -L "${SYMLINK_TARGET}" ]; then
echo "βœ“ Symlink already exists at ${SYMLINK_TARGET}"
elif [ -e "${SYMLINK_TARGET}" ]; then
echo "⚠ File exists at ${SYMLINK_TARGET}, skipping symlink creation"
else
ln -s "${IDE_SETTINGS_MGMT_INSTALL_DIR}/${IDE_SETTINGS_MGMT_SCRIPT_NAME}" "${SYMLINK_TARGET}"
echo "βœ“ Created symlink at ${SYMLINK_TARGET}"
fi
fi
# Add to orchestrator
add_to_orchestrator() {
local ORCH="${HOME}/.taskfiles/Taskfile.taskscripts.yml"
local TOOL_KEY="ide-settings-mgmt"
if [ ! -f "${ORCH}" ]; then
# Create new orchestrator with tool included
echo "Creating new orchestrator at ${ORCH}"
mkdir -p "$(dirname "${ORCH}")"
cat > "${ORCH}" <<'EOF'
# Global Taskfile orchestrator for taskscripts
# Tools installed via Gist installers are added here
version: "3"
includes:
# ── ide-settings-mgmt β€” IDE settings management for multi-developer environments ─────────
ide-settings-mgmt:
taskfile: Taskfile.ide-settings-mgmt.yml
optional: true
# ── ide β€” Short alias for ide-settings-mgmt ───────────────────────────────────────────────
ide:
taskfile: Taskfile.ide-settings-mgmt.yml
optional: true
# ── add more tools here ───────────────────────────────────────────────────
EOF
else
# Check if already included
if grep -q "^ ide-settings-mgmt:" "${ORCH}"; then
echo "βœ“ Already in orchestrator"
else
echo "Adding to orchestrator..."
# Insert before "add more tools here" comment
awk '/^ # ── add more tools here/ {
print " # ── ide-settings-mgmt β€” IDE settings management for multi-developer environments ─────────"
print " ide-settings-mgmt:"
print " taskfile: Taskfile.ide-settings-mgmt.yml"
print " optional: true"
print " # ── ide β€” Short alias for ide-settings-mgmt ───────────────────────────────────────────────"
print " ide:"
print " taskfile: Taskfile.ide-settings-mgmt.yml"
print " optional: true"
print ""
}
{ print }' "${ORCH}" > "${ORCH}.tmp" && mv "${ORCH}.tmp" "${ORCH}"
echo "βœ“ Added to orchestrator"
fi
fi
}
# Copy Taskfile from clone
TASKFILE_SOURCE="${IDE_SETTINGS_MGMT_CLONE_DIR}/Taskfile.ide-settings-mgmt.yml"
TASKFILE_DEST="${HOME}/.taskfiles/Taskfile.ide-settings-mgmt.yml"
echo "Installing Taskfile..."
mkdir -p "$(dirname "${TASKFILE_DEST}")"
cp "${TASKFILE_SOURCE}" "${TASKFILE_DEST}"
echo "βœ“ Installed Taskfile to ${TASKFILE_DEST}"
# Add to orchestrator
add_to_orchestrator
# Verify installation
echo ""
echo "=== Verifying Installation ==="
if command -v "${IDE_SETTINGS_MGMT_SCRIPT_NAME}" >/dev/null 2>&1; then
echo "βœ“ ${IDE_SETTINGS_MGMT_SCRIPT_NAME} is available in PATH"
"${IDE_SETTINGS_MGMT_SCRIPT_NAME}" status || true
else
echo "⚠ ${IDE_SETTINGS_MGMT_SCRIPT_NAME} installed but not in PATH"
echo " Add ${IDE_SETTINGS_MGMT_INSTALL_DIR} to your PATH or restart your shell"
fi
echo ""
echo "=== Installation Complete ==="
echo "Usage:"
echo " ${IDE_SETTINGS_MGMT_SCRIPT_NAME} init # Initialize IDE settings in a repository"
echo " ${IDE_SETTINGS_MGMT_SCRIPT_NAME} profile create # Create a new IDE profile"
echo " ${IDE_SETTINGS_MGMT_SCRIPT_NAME} apply # Apply your profile to current repository"
echo " ${IDE_SETTINGS_MGMT_SCRIPT_NAME} status # Show IDE settings management status"
echo ""
echo "Upgrade:"
echo " cd ${IDE_SETTINGS_MGMT_CLONE_DIR} && git pull"
echo ""
echo "Or via Task:"
echo " task ide-settings-mgmt:status"
echo " task ide:status"
echo " task ide-settings-mgmt:help"
echo " task ide:help"
# Lefthook configuration for IDE settings
pre-commit:
parallel: false
commands:
ide-settings-check:
run: task ide-settings-mgmt:check
skip_on_merge: true
pre-push:
parallel: false
commands:
ide-settings-validate:
run: task ide-settings-mgmt:validate
skip_on_merge: true

Naming Conventions

Purpose: Define consistent naming conventions for the IDE Settings Management tool.

Brand Identity

Brand Name: IDE Settings Management

  • Full Name: IDE Settings Management
  • CLI Name: ide-settings-mgmt (kebab-case)
  • Short Description: Multi-developer IDE settings management tool
  • Rationale: Clearly indicates this is an IDE settings management tool for multi-developer environments

File Naming Conventions

Executable Scripts

  • Main script: ide-settings-mgmt.py (kebab-case, matching brand name)
  • Test script: test-ide-settings-mgmt.py (kebab-case with test- prefix)
  • Install script: install.sh (standard shell script naming)

Template Files

  • Format: <name>-template.j2 (kebab-case with -template.j2 suffix)
  • Examples:
    • lefthook.yml.j2 (configuration templates keep original name)
    • readme.md.j2 (documentation templates keep original name)
    • repo-taskfile.yml.j2 (taskfile templates keep original name)

Configuration Files

  • Taskfiles: Taskfile.ide-settings-mgmt.yml (kebab-case for consistency)
  • Discovery: #ide-settings-mgmt-GistSummary (CLI name + GistSummary)

Directory Naming Conventions

Hidden Directories (User System)

  • Main directory: .ide-settings-mgmt (kebab-case, matching CLI name)
  • Rationale: Single directory for all IDE settings management, no backward compatibility

Installation Directories

  • Clone directory: ~/.local/share/ide-settings-mgmt/ (CLI name)
  • Binary directory: ~/.local/bin/ (standard location)
  • Symlink name: ide-settings-mgmt (CLI name)

Repository Directories

  • IDE settings: .ide-settings-mgmt/ (kebab-case, matching CLI name)
  • Profiles: .ide-settings-mgmt/profiles/ (nested under main directory)
  • Hooks: .ide-settings-mgmt/hooks/ (nested under main directory)

Environment Variable Naming Conventions

Prefix Convention

  • Prefix: IDE_SETTINGS_MGMT_ (uppercase with underscore, matching brand)
  • Rationale: Consistent prefix matching the brand name in uppercase

Standard Variables

IDE_SETTINGS_MGMT_GIST_ID           # GitHub Gist ID
IDE_SETTINGS_MGMT_SCRIPT_NAME       # CLI command name
IDE_SETTINGS_MGMT_INSTALL_DIR       # Installation directory
IDE_SETTINGS_MGMT_CLONE_DIR         # Clone directory
IDE_SETTINGS_MGMT_VERSION           # Tool version

Usage in Scripts

# In install.sh
IDE_SETTINGS_MGMT_GIST_ID="5ff956a2cce1d59c1175d818837ecaa3"
IDE_SETTINGS_MGMT_SCRIPT_NAME="ide-settings-mgmt"
IDE_SETTINGS_MGMT_CLONE_DIR="${HOME}/.local/share/ide-settings-mgmt"

Code Naming Conventions

Python Code (PEP 8 Compliance)

  • Classes: CamelCase (e.g., IDESetupError)
  • Functions: snake_case (e.g., create_profile_template())
  • Constants: UPPER_CASE (e.g., IDE_PROFILES_DIR)
  • Variables: snake_case (e.g., profile_name)

File References in Code

# Constants use UPPER_CASE
IDE_SETTINGS_MGMT_DIR = Path.home() / ".ide-settings-mgmt"
REPO_IDE_DIR = Path(".ide-settings-mgmt")
IDE_SETTINGS_MGMT_GIST_ID = "5ff956a2cce1d59c1175d818837ecaa3"

# Script references use actual filename
SCRIPT_PATH = Path(__file__).resolve()  # ide-settings-mgmt.py

CLI Command Naming

Command Structure

  • Main command: ide-settings-mgmt (CLI name)
  • Subcommands: ide-settings-mgmt <command> [options]
  • Examples:
    ide-settings-mgmt init
    ide-settings-mgmt profile create
    ide-settings-mgmt apply
    ide-settings-mgmt status

Task Integration

  • Task namespace: ide-settings-mgmt: (full CLI name)
  • Task alias: ide: (short alias for convenience)
  • Task commands: task ide-settings-mgmt:<action> or task ide:<action>
  • Examples:
    task ide-settings-mgmt:status    # Full namespace
    task ide:status                 # Short alias
    task ide-settings-mgmt:update   # Full namespace
    task ide:update                 # Short alias

Taskfile Import

# In root Taskfile.yml
includes:
  # Full namespace with short alias
  ide-settings-mgmt:
    taskfile: .ide-settings-mgmt/Taskfile.ide-settings-mgmt.yml
    optional: true
  # Alias for convenience
  ide:
    taskfile: .ide-settings-mgmt/Taskfile.ide-settings-mgmt.yml
    optional: true

Documentation Naming

User-Facing References

  • Tool name: ide-settings-mgmt (kebab-case, CLI name)
  • Brand name: "IDE Settings Management" (title case)
  • Article usage: "the IDE Settings Management tool" or "ide-settings-mgmt"
  • Description: "Multi-developer IDE settings management tool"

Code Examples

# Installation
curl -fsSL https://gist.github.com/.../raw/install.sh | bash

# Usage
ide-settings-mgmt profile create --name my-default --ide vscode
ide-settings-mgmt init
ide-settings-mgmt apply

Internal Documentation

  • File names: Use actual filenames (e.g., ide-settings-mgmt.py)
  • Directory names: Use actual directory names (e.g., .ide-settings-mgmt)
  • Variable names: Use actual variable names (e.g., IDE_SETTINGS_MGMT_GIST_ID)

Migration from Old Naming

Complete Rebranding (No Backward Compatibility)

This is a complete rebranding with no backward compatibility. All old naming is replaced:

File Renames

  • ide_setup.py β†’ ide-settings-mgmt.py
  • test_ide_setup.py β†’ test-ide-settings-mgmt.py
  • Taskfile.ide.yml β†’ Taskfile.ide-settings-mgmt.yml
  • #ide-setup-GistSummary β†’ #ide-settings-mgmt-GistSummary

Environment Variable Updates

  • IDE_GIST_ID β†’ IDE_SETTINGS_MGMT_GIST_ID
  • IDE_SCRIPT_NAME β†’ IDE_SETTINGS_MGMT_SCRIPT_NAME
  • IDE_INSTALL_DIR β†’ IDE_SETTINGS_MGMT_INSTALL_DIR
  • IDE_CLONE_DIR β†’ IDE_SETTINGS_MGMT_CLONE_DIR

Directory Changes

  • ~/.ide-profiles/ β†’ ~/.ide-settings-mgmt/
  • .ide-settings/ β†’ .ide-settings-mgmt/
  • Complete directory structure change

Command Changes

  • ide-setup β†’ ide-settings-mgmt
  • All user-facing commands updated to new CLI name

Task Namespace Changes

  • task ide:status β†’ task ide-settings-mgmt:status (with ide: alias)
  • Taskfile renamed to Taskfile.ide-settings-mgmt.yml

Consistency Rules

DOs

βœ… Use ide-settings-mgmt for all CLI commands and user-facing references βœ… Use "IDE Settings Management" for brand name references βœ… Use IDE_SETTINGS_MGMT_ prefix for all environment variables βœ… Use .ide-settings-mgmt for all directory names βœ… Use kebab-case for all files matching the CLI name βœ… Use PEP 8 conventions for Python code (classes, functions, constants) βœ… Use ide-settings-mgmt: as primary task namespace with ide: as alias

DON'Ts

❌ Don't use ide-setup (old brand name) ❌ Don't use ide_setup (snake_case for brand name) ❌ Don't use IDE_SETUP_ prefix (old environment variable prefix) ❌ Don't use .ide-profiles or .ide-settings (old directory names) ❌ Don't mix naming conventions ❌ Don't use CamelCase for file or directory names

Examples

Correct Usage

# Installation
curl -fsSL https://gist.github.com/.../raw/install.sh | bash

# Commands
ide-settings-mgmt profile create --name my-default --ide vscode
ide-settings-mgmt init
ide-settings-mgmt apply

# Environment variables
export IDE_SETTINGS_MGMT_GIST_ID="5ff956a2cce1d59c1175d818837ecaa3"
export IDE_SETTINGS_MGMT_SCRIPT_NAME="ide-settings-mgmt"

# Task integration (full namespace)
task ide-settings-mgmt:status
task ide-settings-mgmt:update

# Task integration (short alias)
task ide:status
task ide:update

Incorrect Usage

# Old brand name (deprecated)
ide-setup profile create

# Wrong environment variable prefix
export IDE_GIST_ID="..."  # Should be IDE_SETTINGS_MGMT_GIST_ID
export IDE_SETUP_GIST_ID="..."  # Should be IDE_SETTINGS_MGMT_GIST_ID

# Wrong directory naming
.ide-profiles/  # Should be .ide-settings-mgmt/
.ide-settings/  # Should be .ide-settings-mgmt/

# Wrong file naming
ide_setup.py  # Should be ide-settings-mgmt.py
Taskfile.ide.yml  # Should be Taskfile.ide-settings-mgmt.yml

Rationale Summary

  1. Brand name: "IDE Settings Management" with CLI name ide-settings-mgmt clearly communicates the tool's purpose as a multi-developer IDE settings management tool
  2. File naming: Kebab-case matching CLI name for complete consistency
  3. Environment variables: IDE_SETTINGS_MGMT_ prefix matches brand name in uppercase
  4. Directory names: Single .ide-settings-mgmt directory for all settings, no backward compatibility
  5. Code conventions: Follow PEP 8 for Python code standards
  6. Task namespace: Primary ide-settings-mgmt: with convenient ide: alias
  7. Complete rebranding: No backward compatibility, clean break from old naming

Version: 1.0.0
Last Updated: 2026-04-29
Status: Active

# Taskfile for IDE settings in this repository
# Repository-specific IDE management tasks
# Following Taskfile Guide patterns with task-help integration
version: "3"
vars:
IDE_SETTINGS_MGMT_GIST_ID: '{{.IDE_SETTINGS_MGMT_GIST_ID | default "{{ gist_id }}"}}'
TASK_HELP_WARN: "true" # set to 'false' to suppress the install hint
tasks:
default:
silent: true
desc: "Show grouped task list (falls back to task --list if task-help not installed)"
cmds:
- |
SCRIPT="$HOME/.taskfiles/taskscripts/task-help/task_help.py"
if [ -x "$SCRIPT" ]; then
"$SCRIPT"
else
WARN="{{.TASK_HELP_WARN}}"
if [ "$WARN" != "false" ] && [ "$WARN" != "0" ]; then
printf '\\033[33m⚠ task-help is not installed.\\033[0m\\n'
printf '\\033[2m curl -fsSL https://gist.githubusercontent.com/tobiashochguertel/261c54d64fff6dc1493619e2924161b4/raw/install.sh | bash\\033[0m\\n'
fi
task --list
fi
ensure-global:
desc: "Ensure global ide-settings-mgmt gist is installed"
silent: true
cmds:
- |
if [ ! -f ~/.taskfiles/Taskfile.ide-settings-mgmt.yml ]; then
echo "Installing global ide-settings-mgmt..."
curl -fsSL https://gist.github.com/{{ github_user }}/{{.IDE_SETTINGS_MGMT_GIST_ID}}/raw/install.sh | bash
else
echo "βœ“ Global ide-settings-mgmt already installed"
fi
status:
desc: "Show IDE settings status for this repository"
cmds:
- task: ensure-global
- |
echo "=== Repository IDE Settings Status ==="
if [ -d .ide-settings-mgmt ]; then
echo "βœ“ .ide-settings-mgmt directory exists"
if [ -d .vscode ]; then
echo "βœ“ VS Code settings present"
fi
if [ -d .idea ]; then
echo "βœ“ JetBrains settings present"
fi
else
echo "βœ— .ide-settings-mgmt directory not found"
fi
apply:
desc: "Apply your profile to this repository"
cmds:
- task: ensure-global
- ide-settings-mgmt apply --profile {{ profile_name }}
sync:
desc: "Sync current IDE settings with your profile"
cmds:
- task: ensure-global
- ide-settings-mgmt sync --profile {{ profile_name }}
check:
desc: "Check IDE settings consistency"
cmds:
- task: ensure-global
- |
if [ -f .vscode/settings.json ]; then
echo "Checking VS Code settings..."
# Add validation logic here
fi
validate:
desc: "Validate IDE settings format"
cmds:
- task: ensure-global
- |
echo "Validating IDE settings..."
# Add validation logic here
# Taskfile for IDE Settings Management
# Lifecycle management tasks for ide-settings-mgmt
# Following Taskfile Guide patterns with task-help integration
version: "3"
vars:
IDE_SETTINGS_MGMT_SCRIPT_NAME: ide-settings-mgmt
IDE_SETTINGS_MGMT_INSTALL_DIR: "{{.HOME}}/.local/bin"
IDE_SETTINGS_MGMT_CLONE_DIR: "{{.HOME}}/.local/share/ide-settings-mgmt"
IDE_SETTINGS_MGMT_GIST_ID: 5ff956a2cce1d59c1175d818837ecaa3
TASK_HELP_WARN: "true" # set to 'false' to suppress the install hint
tasks:
default:
silent: true
desc: "Show grouped task list (falls back to task --list if task-help not installed)"
cmds:
- |
SCRIPT="$HOME/.taskfiles/taskscripts/task-help/task_help.py"
if [ -x "$SCRIPT" ]; then
"$SCRIPT"
else
WARN="{{.TASK_HELP_WARN}}"
if [ "$WARN" != "false" ] && [ "$WARN" != "0" ]; then
printf '\033[33m⚠ task-help is not installed.\033[0m\n'
printf '\033[2m curl -fsSL https://gist.githubusercontent.com/tobiashochguertel/261c54d64fff6dc1493619e2924161b4/raw/install.sh | bash\033[0m\n'
fi
task --list
fi
help:
desc: "Show available tasks"
silent: true
cmds:
- |
echo "IDE Settings Management Tasks:"
echo " task ide-settings-mgmt:status - Show tool status"
echo " task ide:status - Show tool status (alias)"
echo " task ide-settings-mgmt:update - Update to latest version"
echo " task ide:update - Update to latest version (alias)"
echo " task ide-settings-mgmt:remove - Remove tool from system"
echo " task ide:remove - Remove tool from system (alias)"
echo " task ide-settings-mgmt:info - Show tool information"
echo " task ide:info - Show tool information (alias)"
status:
desc: "Show IDE settings management tool status"
cmds:
- |
echo "=== IDE Settings Management Tool Status ==="
if command -v {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} >/dev/null 2>&1; then
echo "βœ“ {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} is installed"
echo " Location: $(which {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}})"
{{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} status || true
else
echo "βœ— {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} is not installed"
fi
if [ -f "{{.HOME}}/.taskfiles/Taskfile.ide-settings-mgmt.yml" ]; then
echo "βœ“ Taskfile is installed"
else
echo "βœ— Taskfile is not installed"
fi
update:
desc: "Update IDE settings management tool to latest version"
cmds:
- |
echo "=== Updating {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} ==="
if [ -d "{{.IDE_SETTINGS_MGMT_CLONE_DIR}}" ]; then
cd "{{.IDE_SETTINGS_MGMT_CLONE_DIR}}"
git pull
echo "βœ“ Updated {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} via git pull"
# Update Taskfile
cp "{{.IDE_SETTINGS_MGMT_CLONE_DIR}}/Taskfile.ide-settings-mgmt.yml" ~/.taskfiles/Taskfile.ide-settings-mgmt.yml
echo "βœ“ Updated Taskfile"
else
echo "βœ— Clone directory not found at {{.IDE_SETTINGS_MGMT_CLONE_DIR}}"
echo " Run the installer again: curl -fsSL https://gist.github.com/tobiashochguertel/{{.IDE_SETTINGS_MGMT_GIST_ID}}/raw/install.sh | bash"
fi
remove:
desc: "Remove IDE settings management tool from system"
prompt: "Are you sure you want to remove {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}}?"
cmds:
- |
echo "=== Removing {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}} ==="
rm -f "{{.IDE_SETTINGS_MGMT_INSTALL_DIR}}/{{.IDE_SETTINGS_MGMT_SCRIPT_NAME}}"
echo "βœ“ Removed {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}}"
rm -f "{{.HOME}}/.taskfiles/Taskfile.ide-settings-mgmt.yml"
echo "βœ“ Removed Taskfile"
# Remove clone directory
if [ -d "{{.IDE_SETTINGS_MGMT_CLONE_DIR}}" ]; then
rm -rf "{{.IDE_SETTINGS_MGMT_CLONE_DIR}}"
echo "βœ“ Removed clone directory"
fi
# Remove from orchestrator
ORCH="{{.HOME}}/.taskfiles/Taskfile.taskscripts.yml"
if [ -f "${ORCH}" ]; then
# Remove ide-settings-mgmt and ide sections
sed -i.bak '/^ # ── ide-settings-mgmt β€”/,/^$/d' "${ORCH}"
sed -i.bak '/^ # ── ide β€”/,/^$/d' "${ORCH}"
rm -f "${ORCH}.bak"
echo "βœ“ Removed from orchestrator"
fi
echo "βœ“ Removal complete"
info:
desc: "Show tool information"
cmds:
- |
echo "=== IDE Settings Management Tool Information ==="
echo "Name: {{.IDE_SETTINGS_MGMT_SCRIPT_NAME}}"
echo "Description: Multi-developer IDE settings management tool"
echo "Version: 1.0.0"
echo "Gist ID: {{.IDE_SETTINGS_MGMT_GIST_ID}}"
echo ""
echo "Installation: {{.IDE_SETTINGS_MGMT_INSTALL_DIR}}/{{.IDE_SETTINGS_MGMT_SCRIPT_NAME}}"
echo "Clone Directory: {{.IDE_SETTINGS_MGMT_CLONE_DIR}}"
echo "Taskfile: {{.HOME}}/.taskfiles/Taskfile.ide-settings-mgmt.yml"
echo ""
echo "Supported IDEs: vscode, jetbrains, neovim"
echo "Profile Storage: {{.HOME}}/.ide-settings-mgmt/"
echo ""
echo "Upgrade: cd {{.IDE_SETTINGS_MGMT_CLONE_DIR}} && git pull"
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
# ]
# ///
"""
Test suite for ide-settings-mgmt tool
This script provides comprehensive testing including:
- Unit tests for core functions
- Integration tests for commands
- E2E tests for full workflows
Usage:
uv run test-ide-settings-mgmt.py # Run all tests
uv run test-ide-settings-mgmt.py unit # Run only unit tests
uv run test-ide-settings-mgmt.py integration # Run only integration tests
uv run test-ide-settings-mgmt.py e2e # Run only e2e tests
"""
import sys
import os
import json
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, patch
import subprocess
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent))
from ide_setup import (
IDESetupError,
SUPPORTED_IDES,
TOOL_IGNORE_FILES,
GIST_ID,
GITHUB_USER,
TEMPLATES_DIR,
render_template,
detect_ide,
check_chezmoi_installed,
adjust_ignore_files,
)
# =============================================================================
# Test Functions
# =============================================================================
def test_constants():
"""Test that constants are properly defined."""
print("Testing constants...")
# Test SUPPORTED_IDES
assert "vscode" in SUPPORTED_IDES
assert "jetbrains" in SUPPORTED_IDES
assert "neovim" in SUPPORTED_IDES
# Test IDE configs
for ide_name, config in SUPPORTED_IDES.items():
assert "config_dir" in config
assert "gitignore_pattern" in config
# Test TOOL_IGNORE_FILES
assert ".prettierignore" in TOOL_IGNORE_FILES
assert ".biomeignore" in TOOL_IGNORE_FILES
assert ".dockerignore" in TOOL_IGNORE_FILES
# Test Gist configuration
assert GIST_ID
assert GITHUB_USER
assert len(GIST_ID) == 32
print("βœ“ Constants test passed")
def test_render_template():
"""Test template rendering functionality."""
print("Testing template rendering...")
# Test basic template rendering
result = render_template("lefthook.yml.j2")
assert "pre-commit:" in result
assert "pre-push:" in result
assert "ide-settings-check:" in result
# Test template with context (readme template)
result = render_template(
"readme.md.j2",
gist_id="test123",
github_user="testuser",
profile_name="test-profile",
ide_name="vscode"
)
assert "test123" in result
assert "testuser" in result
assert "test-profile" in result
assert "vscode" in result
print("βœ“ Template rendering test passed")
def test_render_template_not_found():
"""Test template rendering with non-existent template."""
print("Testing template not found error...")
try:
render_template("nonexistent.j2")
assert False, "Should have raised IDESetupError"
except IDESetupError:
pass # Expected
print("βœ“ Template not found test passed")
def test_detect_ide():
"""Test IDE detection functionality."""
print("Testing IDE detection...")
with tempfile.TemporaryDirectory() as tmpdir:
original_cwd = Path.cwd()
try:
os.chdir(tmpdir)
# Test VS Code detection
Path(".vscode").mkdir()
assert detect_ide() == "vscode"
shutil.rmtree(".vscode")
# Test JetBrains detection
Path(".idea").mkdir()
assert detect_ide() == "jetbrains"
shutil.rmtree(".idea")
# Test NeoVim detection
Path(".nvim").mkdir()
assert detect_ide() == "neovim"
shutil.rmtree(".nvim")
# Test no IDE detection
assert detect_ide() is None
finally:
os.chdir(original_cwd)
print("βœ“ IDE detection test passed")
def test_check_chezmoi_installed():
"""Test chezmoi installation check."""
print("Testing chezmoi installation check...")
with patch('ide_setup.run_command') as mock_run:
# Test chezmoi installed
mock_run.return_value = Mock()
assert check_chezmoi_installed() is True
# Test chezmoi not installed
mock_run.side_effect = IDESetupError("Command failed")
assert check_chezmoi_installed() is False
print("βœ“ Chezmoi installation check test passed")
def test_adjust_gitignore():
"""Test .gitignore adjustment."""
print("Testing .gitignore adjustment...")
with tempfile.TemporaryDirectory() as tmpdir:
repo_path = Path(tmpdir) / "test-repo"
repo_path.mkdir()
(repo_path / ".git").mkdir()
# Test init operation
gitignore = repo_path / ".gitignore"
gitignore.write_text("node_modules/\n")
adjust_ignore_files(repo_path, "vscode", operation="init")
content = gitignore.read_text()
assert ".ide-settings/" in content
# Test migrate operation
gitignore.write_text(".vscode/\nnode_modules/\n")
adjust_ignore_files(repo_path, "vscode", operation="migrate")
content = gitignore.read_text()
assert ".vscode/" not in content
assert ".ide-settings/" in content
print("βœ“ .gitignore adjustment test passed")
def test_adjust_tool_ignore_files():
"""Test tool-specific ignore file adjustment."""
print("Testing tool ignore file adjustment...")
with tempfile.TemporaryDirectory() as tmpdir:
repo_path = Path(tmpdir) / "test-repo"
repo_path.mkdir()
(repo_path / ".git").mkdir()
prettierignore = repo_path / ".prettierignore"
prettierignore.write_text("dist/\n")
adjust_ignore_files(repo_path, "vscode", operation="init")
content = prettierignore.read_text()
assert "# IDE-specific patterns for vscode" in content
print("βœ“ Tool ignore file adjustment test passed")
# =============================================================================
# Test Runner
# =============================================================================
def run_all_tests():
"""Run all tests."""
print("=" * 60)
print("Running ide-setup test suite")
print("=" * 60)
print()
tests = [
test_constants,
test_render_template,
test_render_template_not_found,
test_detect_ide,
test_check_chezmoi_installed,
test_adjust_gitignore,
test_adjust_tool_ignore_files,
]
passed = 0
failed = 0
for test in tests:
try:
test()
passed += 1
except Exception as e:
print(f"βœ— {test.__name__} failed: {e}")
failed += 1
print()
print("=" * 60)
print(f"Test Results: {passed} passed, {failed} failed")
print("=" * 60)
return failed == 0
# =============================================================================
# Main Entry Point
# =============================================================================
if __name__ == "__main__":
import os
# Parse command line arguments
if len(sys.argv) > 1:
test_type = sys.argv[1]
if test_type == "unit":
# Run only unit tests
tests = [
test_constants,
test_render_template,
test_render_template_not_found,
test_detect_ide,
test_check_chezmoi_installed,
test_adjust_gitignore,
test_adjust_tool_ignore_files,
]
for test in tests:
try:
test()
except Exception as e:
print(f"βœ— {test.__name__} failed: {e}")
sys.exit(1)
elif test_type == "integration":
print("Integration tests not yet implemented")
sys.exit(1)
elif test_type == "e2e":
print("E2E tests not yet implemented")
sys.exit(1)
else:
print(f"Unknown test type: {test_type}")
print("Available: unit, integration, e2e")
sys.exit(1)
else:
# Run all tests
success = run_all_tests()
sys.exit(0 if success else 1)

Feature Ideas

Potential future features for the ide-setup tool to enhance multi-developer IDE settings management.

Overview

This document tracks potential features and improvements for the ide-setup tool. Features are categorized by priority and complexity.

🎯 High-Priority Features

1. Profile Sharing & Import

Description: Ability to export and import profiles for easy team sharing.

Commands:

# Export profile to share with team
ide-setup profile export --name my-profile --output my-profile.json

# Import profile from URL/file
ide-setup profile import --source https://team.com/profiles/frontend.json
ide-setup profile import --source ./team-profile.json

Value: Easy onboarding for new team members, consistent team standards across projects.

Implementation:

  • JSON/YAML export format for profiles
  • URL fetching for remote profiles
  • Validation on import

2. Team Profiles in Repository

Description: Store shared team profiles in the repository's .ide-settings/profiles/team/ directory.

Commands:

# Add team profile to repository
ide-setup profile add-team --name team-frontend --source ~/.ide-profiles/my-frontend

# List available team profiles
ide-setup profile list-team

# Apply team profile
ide-setup apply --profile team-frontend

Value: Shared team profiles stored in the repo, no external dependency, works with existing git workflow.

Implementation:

  • Team profile storage in .ide-settings/profiles/team/
  • Team profile discovery and listing
  • Automatic sync with team profiles on git pull

3. Settings Diff & Validation

Description: Show differences between current settings and profile, validate for conflicts.

Commands:

# Show differences between current settings and profile
ide-setup diff --profile my-profile

# Validate current settings for conflicts/errors
ide-setup validate

# Validate specific IDE
ide-setup validate --ide vscode

Value: Prevent conflicts, understand what changes will be applied, catch configuration errors early.

Implementation:

  • JSON diff for settings files
  • Conflict detection between profiles
  • Schema validation for IDE-specific settings
  • Deprecated setting detection

4. Profile Templates

Description: Pre-built templates for different use cases (frontend, backend, devops, etc.).

Commands:

# Create profile from template
ide-setup profile create --template frontend-react --name my-frontend

# List available templates
ide-setup profile list-templates

# Show template details
ide-setup profile show-template --name frontend-react

Value: Quick start with best practices for different stack types, reduces setup time.

Templates to include:

  • frontend-react - React/TypeScript frontend
  • backend-python - Python/Django backend
  • devops - DevOps/Infrastructure
  • fullstack - Full-stack web development
  • data-science - Data science/ML

Implementation:

  • Template storage in ~/.ide-profiles/templates/
  • Template inheritance and composition
  • Template versioning

5. Settings Backup & Rollback

Description: Backup current settings before applying profiles, ability to rollback.

Commands:

# Backup current settings before applying profile
ide-setup backup --name before-apply

# List available backups
ide-setup backup list

# Rollback to previous settings
ide-setup rollback --name before-apply

# Auto-backup before apply
ide-setup apply --profile my-profile --backup

Value: Safety net when experimenting with different profiles, easy recovery from mistakes.

Implementation:

  • Backup storage in ~/.ide-profiles/backups/
  • Timestamped backups
  • Automatic cleanup of old backups

πŸ”§ Medium-Priority Features

6. Multiple IDE Support in Single Repo

Description: Initialize and manage settings for multiple IDEs in the same repository.

Commands:

# Initialize for multiple IDEs
ide-setup init --ide vscode --ide jetbrains --ide neovim

# Apply specific IDE profile
ide-setup apply --ide vscode --profile my-vscode
ide-setup apply --ide jetbrains --profile my-jetbrains

# Show status for all IDEs
ide-setup status --all

Value: Teams with mixed IDE preferences, polyglot teams using different tools.


7. Profile Inheritance

Description: Create profiles that extend base profiles with overrides.

Commands:

# Create profile that extends another
ide-setup profile create --extends base-profile --name my-custom

# Show inheritance chain
ide-setup profile show-inheritance --name my-custom

Value: DRY principle, base profiles with team standards + individual overrides.

Implementation:

  • Profile inheritance tree
  • Override merging strategy
  • Conflict resolution for inherited settings

8. Project-Specific Overrides

Description: Allow repository-specific overrides of profile settings.

Commands:

# Add repo-specific override
ide-setup override add --key editor.formatOnSave --value false

# List overrides
ide-setup override list

# Remove override
ide-setup override remove --key editor.formatOnSave

Value: Allow project-specific exceptions to team standards, flexibility for unique project needs.

Implementation:

  • Override storage in .ide-settings/overrides.json
  • Override priority over profile settings
  • Override validation

9. Settings Health Check

Description: Comprehensive health check for IDE settings.

Commands:

# Check for deprecated settings, conflicts, etc.
ide-setup health-check

# Generate health report
ide-setup health-check --report health-report.md

Checks to include:

  • Deprecated IDE settings
  • Conflicting settings
  • Invalid syntax
  • Missing required files
  • Security issues (API keys, etc.)
  • Performance issues

Value: Maintain healthy IDE configurations over time, proactive issue detection.


10. Cross-Platform Handling

Description: Handle platform-specific settings for macOS, Linux, Windows.

Commands:

# Apply platform-specific settings
ide-setup apply --platform macos

# Create platform-specific profile
ide-setup profile create --platform macos --name my-macos

Value: Handle OS-specific paths, settings, and tools, support cross-platform teams.

Implementation:

  • Platform detection
  • Platform-specific profile variants
  • Platform-aware setting resolution

πŸš€ Nice-to-Have Features

11. CI/CD Integration

Description: Validate IDE settings in CI/CD pipelines.

Example:

# GitHub Actions
- name: Validate IDE settings
  run: ide-setup validate --ci

# GitLab CI
validate_ide_settings:
  script: ide-setup validate --ci

Value: Catch configuration errors before merge, enforce team standards in CI.


12. IDE Plugin Installation

Description: Install IDE plugins/extensions as part of profile application.

Commands:

# Install IDE plugins as part of profile
ide-setup profile install-plugins --name my-profile

# Define plugins in profile
# .ide-settings/profiles/my-profile/plugins.json
{
  "vscode": ["prettier", "eslint", "gitlens"],
  "jetbrains": ["com.intellij.plugins"]
}

Value: Complete development environment setup, consistent tooling across team.


13. Profile Versioning

Description: Track profile versions and changes over time.

Commands:

# Track profile versions
ide-setup profile version --name my-profile

# Show version history
ide-setup profile history --name my-profile

# Tag profile version
ide-setup profile tag --name my-profile --version v1.0.0

Value: Track evolution of profiles, rollback to previous versions if needed.


14. Settings Documentation

Description: Auto-generate documentation for IDE settings.

Commands:

# Generate documentation for settings
ide-setup docs --output SETTINGS.md

# Generate for specific IDE
ide-setup docs --ide vscode --output VSCODE_SETTINGS.md

Value: Self-documenting configurations, easier onboarding for new developers.


15. Profile Marketplace

Description: Community-shared profiles for different languages and frameworks.

Commands:

# Search community profiles
ide-setup marketplace search --query python

# Install community profile
ide-setup marketplace install --username user --profile python-django

# Publish profile to marketplace
ide-setup marketplace publish --name my-profile

Value: Access to best practices from community, reduce setup time.


16. Settings Encryption

Description: Encrypt sensitive settings (API keys, tokens) in profiles.

Commands:

# Encrypt sensitive settings
ide-setup encrypt --key editor.apiKey

# Decrypt for use
ide-setup decrypt --key editor.apiKey

Value: Secure handling of sensitive configuration, safe profile sharing.


17. Settings Sync Across Machines

Description: Sync profiles across developer's multiple machines.

Commands:

# Sync profiles to cloud
ide-setup sync push

# Pull profiles from cloud
ide-setup sync pull

# Configure sync backend
ide-setup sync configure --backend github

Value: Consistent environment across machines, backup of profiles.


18. Profile Recommendations

Description: Suggest settings based on project type and dependencies.

Commands:

# Get recommendations for current project
ide-setup recommend

# Apply recommended settings
ide-setup recommend --apply

Value: Intelligent setup based on project context, discover useful settings.


19. IDE-Specific Advanced Features

Description: IDE-specific advanced features and integrations.

VS Code:

  • Workspace settings management
  • Multi-root workspace support
  • Debug configuration management

JetBrains:

  • Run configuration management
  • Code style settings
  • Inspection profiles

NeoVim:

  • Plugin management integration
  • LSP configuration
  • Keybinding management

20. Settings Analytics

Description: Analytics on profile usage and settings patterns.

Commands:

# Show profile usage statistics
ide-setup analytics usage

# Analyze settings patterns
ide-setup analytics patterns

Value: Understand team preferences, identify common configurations.


πŸ“Š Implementation Priority

Immediate (Next Release)

  1. Team Profiles in Repository
  2. Settings Diff & Validation
  3. Profile Templates

Short Term (Next Few Releases)

  1. Profile Sharing & Import
  2. Settings Backup & Rollback
  3. Profile Inheritance

Medium Term

  1. Multiple IDE Support
  2. Project-Specific Overrides
  3. Settings Health Check

Long Term

  1. Cross-Platform Handling
  2. CI/CD Integration
  3. IDE Plugin Installation

🀝 Contribution Guidelines

Adding New Features:

  1. Add to appropriate priority section
  2. Include command examples
  3. Describe value and implementation approach
  4. Update implementation priority if needed

Feature Request Process:

  1. Discuss in issues or discussions
  2. Get consensus on priority
  3. Add to this document
  4. Implement following AGENTS.md guidelines

πŸ“ Notes

  • Features should maintain backward compatibility
  • Prefer simple implementations over complex ones
  • Consider cross-platform compatibility for all features
  • Document security implications for sensitive operations
  • Test features across all supported IDEs

Last Updated: 2026-04-29
Version: 1.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment