| name | add-screen |
| description | Add a new screen to an existing feature module with Store, UI, factories, and tests |
| disable-model-invocation | true |
| argument-hint | <feature-name> <ScreenName> |
Add Screen
Add a new screen to an existing feature module. This scaffolds the Screen, State, Event, Store, UI, and Factory within the feature's directory.
Input: $ARGUMENTS
Step 1: Validate the Feature
Extract the feature name and screen name from the input.
Verify the feature directory exists at <AppTarget>/<FeatureName>/. If the feature doesn't exist, suggest running /add-feature first.
Step 2: Read Existing Patterns
Read the existing screen files in the feature module to match:
- Import patterns
- Store constructor patterns (what dependencies are injected)
- Factory registration patterns
Also read existing screens from other features if the current feature has no prior screens.
Step 3: Determine Screen Details
Ask the user:
- Does this screen receive navigation arguments? If so, what are they? (These become properties on the
TrapezioScreen struct)
- Does this screen need
TrapezioNavigator injection?
- Does this screen need
TrapezioInterop injection?
Step 4: Create Files
Screen + State + Event
Place at: <AppTarget>/<FeatureName>/<ScreenName>Screen.swift
import Trapezio
struct <ScreenName>Screen: TrapezioScreen {
}
struct <ScreenName>State: TrapezioState {
}
enum <ScreenName>Event: TrapezioEvent {
}
Store
Place at: <AppTarget>/<FeatureName>/<ScreenName>Store.swift
import Foundation
import Trapezio
import TrapezioNavigation
import Strata
@MainActor
final class <ScreenName>Store: TrapezioStore<<ScreenName>Screen, <ScreenName>State, <ScreenName>Event> {
private let navigator: (any TrapezioNavigator)?
init(
screen: <ScreenName>Screen,
navigator: (any TrapezioNavigator)?
) {
self.navigator = navigator
super.init(screen: screen, initialState: <ScreenName>State())
setupBindings()
}
private func setupBindings() {
}
override func handle(event: <ScreenName>Event) {
switch event {
}
}
}
UI
Place at: <AppTarget>/<FeatureName>/<ScreenName>Ui.swift
import SwiftUI
import Trapezio
struct <ScreenName>UI: TrapezioUI {
func map(state: <ScreenName>State, onEvent: @escaping @MainActor (<ScreenName>Event) -> Void) -> some View {
}
}
Factory
Place at: <AppTarget>/<FeatureName>/<ScreenName>Factory.swift
import SwiftUI
import Trapezio
import TrapezioNavigation
struct <ScreenName>Factory {
@ViewBuilder @MainActor
static func make(screen: <ScreenName>Screen, navigator: (any TrapezioNavigator)?, interop: (any TrapezioInterop)?) -> some View {
TrapezioContainer(
makeStore: <ScreenName>Store(
screen: screen,
navigator: navigator
),
ui: <ScreenName>UI()
)
}
}
Step 5: Register in Navigation
Add the screen to the ContentView builder closure:
case let <name> as <ScreenName>Screen:
<ScreenName>Factory.make(screen: <name>, navigator: navigator, interop: interop)
Step 6: Generate Tests
Automatically generate test files following the conventions from the /add-tests skill:
Store Unit Test
For Counter app (XCTest):
import XCTest
import Trapezio
import TrapezioNavigation
@testable import <AppTarget>
@MainActor
final class <ScreenName>StoreTests: XCTestCase {
var store: <ScreenName>Store!
override func setUp() {
super.setUp()
let screen = <ScreenName>Screen()
store = <ScreenName>Store(
screen: screen,
navigator: nil
)
}
func test_initialState_isCorrect() {
}
}
For MESA library (Swift Testing) — use TrapezioTest utilities when testing navigation:
import Foundation
import Testing
import TrapezioTest
@testable import <Target>
@Suite("<ScreenName>Store")
struct <ScreenName>StoreTests {
@Test("initial state is correct")
@MainActor func initialState() {
let store = <ScreenName>Store(
screen: <ScreenName>Screen(),
navigator: nil
)
#expect(store.state == <ScreenName>State())
}
}
Step 7: License Headers
All generated files MUST include the Apache 2.0 license header:
Step 8: Verify
Run cd MESA && swift build to verify compilation.
Report:
- Which files were created
- Screen navigation arguments (if any)
Then ask:
- "Would you like to add interactors for this screen?" → suggest running
/add-interactor <feature-name> <InteractorName>