Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .agents/external-skills.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"swiftui-pro": {
"repo": "twostraws/swiftui-agent-skill",
"path": "swiftui-pro",
"ref": "61b74001b64b292da8397355464d7c8a4c2c7d89"
}
}
2 changes: 2 additions & 0 deletions .agents/skills/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# External skills — fetched via ./sync-agents --install
/swiftui-pro/
21 changes: 21 additions & 0 deletions .agents/skills/custom-container-view-controller/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: custom-container-view-controller
description: Build UIKit custom container view controllers with correct child lifecycle management. Use when creating container view controllers, embedding child VCs, or managing view controller containment.
---

# Custom Container View Controller

## For non-lazy single child containers (the default)

- Add child view controllers in `init` (do NOT load the view in init).
- Add the child view controllers' view in `viewDidLoad`. Set the frame before adding the subview to self.view.
- Override `viewWillLayoutSubviews` and set the child view controller's view.frame to self.view.bounds.

## For lazy single child containers (special case)

Lazily loading / containers are rare, but sometimes are needed, eg if we need the entire view controller and view hierarchy set up before we can create a child. `BRootViewController` is an example of this. In these cases:

- Add a `setUpIfNeeded` method.
- It should run once, from within `viewIsAppearing`.
- It should create the child view controller (based on a provider closure) and insert it into the view controller hierarchy, and THEN the view hierarchy.
- Override `viewWillLayoutSubviews` and set the child view controller's view.frame to self.view.bounds.
3 changes: 3 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ if [ -n "$staged" ]; then
git diff --cached --name-only --diff-filter=ACMR -z -- '*.swift' \
| xargs -0 git add
fi

# Sync AGENTS.md + .agents/skills → CLAUDE.md + .claude/skills.
./sync-agents --git-add
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ xcuserdata/
## macOS
.DS_Store

## Generated agent files (re-create via ./sync-agents --install)
CLAUDE.md
.claude/skills/

## Obj-C/Swift specific
*.hmap

Expand Down
131 changes: 29 additions & 102 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,130 +1,57 @@
# AGENTS.md — Repository Shape

This document describes the structure and conventions of the Broadway repository for AI agents and contributors.

## Overview

Broadway is a SwiftUI iOS + Mac Catalyst application managed by **Tuist**. The Xcode project is not checked in — it is generated from the `Project.swift` manifest.
Broadway is a SwiftUI iOS + Mac Catalyst design system managed by **Tuist**. The Xcode project is not checked in — it is generated from `Project.swift`.

## Directory Layout

```
/
├── .githooks/pre-commit # Git pre-commit hook (SwiftFormat lint)
├── .mise.toml # mise tool versions (pins Tuist, SwiftFormat)
├── .swiftformat # SwiftFormat configuration
├── Tuist.swift # Tuist global configuration
├── Project.swift # Tuist project manifest (root level)
├── BroadwayCatalog/
│ ├── Sources/ # Catalog app source code (Swift / SwiftUI)
│ │ ├── BroadwayApp.swift # @main entry point
│ │ └── ContentView.swift # Root view
│ ├── Resources/ # Bundled resources (assets, localization, etc.)
│ └── Tests/ # Catalog app unit tests (Swift Testing)
│ └── BroadwayCatalogTests.swift
├── BroadwayUI/
│ ├── Sources/ # UI framework source code
│ │ └── BRootViewController.swift # Root container VC (context + trait propagation)
│ └── Tests/ # UI framework unit tests (Swift Testing)
│ └── BRootViewControllerTests.swift
├── BroadwayTestHost/
│ └── Sources/ # Minimal app used as test host for unit tests
│ └── TestHostApp.swift # @main entry point (empty window)
├── BroadwayTesting/
│ └── Sources/ # Test utilities framework (depends on BroadwayCore)
│ └── BroadwayTesting.swift # Module entry point
├── BroadwayCore/
│ ├── Sources/ # Core framework source code
│ │ ├── AnyEquatable.swift # Type-erased Equatable wrapper
│ │ ├── BAccessibility.swift # Accessibility snapshot + Observer
│ │ ├── BContext.swift # Root environment container
│ │ ├── BContext+UITraits.swift # UITraitDefinition bridge (#if canImport(UIKit))
│ │ ├── BStylesheets.swift # Lazy cached stylesheet resolver
│ │ ├── BThemes.swift # Type-keyed theme container
│ │ ├── BTraits.swift # Type-keyed trait container
│ │ ├── CopyOnWrite.swift # COW property wrapper
│ │ └── TypeIdentifier.swift # Lightweight type-keyed identifier
│ └── Tests/ # Core framework unit tests (Swift Testing)
│ ├── AnyEquatableTests.swift
│ ├── BAccessibilityTests.swift
│ ├── BContextTests.swift
│ ├── BThemesTests.swift
│ ├── BTraitsTests.swift
│ ├── CopyOnWriteTests.swift
│ └── TypeIdentifierTests.swift
├── Plans/ # Archived implementation plans (see index below)
├── swiftformat # Run SwiftFormat (--lint to check only)
├── ide # Dev script (installs hooks, runs tuist generate)
├── LICENSE # Apache 2.0
├── README.md # Project overview and setup instructions
└── AGENTS.md # This file
BroadwayCatalog/ # Catalog app (Sources/, Resources/, Tests/)
BroadwayUI/ # Reusable UI component framework (Sources/, Tests/)
BroadwayCore/ # Foundational utilities framework (Sources/, Tests/)
BroadwayTestHost/ # Minimal test host app (Sources/)
BroadwayTesting/ # Shared test utilities framework (Sources/)
Project.swift # Tuist project manifest
Tuist.swift # Tuist global configuration
ide # Dev script (installs hooks, runs tuist generate)
swiftformat # Run SwiftFormat (--lint to check only)
sync-agents # Generate CLAUDE.md + .claude/skills from AGENTS.md
```

## Build System

- **Tuist 4+** is used to generate the Xcode project from `Project.swift`.
- **Tuist 4+** generates the Xcode project from `Project.swift`. The `.xcodeproj` and `Derived/` are git-ignored.
- Tuist and SwiftFormat are version-pinned via **mise** in `.mise.toml`. Run `mise install` to install them.
- Run `./ide` to generate the Xcode project (or `./ide -i` to run `mise exec -- tuist install` first).
- Run `mise exec -- tuist test` to execute all tests.
- Run `mise exec -- tuist test <SchemeName>` to test a specific target. The scheme name is the **framework name** (e.g., `BroadwayCore`), not the test target name (`BroadwayCoreTests`).
- The generated `.xcodeproj` and `Derived/` directory are git-ignored.
- Run `./ide` to generate the project (or `./ide -i` to run `mise exec -- tuist install` first).
- Run `mise exec -- tuist test` to execute all tests, or `mise exec -- tuist test <SchemeName>` for a specific target.

## Formatting

- **SwiftFormat** enforces consistent code style. Configuration lives in `.swiftformat`.
- Run `./swiftformat` to format all Swift files in-place.
- Run `./swiftformat --lint` to check without modifying (used in CI and pre-commit).
- The `./ide` script configures `core.hooksPath` to `.githooks/`, which installs a pre-commit hook that lints staged `.swift` files.
- CI runs `./swiftformat --lint` as a gate before build & test.
- **SwiftFormat** enforces code style via `.swiftformat`. Run `./swiftformat` to format, `./swiftformat --lint` to check.
- The pre-commit hook lints staged `.swift` files automatically.

## Targets
## Agent Instructions Sync

| Target | Type | Bundle ID | Destinations | Min Deployment |
|---|---|---|---|---|
| `BroadwayCatalog` | `.app` | `com.broadway.catalog` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayCatalogTests` | `.unitTests` | `com.broadway.catalog.tests` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayUI` | `.framework` | `com.broadway.ui` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayUITests` | `.unitTests` | `com.broadway.ui.tests` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayCore` | `.framework` | `com.broadway.core` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayCoreTests` | `.unitTests` | `com.broadway.core.tests` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayTestHost` | `.app` | `com.broadway.testhost` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
| `BroadwayTesting` | `.framework` | `com.broadway.testing` | iPhone, iPad, Mac Catalyst | iOS 26.0 |
`AGENTS.md` is the source of truth for AI agent instructions. Cursor and Codex read nested `AGENTS.md` natively; Claude Code uses `CLAUDE.md` and `.claude/skills/`. Generated files (`CLAUDE.md`, `.claude/skills/`) are gitignored and created by `./sync-agents`.

### Dependency Graph
- `./sync-agents` — generate `CLAUDE.md` files and sync skills to `.claude/skills/`.
- `./sync-agents --install` — fetch external skills from `.agents/external-skills.json` (run automatically by `./ide`).
- `./sync-agents --add <url> [name]` — add a new external skill from GitHub.
- `./sync-agents --update` — force re-fetch all external skills to latest.

## Dependency Graph

```
BroadwayCatalog (app) ──▶ BroadwayUI (framework) ──▶ BroadwayCore (framework)
BroadwayTestHost (app) ──▶ BroadwayUI ──────────────────────┤
BroadwayTesting (framework) ────────────────────────────────┘
BroadwayCatalog (app) --> BroadwayUI (framework) --> BroadwayCore (framework)
BroadwayTestHost (app) --> BroadwayUI --> BroadwayCore
BroadwayTesting (framework) --> BroadwayCore

All framework test targets use BroadwayTestHost and depend on BroadwayTesting.
```

## Key Conventions

- **SwiftUI** is the UI framework. Catalog app views live under `BroadwayCatalog/Sources/`.
- **BroadwayUI** is the reusable component library. All shared UI lives under `BroadwayUI/Sources/`.
- **BroadwayCore** provides foundational utilities and shared logic. Source lives under `BroadwayCore/Sources/`.
- **BroadwayTestHost** is a minimal app that serves as the test host for framework unit tests. Source lives under `BroadwayTestHost/Sources/`.
- **BroadwayTesting** provides shared test utilities. All test targets depend on it. Source lives under `BroadwayTesting/Sources/`.
- **Shell scripts** should be kept short (≤ ~20 lines). For anything longer, use **Ruby**.
- **Swift Testing** (`import Testing`) is used for unit tests, not XCTest.
- Source files use `<Target>/Sources/**` globs; test files use `<Target>/Tests/**`.
- Resources (asset catalogs, localization files, etc.) go in `BroadwayCatalog/Resources/`.
- The catalog app uses `@main` via `BroadwayApp.swift` as the app entry point.
- Info.plist is auto-generated by Tuist via `infoPlist: .extendingDefault(with:)`.

## Plans

Implementation plans are stored in the `Plans/` directory. When you develop a plan using Cursor's plan mode, copy the final plan into `Plans/` and add an entry to the index below.

**Naming convention**: `<NNN>-<YYYY-MM-DD>-<slug>.md`, where `<NNN>` is the next sequential number (zero-padded to 3 digits), `<YYYY-MM-DD>` is the date the plan was created, and `<slug>` is a short snake_case description. For example: `002-2026-03-15-dark_mode_support.md`.

## Adding New Files

- **New catalog app files**: Add `.swift` files to `BroadwayCatalog/Sources/`.
- **New UI components**: Add `.swift` files to `BroadwayUI/Sources/`. Mark public API as `public`.
- **New test files**: Add `.swift` files to the appropriate `Tests/` directory.
- **New resources**: Add to `BroadwayCatalog/Resources/`. They are bundled via the `Resources/**` glob.
- **New targets or dependencies**: Edit `Project.swift` at the repository root.
- New targets or dependencies: edit `Project.swift`.
13 changes: 13 additions & 0 deletions BroadwayCatalog/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# BroadwayCatalog

The catalog app — a living showcase of BroadwayUI components. Depends on BroadwayUI.

## Structure

- `Sources/` — App views and logic. Entry point is `BroadwayApp.swift` (`@main`).
- `Resources/` — Asset catalogs, localization files, and other bundled resources.

## Conventions

- App-specific views go here, not in BroadwayUI.
- Resources are bundled via the `Resources/**` glob in `Project.swift`.
18 changes: 18 additions & 0 deletions BroadwayCore/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# BroadwayCore

Foundational utilities and shared logic. All other frameworks depend on this module.

## Key Types

- **BContext** — Root environment container with type-keyed themes, traits, and stylesheets.
- **BThemes** / **BTraits** — Type-keyed containers for theme and trait values.
- **BStylesheets** — Lazy cached stylesheet resolver.
- **BAccessibility** — Accessibility snapshot and observer.
- **AnyEquatable** — Type-erased Equatable wrapper.
- **CopyOnWrite** — COW property wrapper.
- **TypeIdentifier** — Lightweight type-keyed identifier.

## Conventions

- Mark all public API as `public`.
- `BContext+UITraits.swift` bridges to `UITraitDefinition` via `#if canImport(UIKit)`.
12 changes: 12 additions & 0 deletions BroadwayUI/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# BroadwayUI

Reusable UI component library. Depends on BroadwayCore.

## Key Types

- **BRootViewController** — Root container view controller that propagates BContext and traits to its children.

## Conventions

- Mark all public API as `public`.
- UI components go in `Sources/`. This is the shared component library — app-specific views belong in BroadwayCatalog.
55 changes: 0 additions & 55 deletions Plans/001-2026-02-27-github_ci_setup.md

This file was deleted.

Loading
Loading