Skip to content

Add Xcode-style project visualizer pane - #4996

Merged
azooz2003-bit merged 2 commits into
mainfrom
feat-xcode-project-pane
May 29, 2026
Merged

azooz2003-bit merged 2 commits into
mainfrom
feat-xcode-project-pane

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented May 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a new `project` pane type that opens a `.xcodeproj` or `.xcworkspace` and renders it the way Xcode does: workspace + project navigator, targets table, Build Settings Levels view, and schemes. The pane hides low-value Xcode plumbing (raw pbxproj UUIDs, xcuserdata, container blueprint strings) while keeping the model honest about what's resolved vs derived.

  • New leaf SPM package CMUXProjectModel with an XcodeProjectAdapter (tuist/XcodeProj-backed) and an XcconfigParser (handles #include/#include?, cycle detection, line comments). 15 Swift Testing tests, green in 58ms.
  • New ProjectPanel runtime + four SwiftUI tab views (Files, Targets, Build Settings, Schemes) wired through the existing Panel plumbing, session persistence, and Workspace surface lifecycle.
  • New cmux project open <path-to-.xcodeproj-or-.xcworkspace> CLI subcommand, backed by project.open JSON-RPC method. Debug RPCs (project.set_tab, project.set_selected_target, project.set_selected_file, project.set_settings_filter, project.get_state) added for headless verification.
  • Opening an .xcodeproj or .xcworkspace path through any existing cmux file-open path (right sidebar Files mode, drag-drop, file.open RPC, cmux open <path> CLI) now routes to a project surface instead of falling through to FilePreview.

What's in the pane

Files — workspace + project group tree with target-membership chips per file. Filter bar at the top with magnifier + clear + match count. Filter mode auto-expands matching groups. Detail strip slides in when a file is selected; single-pane otherwise.

Targets — table of targets with product type, platforms, deployment target, bundle id (resolved via xcconfig chain), dependency count. Detail panel shows product-type icon header, metadata grid, dependencies list, and a build summary with "Open in Build Settings" jump.

Build Settings — Levels-style table with Setting / Effective / Target / Project columns. In-header Target picker, search filter, "Customized only" toggle, settings count. Target overrides marked with a leading accent bar + accent value cell. Inherited rows dimmed.

Schemes — shared + per-user schemes flattened across modules with dedup, single shared/personal badge in row + detail header. Detail shows Run / Test / Profile / Archive target attribution, launch args, environment.

Iteration history

Built end-to-end in this branch over 11+ iterations driven by three reviewer agents per pass (design/UX, product/PM, technical/Swift). Each pass screenshotted all four tabs, surfaced top fixes, applied the highest-impact ones, rebuilt, verified. Full log under `.iter-logs/CHANGELOG.md` in the worktree (committed for repro).

Highlights from the loop:

  • iter1: killed a force-cast crash in Targets metadata helper, renamed Build Settings "Resolved" to "Effective" with disclaimer, shared empty-state view.
  • iter2-3: segmented tab strip, file filter, single-pane Files when nothing selected, dropped duplicate Configurations subsection, default tree depth-1 collapse, fixed Build Settings multi-module owning-module bug, dropped hardcoded "Debug" fallback.
  • iter4-5: hot-path let rows = ... hoist in body; chrome scheme/config pickers flatMap across modules with dedup; applyLoaded validates persisted selections; scheme blueprintIdentifier no longer fabricates fake TargetIDs.
  • iter6-7: scheme dedup via compositeID; new XcconfigParser; bundle id / deployment target / platforms now actually populate on cmux (was blank, came from `.xcconfig`).
  • iter8-9: chrome load-warning pill, inline shared/personal badge in scheme header, target detail header with product-type icon, full test suite for parser + adapter.
  • iter10-11: outer VStack now claims full pane frame so Build Settings table top-aligns even when filter narrows to a few rows; configuration picker widened; .xcodeproj/.xcworkspace routing through openFileSurfaces.

Open backlog (intentional deferrals, called out in CHANGELOG.md)

  • `xcodebuild -showBuildSettings -json` integration for a true Resolved column.
  • Per-module load-warning aggregation across workspace projects (currently uses `try?` in `loadWorkspace`, silently dropping failed modules).
  • Edit affordances (target membership toggle, settings edit, scheme env edit — data model + RPCs ready, no write-back yet).
  • File watcher / reactive sync via `DispatchSource.makeFileSystemObjectSource` (currently manual reload).
  • Fixture-driven adapter tests (tests currently exercise the live cmux project).

Test plan

  • Open the pane via `cmux project open ` from any cmux terminal; verify Files / Targets / Build Settings / Schemes tabs all render against the project.
  • Filter Build Settings to one or two settings; row should stick at the top of the table area, not float in the middle of the pane.
  • Change scheme/configuration in the chrome pickers; Build Settings table refreshes accordingly.
  • On a multi-module `.xcworkspace`, switch between modules' targets in the Build Settings target picker; settings rows reflect the owning module.
  • Drag-drop or right-sidebar click an `.xcodeproj` / `.xcworkspace` file; lands as a project surface, not a file preview.
  • `swift test` in `Packages/CMUXProjectModel` is green (15/15).

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds an Xcode-style project pane that opens .xcodeproj and .xcworkspace files and shows Files, Targets, Build Settings, and Schemes. Routes these files to the new pane across the app and adds CLI/JSON‑RPC controls.

  • New Features

    • Added CMUXProjectModel with XcodeProjectAdapter and XcconfigParser (15 tests).
    • Introduced ProjectPanel with tabs (Files, Targets, Build Settings, Schemes), focus/search, and session persistence (SessionProjectPanelSnapshot).
    • Added CLI cmux project open <path> and JSON‑RPC: project.open, project.set_tab, project.set_scheme, project.set_configuration, project.set_selected_target, project.set_selected_file, project.set_settings_filter, project.get_state.
    • Opening .xcodeproj/.xcworkspace via sidebar, drag‑drop, file.open RPC, or cmux open <path> now loads the project pane.
  • Bug Fixes

    • Fixed concurrent self-capture warnings in ProjectPanel.reload by switching to applyLoaded/applyLoadError on the main actor.

Written for commit 84bbf3a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Project panel for opening/viewing Xcode projects/workspaces (Files, Targets, Build Settings, Schemes).
    • New cmux project open CLI command (JSON or human-readable output).
  • Tests

    • Added test suites for project adapter and xcconfig parsing.
  • Documentation

    • Added project iteration CHANGELOG documenting project pane work.
  • Chores

    • Updated package manifests/lockfiles and added .gitignore rule to ignore generated iter-logs PNGs.

Review Change Stack

@vercel

vercel Bot commented May 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 29, 2026 7:08pm
cmux-staging Building Building Preview, Comment May 29, 2026 7:08pm

@coderabbitai

coderabbitai Bot commented May 29, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces a complete Project Panel feature for cmux, enabling users to browse, inspect, and interact with Xcode projects. The implementation spans a new local Swift package (CMUXProjectModel) with data models and adapters, multiple SwiftUI view layers for project navigation and settings editing, RPC command infrastructure, workspace integration, and session persistence. The feature is wired into the existing panel system and CLI.

Changes

Project Panel Feature

Layer / File(s) Summary
Project model identifier types
Packages/CMUXProjectModel/Sources/CMUXProjectModel/IdentifierTypes.swift
Six stable identifier value types (ProjectModelID, ProjectModuleID, ProjectNodeID, TargetID, BuildConfigID, SchemeID) conforming to Sendable, Hashable, and RawRepresentable for content-derived, reload-stable IDs.
Project data model structures
Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectModel.swift, ProjectModule.swift, ProjectGroup.swift, ProjectFileNode.swift, ProjectNodeKind.swift, TargetSummary.swift, BuildConfigSummary.swift, SchemeSummary.swift, TargetMembership.swift
Immutable, snapshot-based data structures (ProjectModel, ProjectModule, ProjectGroup, ProjectFileNode, ProjectNodeKind, TargetSummary, BuildConfigSummary, SchemeSummary, TargetMembership) representing parsed project content with Sendable, Hashable, Identifiable, and SwiftUI integration.
Project enums and classifications
Packages/CMUXProjectModel/Sources/CMUXProjectModel/BuildConfigScope.swift, ProjectAdapterKind.swift, ProjectGroupStyle.swift, TargetProductType.swift, TargetMembershipRole.swift, ProjectLoadError.swift
Enums for project element classification (ProjectAdapterKind, ProjectGroupStyle, TargetProductType, TargetMembershipRole, BuildConfigScope) and error types (ProjectLoadError) used throughout the model.
ProjectAdapter protocol
Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectAdapter.swift
Protocol specifying the interface for loading ProjectModel from URLs (kind property, canLoad check, throwing load method) enabling per-ecosystem adapters.
Xcconfig file parser
Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcconfigParser.swift
Parser for .xcconfig files supporting recursive includes with cycle detection, comment stripping, and deterministic merge order; includes parseChain for combining multiple configs.
Xcode project adapter and loader
Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
Full implementation of ProjectAdapter loading ProjectModel from .xcworkspace and .xcodeproj files; extracts project structure, targets, schemes, build configurations, file memberships, and navigator tree using XcodeProj library.
XcodeProjectAdapter and XcconfigParser tests
Packages/CMUXProjectModel/Tests/CMUXProjectModelTests/XcconfigParserTests.swift, XcodeProjectAdapterTests.swift
Comprehensive test suites validating .xcconfig parsing (assignments, comments, includes, chains, cycles) and XcodeProjectAdapter behavior (loading, canLoad, bundle ID resolution, scheme target mapping, configurations).
CMUXProjectDump verification tool
Packages/CMUXProjectModel/Sources/CMUXProjectDump/main.swift
CLI executable for manual verification of project loading, printing project metadata, module/target details, and file tree with membership information.
ProjectPanel state management and loading
Sources/Panels/ProjectPanel.swift
Observable panel class managing project URL, load state (idle/loading/loaded/failed), active tab, selection state (scheme/config/target/file), search filters, and collapsed nodes; includes async reload with state reconciliation and default expansion seeding.
ProjectPanelView main UI and chrome
Sources/Panels/ProjectPanelView.swift
Top-level SwiftUI view rendering project panel chrome (title with URL help, scheme/config pickers, reload button, error banner), tab strip, and state-based content dispatch; includes reusable helper views for status messaging and empty states.
Files tab implementation
Sources/Panels/ProjectFilesTabView.swift
SwiftUI view with flattened tree navigation (expand/collapse), file search filtering with special group handling, file selection with detail strip showing path/type/on-disk status and target memberships with compiler flags.
Targets tab implementation
Sources/Panels/ProjectTargetsTabView.swift
Module/target list grouped by modules with product-type glyphs and metadata (platforms, bundle ID, deployment target, dependency count); detail pane showing metadata grid, dependencies (with label resolution), and configuration override counts with navigation to Build Settings.
Build Settings tab implementation
Sources/Panels/ProjectBuildSettingsTabView.swift
Settings table with target picker, search filter, "Customized only" toggle, and row entries showing effective/target/project values; visual winner highlighting on winning column and monospaced display with tooltips.
Schemes tab implementation
Sources/Panels/ProjectSchemesTabView.swift
Flattened scheme list across modules with selection and detail pane showing run/test/profile/archive targets (with label resolution), launch arguments, and sorted environment variables.
Panel type and focus intent definitions
Sources/Panels/Panel.swift
Extend PanelType enum with .project case; introduce ProjectPanelFocusIntent with navigator/detail cases; extend PanelFocusIntent with .project(...) case for focus routing.
PanelContentView project rendering
Sources/Panels/PanelContentView.swift
Update PanelContentView to render ProjectPanel when panel type is .project, casting panel and passing focus callback; add .project to pane drop-target eligibility.
RPC command handlers for project
Sources/TerminalController.swift
Implement v2ProjectOpen to create ProjectPanel surfaces with path validation and window/workspace targeting; add setters for tab/scheme/configuration/target/file/filter; add v2ProjectGetState for inspection snapshots.
Workspace panel snapshotting and restoration
Sources/Workspace.swift
Update Workspace to snapshot ProjectPanel state in sessionPanelSnapshot, restore ProjectPanel during session restoration, apply custom layout configurations for project surfaces, and implement newProjectSurface method for creating project panels with optional focus.
Session persistence for project panels
Sources/SessionPersistence.swift
Add SessionProjectPanelSnapshot (Codable, Sendable) capturing project path and selected UI state; extend SessionPanelSnapshot with optional project field.
File opening support for project files
Sources/Panels/FilePreviewWorkspaceOpenSupport.swift
Update file opening logic to detect .xcodeproj and .xcworkspace file extensions and route them to newProjectSurface creation with focus and tab ordering.
Config, lifecycle, and panel utilities
Sources/CmuxConfig.swift, Sources/CmuxLifecycleEventPublishing.swift, Sources/ClosedItemHistory.swift, Sources/ContentView.swift, Sources/Panels/FilePreviewPanel.swift, Sources/TerminalPaneDropTargetView.swift, Sources/Search/GlobalSearchDocuments.swift
Minor updates across multiple files: add .project surface type, map to "project" surface kind, label project panels in recently-closed history, provide search keywords and debug focus labels, exclude .project from focus restoration and file-drop routing, classify project hits in global search.
Build system and project file updates
Packages/CMUXProjectModel/Package.swift, Packages/CMUXProjectModel/Package.resolved, cmux.xcodeproj/project.pbxproj, cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
Wire CMUXProjectModel local package into cmux app target (frameworks, products, dependencies); add six ProjectPanel UI source files to build phases; define SwiftPM package manifest and lockfile with external dependencies (XcodeProj, aexml, pathkit, spectre).
CLI project command support
CLI/cmux.swift
Add project command dispatcher parsing subcommand/path/options and calling v2 project.open RPC; format output as JSON (with IDs) or plain text (surface/pane handles and path).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#4160: Related changes to recently-closed history handling that this PR extends for .project panel titles.
  • manaflow-ai/cmux#4550: Modifies SessionPanelSnapshot structures; related to session persistence additions here.
  • manaflow-ai/cmux#4982: Touches ClosedItemHistory; potentially overlaps with .project title handling.

Poem

🐰 A project pane now springs to life,
Files and schemes hop into sight,
Xcconfigs parse without much strife,
Targets gleam in monospaced light,
Build settings hum through day and night.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-xcode-project-pane

New `project` pane type that opens a `.xcodeproj` or `.xcworkspace` and
renders it the way Xcode does: workspace + project navigator, targets
table, Build Settings Levels view, and schemes. Hides low-value pbxproj
plumbing (raw UUIDs, xcuserdata, container blueprint strings) while
keeping the model honest about resolved vs derived values.

New `CMUXProjectModel` leaf SPM package with:
- `XcodeProjectAdapter` (tuist/XcodeProj-backed) that walks groups,
  targets, schemes, build configurations, and target memberships.
- `XcconfigParser` (handles `#include` / `#include?`, cycle detection,
  trailing line comments) merged into target metadata so
  PRODUCT_BUNDLE_IDENTIFIER, *_DEPLOYMENT_TARGET, SUPPORTED_PLATFORMS
  populate even when the values live in `.xcconfig`.
- 15 Swift Testing tests, green in ~60 ms total.

Surfaces through the existing Panel plumbing:
- `ProjectPanel` runtime + four SwiftUI tab views (Files, Targets,
  Build Settings, Schemes) routed through `PanelContentView`.
- Session persistence as `SessionProjectPanelSnapshot`.
- `cmux project open <path-to-.xcodeproj-or-.xcworkspace>` CLI, backed
  by `project.open` JSON-RPC method on `TerminalController`.
- Debug RPCs (`project.set_tab`, `project.set_selected_target`,
  `project.set_selected_file`, `project.set_settings_filter`,
  `project.get_state`) for headless verification.
- `openFileSurfaces` now routes `.xcodeproj` / `.xcworkspace` paths to
  a project surface (right sidebar Files mode, drag-drop, `file.open`
  RPC, `cmux open <path>` CLI all benefit).

UI polish notes:
- Tab strip rendered as a segmented control (active tab filled accent).
- Files tab: filter bar at top, single-pane when nothing selected,
  target-membership chips per file, default depth-1 expansion.
- Targets tab: product-type icon header, dependencies list, build
  summary with jump to Build Settings.
- Build Settings tab: Levels-style table with Setting / Effective /
  Target / Project columns, leading accent bar on overridden rows,
  in-header Target picker, search filter, "Customized only" toggle.
- Schemes tab: shared + per-user schemes flattened across modules with
  dedup, inline shared/personal badge.
- Outer pane VStack anchored to .topLeading so the Build Settings
  table stays at the top of the pane even when filter narrows rows.

Iteration log preserved in `.iter-logs/CHANGELOG.md` (11 passes with
three reviewer agents per pass: design, product, technical).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@azooz2003-bit
azooz2003-bit force-pushed the feat-xcode-project-pane branch from 091acc8 to 6b41ec8 Compare May 29, 2026 18:23
@greptile-apps

greptile-apps Bot commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a full Xcode-style project visualizer pane backed by a new CMUXProjectModel SPM package, four SwiftUI tab views (Files, Targets, Build Settings, Schemes), ProjectPanel runtime, session persistence, JSON-RPC methods, and CLI routing. The overall architecture follows existing cmux panel conventions well.

  • Session restore is incomplete: SessionProjectPanelSnapshot serializes activeTab, selectedSchemeName, selectedConfigurationName, and selectedNodePath, but the restore path in Workspace.swift only uses projectPath — the other four fields are silently discarded on every relaunch.
  • XcconfigParser misparses += assignments: firstIndex(of: "=") hits the = inside +=, producing a garbage key like "SETTING +" instead of "SETTING", affecting any xcconfig using the append operator.
  • Minor: The xcconfig include resolver reads each file twice — once via parseRecursive(try?) and again via Data(contentsOf:) to check existence — doubling I/O on the targets-loading path.

Confidence Score: 3/5

The session restore gap means users lose their tab, scheme, configuration, and file selection every relaunch with a project pane open.

Session persistence is one of the explicitly listed features of this PR, yet the restore branch silently drops four of the five persisted fields. The xcconfig += parser bug produces wrong keys for a common xcconfig pattern. Both are present-defect issues on the changed path that affect user-visible behavior today.

Sources/Workspace.swift (session restore branch) and Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcconfigParser.swift (assignment parsing)

Important Files Changed

Filename Overview
Sources/Workspace.swift newProjectSurface wiring looks correct, but session restore only uses projectPath — the four other persisted fields (activeTab, selectedSchemeName, selectedConfigurationName, selectedNodePath) are captured but never applied to the reconstructed panel.
Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcconfigParser.swift Two parser issues: += lines produce garbage keys; include files are I/O-read twice per include. Cycle detection and optional include handling are correct.
Sources/Panels/ProjectPanel.swift Core runtime; uses ObservableObject/@published (flagged in prior review). reload() correctly dispatches to Task.detached. applyLoaded validates and resets selections correctly.
Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift 738-line adapter with clean static helper decomposition. Filesystem walks for synchronized groups run on the background task (correct).
Sources/TerminalController.swift v2ProjectSetSelectedTarget only searches modules.first (flagged in prior review). Other RPCs use v2MainSync correctly.
Sources/SessionPersistence.swift SessionProjectPanelSnapshot is correctly Codable/Sendable. Fields serialized but only projectPath used during restore (see Workspace.swift).
Sources/Panels/ProjectFilesTabView.swift Uses LazyVStack for the navigator (good). flattenedRows computed inline in body on every @published change (flagged in prior review).

Sequence Diagram

sequenceDiagram
    participant CLI as cmux CLI
    participant TC as TerminalController
    participant WS as Workspace (MainActor)
    participant PP as ProjectPanel (MainActor)
    participant ADP as XcodeProjectAdapter (detached)

    CLI->>TC: "project.open {path}"
    TC->>WS: newProjectSurface(inPane:projectPath:focus:)
    WS->>PP: init(projectURL:)
    WS->>PP: reload()
    PP->>ADP: Task.detached — adapter.load(at:)
    ADP-->>PP: await applyLoaded(model)
    PP->>PP: validate/reset selections

    note over WS,PP: Session restore path
    WS->>PP: newProjectSurface(focus:false)
    WS--xPP: activeTab/selectedScheme/selectedConfig/selectedFile NOT applied
    PP->>ADP: Task.detached — adapter.load(at:)
    ADP-->>PP: await applyLoaded(model) sets defaults not saved state
Loading

Reviews (2): Last reviewed commit: "Fix concurrent self capture warnings in ..." | Re-trigger Greptile

Comment on lines +57 to +58
@MainActor
public final class ProjectPanel: NSObject, Panel, ObservableObject {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 ObservableObject/@Published instead of @Observable

ProjectPanel uses ObservableObject + @Published while all four tab views use @ObservedObject var panel: ProjectPanel. This causes whole-panel invalidation: any @Published write — settingsSearchText, selectedSchemeName, selectedTargetID, loadState, etc. — invalidates every view that holds an @ObservedObject reference, regardless of whether the changed property is used in that view's rendered subtree. With @Observable, SwiftUI tracks only the properties actually read during a view's body, so a keystroke in the Settings filter won't recompute flattenedRows in ProjectFilesTabView or the target list in ProjectTargetsTabView. The rule for new cmux-owned SwiftUI state is @Observable + @State or value snapshots.

Rule Used: Flag SwiftUI changes that can cause stale state, b... (source)

Comment on lines +22 to +24
var body: some View {
let rows = flattenedRows
VStack(alignment: .leading, spacing: 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Full tree walk inside body on every invalidation

let rows = flattenedRows is computed inline inside body. flattenedRows recursively walks the entire project file tree — which can span thousands of nodes in a real workspace. Because panel is @ObservedObject, any @Published change on ProjectPanel triggers a full re-render and a fresh tree walk. The same pattern appears in ProjectBuildSettingsTabView (let computedRows = rows). The fix is to switch ProjectPanel to @Observable and cache the row computation with a @State snapshot updated only when filesSearchText or collapsedNodeIDs change.

Rule Used: Flag production code that adds nested full-collect... (source)

Comment on lines +46 to +47
TextField("Filter files (e.g. AppDelegate)", text: $panel.filesSearchText)
.textFieldStyle(.plain)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Hardcoded English strings throughout new production SwiftUI views

None of the new user-facing text is routed through String(localized:defaultValue:). Examples across all five new view files: "Filter files (e.g. AppDelegate)" and "Select a file" here; column headers and "Customized only" in ProjectBuildSettingsTabView; "shared" / "personal" badges in ProjectSchemesTabView; "Reload project", "Loading …", "Failed:" in ProjectPanelView; and error strings in ProjectPanel.describe. Every string visible in the UI must use a stable localization key, an English defaultValue, and a matching entry in Resources/Localizable.xcstrings for every locale already in the catalog.

Rule Used: Flag production user-facing text that is not fully... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@@ -12248,6 +12266,182 @@ class TerminalController {
return result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 v2ProjectSetSelectedTarget silently misses targets in non-first modules

panel.loadState.model?.modules.first is used as the only search scope. For a .xcworkspace with multiple projects, any target belonging to the second or later module will never be found; the RPC returns {"target_name": "…", "target_id": ""} and panel.selectedTargetID is set to nil. Other RPCs like v2ProjectGetState already aggregate across all modules, so callers can legitimately pass names from non-first modules. The loop should search across model.modules in order, the same way applyLoaded and ProjectBuildSettingsTabView.selectedModule do.

Comment on lines +218 to +223
return "Unsupported project at \(url.path)"
case let .parseFailure(url, reason):
return "Parse failed at \(url.path): \(reason)"
}
}
return String(describing: error)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 parseFailure reason exposes raw XcodeProj library error strings in the UI

ProjectLoadError.parseFailure(url, reason: String(describing: error)) is constructed from the raw error thrown by XcodeProj(path:), which can include XcodeProj-internal details. It is surfaced verbatim in the load-warning pill and the failed-state label. The UI message should stay in cmux terms (e.g. "Could not parse the project file") and the upstream detail should be kept in sanitized logs or internal telemetry only.

coderabbitai[bot]
coderabbitai Bot previously requested changes May 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 30

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 4734-4745: The single-token branch currently treats any one-word
token as a path and should instead validate it using the looksLikePath(_:)
helper: capture the original-cased first token (e.g. rawFirst = args[0]) before
any lowercasing, then when args.count == 1 call looksLikePath(rawFirst) and only
accept it as subArgs if it returns true; otherwise throw the existing CLIError
usage message for unknown project subcommands (keep the existing throw path for
multi-token cases), and ensure subArgs is set from the validated rawFirst so the
later guard uses the correct rawPath.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/BuildConfigSummary.swift`:
- Around line 12-30: Add Swift-DocC triple-slash comments for each public stored
property (id, name, scope, baseConfigurationPath, rawSettings) and the public
initializer in BuildConfigSummary so every public symbol is documented; for the
initializer (init(id:name:scope:baseConfigurationPath:rawSettings:)) include a
brief overview plus /// - Parameters: entries describing each parameter and
their meanings, and for optional baseConfigurationPath note it can be nil,
keeping all comments concise and formatted with /// so they appear in generated
docs.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectFileNode.swift`:
- Around line 11-32: Add Swift-DocC triple-slash comments for every public
symbol in ProjectFileNode: document each stored property (id, displayName,
resolvedPath, fileType, existsOnDisk, memberships) with a brief description of
its purpose and any important behavior, and document the public initializer
(init(id:displayName:resolvedPath:fileType:existsOnDisk:memberships:)) including
parameter descriptions; ensure each comment uses /// and follows the project's
DocC style so all public symbols in ProjectFileNode are documented.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectGroup.swift`:
- Around line 12-30: Add Swift-DocC triple-slash documentation for all public
stored properties and the initializer in ProjectGroup: document the purpose of
each property (id: ProjectNodeID, displayName: String, resolvedPath: URL?,
style: ProjectGroupStyle, children: [ProjectNodeKind]) with a short description
and for the initializer add a summary plus /// - Parameters: entries describing
each parameter (id, displayName, resolvedPath, style, children). Place the ///
comments immediately above the property declarations and above the public init
to satisfy the package documentation guideline.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectLoadError.swift`:
- Around line 14-19: ProjectLoadError.parseFailure currently carries raw
third-party error text which gets shown in the UI; update the surface so
user-visible text is redacted while preserving raw details for logs.
Specifically, either change XcodeProjectAdapter so it no longer constructs
parseFailure(..., reason: String(describing: error)) for UI consumption but
instead passes a generic message like "Project parsing failed" and sends
String(describing: error) to an internal logger, or modify
ProjectPanel.describe(_:) to map ProjectLoadError.parseFailure(_, _) to a safe,
generic message (e.g. "Parse failed: internal parsing error") and write the
original reason to debug logs; ensure references: ProjectLoadError.parseFailure,
XcodeProjectAdapter, ProjectPanel.describe(_:), and ProjectPanelView are updated
accordingly.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectModule.swift`:
- Around line 9-41: Add Swift-DocC triple-slash comments for every public
symbol: document each public stored property (id, displayName, rootURL,
rootGroup, targets, configurations, schemes) with a short description; add a
triple-slash doc for the public initializer ProjectModule.init(...) describing
purpose and each parameter using - Parameter name: for id, displayName, rootURL,
rootGroup, targets, configurations, schemes; and update the target(for:) doc to
include - Parameter id: description and - Returns: description explaining it
returns the matching TargetSummary or nil. Ensure comments use /// and include
concise sentences for discoverability.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectNodeKind.swift`:
- Around line 13-29: Add Swift-DocC triple-slash documentation for the public
computed properties on ProjectNodeKind: document the `id` property to state it
returns the underlying ProjectNodeID for the enum case (delegating to group.id
or file.id) and document the `displayName` property to state it returns the
user-facing name for the node (delegating to group.displayName or
file.displayName); place concise /// comments immediately above the `public var
id: ProjectNodeID` and `public var displayName: String` declarations, mention
they are stable/public and which enum cases they map to (group, file) and any
guarantees (e.g., non-empty or localized if applicable).

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/SchemeSummary.swift`:
- Around line 10-40: Add Swift-DocC triple-slash documentation for every public
symbol in this type: document each public stored property (id, name, isShared,
runTargetIDs, testTargetIDs, profileTargetID, archiveTargetID, launchArguments,
environmentVariables) with a brief one-line description of what it represents,
and add a /// doc comment for the public initializer (init(...)) describing the
initializer and each parameter (id, name, isShared, runTargetIDs, testTargetIDs,
profileTargetID, archiveTargetID, launchArguments, environmentVariables). Ensure
the comments use Swift-DocC style (///) immediately above each property and the
init, are concise, and mention optional semantics for optional properties
(profileTargetID, archiveTargetID) where applicable.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetMembership.swift`:
- Around line 11-23: Add Swift-DocC triple-slash comments for each public symbol
in TargetMembership.swift: document the public stored properties targetID, role,
and compilerFlags with one-line descriptions (include that compilerFlags has a
default empty array), and document the public initializer
init(targetID:role:compilerFlags:) describing the parameters and behavior; place
the /// comments immediately above each property and the initializer and use
parameter tags or brief descriptions for each parameter to satisfy the package
documentation guideline.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetSummary.swift`:
- Around line 10-34: Add Swift-DocC triple-slash comments to each public stored
property (id, displayName, productType, platforms, bundleIdentifier,
deploymentTarget, dependencies) and to the public initializer in TargetSummary
to satisfy the package documentation guideline; for each property write a
concise one-line description of its purpose/type (e.g., "/// The unique
identifier for the target.") and add a short doc-line for the init describing
parameters (using /// - Parameters: and list each parameter name and purpose) so
every public symbol in TargetSummary is documented.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcconfigParser.swift`:
- Around line 21-37: Add DocC callouts to the public APIs: update the doc
comments for parse(at:) and parseChain(_:) to use /// style and include the
required "- Parameter", "- Returns", and "- Throws" callouts where appropriate —
for parse(at: URL) document the "url" parameter, the returned [String: String]
map under "- Returns:", and the error behavior under "- Throws:"; for
parseChain(_ urls: [URL]) document the "urls" parameter and the returned merged
[String: String] under "- Returns:". Ensure the comment blocks precede the
public static func declarations for parse(at:) and parseChain(_:) and follow the
project's DocC formatting guidelines.
- Around line 39-62: The current cycle guard in parseRecursive(at:visited:)
inserts canonical into visited and never removes it, which prevents legitimate
repeated includes via different paths; make visited path-scoped by removing the
canonical URL when leaving the function (use defer { visited.remove(canonical) }
immediately after inserting canonical) so ancestors remain marked during
recursion but siblings/other branches can re-include the file; keep the existing
contains check and behavior otherwise.

In
`@Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift`:
- Around line 31-47: The public API surface is undocumented: add Swift-DocC
comments for the public symbols `kind`, `init()`, `canLoad(_:)`, and `load(at:)`
in XcodeProjectAdapter so they include a short summary and the appropriate tags
(`- Parameter:` for parameters, `- Returns:` for return values, and `- Throws:`
for throwable methods); update the doc comments for `canLoad(_:)` to describe
the `url` parameter and boolean result, and for `load(at:)` to describe the
`url`, the returned `ProjectModel`, and the `ProjectLoadError` thrown, ensuring
each public symbol has a /// comment immediately above its declaration.
- Around line 23-25: Update the stale documentation to reflect current behavior:
change the comment that says build settings, schemes, and `.xcconfig` resolution
are "deliberately out of scope" to indicate that loadModule now collects these
via collectBuildConfigurations, collectSchemes, and mergedXcconfigSettings and
that ProjectModel will be populated with those artifacts; refer to the
loadModule, collectBuildConfigurations, collectSchemes, and
mergedXcconfigSettings symbols when editing the doc comment so it accurately
describes the adapter's responsibilities.
- Around line 202-204: The current construction of targetNameToID using
Dictionary(uniqueKeysWithValues:) will crash if two PBXTarget instances in
targets share the same name; change it to build the dictionary with
Dictionary(uniqueKeysWithValues: , uniquingKeysWith:) (or use
Dictionary(grouping:) and then mapValues) so duplicate names are resolved
deterministically (e.g., keep the first or last) instead of trapping; update the
code that uses targetNameToID accordingly and ensure the mapping still yields
TargetID(rawValue: $0.uuid) for each entry.
- Around line 180-193: The normalizeRawSettings helper and the resolveSetting
call must handle XcodeProj's BuildSetting enum instead of only String/[String];
update normalizeRawSettings(_ source: [String: Any]) to detect and unwrap
BuildSetting cases (e.g. .string and .array) as well as existing String/[String]
and fallback to String(describing:) only for unknowns, and update any code that
passes config.buildSettings into resolveSetting to accept BuildSetting values
(or add a conversion path) so values like PRODUCT_BUNDLE_IDENTIFIER, deployment
targets, SUPPORTED_PLATFORMS and SDKROOT are extracted from BuildSetting.string
/ BuildSetting.array rather than serialized via String(describing:
BuildSetting); target symbols: normalizeRawSettings, resolveSetting,
rawSettings, and config.buildSettings/BuildSetting.

In
`@Packages/CMUXProjectModel/Tests/CMUXProjectModelTests/XcconfigParserTests.swift`:
- Around line 8-84: Each test that creates temp files/dirs
(parsesSimpleAssignments, stripsTrailingLineComments, followsRelativeIncludes,
optionalIncludeIsIgnoredWhenMissing, parseChainMergesInDeclaredOrder,
cycleDetectionAvoidsInfiniteRecursion) must register a defer cleanup immediately
after creating the resource (e.g., after let url = try writeTempXcconfig(...) or
after creating dir/baseURL/derivedURL/a/b) so removal
(FileManager.default.removeItem(at: ...)) always runs even on throws/failures;
update each test to remove the try? cleanup call at the end and instead call
try? FileManager.default.removeItem(...) inside a defer placed right after the
resource is created.

In
`@Packages/CMUXProjectModel/Tests/CMUXProjectModelTests/XcodeProjectAdapterTests.swift`:
- Around line 10-27: The init() in XcodeProjectAdapterTests incorrectly appends
sibling bundle names inside a provided bundle path; when CMUX_PROJECT_FIXTURE
points to an .xcworkspace or .xcodeproj you must compute the sibling by using
the bundle's parent directory. Update the override branch to: read override into
base (URL), test base.pathExtension, and when one bundle is provided build the
other by calling
base.deletingLastPathComponent().appendingPathComponent("cmux.xcodeproj") or
.appendingPathComponent("cmux.xcworkspace") as needed so workspaceURL and
projectURL are siblings rather than children.

In `@Sources/ContentView.swift`:
- Around line 5825-5826: The code references the localized key
"commandPalette.kind.project" via String(localized:
"commandPalette.kind.project", defaultValue: "Project") (in
Sources/ContentView.swift, case .project) but the key is missing from the string
catalogs; add "commandPalette.kind.project" with the base English value
"Project" to every Localizable.xcstrings catalog used by the app (e.g.
Resources/Localizable.xcstrings and any module-specific Localizable.xcstrings),
and include equivalent translated values for all supported locales so the lookup
succeeds at runtime and falls back to the default only if a locale-specific
entry is missing.

In `@Sources/Panels/ProjectBuildSettingsTabView.swift`:
- Around line 27-50: Replace all hardcoded UI strings in controlsRow (e.g.,
Text("Target"), TextField placeholder "Filter settings", Toggle("Customized
only"), and the count Text("\(rowCount) settings")) with localized
string-catalog keys and use pluralized .stringsdict entries for the count.
Concretely: swap Text("Target") ->
Text(LocalizedStringKey("BuildSettings.Target")), targetPicker/ TextField
placeholder -> LocalizedStringKey("BuildSettings.FilterSettings"), Toggle label
-> LocalizedStringKey("BuildSettings.CustomizedOnly"), and change the count to
use the .stringsdict plural key (e.g. use
String.localizedStringWithFormat(NSLocalizedString("BuildSettings.SettingsCount",
comment: ""), rowCount) or
Text(LocalizedStringKey("BuildSettings.SettingsCount"), table: nil) with a
.stringsdict that defines .one/.other forms). Also update the other header
labels referenced in this view (the same keys used for "Setting", "Effective",
"Target", "Project") to their corresponding localized keys and add matching
.one/.other entries for the count in the string catalog.

In `@Sources/Panels/ProjectFilesTabView.swift`:
- Around line 101-119: The current implementation calls
Self.collectMatchingFiles(in:filter:) for each group inside the traversal,
causing repeated recursive scans; instead modify the traversal (the walk
function that emits FlattenedRow entries) to perform a single DFS per filter
pass that both detects matches and emits rows without calling
collectMatchingFiles for subtrees. Concretely: change the group-handling path to
recurse into children once, have the recursive routine return a Boolean (or
count) indicating whether any descendant matched the filter, and only when true
append the group row (using Self.presentationGroup) and the matching file rows
inline (creating FlattenedRow entries as you encounter matching files). Remove
the per-group collectMatchingFiles calls so each node is visited exactly once
per filter operation.
- Around line 42-66: The UI strings in ProjectFilesTabView are hardcoded
English; update filterBar and the file-list/detail labels to use localized keys
(e.g., replace "Filter files (e.g. AppDelegate)" in the TextField in filterBar,
and the labels "Select a file", "Path", "Type", "On disk", "Targets", "Yes",
"Missing", and "flags:" wherever they're rendered) by using SwiftUI-localized
APIs (LocalizedStringKey/Text or NSLocalizedString) with clear keys like
"Files.FilterPlaceholder", "Files.SelectFile", "Files.Path", "Files.Type",
"Files.OnDisk", "Files.Targets", "Files.Yes", "Files.Missing", "Files.Flags" and
add matching entries to the .strings catalogs; ensure you bind the placeholder
as a LocalizedStringKey for TextField and replace any hardcoded Text(...) calls
with Text("Your.Key") so translations are picked up.

In `@Sources/Panels/ProjectPanel.swift`:
- Around line 212-223: The describe(_:) helper currently returns raw parser
reasons and absolute paths from ProjectLoadError; change it to map each
ProjectLoadError case (.unreadable, .unsupported, .parseFailure) to a
user-facing, localized high-level message (use NSLocalizedString or the app's
localization helper) that does not include url.path or the raw reason, and
instead uses neutral text like "The project could not be read", "This project
format is not supported", and "The project could not be parsed"; keep the
original error/URL and reason only in debug logging/telemetry (e.g., log inside
describe or where the error is caught) so diagnostics are preserved but not
shown in the UI, and update any callers of describe(_:) in ProjectPanel to
expect the localized strings.
- Around line 67-68: selectedSchemeName (and similarly
selectedConfigurationName) uses only the display name which can collide across
modules/scopes; change the panel state to store a stable identifier (e.g.
SchemeID or a composite of scheme UUID + scope/module) instead of the plain name
and derive the human-readable name from the loaded model when rendering. Update
ProjectPanel's published properties to hold SchemeID (and ConfigurationID) and
replace usages that compare or restore by name with comparisons/lookup by the
stable ID (including the code paths referenced around lines 123–130) so
reload/reconciliation picks the exact scheme; keep a computed/display property
that maps the stored ID to the scheme's name for UI.
- Around line 14-20: The hardcoded English tab labels in the displayLabel
computed property should be localized: replace the literal returns for .files,
.targets, .buildSettings, and .schemes with String(localized:_, defaultValue:_ )
calls (e.g., String(localized: "Files", defaultValue: "Files")) using the same
keys for each case, and add corresponding entries to the app's string catalog /
Localizable strings so translations exist for those keys; update the
displayLabel implementation to return these localized strings for the enum
cases.

In `@Sources/Panels/ProjectPanelView.swift`:
- Around line 33-46: Replace all hardcoded user-facing literals in
ProjectPanelView (e.g., the Label title via panel.displayTitle, the Button help
"Reload project", the "Reload returned errors" status, picker labels used by
schemePicker and configurationPicker, loading/failure text, and both empty-state
messages referenced in the ranges called out) with localized calls using
String(localized: , defaultValue: ). Update the Label, Button.help, any
Text(...) or .help(...) usages, and picker label initializers inside the
ProjectPanelView struct and related view helpers to call String(localized:
"KEY", defaultValue: "English text") with descriptive localization keys; keep
the same semantics and pass the localized strings into schemePicker and
configurationPicker where labels are created, and ensure any status text
displayed around panel.reload() uses localized variants as well.

In `@Sources/Panels/ProjectSchemesTabView.swift`:
- Around line 52-69: The hardcoded English UI strings in ProjectSchemesTabView
(e.g., Text(scheme.isShared ? "shared" : "personal"), Text("run: ..."),
Text("test: ..."), and labels like "Visibility", "Launch arguments",
"Environment") must be replaced with localized string keys and lookup APIs;
update occurrences that reference runTargetIDs, testTargetIDs, and the
targetNames(for:in:) call to use localized keys (for example replace literal
"shared"/"personal" with localized keys like
"schemes.visibility.shared"/"schemes.visibility.personal" and use
Text(LocalizedStringKey("schemes.visibility.shared")) or String(localized:
"schemes.run_count", count: ...) as appropriate), add matching entries to the
app’s .strings/.stringsdict catalog for each key and pluralized forms, and
ensure all other hardcoded labels in the same file (lines ~93-149) follow the
same pattern.
- Around line 25-33: entries currently flattens every module's schemes and keys
rows by compositeID so the same logical scheme can appear multiple times; change
construction of entries to first group schemes by a stable scheme identity
(e.g., scheme.name or scheme.id on SchemeSummary) into a dictionary mapping that
identity to a single representative SchemeSummary plus an array of associated
ProjectModule(s), then build a uniqueEntries array of tuples like (scheme:
SchemeSummary, modules: [ProjectModule], id: String) and iterate
ForEach(uniqueEntries, id: \.id) { entry in ... } while keeping
schemeRow(entry.scheme, module: entry.modules.first) (and surface the full
modules array in the detail view) so the sidebar shows one row per scheme but
still retains module attribution for details.

In `@Sources/Panels/ProjectTargetsTabView.swift`:
- Around line 67-77: Replace all hardcoded user-facing strings in this view with
localized keys: change calls to metadata("min", ...), metadata("platforms",
...), metadata("bundle", ...), the Text("deps: \(...)") and other literal Text
labels ("Select a target", "Dependencies", "Build", "Open in Build Settings",
"\(configCount) configurations", "\(totalKeys) target overrides") to use
localization APIs; for simple labels use LocalizedStringKey or NSLocalizedString
with descriptive keys (e.g. "Targets.min", "Targets.platforms",
"Targets.bundle", "Targets.select") and for plural/counted strings add
.one/.other entries in a .stringsdict and format counts via
String.localizedStringWithFormat or use LocalizedStringKey with plural support
so the Text views and metadata(_:,_:) calls reference the localized strings and
the counts use the stringsdict keys.

In `@Sources/TerminalController.swift`:
- Around line 12369-12386: The v2ProjectSetSelectedTarget function currently
only checks the first module (panel.loadState.model?.modules.first) so targets
in other modules are missed; modify the lookup inside the v2MainSync block to
iterate/search across all modules (e.g., gather modules.flatMap { $0.targets }
or otherwise traverse panel.loadState.model?.modules) and find the first target
whose displayName matches the provided name, then set panel.selectedTargetID =
target.id and resolvedID = target.id.rawValue (or set selectedTargetID = nil if
no match) while preserving existing name handling and thread-safety in
v2MainSync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aac1bcff-4fe6-4a4d-b5c6-2517015b2362

📥 Commits

Reviewing files that changed from the base of the PR and between 0c4539f and 6b41ec8.

📒 Files selected for processing (48)
  • .gitignore
  • .iter-logs/CHANGELOG.md
  • CLI/cmux.swift
  • Packages/CMUXProjectModel/Package.resolved
  • Packages/CMUXProjectModel/Package.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectDump/main.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/BuildConfigScope.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/BuildConfigSummary.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/IdentifierTypes.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectAdapter.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectAdapterKind.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectFileNode.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectGroup.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectGroupStyle.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectLoadError.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectModel.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectModule.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectNodeKind.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/SchemeSummary.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetMembership.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetMembershipRole.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetProductType.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/TargetSummary.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcconfigParser.swift
  • Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
  • Packages/CMUXProjectModel/Tests/CMUXProjectModelTests/XcconfigParserTests.swift
  • Packages/CMUXProjectModel/Tests/CMUXProjectModelTests/XcodeProjectAdapterTests.swift
  • Sources/ClosedItemHistory.swift
  • Sources/CmuxConfig.swift
  • Sources/CmuxLifecycleEventPublishing.swift
  • Sources/ContentView.swift
  • Sources/Panels/FilePreviewPanel.swift
  • Sources/Panels/FilePreviewWorkspaceOpenSupport.swift
  • Sources/Panels/Panel.swift
  • Sources/Panels/PanelContentView.swift
  • Sources/Panels/ProjectBuildSettingsTabView.swift
  • Sources/Panels/ProjectFilesTabView.swift
  • Sources/Panels/ProjectPanel.swift
  • Sources/Panels/ProjectPanelView.swift
  • Sources/Panels/ProjectSchemesTabView.swift
  • Sources/Panels/ProjectTargetsTabView.swift
  • Sources/Search/GlobalSearchDocuments.swift
  • Sources/SessionPersistence.swift
  • Sources/TerminalController.swift
  • Sources/TerminalPaneDropTargetView.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Comment thread CLI/cmux.swift
Comment on lines +4734 to +4745
let subArgs: [String]
if first == "open" {
subArgs = Array(args.dropFirst())
} else if args.count == 1 {
subArgs = args
} else {
throw CLIError(message: "Unknown project subcommand: \(first). Usage: cmux project open <path>")
}

guard let rawPath = subArgs.first, !rawPath.isEmpty else {
throw CLIError(message: "project open requires a path. Usage: cmux project open <path-to-.xcodeproj-or-.xcworkspace>")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Lone non-open token is silently treated as a path, masking mistyped subcommands.

The else if args.count == 1 branch accepts any single token as the path, so cmux project status (or any typo'd one-word subcommand) becomes a bogus path and surfaces a confusing "file not found"-style error from project.open instead of a clear usage error. Multi-token typos are already caught by the final else. The looksLikePath(_:) helper right below (Line 4774) can gate this cleanly.

🛠️ Proposed fix to reject non-path single tokens
         let subArgs: [String]
         if first == "open" {
             subArgs = Array(args.dropFirst())
-        } else if args.count == 1 {
+        } else if args.count == 1, looksLikePath(rawFirst) {
             subArgs = args
         } else {
             throw CLIError(message: "Unknown project subcommand: \(first). Usage: cmux project open <path>")
         }

This requires capturing the original-cased first token (e.g. let rawFirst = args[0]) since looksLikePath should run against the un-lowercased value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLI/cmux.swift` around lines 4734 - 4745, The single-token branch currently
treats any one-word token as a path and should instead validate it using the
looksLikePath(_:) helper: capture the original-cased first token (e.g. rawFirst
= args[0]) before any lowercasing, then when args.count == 1 call
looksLikePath(rawFirst) and only accept it as subArgs if it returns true;
otherwise throw the existing CLIError usage message for unknown project
subcommands (keep the existing throw path for multi-token cases), and ensure
subArgs is set from the validated rawFirst so the later guard uses the correct
rawPath.

Comment on lines +12 to +30
public let id: BuildConfigID
public let name: String
public let scope: BuildConfigScope
public let baseConfigurationPath: URL?
public let rawSettings: [String: String]

public init(
id: BuildConfigID,
name: String,
scope: BuildConfigScope,
baseConfigurationPath: URL?,
rawSettings: [String: String]
) {
self.id = id
self.name = name
self.scope = scope
self.baseConfigurationPath = baseConfigurationPath
self.rawSettings = rawSettings
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the public properties and initializer.

The public stored properties (Lines 12–16) and the memberwise initializer (Lines 18–24) lack the required Swift-DocC /// comments.

As per coding guidelines: "Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/BuildConfigSummary.swift`
around lines 12 - 30, Add Swift-DocC triple-slash comments for each public
stored property (id, name, scope, baseConfigurationPath, rawSettings) and the
public initializer in BuildConfigSummary so every public symbol is documented;
for the initializer (init(id:name:scope:baseConfigurationPath:rawSettings:))
include a brief overview plus /// - Parameters: entries describing each
parameter and their meanings, and for optional baseConfigurationPath note it can
be nil, keeping all comments concise and formatted with /// so they appear in
generated docs.

Comment on lines +11 to +32
public let id: ProjectNodeID
public let displayName: String
public let resolvedPath: URL?
public let fileType: String?
public let existsOnDisk: Bool
public let memberships: [TargetMembership]

public init(
id: ProjectNodeID,
displayName: String,
resolvedPath: URL?,
fileType: String?,
existsOnDisk: Bool,
memberships: [TargetMembership]
) {
self.id = id
self.displayName = displayName
self.resolvedPath = resolvedPath
self.fileType = fileType
self.existsOnDisk = existsOnDisk
self.memberships = memberships
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the public properties and initializer.

The public stored properties (Lines 11–16) and the memberwise initializer (Lines 18–24) lack the required Swift-DocC /// comments.

As per coding guidelines: "Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectFileNode.swift`
around lines 11 - 32, Add Swift-DocC triple-slash comments for every public
symbol in ProjectFileNode: document each stored property (id, displayName,
resolvedPath, fileType, existsOnDisk, memberships) with a brief description of
its purpose and any important behavior, and document the public initializer
(init(id:displayName:resolvedPath:fileType:existsOnDisk:memberships:)) including
parameter descriptions; ensure each comment uses /// and follows the project's
DocC style so all public symbols in ProjectFileNode are documented.

Comment on lines +12 to +30
public let id: ProjectNodeID
public let displayName: String
public let resolvedPath: URL?
public let style: ProjectGroupStyle
public let children: [ProjectNodeKind]

public init(
id: ProjectNodeID,
displayName: String,
resolvedPath: URL?,
style: ProjectGroupStyle,
children: [ProjectNodeKind]
) {
self.id = id
self.displayName = displayName
self.resolvedPath = resolvedPath
self.style = style
self.children = children
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Document the public properties and initializer.

The public stored properties (Lines 12–16) and the memberwise initializer (Lines 18–24) lack the required Swift-DocC /// comments.

As per coding guidelines: "Every public symbol in any new Swift package under Packages/ must be documented with a Swift-DocC triple-slash comment at the time of writing."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectGroup.swift` around
lines 12 - 30, Add Swift-DocC triple-slash documentation for all public stored
properties and the initializer in ProjectGroup: document the purpose of each
property (id: ProjectNodeID, displayName: String, resolvedPath: URL?, style:
ProjectGroupStyle, children: [ProjectNodeKind]) with a short description and for
the initializer add a summary plus /// - Parameters: entries describing each
parameter (id, displayName, resolvedPath, style, children). Place the ///
comments immediately above the property declarations and above the public init
to satisfy the package documentation guideline.

Comment on lines +14 to +19
/// The artifact exists and is the right kind, but parsing failed.
///
/// The underlying reason is rendered as a string because adapter
/// implementations wrap third-party errors (XcodeProj, libxml2, etc.)
/// whose types are not part of this package's public API surface.
case parseFailure(URL, reason: String)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find where parseFailure is constructed (reason source) and where it's rendered to users.
rg -nP -C3 '\bparseFailure\s*\(' --type=swift
rg -nP -C3 '\bProjectLoadError\b' --type=swift -g '!**/Tests/**'

Repository: manaflow-ai/cmux

Length of output: 10325


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect UI rendering of ProjectLoadError, especially parseFailure(reason:)
sed -n '180,260p' Sources/Panels/ProjectPanel.swift

# Inspect ProjectLoadError definition around parseFailure
sed -n '1,120p' Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectLoadError.swift

# Inspect adapter construction of parseFailure(reason:) more narrowly
sed -n '1,140p' Packages/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift

Repository: manaflow-ai/cmux

Length of output: 8913


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where ProjectPanel.describe(_:) is called / displayed
rg -n "ProjectPanel\.describe\(_" -S Sources/Panels/ProjectPanel.swift Sources -g'*.swift'
rg -n "describe\(_ error: Error\)" Sources/Panels/ProjectPanel.swift
rg -n "Parse failed at" -S Sources/Panels/ProjectPanel.swift Sources -g'*.swift'

# Trace ProjectLoadError handling in UI
rg -n "ProjectLoadError\." -S Sources -g'*.swift'
rg -n "parseFailure\(" -S Sources Packages -g'*.swift'

Repository: manaflow-ai/cmux

Length of output: 42


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate how ProjectPanel.describe(_:) is used (so we know if it's user-facing)
rg -n "private static func describe\(_ error: Error\)" Sources/Panels/ProjectPanel.swift
rg -n "describe\(_ error: Error\)" Sources -g'*.swift'
rg -n "Parse failed at" Sources -g'*.swift'
rg -n "Cannot read" Sources -g'*.swift'
rg -n "Unsupported project at" Sources -g'*.swift'

# Find likely call sites that present panel error strings
rg -n "triggerFlash|WorkspaceAttentionFlashReason|alert|NSAlert|error.*reason|show.*error|display.*error" Sources -g'*.swift'

Repository: manaflow-ai/cmux

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrowly find where the ProjectPanel error string (including parseFailure reason) is used/displayed.
rg -n "Parse failed at" -S Sources -g'*.swift' --max-count 50
rg -n "Cannot read \\(url\\.path\\)|Unsupported project at \\(url\\.path\\)" -S Sources -g'*.swift' --max-count 50

# Look for the private static describe(_:) being invoked indirectly.
rg -n "describe\\(.*\\)\\s*\\)" -S Sources/Panels/ProjectPanel.swift -g'*.swift' --max-count 50
rg -n "ProjectPanel\\.describe" -S Sources -g'*.swift' --max-count 50

# Trace ProjectPanel error plumbing (load failure -> string -> UI).
rg -n "ProjectLoadError|load\\(at:.*\\)|catch.*ProjectLoadError|catch.*ProjectLoadError" -S Sources -g'*.swift' --max-count 80

Repository: manaflow-ai/cmux

Length of output: 162


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the surrounding code where describe(_:) is defined and any nearby error UI usage.
sed -n '160,320p' Sources/Panels/ProjectPanel.swift

# Find any call sites within ProjectPanel.swift for the describe(_:) helper.
rg -n "describe\(_ error: Error\)|ProjectPanel\.describe|describe\(" Sources/Panels/ProjectPanel.swift -S

Repository: manaflow-ai/cmux

Length of output: 3389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Trace where lastLoadError is set and how it is rendered/displayed.
rg -n "lastLoadError" Sources/Panels/ProjectPanel.swift

# Inspect the render/UI portion near the load error usage (small windows around call sites).
sed -n '60,140p' Sources/Panels/ProjectPanel.swift
sed -n '140,240p' Sources/Panels/ProjectPanel.swift
sed -n '240,360p' Sources/Panels/ProjectPanel.swift
sed -n '360,520p' Sources/Panels/ProjectPanel.swift

Repository: manaflow-ai/cmux

Length of output: 7463


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the load state enum and where the .failed(lastLoadError) string is rendered.
rg -n "ProjectPanelLoadState" Sources/Panels/ProjectPanel.swift
rg -n "case \.failed" Sources/Panels/ProjectPanel.swift Sources -g'*.swift'

# Inspect surrounding code in ProjectPanel.swift where loadState is consumed (UI/state mapping).
sed -n '1,160p' Sources/Panels/ProjectPanel.swift
sed -n '520,760p' Sources/Panels/ProjectPanel.swift

Repository: manaflow-ai/cmux

Length of output: 7010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "lastLoadError" Sources -g'*.swift'

rg -n "ProjectPanelLoadState|case \\.failed\\(let" Sources -g'*.swift' --max-count 80

# Focus on Project-related SwiftUI views/panels
rg -n "ProjectPanel.*loadState|loadState.*ProjectPanel|switch .*loadState" Sources/Panels -g'*.swift' --max-count 80

Repository: manaflow-ai/cmux

Length of output: 1685


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect UI rendering of project load errors.
sed -n '1,120p' Sources/Panels/ProjectPanelView.swift
sed -n '120,260p' Sources/Panels/ProjectPanelView.swift

Repository: manaflow-ai/cmux

Length of output: 8045


Sanitize ProjectLoadError.parseFailure’s reason before showing it in the Project UI.

XcodeProjectAdapter constructs parseFailure(..., reason: String(describing: error)), and ProjectPanel.describe(_:) interpolates that reason into the returned string ("Parse failed at \(url.path): \(reason)"). ProjectPanelView then renders that string directly in both "Reload returned errors: \(error)" and "Failed: \(reason)", so raw third-party parse details can leak to users.

Update the adapter and/or ProjectPanel.describe(_:) to redact/replace reason with a user-safe, generic message, and keep the raw string only for internal logging if needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/CMUXProjectModel/Sources/CMUXProjectModel/ProjectLoadError.swift`
around lines 14 - 19, ProjectLoadError.parseFailure currently carries raw
third-party error text which gets shown in the UI; update the surface so
user-visible text is redacted while preserving raw details for logs.
Specifically, either change XcodeProjectAdapter so it no longer constructs
parseFailure(..., reason: String(describing: error)) for UI consumption but
instead passes a generic message like "Project parsing failed" and sends
String(describing: error) to an internal logger, or modify
ProjectPanel.describe(_:) to map ProjectLoadError.parseFailure(_, _) to a safe,
generic message (e.g. "Parse failed: internal parsing error") and write the
original reason to debug logs; ensure references: ProjectLoadError.parseFailure,
XcodeProjectAdapter, ProjectPanel.describe(_:), and ProjectPanelView are updated
accordingly.

Comment on lines +33 to +46
Label(panel.displayTitle, systemImage: "hammer.fill")
.font(.system(size: 13, weight: .semibold))
.help(panel.projectURL.path)
schemePicker
configurationPicker
Spacer(minLength: 0)
Button {
panel.reload()
} label: {
Image(systemName: "arrow.clockwise")
.font(.system(size: 11, weight: .semibold))
}
.buttonStyle(.plain)
.help("Reload project")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize the new panel chrome and status copy.

This view hardcodes the new visible strings in English (Reload project, Reload returned errors, picker labels, loading/failure text, and both empty states). These should all come from String(localized:defaultValue:) keys instead of raw literals.

As per coding guidelines, production user-facing Swift text must use localized APIs with matching translated string-catalog entries for every supported locale.

Also applies to: 48-70, 102-176, 216-241

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/ProjectPanelView.swift` around lines 33 - 46, Replace all
hardcoded user-facing literals in ProjectPanelView (e.g., the Label title via
panel.displayTitle, the Button help "Reload project", the "Reload returned
errors" status, picker labels used by schemePicker and configurationPicker,
loading/failure text, and both empty-state messages referenced in the ranges
called out) with localized calls using String(localized: , defaultValue: ).
Update the Label, Button.help, any Text(...) or .help(...) usages, and picker
label initializers inside the ProjectPanelView struct and related view helpers
to call String(localized: "KEY", defaultValue: "English text") with descriptive
localization keys; keep the same semantics and pass the localized strings into
schemePicker and configurationPicker where labels are created, and ensure any
status text displayed around panel.reload() uses localized variants as well.

Comment on lines +25 to +33
let entries: [(module: ProjectModule, scheme: SchemeSummary, compositeID: String)] = model.modules.flatMap { module in
module.schemes.map { scheme in
(module: module, scheme: scheme, compositeID: "\(module.id.rawValue)|\(scheme.name)")
}
}
ScrollView {
VStack(alignment: .leading, spacing: 0) {
ForEach(entries, id: \.compositeID) { entry in
schemeRow(entry.scheme, module: entry.module)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Deduplicate schemes before rendering the sidebar.

entries flattens every module’s schemes and keys each row by module.id|scheme.name, so the same scheme can appear multiple times across a workspace. That conflicts with the intended shared/per-user dedup behavior and makes the sidebar diverge from the single selected-scheme model. Merge the rows by stable scheme identity first, then carry module attribution separately in the detail view.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/ProjectSchemesTabView.swift` around lines 25 - 33, entries
currently flattens every module's schemes and keys rows by compositeID so the
same logical scheme can appear multiple times; change construction of entries to
first group schemes by a stable scheme identity (e.g., scheme.name or scheme.id
on SchemeSummary) into a dictionary mapping that identity to a single
representative SchemeSummary plus an array of associated ProjectModule(s), then
build a uniqueEntries array of tuples like (scheme: SchemeSummary, modules:
[ProjectModule], id: String) and iterate ForEach(uniqueEntries, id: \.id) {
entry in ... } while keeping schemeRow(entry.scheme, module:
entry.modules.first) (and surface the full modules array in the detail view) so
the sidebar shows one row per scheme but still retains module attribution for
details.

Comment on lines +52 to +69
Text(scheme.isShared ? "shared" : "personal")
.font(.system(size: 9, weight: .medium))
.padding(.horizontal, 4)
.padding(.vertical, 1)
.background(
RoundedRectangle(cornerRadius: 3)
.fill((scheme.isShared ? Color.accentColor : Color.orange).opacity(0.18))
)
.foregroundStyle(scheme.isShared ? Color.accentColor : Color.orange)
}
HStack(spacing: 8) {
if !scheme.runTargetIDs.isEmpty {
Text("run: \(targetNames(for: scheme.runTargetIDs, in: module))")
.font(.system(size: 10))
.foregroundStyle(.secondary)
}
if !scheme.testTargetIDs.isEmpty {
Text("test: \(scheme.testTargetIDs.count)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize the Schemes tab copy.

The new badges, labels, and empty-state text (shared, personal, run, test, Visibility, Launch arguments, Environment, etc.) are hardcoded English strings. These should all come from localized string-catalog keys.

As per coding guidelines, production user-facing Swift text must use localized APIs with matching translated string-catalog entries for every supported locale.

Also applies to: 93-149

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/ProjectSchemesTabView.swift` around lines 52 - 69, The
hardcoded English UI strings in ProjectSchemesTabView (e.g.,
Text(scheme.isShared ? "shared" : "personal"), Text("run: ..."), Text("test:
..."), and labels like "Visibility", "Launch arguments", "Environment") must be
replaced with localized string keys and lookup APIs; update occurrences that
reference runTargetIDs, testTargetIDs, and the targetNames(for:in:) call to use
localized keys (for example replace literal "shared"/"personal" with localized
keys like "schemes.visibility.shared"/"schemes.visibility.personal" and use
Text(LocalizedStringKey("schemes.visibility.shared")) or String(localized:
"schemes.run_count", count: ...) as appropriate), add matching entries to the
app’s .strings/.stringsdict catalog for each key and pluralized forms, and
ensure all other hardcoded labels in the same file (lines ~93-149) follow the
same pattern.

Comment on lines +67 to +77
HStack(spacing: 8) {
if let deploy = target.deploymentTarget {
metadata("min", deploy)
}
if !target.platforms.isEmpty {
metadata("platforms", target.platforms.joined(separator: ","))
}
if let bundle = target.bundleIdentifier {
metadata("bundle", bundle)
}
Text("deps: \(target.dependencies.count)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Localize the Targets tab labels and pluralized counts.

Strings like min, platforms, bundle, deps, Select a target, Dependencies, Build, Open in Build Settings, and the \(configCount) configurations / \(totalKeys) target overrides labels are hardcoded. These should move to localized keys, with .one / .other variants for the count-based copy.

Based on learnings, pluralized Swift strings in this repo should use explicit .one / .other localization keys. As per coding guidelines, production user-facing Swift text must use localized APIs with matching translated string-catalog entries for every supported locale.

Also applies to: 125-129, 146-160, 190-205

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Panels/ProjectTargetsTabView.swift` around lines 67 - 77, Replace all
hardcoded user-facing strings in this view with localized keys: change calls to
metadata("min", ...), metadata("platforms", ...), metadata("bundle", ...), the
Text("deps: \(...)") and other literal Text labels ("Select a target",
"Dependencies", "Build", "Open in Build Settings", "\(configCount)
configurations", "\(totalKeys) target overrides") to use localization APIs; for
simple labels use LocalizedStringKey or NSLocalizedString with descriptive keys
(e.g. "Targets.min", "Targets.platforms", "Targets.bundle", "Targets.select")
and for plural/counted strings add .one/.other entries in a .stringsdict and
format counts via String.localizedStringWithFormat or use LocalizedStringKey
with plural support so the Text views and metadata(_:,_:) calls reference the
localized strings and the counts use the stringsdict keys.

Comment on lines +12369 to +12386
private func v2ProjectSetSelectedTarget(params: [String: Any]) -> V2CallResult {
guard let (_, panel) = v2ResolveProjectPanel(params: params) else {
return .err(code: "not_found", message: "Project surface not found", data: nil)
}
let name = v2String(params, "name")
var resolvedID: String?
v2MainSync {
if let name, !name.isEmpty,
let module = panel.loadState.model?.modules.first,
let target = module.targets.first(where: { $0.displayName == name }) {
panel.selectedTargetID = target.id
resolvedID = target.id.rawValue
} else {
panel.selectedTargetID = nil
}
}
return .ok(["target_name": name ?? "", "target_id": resolvedID ?? ""])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Target name matching only searches the first module.

Lines 12377-12378 search only panel.loadState.model?.modules.first for a target matching the given name. In multi-module projects (e.g., a workspace containing multiple .xcodeproj entries), targets in subsequent modules will not be found, making them unreachable via this RPC.

🔍 Proposed fix to search all modules
     private func v2ProjectSetSelectedTarget(params: [String: Any]) -> V2CallResult {
         guard let (_, panel) = v2ResolveProjectPanel(params: params) else {
             return .err(code: "not_found", message: "Project surface not found", data: nil)
         }
         let name = v2String(params, "name")
         var resolvedID: String?
         v2MainSync {
             if let name, !name.isEmpty,
-               let module = panel.loadState.model?.modules.first,
-               let target = module.targets.first(where: { $0.displayName == name }) {
+               let model = panel.loadState.model {
+                let allTargets = model.modules.flatMap(\.targets)
+                if let target = allTargets.first(where: { $0.displayName == name }) {
-                panel.selectedTargetID = target.id
-                resolvedID = target.id.rawValue
+                    panel.selectedTargetID = target.id
+                    resolvedID = target.id.rawValue
+                }
             } else {
                 panel.selectedTargetID = nil
             }
         }
         return .ok(["target_name": name ?? "", "target_id": resolvedID ?? ""])
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/TerminalController.swift` around lines 12369 - 12386, The
v2ProjectSetSelectedTarget function currently only checks the first module
(panel.loadState.model?.modules.first) so targets in other modules are missed;
modify the lookup inside the v2MainSync block to iterate/search across all
modules (e.g., gather modules.flatMap { $0.targets } or otherwise traverse
panel.loadState.model?.modules) and find the first target whose displayName
matches the provided name, then set panel.selectedTargetID = target.id and
resolvedID = target.id.rawValue (or set selectedTargetID = nil if no match)
while preserving existing name handling and thread-safety in v2MainSync.

Replace `await MainActor.run { guard let self else { return }; ... }`
with `await self?.applyLoaded(model)` / `await self?.applyLoadError(...)`.
Hops back to the main actor via the @MainActor-isolated methods,
satisfies Sendable closure capture rules, and clears the warning-budget
red on tests-build-and-lag.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread Sources/Workspace.swift
Comment on lines 1775 to +1787
}
applySessionPanelMetadata(snapshot, toPanelId: toolPanel.id)
return toolPanel.id
case .project:
guard let projectPath = snapshot.project?.projectPath,
let projectPanel = newProjectSurface(
inPane: paneId,
projectPath: projectPath,
focus: false
) else {
return nil
}
applySessionPanelMetadata(snapshot, toPanelId: projectPanel.id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Session snapshot state fields are serialized but never restored

SessionProjectPanelSnapshot captures activeTab, selectedSchemeName, selectedConfigurationName, and selectedNodePath, but the session-restore path only uses projectPath to reconstruct the panel. The other four fields are written to disk and silently discarded on restore — every session open lands on Files tab with no scheme/config/file selection, even if the user saved a different state. Since projectPanel.reload() runs asynchronously via a detached task, the snapshot values can be applied to the panel immediately after newProjectSurface returns and applyLoaded will validate (and keep or nil) each one when the load completes.

@azooz2003-bit
azooz2003-bit dismissed coderabbitai[bot]’s stale review May 29, 2026 19:43

Ignoring AI reviewer per author request; required CI is green.

@azooz2003-bit
azooz2003-bit merged commit 9a1a7d1 into main May 29, 2026
20 checks passed
@azooz2003-bit
azooz2003-bit deleted the feat-xcode-project-pane branch May 29, 2026 19:43

This branch was successfully deployed

1 active deployment
Preview – cmux — 84bbf3ad Deployed May 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant