{"owner":"gnachman","repo":"iTerm2","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md"],"skills":{"AGENTS.md":"# iTerm2 Agent Guide\n\n> Essential guide for AI agents working on iTerm2.\n\n## Critical Rules\n\n**Read `CLAUDE.md` first** - it contains mandatory coding practices. Key rules:\n\n1. **Never** write >1 line of JavaScript/HTML/CSS inline - use external files with `iTermBrowserTemplateLoader.swift`\n2. Use `it_fatalError` and `it_assert` (not standard `fatalError`/`assert`) for proper crash logs\n3. **Never** create dependency cycles - use delegates/closures instead\n4. `git add` new files immediately after creation\n\n## Architecture\n\n**iTerm2** uses hybrid Objective-C/Swift: core system in Objective-C, modern features in Swift.\n\n**Application Flow:** App → Window/Tab → Session → Terminal Emulation → Rendering\n\n### Key Components\n\n- **Application:** `iTermController` - Main coordinator\n- **Window/Tab:** `PseudoTerminal`, `PTYTab` - Window and tab management\n- **Session:** `PTYSession` - Session lifecycle, I/O, state\n- **Terminal Emulation:** `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`, `VT100Screen`, `VT100Grid`\n- **Rendering:** `PTYTextView` - Metal-accelerated rendering\n\n## Directory Structure\n\n```\niTerm2/\n├── sources/               # Main application code\n├── tests/iTerm2XCTests/   # Unit tests\n├── proto/api.proto        # Protocol Buffer API\n├── tools/                 # Build scripts\n├── submodules/            # Git submodules\n├── WebExtensionsFramework/  # Swift SPM framework (see WebExtensionsFramework/CLAUDE.md)\n├── iTerm2.sdef            # AppleScript API\n├── CLAUDE.md              # Code best practices\n└── iTerm2.xcodeproj/      # Xcode project\n```\n\n## Common Development Tasks\n\n### Modifying Terminal Emulation\n- Escape sequences flow: `VT100Parser`/`VT100Terminal` → `VT100ScreenMutableState`/`VT100Screen` → `VT100Grid`\n- Look at `VT100ScreenTest.m` for examples\n- Test changes thoroughly\n\n### Extending APIs\n- **WebSocket API:** Edit `proto/api.proto`, run `tools/build_proto.sh`\n- **AppleScript:** Edit `iTerm2.sdef`, implement in `*+Scripting.{h,m}` files\n\n## Code Patterns\n\n### Avoiding Dependency Cycles\n```swift\n// ❌ Bad: Strong reference cycle\nclass Parent { var child: Child? }\nclass Child { var parent: Parent? }\n\n// ✅ Good: Use weak reference\nclass Child { weak var parent: Parent? }\n```\n\n### Using External Templates\n```objc\n// ✅ Good\nNSString *html = [iTermBrowserTemplateLoader loadTemplateNamed:@\"chat\"];\n\n// ❌ Bad: Inline HTML\nNSString *html = @\"<html><body>...</body></html>\";\n```\n\n### Error Handling\n```swift\n// ✅ Good\nit_fatalError(\"Unexpected state\")\nit_assert(value != nil, \"Value required\")\n\n// ❌ Bad: Won't create crash logs\nfatalError(\"Unexpected state\")\nassert(value != nil)\n```\n\n## Finding Your Way\n\n**Language choice:**\n- Use Objective-C when modifying existing Objective-C code\n- Use Swift for new features\n- Use `@objc` attributes for Swift/Objective-C interop\n- The Swift bridging header is `sources/iTerm2SharedARC-Bridging-Header.h` - check here for available Objective-C types and constants in Swift\n\n**Where code lives:**\n- Session logic → `PTYSession.{h,m}`\n- Terminal emulation → `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`\n- UI rendering → `PTYTextView.{h,m}`\n- Tests → `tests/iTerm2XCTests/`\n","CLAUDE.md":"## Code Best Practices\n\n- Avoid writing javascript, html, or CSS that's more than one line long in Swift. Create a new file and use the existing template mechanism to load it.\n- After creating a new file, `git add` it immediately\n- To add a file to the Xcode project, use `tools/add_file_to_xcodeproj.rb <file_path> <target_name>` (e.g., `tools/add_file_to_xcodeproj.rb sources/Example.swift iTerm2SharedARC`)\n- The Companion app's Xcode project (`Companion/iTerm2Companion.xcodeproj`) is generated from scratch by `Companion/tools/generate_companion_project.rb`, which is the source of truth. That script does NOT use `add_file_to_xcodeproj.rb`. After adding, removing, or renaming a Companion source file, re-run the generator and commit its output. Crucially, any structural change made in Xcode (a new target, a Swift package dependency, entitlements, or a build setting) MUST be mirrored back into the generator in the same commit, because the next regeneration overwrites the project wholesale and would otherwise silently drop it. If the two drift, regenerating produces a broken build.\n- In Swift, use it_fatalError and it_assert instead of fatalError and assert, which do not create useful crash logs. In ObjC, assert is ok although ITAssertWithMessage is preferable. Asserts are enabled in release builds.\n- Don't write more than one line of inline javascript, html, or css. Instead create a new file and load it using iTermBrowserTemplateLoader.swift\n- Don't create dependency cycles. Use delegates or closures instead.\n- To run unit tests in ModernTests, use tools/run_tests.expect. It takes an argument naming the test or tests, such as `tools/run_tests.expect ModernTests/iTermScriptFunctionCallTest/testSignature`\n- After changes that affect AI chat (request builders, response parsers, AITermController, AIConversation, anything in sources/AITerm/, ChatAgent, ChatClient, etc.), run `tools/run_ai_live.sh` against real vendor APIs. This is a separate live harness from the regular ModernTests; it costs real money but exercises end-to-end round-trips (smoke, multi-turn, tool calls, both streaming and non-streaming) against OpenAI/Anthropic/Gemini/DeepSeek. The default ModernTests run skips the live harness, so unit tests passing alone is not sufficient evidence. Pass a filter to scope the run: `tools/run_ai_live.sh openai`, `tools/run_ai_live.sh smoke`, or an exact method name like `tools/run_ai_live.sh test_anthropic_toolCall_nonStreaming`.\n- After changes that affect attachment serialization (per-vendor file/image/document content blocks, MIME allowlists in LLMProvider, anything in CompletionsAnthropic.swift / Gemini.swift / DeepSeek.swift / Llama.swift / LLMModernProtocol.swift / ResponsesAPIRequest.swift attachment paths), run the 96-cell attachment matrix: `tools/run_ai_live.sh attachmentMatrix`. It bypasses the LLMProvider.accepts gate and sends each of 16 MIME fixtures through each of 6 vendor lanes, asserting whether the vendor accepted-with-content, rejected at HTTP, or accepted-but-garbled. Drift in either direction fails loudly with a `MATRIX DRIFT:` message that tells you whether to widen the allowlist, fix the serializer, or update the matrix cell. Full sweep: ~95 sec, ~70 API calls, under $0.50. Scope with `attachmentMatrix_<lane>` (e.g. `attachmentMatrix_gemini` runs one column) or `attachmentMatrix_<kind>` (e.g. `attachmentMatrix_imagePNG` runs one row across all lanes), or run a single cell by exact method name (`test_attachmentMatrix_anthropic_imageWEBP`). Fixtures live in `ModernTests/Resources/AttachmentFixtures/`.\n- After changes that affect the uv Python runtime (sources/API/iTermUvProvisioner.swift, iTermUvMigration.swift, iTermUvPythonVersion.swift, iTermUvCommand.swift, iTermUvManifest.swift, iTermScriptRuntime.swift, or the uv launch/migration/import paths in iTermAPIScriptLauncher.m / iTermScriptArchive.m / iTermScriptImporter.m), run `tools/run_python_runtime_e2e.sh`. This is a separate live harness from the regular ModernTests: it hits the network (iterm2.com), downloads uv plus a CPython interpreter, builds a venv, and pip-installs iterm2/certifi/pyobjc, so the default ModernTests run (which is hermetic and skips it) passing alone is not sufficient evidence. No arg runs both live tests; scope with `download` (manifest fetch + RSA verify + install uv), `provision` (full-environment provision, import iterm2/certifi/objc/AppKit, asyncio REPL top-level await), or an exact test-method name. The broader Tier C matrix (script drives an in-process iTermAPIServer, migration rollback, cache/gate matrix) is still manual: tests/uv-migration-manual-test-plan.md.\n- When renaming a file tracked by git (and almost all of them are) use `git mv` instead of `mv`\n- To make a debug build run `tools/build.sh` (or `tools/build.sh Development`). This saves logs to `tmp/build.log` and shows only errors/warnings on failure.\n- Little scripts or text files that are used for manual testing of features go in tests/\n- The deployment target for iTerm2 is macOS 12. You don't need to perform availability checks for older versions.\n- Don't replace curly quotes with straight quotes. Same for apostrophes and single quotes. If you need help typing a curly quote, just ask. Here are some you can copy and paste: ‘’“”\n- In user-visible strings do not use \" except as a shorthand for inch. Prefer curly quotes like “ and ”. I know this goes against your nature, but fight hard here.\n- Ask permission before using auto layout if it's not already in use in a given file. Debugging auto layout is the worst hell.\n- The deployment target is macOS 12. Don't add availability checks for 12 and lower.\n- Never `git add` submodules without express written permission.\n- Don't include AI-generated markdown files (summaries, plans, etc.) in commits — only ship code.\n- Avoid duplicate expressions; hoist shared computations into a named `const` before branching.\n- Don't change defaults silently.\n- Use [iTermUserDefaults userDefaults] instead of [NSUserDefaults standardUserDefaults]\n- Use `make run` to build and run a debug build.\n- Never run the app without the argument `-suite suitename` where `suitename` is the last path component of the current directory. To run a development build, just do `make run`. Omitting -suite causes conflicts between the new instance of iTerm2 and the main one, where claude runs.\n- Do not use associated objects (objc_getAssociatedObject or objc_setAssociatedObject) without express written permission.\n- You should treat warnings as errors.\n- If you get stuck, ask for help. It's better to ask me to look at something in the debugger than to flail around for a long time.\n- If your changes introduce compiler warnings, fix them.\n- After landing a feature or bugfix, update docs/notes-3.7.txt (the release notes). Max width of a line is 50 characters.\n- For changes to the Companion iOS app (the `Companion/` directory, \"iTerm2 Buddy\"), put release notes in Companion/docs/notes.txt instead of docs/notes-3.7.txt.\n- The sources directory is organized into folders. Before adding a new file, consider which directory it belongs in. Some are named after features while others are named after their role.\n- User Defaults keys that should only be stored locally begin with the prefix NoSync. If a user chooses to load prefs from a custom location (e.g., Dropbox) they may be prompted to write settings when a non-NoSync key changes. To avoid disrupting them in this manner, user defaults that are not actual configuration settings (e.g., a list of recent items) get a NoSync prefix.\n- Use DLog statements so we can debug problems in the field. These statements have no effect when debug logging is off (the default) and it's OK for them to do somewhat expensive operations like getting a stack trace.\n- Use RLog statements to log debug messages to memory even when debug logging is not on. Creating a debug log later will pull in the last 10 megabytes of RLog statements. RLog runs always so don't do anything expensive (such as stack traces) and do not use them in hot paths that could burn a lot of CPU logging.\n- When adding temporary code for debugging, use NSFuckingLog instead of NSLog because NSLog truncates long output. Logging code that is intended to remain long-term should use DLog.\n- Do not use an SF Symbols name as a string literal. Get it using SFSymbolGetString in Objective C or the SFSymbol enum in Swift.\n- Don't use sleep to solve concurrency problems.\n- Tests should not be flaky. Don't write tests that will fail if the system is slower than usual.\n- Write new classes in Swift unless there is a very good reason not to.\n- Do not modify CLAUDE.md without express written permission\n"},"files":{"AGENTS.md":"# iTerm2 Agent Guide\n\n> Essential guide for AI agents working on iTerm2.\n\n## Critical Rules\n\n**Read `CLAUDE.md` first** - it contains mandatory coding practices. Key rules:\n\n1. **Never** write >1 line of JavaScript/HTML/CSS inline - use external files with `iTermBrowserTemplateLoader.swift`\n2. Use `it_fatalError` and `it_assert` (not standard `fatalError`/`assert`) for proper crash logs\n3. **Never** create dependency cycles - use delegates/closures instead\n4. `git add` new files immediately after creation\n\n## Architecture\n\n**iTerm2** uses hybrid Objective-C/Swift: core system in Objective-C, modern features in Swift.\n\n**Application Flow:** App → Window/Tab → Session → Terminal Emulation → Rendering\n\n### Key Components\n\n- **Application:** `iTermController` - Main coordinator\n- **Window/Tab:** `PseudoTerminal`, `PTYTab` - Window and tab management\n- **Session:** `PTYSession` - Session lifecycle, I/O, state\n- **Terminal Emulation:** `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`, `VT100Screen`, `VT100Grid`\n- **Rendering:** `PTYTextView` - Metal-accelerated rendering\n\n## Directory Structure\n\n```\niTerm2/\n├── sources/               # Main application code\n├── tests/iTerm2XCTests/   # Unit tests\n├── proto/api.proto        # Protocol Buffer API\n├── tools/                 # Build scripts\n├── submodules/            # Git submodules\n├── WebExtensionsFramework/  # Swift SPM framework (see WebExtensionsFramework/CLAUDE.md)\n├── iTerm2.sdef            # AppleScript API\n├── CLAUDE.md              # Code best practices\n└── iTerm2.xcodeproj/      # Xcode project\n```\n\n## Common Development Tasks\n\n### Modifying Terminal Emulation\n- Escape sequences flow: `VT100Parser`/`VT100Terminal` → `VT100ScreenMutableState`/`VT100Screen` → `VT100Grid`\n- Look at `VT100ScreenTest.m` for examples\n- Test changes thoroughly\n\n### Extending APIs\n- **WebSocket API:** Edit `proto/api.proto`, run `tools/build_proto.sh`\n- **AppleScript:** Edit `iTerm2.sdef`, implement in `*+Scripting.{h,m}` files\n\n## Code Patterns\n\n### Avoiding Dependency Cycles\n```swift\n// ❌ Bad: Strong reference cycle\nclass Parent { var child: Child? }\nclass Child { var parent: Parent? }\n\n// ✅ Good: Use weak reference\nclass Child { weak var parent: Parent? }\n```\n\n### Using External Templates\n```objc\n// ✅ Good\nNSString *html = [iTermBrowserTemplateLoader loadTemplateNamed:@\"chat\"];\n\n// ❌ Bad: Inline HTML\nNSString *html = @\"<html><body>...</body></html>\";\n```\n\n### Error Handling\n```swift\n// ✅ Good\nit_fatalError(\"Unexpected state\")\nit_assert(value != nil, \"Value required\")\n\n// ❌ Bad: Won't create crash logs\nfatalError(\"Unexpected state\")\nassert(value != nil)\n```\n\n## Finding Your Way\n\n**Language choice:**\n- Use Objective-C when modifying existing Objective-C code\n- Use Swift for new features\n- Use `@objc` attributes for Swift/Objective-C interop\n- The Swift bridging header is `sources/iTerm2SharedARC-Bridging-Header.h` - check here for available Objective-C types and constants in Swift\n\n**Where code lives:**\n- Session logic → `PTYSession.{h,m}`\n- Terminal emulation → `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`\n- UI rendering → `PTYTextView.{h,m}`\n- Tests → `tests/iTerm2XCTests/`\n","CLAUDE.md":"## Code Best Practices\n\n- Avoid writing javascript, html, or CSS that's more than one line long in Swift. Create a new file and use the existing template mechanism to load it.\n- After creating a new file, `git add` it immediately\n- To add a file to the Xcode project, use `tools/add_file_to_xcodeproj.rb <file_path> <target_name>` (e.g., `tools/add_file_to_xcodeproj.rb sources/Example.swift iTerm2SharedARC`)\n- The Companion app's Xcode project (`Companion/iTerm2Companion.xcodeproj`) is generated from scratch by `Companion/tools/generate_companion_project.rb`, which is the source of truth. That script does NOT use `add_file_to_xcodeproj.rb`. After adding, removing, or renaming a Companion source file, re-run the generator and commit its output. Crucially, any structural change made in Xcode (a new target, a Swift package dependency, entitlements, or a build setting) MUST be mirrored back into the generator in the same commit, because the next regeneration overwrites the project wholesale and would otherwise silently drop it. If the two drift, regenerating produces a broken build.\n- In Swift, use it_fatalError and it_assert instead of fatalError and assert, which do not create useful crash logs. In ObjC, assert is ok although ITAssertWithMessage is preferable. Asserts are enabled in release builds.\n- Don't write more than one line of inline javascript, html, or css. Instead create a new file and load it using iTermBrowserTemplateLoader.swift\n- Don't create dependency cycles. Use delegates or closures instead.\n- To run unit tests in ModernTests, use tools/run_tests.expect. It takes an argument naming the test or tests, such as `tools/run_tests.expect ModernTests/iTermScriptFunctionCallTest/testSignature`\n- After changes that affect AI chat (request builders, response parsers, AITermController, AIConversation, anything in sources/AITerm/, ChatAgent, ChatClient, etc.), run `tools/run_ai_live.sh` against real vendor APIs. This is a separate live harness from the regular ModernTests; it costs real money but exercises end-to-end round-trips (smoke, multi-turn, tool calls, both streaming and non-streaming) against OpenAI/Anthropic/Gemini/DeepSeek. The default ModernTests run skips the live harness, so unit tests passing alone is not sufficient evidence. Pass a filter to scope the run: `tools/run_ai_live.sh openai`, `tools/run_ai_live.sh smoke`, or an exact method name like `tools/run_ai_live.sh test_anthropic_toolCall_nonStreaming`.\n- After changes that affect attachment serialization (per-vendor file/image/document content blocks, MIME allowlists in LLMProvider, anything in CompletionsAnthropic.swift / Gemini.swift / DeepSeek.swift / Llama.swift / LLMModernProtocol.swift / ResponsesAPIRequest.swift attachment paths), run the 96-cell attachment matrix: `tools/run_ai_live.sh attachmentMatrix`. It bypasses the LLMProvider.accepts gate and sends each of 16 MIME fixtures through each of 6 vendor lanes, asserting whether the vendor accepted-with-content, rejected at HTTP, or accepted-but-garbled. Drift in either direction fails loudly with a `MATRIX DRIFT:` message that tells you whether to widen the allowlist, fix the serializer, or update the matrix cell. Full sweep: ~95 sec, ~70 API calls, under $0.50. Scope with `attachmentMatrix_<lane>` (e.g. `attachmentMatrix_gemini` runs one column) or `attachmentMatrix_<kind>` (e.g. `attachmentMatrix_imagePNG` runs one row across all lanes), or run a single cell by exact method name (`test_attachmentMatrix_anthropic_imageWEBP`). Fixtures live in `ModernTests/Resources/AttachmentFixtures/`.\n- After changes that affect the uv Python runtime (sources/API/iTermUvProvisioner.swift, iTermUvMigration.swift, iTermUvPythonVersion.swift, iTermUvCommand.swift, iTermUvManifest.swift, iTermScriptRuntime.swift, or the uv launch/migration/import paths in iTermAPIScriptLauncher.m / iTermScriptArchive.m / iTermScriptImporter.m), run `tools/run_python_runtime_e2e.sh`. This is a separate live harness from the regular ModernTests: it hits the network (iterm2.com), downloads uv plus a CPython interpreter, builds a venv, and pip-installs iterm2/certifi/pyobjc, so the default ModernTests run (which is hermetic and skips it) passing alone is not sufficient evidence. No arg runs both live tests; scope with `download` (manifest fetch + RSA verify + install uv), `provision` (full-environment provision, import iterm2/certifi/objc/AppKit, asyncio REPL top-level await), or an exact test-method name. The broader Tier C matrix (script drives an in-process iTermAPIServer, migration rollback, cache/gate matrix) is still manual: tests/uv-migration-manual-test-plan.md.\n- When renaming a file tracked by git (and almost all of them are) use `git mv` instead of `mv`\n- To make a debug build run `tools/build.sh` (or `tools/build.sh Development`). This saves logs to `tmp/build.log` and shows only errors/warnings on failure.\n- Little scripts or text files that are used for manual testing of features go in tests/\n- The deployment target for iTerm2 is macOS 12. You don't need to perform availability checks for older versions.\n- Don't replace curly quotes with straight quotes. Same for apostrophes and single quotes. If you need help typing a curly quote, just ask. Here are some you can copy and paste: ‘’“”\n- In user-visible strings do not use \" except as a shorthand for inch. Prefer curly quotes like “ and ”. I know this goes against your nature, but fight hard here.\n- Ask permission before using auto layout if it's not already in use in a given file. Debugging auto layout is the worst hell.\n- The deployment target is macOS 12. Don't add availability checks for 12 and lower.\n- Never `git add` submodules without express written permission.\n- Don't include AI-generated markdown files (summaries, plans, etc.) in commits — only ship code.\n- Avoid duplicate expressions; hoist shared computations into a named `const` before branching.\n- Don't change defaults silently.\n- Use [iTermUserDefaults userDefaults] instead of [NSUserDefaults standardUserDefaults]\n- Use `make run` to build and run a debug build.\n- Never run the app without the argument `-suite suitename` where `suitename` is the last path component of the current directory. To run a development build, just do `make run`. Omitting -suite causes conflicts between the new instance of iTerm2 and the main one, where claude runs.\n- Do not use associated objects (objc_getAssociatedObject or objc_setAssociatedObject) without express written permission.\n- You should treat warnings as errors.\n- If you get stuck, ask for help. It's better to ask me to look at something in the debugger than to flail around for a long time.\n- If your changes introduce compiler warnings, fix them.\n- After landing a feature or bugfix, update docs/notes-3.7.txt (the release notes). Max width of a line is 50 characters.\n- For changes to the Companion iOS app (the `Companion/` directory, \"iTerm2 Buddy\"), put release notes in Companion/docs/notes.txt instead of docs/notes-3.7.txt.\n- The sources directory is organized into folders. Before adding a new file, consider which directory it belongs in. Some are named after features while others are named after their role.\n- User Defaults keys that should only be stored locally begin with the prefix NoSync. If a user chooses to load prefs from a custom location (e.g., Dropbox) they may be prompted to write settings when a non-NoSync key changes. To avoid disrupting them in this manner, user defaults that are not actual configuration settings (e.g., a list of recent items) get a NoSync prefix.\n- Use DLog statements so we can debug problems in the field. These statements have no effect when debug logging is off (the default) and it's OK for them to do somewhat expensive operations like getting a stack trace.\n- Use RLog statements to log debug messages to memory even when debug logging is not on. Creating a debug log later will pull in the last 10 megabytes of RLog statements. RLog runs always so don't do anything expensive (such as stack traces) and do not use them in hot paths that could burn a lot of CPU logging.\n- When adding temporary code for debugging, use NSFuckingLog instead of NSLog because NSLog truncates long output. Logging code that is intended to remain long-term should use DLog.\n- Do not use an SF Symbols name as a string literal. Get it using SFSymbolGetString in Objective C or the SFSymbol enum in Swift.\n- Don't use sleep to solve concurrency problems.\n- Tests should not be flaky. Don't write tests that will fail if the system is slower than usual.\n- Write new classes in Swift unless there is a very good reason not to.\n- Do not modify CLAUDE.md without express written permission\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# iTerm2 Agent Guide\n\n> Essential guide for AI agents working on iTerm2.\n\n## Critical Rules\n\n**Read `CLAUDE.md` first** - it contains mandatory coding practices. Key rules:\n\n1. **Never** write >1 line of JavaScript/HTML/CSS inline - use external files with `iTermBrowserTemplateLoader.swift`\n2. Use `it_fatalError` and `it_assert` (not standard `fatalError`/`assert`) for proper crash logs\n3. **Never** create dependency cycles - use delegates/closures instead\n4. `git add` new files immediately after creation\n\n## Architecture\n\n**iTerm2** uses hybrid Objective-C/Swift: core system in Objective-C, modern features in Swift.\n\n**Application Flow:** App → Window/Tab → Session → Terminal Emulation → Rendering\n\n### Key Components\n\n- **Application:** `iTermController` - Main coordinator\n- **Window/Tab:** `PseudoTerminal`, `PTYTab` - Window and tab management\n- **Session:** `PTYSession` - Session lifecycle, I/O, state\n- **Terminal Emulation:** `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`, `VT100Screen`, `VT100Grid`\n- **Rendering:** `PTYTextView` - Metal-accelerated rendering\n\n## Directory Structure\n\n```\niTerm2/\n├── sources/               # Main application code\n├── tests/iTerm2XCTests/   # Unit tests\n├── proto/api.proto        # Protocol Buffer API\n├── tools/                 # Build scripts\n├── submodules/            # Git submodules\n├── WebExtensionsFramework/  # Swift SPM framework (see WebExtensionsFramework/CLAUDE.md)\n├── iTerm2.sdef            # AppleScript API\n├── CLAUDE.md              # Code best practices\n└── iTerm2.xcodeproj/      # Xcode project\n```\n\n## Common Development Tasks\n\n### Modifying Terminal Emulation\n- Escape sequences flow: `VT100Parser`/`VT100Terminal` → `VT100ScreenMutableState`/`VT100Screen` → `VT100Grid`\n- Look at `VT100ScreenTest.m` for examples\n- Test changes thoroughly\n\n### Extending APIs\n- **WebSocket API:** Edit `proto/api.proto`, run `tools/build_proto.sh`\n- **AppleScript:** Edit `iTerm2.sdef`, implement in `*+Scripting.{h,m}` files\n\n## Code Patterns\n\n### Avoiding Dependency Cycles\n```swift\n// ❌ Bad: Strong reference cycle\nclass Parent { var child: Child? }\nclass Child { var parent: Parent? }\n\n// ✅ Good: Use weak reference\nclass Child { weak var parent: Parent? }\n```\n\n### Using External Templates\n```objc\n// ✅ Good\nNSString *html = [iTermBrowserTemplateLoader loadTemplateNamed:@\"chat\"];\n\n// ❌ Bad: Inline HTML\nNSString *html = @\"<html><body>...</body></html>\";\n```\n\n### Error Handling\n```swift\n// ✅ Good\nit_fatalError(\"Unexpected state\")\nit_assert(value != nil, \"Value required\")\n\n// ❌ Bad: Won't create crash logs\nfatalError(\"Unexpected state\")\nassert(value != nil)\n```\n\n## Finding Your Way\n\n**Language choice:**\n- Use Objective-C when modifying existing Objective-C code\n- Use Swift for new features\n- Use `@objc` attributes for Swift/Objective-C interop\n- The Swift bridging header is `sources/iTerm2SharedARC-Bridging-Header.h` - check here for available Objective-C types and constants in Swift\n\n**Where code lives:**\n- Session logic → `PTYSession.{h,m}`\n- Terminal emulation → `VT100Parser`, `VT100Terminal`, `VT100ScreenMutableState`\n- UI rendering → `PTYTextView.{h,m}`\n- Tests → `tests/iTerm2XCTests/`\n","category":"root","tokens":796},{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"## Code Best Practices\n\n- Avoid writing javascript, html, or CSS that's more than one line long in Swift. Create a new file and use the existing template mechanism to load it.\n- After creating a new file, `git add` it immediately\n- To add a file to the Xcode project, use `tools/add_file_to_xcodeproj.rb <file_path> <target_name>` (e.g., `tools/add_file_to_xcodeproj.rb sources/Example.swift iTerm2SharedARC`)\n- The Companion app's Xcode project (`Companion/iTerm2Companion.xcodeproj`) is generated from scratch by `Companion/tools/generate_companion_project.rb`, which is the source of truth. That script does NOT use `add_file_to_xcodeproj.rb`. After adding, removing, or renaming a Companion source file, re-run the generator and commit its output. Crucially, any structural change made in Xcode (a new target, a Swift package dependency, entitlements, or a build setting) MUST be mirrored back into the generator in the same commit, because the next regeneration overwrites the project wholesale and would otherwise silently drop it. If the two drift, regenerating produces a broken build.\n- In Swift, use it_fatalError and it_assert instead of fatalError and assert, which do not create useful crash logs. In ObjC, assert is ok although ITAssertWithMessage is preferable. Asserts are enabled in release builds.\n- Don't write more than one line of inline javascript, html, or css. Instead create a new file and load it using iTermBrowserTemplateLoader.swift\n- Don't create dependency cycles. Use delegates or closures instead.\n- To run unit tests in ModernTests, use tools/run_tests.expect. It takes an argument naming the test or tests, such as `tools/run_tests.expect ModernTests/iTermScriptFunctionCallTest/testSignature`\n- After changes that affect AI chat (request builders, response parsers, AITermController, AIConversation, anything in sources/AITerm/, ChatAgent, ChatClient, etc.), run `tools/run_ai_live.sh` against real vendor APIs. This is a separate live harness from the regular ModernTests; it costs real money but exercises end-to-end round-trips (smoke, multi-turn, tool calls, both streaming and non-streaming) against OpenAI/Anthropic/Gemini/DeepSeek. The default ModernTests run skips the live harness, so unit tests passing alone is not sufficient evidence. Pass a filter to scope the run: `tools/run_ai_live.sh openai`, `tools/run_ai_live.sh smoke`, or an exact method name like `tools/run_ai_live.sh test_anthropic_toolCall_nonStreaming`.\n- After changes that affect attachment serialization (per-vendor file/image/document content blocks, MIME allowlists in LLMProvider, anything in CompletionsAnthropic.swift / Gemini.swift / DeepSeek.swift / Llama.swift / LLMModernProtocol.swift / ResponsesAPIRequest.swift attachment paths), run the 96-cell attachment matrix: `tools/run_ai_live.sh attachmentMatrix`. It bypasses the LLMProvider.accepts gate and sends each of 16 MIME fixtures through each of 6 vendor lanes, asserting whether the vendor accepted-with-content, rejected at HTTP, or accepted-but-garbled. Drift in either direction fails loudly with a `MATRIX DRIFT:` message that tells you whether to widen the allowlist, fix the serializer, or update the matrix cell. Full sweep: ~95 sec, ~70 API calls, under $0.50. Scope with `attachmentMatrix_<lane>` (e.g. `attachmentMatrix_gemini` runs one column) or `attachmentMatrix_<kind>` (e.g. `attachmentMatrix_imagePNG` runs one row across all lanes), or run a single cell by exact method name (`test_attachmentMatrix_anthropic_imageWEBP`). Fixtures live in `ModernTests/Resources/AttachmentFixtures/`.\n- After changes that affect the uv Python runtime (sources/API/iTermUvProvisioner.swift, iTermUvMigration.swift, iTermUvPythonVersion.swift, iTermUvCommand.swift, iTermUvManifest.swift, iTermScriptRuntime.swift, or the uv launch/migration/import paths in iTermAPIScriptLauncher.m / iTermScriptArchive.m / iTermScriptImporter.m), run `tools/run_python_runtime_e2e.sh`. This is a separate live harness from the regular ModernTests: it hits the network (iterm2.com), downloads uv plus a CPython interpreter, builds a venv, and pip-installs iterm2/certifi/pyobjc, so the default ModernTests run (which is hermetic and skips it) passing alone is not sufficient evidence. No arg runs both live tests; scope with `download` (manifest fetch + RSA verify + install uv), `provision` (full-environment provision, import iterm2/certifi/objc/AppKit, asyncio REPL top-level await), or an exact test-method name. The broader Tier C matrix (script drives an in-process iTermAPIServer, migration rollback, cache/gate matrix) is still manual: tests/uv-migration-manual-test-plan.md.\n- When renaming a file tracked by git (and almost all of them are) use `git mv` instead of `mv`\n- To make a debug build run `tools/build.sh` (or `tools/build.sh Development`). This saves logs to `tmp/build.log` and shows only errors/warnings on failure.\n- Little scripts or text files that are used for manual testing of features go in tests/\n- The deployment target for iTerm2 is macOS 12. You don't need to perform availability checks for older versions.\n- Don't replace curly quotes with straight quotes. Same for apostrophes and single quotes. If you need help typing a curly quote, just ask. Here are some you can copy and paste: ‘’“”\n- In user-visible strings do not use \" except as a shorthand for inch. Prefer curly quotes like “ and ”. I know this goes against your nature, but fight hard here.\n- Ask permission before using auto layout if it's not already in use in a given file. Debugging auto layout is the worst hell.\n- The deployment target is macOS 12. Don't add availability checks for 12 and lower.\n- Never `git add` submodules without express written permission.\n- Don't include AI-generated markdown files (summaries, plans, etc.) in commits — only ship code.\n- Avoid duplicate expressions; hoist shared computations into a named `const` before branching.\n- Don't change defaults silently.\n- Use [iTermUserDefaults userDefaults] instead of [NSUserDefaults standardUserDefaults]\n- Use `make run` to build and run a debug build.\n- Never run the app without the argument `-suite suitename` where `suitename` is the last path component of the current directory. To run a development build, just do `make run`. Omitting -suite causes conflicts between the new instance of iTerm2 and the main one, where claude runs.\n- Do not use associated objects (objc_getAssociatedObject or objc_setAssociatedObject) without express written permission.\n- You should treat warnings as errors.\n- If you get stuck, ask for help. It's better to ask me to look at something in the debugger than to flail around for a long time.\n- If your changes introduce compiler warnings, fix them.\n- After landing a feature or bugfix, update docs/notes-3.7.txt (the release notes). Max width of a line is 50 characters.\n- For changes to the Companion iOS app (the `Companion/` directory, \"iTerm2 Buddy\"), put release notes in Companion/docs/notes.txt instead of docs/notes-3.7.txt.\n- The sources directory is organized into folders. Before adding a new file, consider which directory it belongs in. Some are named after features while others are named after their role.\n- User Defaults keys that should only be stored locally begin with the prefix NoSync. If a user chooses to load prefs from a custom location (e.g., Dropbox) they may be prompted to write settings when a non-NoSync key changes. To avoid disrupting them in this manner, user defaults that are not actual configuration settings (e.g., a list of recent items) get a NoSync prefix.\n- Use DLog statements so we can debug problems in the field. These statements have no effect when debug logging is off (the default) and it's OK for them to do somewhat expensive operations like getting a stack trace.\n- Use RLog statements to log debug messages to memory even when debug logging is not on. Creating a debug log later will pull in the last 10 megabytes of RLog statements. RLog runs always so don't do anything expensive (such as stack traces) and do not use them in hot paths that could burn a lot of CPU logging.\n- When adding temporary code for debugging, use NSFuckingLog instead of NSLog because NSLog truncates long output. Logging code that is intended to remain long-term should use DLog.\n- Do not use an SF Symbols name as a string literal. Get it using SFSymbolGetString in Objective C or the SFSymbol enum in Swift.\n- Don't use sleep to solve concurrency problems.\n- Tests should not be flaky. Don't write tests that will fail if the system is slower than usual.\n- Write new classes in Swift unless there is a very good reason not to.\n- Do not modify CLAUDE.md without express written permission\n","category":"root","tokens":2175}]}