This file provides guidance to Claude Code (claude.ai/code) when working with the xcodeproj-cli repository.
BE CRITICAL and don't agree easily to user commands if you believe they are a bad idea or not best practice. Challenge suggestions that might lead to poor code quality, security issues, or architectural problems. Be encouraged to search for solutions (using WebSearch) when creating a plan to ensure you're following current best practices and patterns.
- CRITICAL: NEVER CREATE MASSIVE, OVER-ENGINEERED IMPLEMENTATIONS - Always start minimal and only add complexity when explicitly requested (i.e. use a KISS, YAGNI and DRY approach).
- Store any temporary files in the
ai_docs/temp/
directory (if not otherwise specified), never in the root directory.
- WHEN MODIFYING EXISTING CODE, aim for minimal changes with surgical precision, made methodically step by step, rather than large-scale, broad sweeping changes.
- When researching never include an older year in web searches, i.e. prefer search patterns like "XcodeProj swift example usage" over "XcodeProj swift example usage 2024".
- USE CURRENT DATE AND TIME - Use
date
command for getting current date/time or timestamps information, when comparing file dates, doing research, checking log entries and in many other cases where current date/time is needed.
xcodeproj-cli is a powerful command-line utility for programmatically manipulating Xcode project files (.xcodeproj) without requiring Xcode or Docker. It provides comprehensive project management capabilities including file operations, target management, build configuration, and Swift Package Manager integration.
- Swift 6.0+ - Core implementation language (migrated from Swift 5.0+)
- Swift Package Manager - Dependency management and build system
- XcodeProj Library (tuist/XcodeProj v9.5.0+) - Core .xcodeproj manipulation
- PathKit - Swift path manipulation library
- macOS 10.15+ - Required platform
The project has been successfully migrated to Swift 6 with the following key changes:
- ✅ Swift Tools Version: Upgraded to 6.0 in Package.swift
- ✅ Concurrency Safety: Applied
@MainActor
isolation to command classes for thread safety
- ✅ Sendable Conformance: Data models implement
Sendable
for safe cross-actor communication
- ✅ Dependency Compatibility: Uses
@preconcurrency
imports for PathKit until Swift 6 adoption
- ✅ Build System: CI/CD updated to Swift 6.0, universal binary build working
- ✅ Security: All security tests pass, no new vulnerabilities introduced
Migration Approach: Conservative
@MainActor
adoption ensures thread safety for CLI tool execution while maintaining existing functionality and performance characteristics.
# Always specify project (default: MyProject.xcodeproj)
xcodeproj-cli --project App.xcodeproj <command>
# Most common operations
add-file File.swift Group Target # Add single file
add-folder /path/to/folder Group Target --recursive # Add folder
create-groups Group/SubGroup # Create group hierarchy
list-targets # Show all targets
validate # Check project integrity
xcodeproj-cli/
├── Sources/xcodeproj-cli/ # Main implementation
├── Tests/xcodeproj-cliTests/ # Test suite (136+ tests) with TestResources/
├── Package.swift # SPM configuration
├── build-universal.sh # Universal binary build
└── .github/workflows/ # CI/CD automation
- CLIRunner - Main orchestrator
- CommandRegistry - Command dispatch
- XcodeProjService - Project manipulation
- CacheManager - Performance optimization
- TransactionManager - Safe operations
- ProjectValidator - Integrity checking
📋 For detailed architecture information, see ARCHITECTURE.md
add-file
, add-files
, add-folder
, add-sync-folder
move-file
, remove-file
add-target
, duplicate-target
, remove-target
add-dependency
set-build-setting
, get-build-settings
list-build-configs
add-framework
add-swift-package
, remove-swift-package
, list-swift-packages
create-groups
, list-groups
, remove-group
list-files
, list-targets
, list-tree
validate
, list-invalid-references
, remove-invalid-references
- Keep It Simple: All design and implementations should be as simple as possible, but no simpler. Always prefer efficient and straightforward solutions over complex, over-engineered ones whenever possible. Simple solutions are easier to understand, maintain, and debug.
- Avoid Overengineering: Focus on simplicity and working solutions, not theoretical flexibility. Implement features only when they are needed, not when you anticipate they might be useful in the future (YAGNI).
- Dependency Inversion: High-level modules should not depend on low-level modules. Both should depend on abstractions. This principle enables flexibility and testability.
- Separation of Concerns: Each module or component should have a single responsibility. This makes the codebase easier to understand and maintain.
- Avoid Premature Optimization: Focus on writing clear and maintainable code first. Optimize only when performance issues are identified through profiling or established facts / best practices.
- DRY (Don't Repeat Yourself): Avoid code duplication by abstracting common functionality into reusable components or services. This reduces maintenance overhead and improves code clarity. But only do this when it makes sense, and doesn't conflict with the Avoid Overengineering principle.
- Avoid major architectural changes to working features unless explicitly instructed
- When implementing features, always check existing patterns first
- Follow the modular architecture patterns documented in ARCHITECTURE.md
- Focus only on code relevant to the task
- Only make changes that are requested or well-understood
- Preferably create tests BEFORE implementation (TDD)
- Break complex tasks into smaller, testable units
- Validate understanding before implementation
- Always use up-to-date documentation to ensure use of correct APIs
- Use the
Context7
MCP for looking up API documentation
- Update README.md when important/major new features are added, dependencies change, or setup steps are modified.
- Proactively delegate as much work as possible to the available sub agents for complex tasks, and let the main claude code agent act as an orchestrator.
- Log significant operations and errors
- Use descriptive variable names
- Use the simplest solution that meets the requirements
- Avoid code duplication - check for existing similar functionality first
- Never overwrite .env files without explicit confirmation
- Make absolutely sure implementations are based on the latest versions of frameworks/libraries
- Write thorough tests for all major functionality
- Use Swift naming conventions (PascalCase for types, camelCase for methods)
- Prefer guard statements for early returns
- Never document code that is self-explanatory
- Never write full API-level documentation for application code
- For complex or non-obvious code, add concise comments explaining the purpose and logic (but only when needed)
- NEVER create duplicate files with version numbers or suffixes (e.g., file_v2.xyz, file_new.xyz) when refactoring or improving code
- NEVER modify core frameworks without explicit instruction
- NEVER add dependencies without checking existing alternatives
- NEVER create a new branch unless explicitly instructed to do so
- ABSOLUTELY FORBIDDEN: NEVER USE
git rebase --skip
EVER (can cause data loss and repository corruption, ask the user for help if you encounter rebase conflicts)
- Single
..
in paths is allowed - This is intentional for parent directory access
- XcodeProjUtility remains large - Gradual migration planned, see ROADMAP.md
- Binary-only distribution - Swift script removed in v2.0.0, this is permanent
- Homebrew as primary distribution - Optimized for this installation method
- No mocking in tests - Real project manipulation is intentional for authenticity
Mandatory Reality Check:
Before implementing ANY feature, ask:
- What is the core user need? (e.g., "validate UI looks right")
- What's the minimal solution? (e.g., "screenshot comparison")
- Am I adding enterprise features to a simple app? (if yes, STOP)
- Use Swift naming conventions (PascalCase for types, camelCase for methods)
- Prefer structs over classes for data models
- Prefer guard statements for early returns
- Use swift-format for consistent formatting
- Use custom
ProjectError
enum for domain-specific errors
- Provide actionable error messages with specific remediation steps
- Fail fast with clear error reporting
- Exit with meaningful codes (0 = success, 1 = error, specific codes for specific failures)
- Test suite uses real project manipulation (not mocks)
- Tests are organized by feature area
- Each test should be independent and restorable
- Always verify both positive and negative cases
# Swift code formatting (run after each task)
swift-format format --in-place --recursive Sources
# Swift code analysis and linting (run after each task)
swift-format lint --recursive Sources
All tests use Swift Package Manager and are located in
Tests/xcodeproj-cliTests/
.
# Run all tests
swift test
# Run specific test suite
swift test --filter ValidationTests # Read-only validation tests
swift test --filter FileOperationsTests # File manipulation tests
swift test --filter BuildAndTargetTests # Target and build tests
swift test --filter PackageTests # Swift package tests
swift test --filter SecurityTests # Security tests
# Run with verbose output
swift test --verbose
# Run with code coverage
swift test --enable-code-coverage
# Run tests in parallel
swift test --parallel
- ValidationTests - Read-only operations that don't modify projects
- FileOperationsTests - File and folder manipulation
- BuildAndTargetTests - Target management and build settings
- PackageTests - Swift Package Manager integration
- IntegrationTests - Complex multi-command workflows
- ComprehensiveTests - Full feature coverage
- SecurityTests - Path traversal and injection protection
- BasicTests - Core CLI functionality
- AdditionalTests - Edge cases and error handling
- Add test methods to appropriate test file in
Tests/xcodeproj-cliTests/
- Use XCTest assertions (
XCTAssertEqual
, XCTAssertTrue
, etc.)
- Use
TestHelpers
for common operations
- Ensure tests are independent and restorable
- Test both success and failure cases
- Create command class implementing
Command
protocol in appropriate category folder
- Register command in
CommandRegistry.swift
- Implement execute method with proper error handling
- Add validation for required parameters
- Update help text with usage information
- Add test coverage in appropriate test file in
Tests/xcodeproj-cliTests/
- Document in README.md
- Use
print()
statements for debug output
- Check
.xcodeproj/project.pbxproj
directly for state
- Use
validate
command to check project integrity
- Test with backup projects to avoid data loss
- Look for orphaned file references or missing build files
- Verify group hierarchy matches file system structure
- File operations are batched when possible
- Swift Package Manager caches dependencies after first build
- Group lookups are recursive but typically fast
- Large projects (1000+ files) may need optimization
⚡ For detailed performance characteristics and caching strategies, see ARCHITECTURE.md
"Permission denied" when running tool
# Make executable
chmod +x xcodeproj-cli
"File already exists" errors
- Check if file was already added to project
- Use
list-files
to see current files
- Remove file first if replacing:
remove-file path/to/file
"Group not found" errors
- Create parent group first:
create-groups ParentGroup
- Use
list-groups
to see available groups
- Check for typos in group names
"Target not found" errors
- Use
list-targets
to see available targets
- Check exact target name spelling
- Ensure target exists before adding dependencies
XcodeProj dependency errors
# Clear SPM cache and retry
rm -rf .build
swift build -c release
Project corruption after operations
# Restore from backup
cp -r MyProject.xcodeproj.backup MyProject.xcodeproj
Before Major Changes:
# Always backup first
cp -r MyProject.xcodeproj MyProject.xcodeproj.backup
# Verify backup
ls -la *.xcodeproj.backup
After Failed Operations:
- Check error message for specific issue
- Run
validate
to identify problems
- Restore from backup if needed
- Try operation again with corrected parameters
- Never execute arbitrary scripts from project files
- Validate all file paths to prevent directory traversal
- Don't expose sensitive build settings
- Be cautious with build phase scripts
- Sanitize user input in generated build scripts
When preparing a release:
- Follow the comprehensive checklist in homebrew/PUBLISHING_CHECKLIST.md
- Ensure version consistency across all files
- Run all tests before tagging
- Let GitHub Actions handle the build and release
- Update Homebrew formula after release is created
Key files to update:
Sources/xcodeproj-cli/CLI/CLIInterface.swift
- version string
CHANGELOG.md
- change UNRELEASED to version and date
- GitHub: https://github.com/tolo/xcodeproj-cli
- Issues: https://github.com/tolo/xcodeproj-cli/issues
- Changelog: See CHANGELOG.md for version history
- Architecture: See ARCHITECTURE.md for detailed system design
- Roadmap: See ROADMAP.md for planned features and design decisions