diff --git a/ROADMAP.md b/ROADMAP.md index 40f4e3d8..05cdd47b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -841,7 +841,7 @@ Completed Milestone 23 by adding the `cloud-inference-skills` child plugin, ship ## History -- Added repo-wide SwiftData and SwiftUI guidance that requires SwiftData-backed SwiftUI screens to stay directly driven by SwiftData's Apple-integrated data path, with regression coverage for every SwiftUI or SwiftData skill entrypoint and stale snippet wording. +- Re-contained SwiftData persistence guidance in a dedicated Apple Dev skill and SwiftUI composition in its architecture skill, while introducing the explicit three-letter Swift prefix and Xcode-friendly concatenated filename grammar. - Made Socket worktree-first for implementation work while keeping the base `main` checkout as the clean coordination and release-verification surface. - Aligned Socket documentation-source routing away from generic documentation aggregators by making Xcode MCP `DocumentationSearch` the Apple SDK default, Dash MCP/HTTP the preferred local-docs path for installed docsets across supported stacks, and canonical upstream docs/source the fallback when Dash/local coverage is missing or stale. - Added the first repo-local Socket Steward prototype under `.agents/socket-steward`, giving the superproject a Python and OpenAI Agents SDK maintainer-agent scaffold with offline docs, guidance, and marketplace audits before any write-capable or background-service behavior. diff --git a/plugins/agent-portability-skills/.codex-plugin/plugin.json b/plugins/agent-portability-skills/.codex-plugin/plugin.json index b0e56b5d..758a2655 100644 --- a/plugins/agent-portability-skills/.codex-plugin/plugin.json +++ b/plugins/agent-portability-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agent-portability-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Maintainer skills for Socket-owned agent skill portability, Codex plugin surfaces, and host adapter guidance.", "author": { "name": "Gale", diff --git a/plugins/agent-portability-skills/pyproject.toml b/plugins/agent-portability-skills/pyproject.toml index 824f8b18..5765006e 100644 --- a/plugins/agent-portability-skills/pyproject.toml +++ b/plugins/agent-portability-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "agent-portability-skills-maintenance" -version = "8.6.0" +version = "8.7.0" description = "Maintainer-only Python tooling baseline for Agent Portability Skills." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/agent-portability-skills/uv.lock b/plugins/agent-portability-skills/uv.lock index fc8b617b..6633183e 100644 --- a/plugins/agent-portability-skills/uv.lock +++ b/plugins/agent-portability-skills/uv.lock @@ -8,7 +8,7 @@ resolution-markers = [ [[package]] name = "agent-portability-skills-maintenance" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/agentdeck/.codex-plugin/plugin.json b/plugins/agentdeck/.codex-plugin/plugin.json index c64be0de..2ae988da 100644 --- a/plugins/agentdeck/.codex-plugin/plugin.json +++ b/plugins/agentdeck/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agentdeck", - "version": "8.6.0", + "version": "8.7.0", "description": "Local Codex runtime utilities for thread, hook, and app-server workflows.", "author": { "name": "Gale", diff --git a/plugins/android-dev-skills/.codex-plugin/plugin.json b/plugins/android-dev-skills/.codex-plugin/plugin.json index 487c947a..c836ea64 100644 --- a/plugins/android-dev-skills/.codex-plugin/plugin.json +++ b/plugins/android-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "android-dev-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Android, Kotlin, Java, Gradle, Android Gradle Plugin, testing, lint, UI implementation, and release-readiness workflow skills.", "author": { "name": "Gale", diff --git a/plugins/apple-dev-skills/.codex-plugin/plugin.json b/plugins/apple-dev-skills/.codex-plugin/plugin.json index d8999472..ef59d4f9 100644 --- a/plugins/apple-dev-skills/.codex-plugin/plugin.json +++ b/plugins/apple-dev-skills/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "apple-dev-skills", - "version": "8.6.0", - "description": "Apple development workflows for Codex, including media and audio repair, XcodeGen migration, Xcode coding intelligence, SwiftUI animation and architecture, Core Animation, Apple typography, SF Symbols, AppKit architecture, Icon Composer app icons, Safari extensions, DeviceCheck/App Attest, Swift OpenAPI clients, and DocC authoring guidance.", + "version": "8.7.0", + "description": "Apple development workflows for Codex, including SwiftData, SwiftUI architecture, media and audio repair, XcodeGen migration, Xcode coding intelligence, Core Animation, typography, SF Symbols, AppKit, Safari, DeviceCheck/App Attest, Swift OpenAPI clients, and DocC.", "author": { "name": "Gale", "email": "mail@galewilliams.com", @@ -71,6 +71,7 @@ "Design a DeviceCheck or App Attest flow for this Apple app, keeping DCDevice, DCAppAttestService, server challenges, entitlements, rollout, and backend validation handoffs explicit.", "Add or diagnose a generated Swift OpenAPI client in this Apple app using OpenAPIURLSession and current Apple docs.", "Help me build, test, or debug this Swift or Xcode project using the repo's Apple workflows.", + "Design, migrate, test, or integrate SwiftData persistence using the dedicated SwiftData workflow and current Apple documentation.", "Write or review DocC symbol comments, articles, extension files, and landing-page structure for this Swift repository.", "Sync the Apple project guidance in this repo without hand-editing Xcode project files, using Productivity Skills when repo-maintenance files must be installed or refreshed." ], diff --git a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh index c6d96522..8391804b 100644 --- a/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh +++ b/plugins/apple-dev-skills/.github/scripts/validate_repo_docs.sh @@ -126,6 +126,7 @@ active_skill_mds=( "./skills/devicecheck-app-attest-workflow/SKILL.md" "./skills/appkit-app-architecture-workflow/SKILL.md" "./skills/swiftui-app-architecture-workflow/SKILL.md" + "./skills/swiftdata-workflow/SKILL.md" "./skills/apple-ui-accessibility-workflow/SKILL.md" "./skills/explore-apple-swift-docs/SKILL.md" "./skills/format-swift-sources/SKILL.md" @@ -137,7 +138,7 @@ active_skill_mds=( "./skills/sync-swift-package-guidance/SKILL.md" "./skills/xcode-coding-intelligence-workflow/SKILL.md" ) -[[ ${#active_skill_mds[@]} -eq 30 ]] || fail "Expected exactly 30 active skills, found ${#active_skill_mds[@]}." +[[ ${#active_skill_mds[@]} -eq 31 ]] || fail "Expected exactly 31 active skills, found ${#active_skill_mds[@]}." shared_xcode_snippet="./shared/agents-snippets/apple-xcode-project-core.md" shared_package_snippet="./shared/agents-snippets/apple-swift-package-core.md" diff --git a/plugins/apple-dev-skills/README.md b/plugins/apple-dev-skills/README.md index 2e895a8e..64789c25 100644 --- a/plugins/apple-dev-skills/README.md +++ b/plugins/apple-dev-skills/README.md @@ -147,6 +147,7 @@ uv run pytest - `safari-extension-control-workflow` - `sf-symbols-workflow` - `structure-swift-sources` +- `swiftdata-workflow` - `swift-openapi-client-workflow` - `swift-package-build-run-workflow` - `swift-package-testing-workflow` diff --git a/plugins/apple-dev-skills/ROADMAP.md b/plugins/apple-dev-skills/ROADMAP.md index d5f1a059..12191b0b 100644 --- a/plugins/apple-dev-skills/ROADMAP.md +++ b/plugins/apple-dev-skills/ROADMAP.md @@ -1,5 +1,7 @@ # Project Roadmap +Swift naming and persistence ownership are now standardized: each project explicitly selects a three-letter prefix, project-owned Swift filenames use concatenated Xcode-friendly names without `+`, runtime/domain values use bare names, `Model` is reserved for persistence, and `swiftdata-workflow` owns SwiftData guidance. + ## Table of Contents - [Vision](#vision) @@ -783,7 +785,7 @@ Completed Milestone 53 by adding `devicecheck-app-attest-workflow`, focused refe - Tightened Xcode project guidance so tracked `.pbxproj` diffs produced by Xcode, XcodeGen, or other project-aware workflows are treated as critical project state that must be reviewed, staged, and committed before push, merge, release, or cleanup. - Updated standalone install guidance so `apple-dev-skills` defaults to Codex's Git-backed marketplace add/upgrade flow without an explicit ref, documents the optional `socket` marketplace path for Gale's broader plugin set, and keeps manual local clone marketplaces as development and fallback paths. - Tightened the Swift public API guidance across shared snippets, skill-local snippet copies, and generated `AGENTS.md` templates so public call sites default to streamlined typed APIs, optional defaulted parameters over overloads, request/options structs at four or more public parameters, and enum-backed choice modeling. -- Aligned Xcode app-project guidance around strict Apple-app MVVM source structure: view-local models beside views as `+Model.swift`, UIKit/AppKit controller support beside views as `+Controller.swift`, app-wide `@Observable` state in `App+ViewModel.swift`, directional `Services/Consumed`, `Services/Internal`, and `Services/Provided` folders, and no root `Controllers/` directory. +- Previously aligned Xcode app-project guidance around strict Apple-app MVVM source structure; the later three-letter-prefix migration superseded its paired-file naming with concatenated Xcode-friendly names while retaining directional service folders and no root `Controllers/` directory. - Added strict app-structure drift reporting to `sync-xcode-project-guidance` and updated the XcodeGen bootstrap scaffold so new SwiftUI app projects start with `Sources/Views/Shared`, `Sources/Views/macOS`, `Sources/Views/iOS`, `Sources/Models`, and directional `Sources/Services` folders. - Registered Xcode's built-in MCP bridge through the Codex plugin manifest so installed `apple-dev-skills` plugins expose the Xcode-owned MCP path without bundling a separate server package. - Added an experimental `xcode_lldb` MCP config entry for Xcode 27 beta-era `xcrun lldb-mcp` investigation while keeping the dedicated debugger workflow planned until startup validation succeeds. diff --git a/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md b/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md index f1930493..890d6297 100644 --- a/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md +++ b/plugins/apple-dev-skills/docs/maintainers/customization-consolidation-review.md @@ -8,8 +8,8 @@ Record the Milestone 20 audit of the current customization system, decide whethe ## Current State Summary -- The active skill surface ships `30` separate `references/customization.template.yaml` files. -- The active skill surface ships `30` separate `scripts/customization_config.py` entrypoints. +- The active skill surface ships `31` separate `references/customization.template.yaml` files. +- The active skill surface ships `31` separate `scripts/customization_config.py` entrypoints. - Those `customization_config.py` files are functionally identical and exist only because installed skills are expected to keep runtime resources inside the skill directory. - The current templates expose `21` knobs total: - `20` are documented as `runtime-enforced` diff --git a/plugins/apple-dev-skills/pyproject.toml b/plugins/apple-dev-skills/pyproject.toml index 665b3065..84d13ff3 100644 --- a/plugins/apple-dev-skills/pyproject.toml +++ b/plugins/apple-dev-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "apple-dev-skills-maintainer" -version = "8.6.0" +version = "8.7.0" description = "Maintainer tooling for the apple-dev-skills repository" requires-python = ">=3.10" dependencies = [] diff --git a/plugins/apple-dev-skills/shared/agents-snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/shared/agents-snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/shared/agents-snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/shared/agents-snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/shared/agents-snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/shared/agents-snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/shared/agents-snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/shared/agents-snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/SKILL.md b/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/SKILL.md index 12ae3f80..e1c9883f 100644 --- a/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide AppKit app-structure decisions for macOS apps across app dele # AppKit App Architecture Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Provide a docs-first workflow for AppKit app-structure decisions in macOS apps. @@ -21,7 +17,7 @@ accessibility workflow, and not the Xcode execution workflow. For Xcode app source layout, keep UIKit/AppKit controller support view-adjacent: `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` own view -surfaces, and controller support files are named `+Controller.swift` +surfaces, and controller support files use concatenated prefixed names such as `GEAWhateverViewController.swift` beside their matching view. Do not collect ordinary app controller support in a root `Controllers/` directory. @@ -125,7 +121,7 @@ root `Controllers/` directory. layers, or wrapper objects inserted between SwiftData and SwiftUI-owned screens - a root `Controllers/` directory used for ordinary view-controller support - instead of `+Controller.swift` beside the matching view + instead of `GEAWhateverViewController.swift` beside the matching view - app-wide runtime work hidden in a view controller - menu or status-item behavior hidden in a leaf view - responder-chain actions replaced by a broad command bus without a real need diff --git a/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/appkit-app-architecture-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/apple-typography-workflow/SKILL.md b/plugins/apple-dev-skills/skills/apple-typography-workflow/SKILL.md index 61bbcc46..fb4d33de 100644 --- a/plugins/apple-dev-skills/skills/apple-typography-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/apple-typography-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide Apple platform typography decisions and implementation across # Apple Typography Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill to choose, implement, repair, and validate Apple platform typography that respects system font behavior, Dynamic Type, accessibility, platform conventions, and custom-font bundle boundaries. diff --git a/plugins/apple-dev-skills/skills/apple-typography-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/apple-typography-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/apple-typography-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/apple-typography-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/SKILL.md b/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/SKILL.md index f739ba71..57ec6b77 100644 --- a/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide Apple UI accessibility implementation and review for SwiftUI- # Apple UI Accessibility Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Provide a docs-first workflow for Apple UI accessibility implementation and review. Keep the first version SwiftUI-first while still covering the UIKit and AppKit bridge surface when SwiftUI code wraps platform-native views or when an accessibility behavior only makes sense in those underlying frameworks. diff --git a/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/apple-ui-accessibility-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/avaudio-engine-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/avaudio-engine-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/avaudio-engine-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/avaudio-engine-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/avfaudio-session-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/avfaudio-session-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/avfaudio-session-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/avfaudio-session-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/avfoundation-media-pipeline-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/avfoundation-media-pipeline-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/avfoundation-media-pipeline-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/avfoundation-media-pipeline-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/bootstrap-swift-package/assets/AGENTS.md b/plugins/apple-dev-skills/skills/bootstrap-swift-package/assets/AGENTS.md index 7d8e85d7..19c17281 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-swift-package/assets/AGENTS.md +++ b/plugins/apple-dev-skills/skills/bootstrap-swift-package/assets/AGENTS.md @@ -100,7 +100,7 @@ - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. - Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. - Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/skills/bootstrap-swift-package/references/snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md index 4c693db9..b8681a4f 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/SKILL.md @@ -5,10 +5,6 @@ description: Bootstrap a new native Apple app project for macOS, iOS, or iPadOS # Bootstrap Xcode App Project -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Create a new native Apple app repository from nothing to a usable baseline on disk. The first implementation prioritizes a deterministic `XcodeGen` path for SwiftUI app projects and a guarded planning path for the standard Xcode-created-project flow. `scripts/run_workflow.py` is the runtime entrypoint, and `scripts/bootstrap_xcode_app_project.py` is the current implementation core for XcodeGen-backed scaffold creation plus `maintain-project-repo` installation with the `xcode-app` profile. @@ -70,10 +66,10 @@ This skill can be discovered from a standalone `apple-dev-skills` install, but i - create the standard top-level Xcode app layout: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/` - keep app-owned implementation/resources/support under `Sources/`, tests under `Tests/`, reusable app/extension source under `Shared/`, extension target roots under `Extensions/`, `.xcconfig` layers under `Configurations/`, project-local automation under `Scripts/`, and justified local Swift package boundaries under `Packages/` - inside `Sources/`, create the strict app structure: `Views/Shared`, `Views/macOS`, `Views/iOS`, `Models`, `Services/Consumed`, `Services/Internal`, and `Services/Provided` - - create the app entry point as `App.swift` unless the supplied app name already ends in `App`, then pair it with `App+ViewModel.swift` containing the app-wide `@Observable final class AppViewModel` - - create SwiftUI views under `Sources/Views/Shared`, with view-local view models beside their view as `+Model.swift` - - put the main app-wide service under `Sources/Services/Internal` as `AppService.swift` - - do not create a root `Controllers/` directory; UIKit and AppKit controller support belongs beside the matching view as `+Controller.swift` + - require an explicit `--file-prefix` containing three uppercase letters after offering reasonable initials-based suggestions + - create `GEAApp.swift` as the lifecycle entry, `GEA.swift` as the application runtime/domain value, and `GEAAppService.swift` as its main service + - create prefixed SwiftUI views and paired view models such as `GEAContentView.swift` and `GEAContentViewModel.swift` + - prefix every project-owned Swift file except `Package.swift`, externally generated Swift, and vendored third-party Swift; never generate `+` filenames - install `.codex/environments/xcode-project.toml` from `templates/codex-local-environments/xcode-project.toml` and replace the scheme placeholder with the generated app target name - keep the generated `project.yml` aligned with the current XcodeGen project spec concepts: project `options`, `configs`, `configFiles`, targets, sources, schemes, packages, project references, test-plan references, and `minimumXcodeGenVersion` - keep the generated `minimumXcodeGenVersion` on the recent validated baseline declared by the templates; when the baseline is raised, update the templates, docs, and tests together diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/assets/AGENTS.md b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/assets/AGENTS.md index fc39ffb6..dd89600f 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/assets/AGENTS.md +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/assets/AGENTS.md @@ -19,12 +19,11 @@ - Keep data flow straight and dependency direction unidirectional. - Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for app integration, schemes, and build settings. - Prefer Xcode-aware tooling or `xcodebuild` over ad hoc filesystem assumptions when project structure or target membership is involved. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` pairs with `WhateverNameApp+ViewModel.swift`, containing `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`; do not create or preserve a root `Controllers/` directory. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` filenames. Name views `GEAWhateverView.swift`, paired view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable components their own files; small private helpers may remain while they do not clutter focused editing. +- Prefix child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Use `GEAApp.swift` for the lifecycle entry, `GEA.swift` for the runtime/domain value, and `GEAAppService.swift` for its main service. - Use the standard top-level Xcode app repository layout: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/`. - `Sources/` owns the main app target implementation and app-owned resources/support files. `Tests/` owns all test targets. `Shared/` owns reusable source intended to be compiled into the app and extension targets. `Extensions/` owns extension target roots, one folder per extension. `Configurations/` owns `.xcconfig` layers. `Scripts/` owns project-local automation and build helper scripts. `Packages/` owns local Swift packages only when a real package boundary is justified. - Keep those top-level roots stable; do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos. @@ -32,7 +31,7 @@ - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` for shared, macOS-specific, and iOS/iPadOS-specific UI. - `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and shared transfer or persistence shapes. - `Sources/Services/` owns services by direction: `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. - For new Xcode app, framework, and workspace repositories, prefer XcodeGen plus synced source folders, checked-in `.xcconfig` files, and checked-in entitlement files by default unless there is a concrete reason to avoid that generator dependency. - If this repo is XcodeGen-backed, treat `project.yml`, `project.yaml`, and any included XcodeGen specs as the source of truth for generated targets, schemes, build settings, build configurations, Swift packages, test-plan references, and file membership. - For XcodeGen-backed projects, edit the spec set and rerun `xcodegen generate` instead of hand-editing generated `.pbxproj` files. diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/bootstrap_xcode_app_project.py b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/bootstrap_xcode_app_project.py index f91ad2e4..d085efdb 100755 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/bootstrap_xcode_app_project.py +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/bootstrap_xcode_app_project.py @@ -9,6 +9,7 @@ import argparse import json import os +import re import shutil import subprocess from pathlib import Path @@ -53,6 +54,7 @@ def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--name", required=True) + parser.add_argument("--file-prefix", required=True) parser.add_argument("--destination", required=True) parser.add_argument("--platform", required=True) parser.add_argument("--ui-stack", required=True) @@ -149,82 +151,75 @@ def install_local_environment(target_dir: Path, scheme_name: str) -> str: return str(target_path) -def app_entry_type_name(name: str) -> str: - return name if name.endswith("App") else f"{name}App" - - -def render_app_file(name: str) -> str: - app_type = app_entry_type_name(name) +def render_app_file(prefix: str) -> str: + app_type = f"{prefix}App" return f"""import SwiftUI @main struct {app_type}: App {{ - @State private var viewModel = {app_type}ViewModel(service: {app_type}Service()) + @State private var service = {prefix}AppService() var body: some Scene {{ WindowGroup {{ - ContentView() - .environment(viewModel) + {prefix}ContentView(viewModel: {prefix}ContentViewModel(service: service)) + .environment(service) }} }} }} """ -def render_app_view_model(name: str) -> str: - app_type = app_entry_type_name(name) - return f"""import Observation - -@Observable -final class {app_type}ViewModel {{ - let service: {app_type}Service - - init(service: {app_type}Service) {{ - self.service = service - }} +def render_app_domain(prefix: str) -> str: + return f"""struct {prefix} {{ + var title = "Hello, world!" }} """ +def render_app_service(prefix: str) -> str: + return f"""import Observation -def render_app_service(name: str) -> str: - app_type = app_entry_type_name(name) - return f"""struct {app_type}Service {{ - func bootstrapMessage() -> String {{ - "Hello, world!" - }} +@Observable +final class {prefix}AppService {{ + var app = {prefix}() }} """ -def render_content_view() -> str: - return """import SwiftUI +def render_content_view(prefix: str) -> str: + return f"""import SwiftUI -struct ContentView: View { - @State private var model = ContentViewModel() +struct {prefix}ContentView: View {{ + @State var viewModel: {prefix}ContentViewModel - var body: some View { - Text(model.title) + var body: some View {{ + Text(viewModel.title) .padding() - } -} + }} +}} """ -def render_content_view_model() -> str: - return """import Observation +def render_content_view_model(prefix: str) -> str: + return f"""import Observation @Observable -final class ContentViewModel { - var title = "Hello, world!" -} +final class {prefix}ContentViewModel {{ + private let service: {prefix}AppService + + var title: String {{ service.app.title }} + + init(service: {prefix}AppService) {{ + self.service = service + }} +}} """ -def render_test_file(name: str) -> str: +def render_test_file(name: str, prefix: str) -> str: return f"""import XCTest @testable import {name} -final class {name}Tests: XCTestCase {{ +final class {prefix}AppTests: XCTestCase {{ func testExample() throws {{ XCTAssertTrue(true) }} @@ -252,6 +247,7 @@ def main() -> int: target_dir = (Path(args.destination).expanduser() / args.name).resolve() normalized_inputs = { "name": args.name, + "file_prefix": args.file_prefix, "destination": args.destination, "platform": args.platform, "ui_stack": args.ui_stack, @@ -282,6 +278,16 @@ def main() -> int: print(json.dumps(payload, indent=2, sort_keys=True)) return 1 + if not re.fullmatch(r"[A-Z]{3}", args.file_prefix): + payload = blocked_payload( + target_dir, + normalized_inputs, + "Choose an explicit three-letter uppercase Swift file prefix and rerun the workflow.", + stderr="--file-prefix must contain exactly three uppercase ASCII letters.", + ) + print(json.dumps(payload, indent=2, sort_keys=True)) + return 1 + if target_dir.exists() and any(target_dir.iterdir()): payload = blocked_payload( target_dir, @@ -326,13 +332,13 @@ def main() -> int: print(json.dumps(payload, indent=2, sort_keys=True)) return 1 - app_type = app_entry_type_name(args.name) - write_text(target_dir / f"Sources/{app_type}.swift", render_app_file(args.name)) - write_text(target_dir / f"Sources/{app_type}+ViewModel.swift", render_app_view_model(args.name)) - write_text(target_dir / f"Sources/Services/Internal/{app_type}Service.swift", render_app_service(args.name)) - write_text(target_dir / "Sources/Views/Shared/ContentView.swift", render_content_view()) - write_text(target_dir / "Sources/Views/Shared/ContentView+Model.swift", render_content_view_model()) - write_text(target_dir / f"Tests/{args.name}Tests/{args.name}Tests.swift", render_test_file(args.name)) + prefix = args.file_prefix + write_text(target_dir / f"Sources/{prefix}App.swift", render_app_file(prefix)) + write_text(target_dir / f"Sources/{prefix}.swift", render_app_domain(prefix)) + write_text(target_dir / f"Sources/Services/Internal/{prefix}AppService.swift", render_app_service(prefix)) + write_text(target_dir / f"Sources/Views/Shared/{prefix}ContentView.swift", render_content_view(prefix)) + write_text(target_dir / f"Sources/Views/Shared/{prefix}ContentViewModel.swift", render_content_view_model(prefix)) + write_text(target_dir / f"Tests/{args.name}Tests/{prefix}AppTests.swift", render_test_file(args.name, prefix)) try: local_environment_path = install_local_environment(target_dir, args.name) except RuntimeError as exc: diff --git a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/run_workflow.py b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/run_workflow.py index d99cfb78..5820a548 100755 --- a/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/run_workflow.py +++ b/plugins/apple-dev-skills/skills/bootstrap-xcode-app-project/scripts/run_workflow.py @@ -87,6 +87,7 @@ def blocked_payload(normalized_inputs: dict, next_step: str, *, stderr: str = "" def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--name") + parser.add_argument("--file-prefix") parser.add_argument("--destination") parser.add_argument("--project-kind") parser.add_argument("--platform") @@ -105,6 +106,7 @@ def main() -> int: settings = config["settings"] name = args.name + file_prefix = args.file_prefix destination = args.destination or "." project_kind = args.project_kind or "app" platform_raw = args.platform or str(settings.get("defaultPlatform", "ask")) @@ -119,6 +121,7 @@ def main() -> int: normalized_inputs = { "name": name, + "file_prefix": file_prefix, "destination": destination, "project_kind": project_kind, "platform": platform_raw if platform is None else platform, @@ -134,6 +137,10 @@ def main() -> int: print(json.dumps(blocked_payload(normalized_inputs, "Provide --name to create a new Xcode app project."), indent=2, sort_keys=True)) return 1 + if not file_prefix: + print(json.dumps(blocked_payload(normalized_inputs, "Choose an explicit --file-prefix containing three uppercase letters."), indent=2, sort_keys=True)) + return 1 + if project_kind != "app": payload = blocked_payload( normalized_inputs, @@ -209,6 +216,8 @@ def main() -> int: str(helper_path), "--name", name, + "--file-prefix", + file_prefix, "--destination", destination, "--platform", diff --git a/plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md b/plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md index b9f0edc3..cab56466 100644 --- a/plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/core-animation-layer-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide Core Animation layer-backed rendering and animation decisions # Core Animation Layer Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill to decide when Core Animation is the right layer-backed rendering or animation surface, then guide implementation, repair, and validation without confusing layer ownership with SwiftUI, AppKit, or UIKit view ownership. diff --git a/plugins/apple-dev-skills/skills/core-animation-layer-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/core-animation-layer-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/core-animation-layer-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/core-animation-layer-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/coreaudio-modernization-repair-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/coremedia-timing-samplebuffer-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/coremedia-timing-samplebuffer-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/coremedia-timing-samplebuffer-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/coremedia-timing-samplebuffer-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/devicecheck-app-attest-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/devicecheck-app-attest-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/devicecheck-app-attest-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/devicecheck-app-attest-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/explore-apple-swift-docs/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/format-swift-sources/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/format-swift-sources/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/format-swift-sources/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/format-swift-sources/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/migrate-xcode-project-to-xcodegen/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/migrate-xcode-project-to-xcodegen/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/migrate-xcode-project-to-xcodegen/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/migrate-xcode-project-to-xcodegen/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/safari-extension-control-workflow/SKILL.md b/plugins/apple-dev-skills/skills/safari-extension-control-workflow/SKILL.md index 9d0660d1..59c3aaf1 100644 --- a/plugins/apple-dev-skills/skills/safari-extension-control-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/safari-extension-control-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide macOS Safari integration decisions across Safari Web Extensio # Safari Extension Control Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Guide Safari integration work for macOS apps, with enough platform awareness to choose the right Safari extension or SafariServices surface before implementation starts. diff --git a/plugins/apple-dev-skills/skills/safari-extension-control-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/safari-extension-control-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/safari-extension-control-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/safari-extension-control-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/sf-symbols-workflow/SKILL.md b/plugins/apple-dev-skills/skills/sf-symbols-workflow/SKILL.md index 4e28ce64..585e849b 100644 --- a/plugins/apple-dev-skills/skills/sf-symbols-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/sf-symbols-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide SF Symbols selection, inspection, customization, rendering mo # SF Symbols Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill to choose, inspect, customize, validate, and integrate SF Symbols in Apple app UI. The skill owns symbol selection, SF Symbols app inspection, rendering-mode decisions, custom-symbol workflow, symbol animation routing, and accessibility semantics. diff --git a/plugins/apple-dev-skills/skills/sf-symbols-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/sf-symbols-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/sf-symbols-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/sf-symbols-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md b/plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md index afaddacf..7397739f 100644 --- a/plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md +++ b/plugins/apple-dev-skills/skills/structure-swift-sources/SKILL.md @@ -5,10 +5,6 @@ description: Organize Swift source trees and oversized Swift files by feature, l # Structure Swift Sources -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill as the top-level workflow for structural cleanup inside existing Swift repositories. It governs file splitting, file moves, section grouping, plain-language file headers, and TODO or FIXME ledger extraction. `scripts/run_workflow.py` is the runtime wrapper for repo-shape detection, cleanup-kind classification, header-policy loading, split-threshold loading, and clean handoffs to DocC or Xcode execution workflows. It is not the formatter or linter integration authority, and it is not the DocC authoring authority. Use `format-swift-sources` before this skill starts mutating source layout, use `author-swift-docc-docs` when the request becomes symbol-doc or DocC-content work, and run `format-swift-sources` again after this skill finishes. @@ -55,7 +51,9 @@ Use this skill as the top-level workflow for structural cleanup inside existing 5. Apply the structure rules: - strongly consider splitting a file once it exceeds the configured soft split threshold and clearly holds `2` or more separate concerns - always split a file once it exceeds the configured hard split threshold - - when the underlying type is still one coherent type, extract grouped concerns into extension files such as `+Models.swift` or `+.swift` + - require one explicit three-letter uppercase prefix for every project-owned Swift source file and primary declaration + - never infer a different prefix after project setup; ask the user or agent to choose explicitly from reasonable initials-based suggestions + - when a coherent type needs an extracted concern, concatenate the concern after the owning type, such as `GEAWhateverServiceAdapter.swift`; do not use `+` filenames - add `// MARK:` groups only when a file is large enough or varied enough that the grouping materially improves navigation, concern ownership, or declaration discovery - skip `// MARK:` groups entirely when a short file or an already-obvious declaration run does not present meaningful navigation ambiguity - when groups are warranted, use explicit `// MARK: - ` sections that name a real responsibility boundary instead of restating declaration kinds or symbol names in slightly different words @@ -71,10 +69,11 @@ Use this skill as the top-level workflow for structural cleanup inside existing - for Swift packages, prefer directories grouped by layer and feature, such as `API//.swift` and `Features//.swift` - for Xcode app projects, ensure important app-facing source directories such as `Views/`, `Models/`, and `Services/`, and do not preserve a root `Controllers/` directory - for SwiftUI views, keep view files in `Views/Shared`, `Views/macOS`, or `Views/iOS`, require exactly one SwiftUI `View` component per file, and keep that component's Xcode SwiftUI preview in the same file - - when splitting grouped SwiftUI views, create one `.swift` file per view component, require any SwiftUI view model for that component to live in `+Model.swift`, and place view-specific modifiers in `+Modifier.swift` - - for UIKit and AppKit view-controller support, use `+Controller.swift` beside the matching view - - for app-wide `@Observable` state, use `App+ViewModel.swift` beside `App.swift` - - for services, use `Services/Consumed`, `Services/Internal`, and `Services/Provided`, with the main app-wide service under `Services/Internal` as `AppService.swift` + - hand SwiftUI component, view-model, and modifier composition decisions to `swiftui-app-architecture-workflow` + - hand SwiftData persistence naming and integration decisions to `swiftdata-workflow` + - for services, use `Services/Consumed`, `Services/Internal`, and `Services/Provided`; a service named `GEAWhateverService.swift` manages the runtime/domain value `GEAWhatever.swift` + - reserve `Model` for persistence representations; use `Record` and `DTO` only when additional representations are genuinely needed + - treat `Package.swift`, externally generated Swift, and vendored third-party Swift as the only default filename-prefix exceptions 7. Finish with `format-swift-sources` again so the moved or split files end in a normalized state. ## Inputs diff --git a/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-header-inventory.template.yaml b/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-header-inventory.template.yaml index 22cbb50a..14c19532 100644 --- a/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-header-inventory.template.yaml +++ b/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-header-inventory.template.yaml @@ -6,5 +6,5 @@ entries: - "FeatureView" - "FeatureState" see_also: - - "FeatureView+Model.swift" - - "FeatureView+Modifier.swift" + - "GEAFeatureViewModel.swift" + - "GEAFeatureViewModifier.swift" diff --git a/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-headers.md b/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-headers.md index 622602ef..e2939ba8 100644 --- a/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-headers.md +++ b/plugins/apple-dev-skills/skills/structure-swift-sources/references/file-headers.md @@ -77,8 +77,8 @@ entries: - "FeatureView" - "FeatureState" see_also: - - "FeatureView+Model.swift" - - "FeatureView+Modifier.swift" + - "GEAFeatureViewModel.swift" + - "GEAFeatureViewModifier.swift" ``` `key_types` and `see_also` may be omitted when they do not add signal. diff --git a/plugins/apple-dev-skills/skills/structure-swift-sources/references/layout-rules.md b/plugins/apple-dev-skills/skills/structure-swift-sources/references/layout-rules.md index e0ade9be..ad465fae 100644 --- a/plugins/apple-dev-skills/skills/structure-swift-sources/references/layout-rules.md +++ b/plugins/apple-dev-skills/skills/structure-swift-sources/references/layout-rules.md @@ -1,5 +1,13 @@ # Layout Rules +## Project Prefix + +- Choose and record one explicit three-letter uppercase prefix for every Swift app or package. +- Suggest app or package initials, or `G` plus two product initials, but require the user or agent to make the final choice. +- Prefix every project-owned Swift filename and its primary declaration. Do not silently derive a new prefix after setup. +- Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift by default. +- Retire `+` filenames completely because they weaken navigator consistency and interfere with Xcode rename and refactoring workflows. + ## Swift Packages - Prefer directories grouped by both layer and feature when the package has real feature boundaries. @@ -15,16 +23,12 @@ - Do not create or preserve a root `Controllers/` directory for ordinary app structure. - `Sources/Views/` must contain `Shared/`, `macOS/`, and `iOS/` subdirectories. - Keep SwiftUI views and UIKit/AppKit view surfaces in `Views/` or the appropriate platform/shared subdirectory. -- Require one SwiftUI `View` component per file, named `.swift`, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one file; split related child views into their own files instead. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` beside the view and must not be shared with any other SwiftUI view. -- Do not use grouped model files, shared view-model files, or view-cluster models for SwiftUI views. -- When a view needs extracted modifiers, use `+Modifier.swift`. -- UIKit and AppKit view-controller support files must live beside their matching view as `+Controller.swift`. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` pairs with `WhateverNameApp+ViewModel.swift`. +- Follow `swiftui-app-architecture-workflow` for component extraction and paired `View`, `ViewModel`, and `ViewModifier` filenames. +- Use concatenated ownership names such as `GEASettingsSheetToggleCard.swift` and `GEAWhateverServiceAdapter.swift`. +- Use `GEAWhatever.swift` for the runtime/domain value, `GEAWhateverModel.swift` for persistence, and `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. - `Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and shared transfer or persistence shapes. - `Services/` owns app-wide and boundary-facing services with `Consumed/`, `Internal/`, and `Provided/` subdirectories. -- Put the main app-wide service, when present, under `Services/Internal/` as `WhateverNameAppService.swift`. +- Put the main app-wide service under `Services/Internal/` as `GEAAppService.swift`; it may manage `GEA.swift` as the application runtime/domain value while `GEAApp.swift` remains the lifecycle-entry special case. - Move files into their proper layer directory rather than leaving them flat at the project root. ## Shared Rule diff --git a/plugins/apple-dev-skills/skills/structure-swift-sources/references/source-organization-rules.md b/plugins/apple-dev-skills/skills/structure-swift-sources/references/source-organization-rules.md index 67289977..fe58ddab 100644 --- a/plugins/apple-dev-skills/skills/structure-swift-sources/references/source-organization-rules.md +++ b/plugins/apple-dev-skills/skills/structure-swift-sources/references/source-organization-rules.md @@ -8,13 +8,8 @@ ## Split Shape -- If one type is still coherent but too large, extract grouped responsibilities into extension files. -- Prefer names such as: - - `+Models.swift` - - `+Persistence.swift` - - `+Validation.swift` - - `+Modifier.swift` -- Do not use `+Models.swift` for SwiftUI view models. SwiftUI view models use the per-view `+Model.swift` rule instead. +- If one type is still coherent but too large, extract grouped responsibilities into files whose names concatenate the owner and concern, such as `GEAWhateverServiceAdapter.swift`, `GEAWhateverPersistence.swift`, or `GEAWhateverValidation.swift`. +- Do not use `+` in project-owned Swift filenames. - Treat Swift access control as part of the design; confirm the extracted file shape still supports the intended visibility. ## MARK Rules @@ -35,22 +30,15 @@ ## SwiftUI Rule -- Require exactly one SwiftUI `View` component per file. Do not group multiple `View` component types in one Swift file, even when the views are small, private, nested, or part of the same feature. -- Name each view file after its component, such as `.swift`, and keep that component's Xcode SwiftUI preview in the same file. -- SwiftUI view models are always per-view, with no exceptions: if `.swift` has a view model, it must live beside the view in `+Model.swift` and belong only to that one `View` component. -- Do not share one SwiftUI view model across multiple views, view families, screens, flows, or view clusters, and do not collect multiple SwiftUI view models in one shared model file. -- When an existing file contains multiple SwiftUI view components, split it into one file per view before adding more behavior. -- Once any one view has more than `3` chained modifiers, strongly consider extracting a custom `ViewModifier`. -- Place that modifier in `+Modifier.swift` when the modifier belongs to one view family. +- Hand SwiftUI component, view-model, and modifier organization to `swiftui-app-architecture-workflow`; this skill enforces only the shared prefix and concatenated filename grammar. ## Xcode App MVVM Rule - Use strict Apple-app MVVM for Xcode app source layout: views own their own view-local state and actions where feasible, and every view model or controller support file is paired with one owning view or app entry point. - Keep `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` as the default UI roots. -- Place UIKit and AppKit view-controller support beside the matching view as `+Controller.swift`; do not collect controllers in `Sources/Controllers`. -- Place app-wide `@Observable` state beside the app entry point as `WhateverNameApp+ViewModel.swift`, containing `@Observable final class WhateverNameAppViewModel`. +- Place UIKit and AppKit view-controller support beside the matching view with a concatenated name such as `GEAWhateverViewController.swift`; do not collect controllers in `Sources/Controllers`. - Put Core Data persistence models, SwiftData `@Model` types, datamodels, DTOs, and app-wide transfer or persistence shapes in `Sources/Models`. -- Put services in `Sources/Services/Consumed`, `Sources/Services/Internal`, or `Sources/Services/Provided` according to direction. Main app-wide services belong in `Sources/Services/Internal` as `WhateverNameAppService.swift`. +- Put services in `Sources/Services/Consumed`, `Sources/Services/Internal`, or `Sources/Services/Provided` according to direction. `GEAWhateverService.swift` manages `GEAWhatever.swift`; `GEAAppService.swift` may manage `GEA.swift`. ## Documentation Boundary diff --git a/plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md b/plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md index 870f1a8b..484839b5 100644 --- a/plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/swift-openapi-client-workflow/SKILL.md @@ -11,10 +11,6 @@ allowed-tools: Read Bash(rg:*) Bash(git:*) Bash(swift:*) Bash(find:*) Bash(sqlit # Swift OpenAPI Client Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Add or diagnose generated Swift OpenAPI clients in Apple-platform code without confusing app-side networking with server-side transport work. diff --git a/plugins/apple-dev-skills/skills/swift-package-build-run-workflow/references/snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/skills/swift-package-build-run-workflow/references/snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/skills/swift-package-build-run-workflow/references/snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/skills/swift-package-build-run-workflow/references/snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/skills/swift-package-testing-workflow/references/snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/skills/swift-package-testing-workflow/references/snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/skills/swift-package-testing-workflow/references/snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/skills/swift-package-testing-workflow/references/snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/skills/swift-package-workflow/references/snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/skills/swift-package-workflow/references/snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/skills/swift-package-workflow/references/snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/skills/swift-package-workflow/references/snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/SKILL.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/SKILL.md new file mode 100644 index 00000000..adfbaf89 --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/SKILL.md @@ -0,0 +1,64 @@ +--- +name: swiftdata-workflow +description: Design, implement, migrate, test, and integrate SwiftData persistence in Apple apps using current Apple documentation. Use for @Model schemas, ModelContainer and ModelContext ownership, @Query integration, relationships, uniqueness, deletion, concurrency, migrations, preview or test stores, and boundaries between persistent models, runtime domain values, records, and DTOs. +--- + +# SwiftData Workflow + +## Purpose + +Own SwiftData persistence decisions without spreading SwiftData policy across unrelated Apple skills. Start from current Apple documentation, keep persistence types distinct from runtime/domain values, and hand view composition or general source-tree work to their focused owners. + +## When To Use + +Use for SwiftData schemas, containers, contexts, queries, relationships, migrations, previews, tests, concurrency, and SwiftUI persistence integration. + +## Single-Path Workflow + +1. Read the relevant Apple SwiftData documentation through `explore-apple-swift-docs` before proposing implementation. +2. Classify the task as schema design, container/context ownership, query and mutation, relationship behavior, migration, testing/previews, or external-boundary conversion. +3. Read the matching reference: + - `references/models-containers-and-contexts.md` + - `references/swiftui-integration.md` + - `references/migrations-testing-and-boundaries.md` +4. Use the project's explicit three-letter prefix. Name runtime/domain values `GEAWhatever.swift`, SwiftData `@Model` persistence types `GEAWhateverModel.swift`, and additional `Record` or `DTO` representations only when a real boundary requires them. +5. Keep SwiftData directly integrated with SwiftUI through `modelContainer`, environment `modelContext`, `@Query`, model objects, and narrow bindings. Do not insert repositories, stores, service mirrors, DTO mirrors, or view-model caches between SwiftData and SwiftUI. +6. Add a separate boundary only for a non-SwiftUI concern such as networking, import/export, migration tooling, server sync, or isolated tests. +7. Hand view ownership and view-model composition to `swiftui-app-architecture-workflow`, filename and directory cleanup to `structure-swift-sources`, execution to `xcode-build-run-workflow`, and tests to `xcode-testing-workflow`. + +## Inputs + +- repository and platform context +- persistence task and existing schema state +- selected three-letter project prefix +- migration and compatibility constraints + +## Outputs + +Return the documented behavior, schema and ownership decision, naming decision, migration or compatibility impact, validation path, and any required handoff. + +## Guards and Stop Conditions + +- Reserve `Model` for persistence representations. +- Do not use `State` as a filename or type suffix for ordinary runtime/domain values. +- Do not use `+` filenames. +- Do not hide `ModelContext` concurrency or lifecycle assumptions. +- Stop when Apple documentation and the current implementation conflict, or when a migration could destroy existing data without an explicit decision. + +## Fallbacks and Handoffs + +- Hand Apple documentation lookup to `explore-apple-swift-docs`. +- Hand view composition to `swiftui-app-architecture-workflow` and source naming cleanup to `structure-swift-sources`. +- Hand build or test execution to the focused Xcode workflows. + +## Customization + +Use `references/customization-flow.md`. The first version has no runtime-enforced knobs; `scripts/customization_config.py` preserves the shared configuration contract. + +## References + +- `references/models-containers-and-contexts.md` +- `references/swiftui-integration.md` +- `references/migrations-testing-and-boundaries.md` +- `references/customization-flow.md` +- Recommend `references/snippets/apple-xcode-project-core.md` when the user needs reusable repository policy rather than a one-off SwiftData decision. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/agents/openai.yaml b/plugins/apple-dev-skills/skills/swiftdata-workflow/agents/openai.yaml new file mode 100644 index 00000000..7b44e16e --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "SwiftData Workflow" + short_description: "Design and maintain SwiftData persistence" + default_prompt: "Use $swiftdata-workflow to design, implement, migrate, test, or integrate SwiftData persistence using current Apple documentation." diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization-flow.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization-flow.md new file mode 100644 index 00000000..0c9fb1e8 --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization-flow.md @@ -0,0 +1,5 @@ +# SwiftData Workflow Customization Contract + +The first version defines no runtime-enforced knobs. `scripts/customization_config.py` preserves the shared Apple Dev Skills customization contract; future settings must be documented here and in `SKILL.md` before runtime behavior depends on them. + +Inspect settings with `scripts/customization_config.py effective`, persist a documented change with `apply --input `, and verify the effective result afterward. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization.template.yaml b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization.template.yaml new file mode 100644 index 00000000..cddd82d1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/customization.template.yaml @@ -0,0 +1,3 @@ +schemaVersion: 1 +isCustomized: false +settings: {} diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/migrations-testing-and-boundaries.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/migrations-testing-and-boundaries.md new file mode 100644 index 00000000..95cf531e --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/migrations-testing-and-boundaries.md @@ -0,0 +1,7 @@ +# Migrations, Testing, And Boundaries + +- Treat schema changes as data-compatibility work and identify the existing store versions before mutation. +- Use explicit migration plans when lightweight evolution is insufficient; never assume destructive recreation is acceptable. +- Use isolated in-memory or temporary containers for previews and tests when persistence across runs is not part of the test. +- Use `GEAWhateverDTO` for network or transfer boundaries and `GEAWhateverRecord` only for a genuinely distinct stored or serialized representation. +- Keep import/export, networking, server sync, and migration tooling outside the direct SwiftUI integration path while converting explicitly at the boundary. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/models-containers-and-contexts.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/models-containers-and-contexts.md new file mode 100644 index 00000000..ee419be1 --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/models-containers-and-contexts.md @@ -0,0 +1,8 @@ +# Models, Containers, And Contexts + +- Use `GEAWhateverModel` only for a SwiftData persistence representation and keep the declaration in `GEAWhateverModel.swift`. +- Use `GEAWhatever` for the corresponding runtime/domain value when one is needed. Do not create a duplicate domain representation automatically. +- Define relationships, delete behavior, uniqueness, defaults, and optionality deliberately from documented SwiftData behavior. +- Place `ModelContainer` ownership at the app or scene boundary that owns the persistent lifetime. +- Keep each `ModelContext` on its intended actor and make cross-context or background work explicit. +- Let `GEAAppService` manage `GEA` when the app needs a main runtime/domain value; `GEAApp.swift` remains the lifecycle-entry naming exception. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/snippets/apple-xcode-project-core.md new file mode 100644 index 00000000..56f3f97a --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/snippets/apple-xcode-project-core.md @@ -0,0 +1,133 @@ +# Apple Xcode Project Core AGENTS Snippet + +Use this snippet in repository `AGENTS.md` files when you want baseline standards for an existing native Apple app project managed through Xcode. + +## General Swift Baseline + +- For any Swift, Apple-framework, Apple-platform, SwiftUI, SwiftData, Observation, AppKit, UIKit, Foundation-on-Apple, or Xcode-related task, read the relevant Apple documentation first before planning, proposing, or making changes. +- Use Xcode MCP `DocumentationSearch` or Xcode-local documentation first for Apple-owned SDK, framework, lifecycle, and Xcode behavior; use Dash MCP or Dash HTTP next when installed local package docs or multi-ecosystem docs are a better fit; use open source Swift project repositories, generated DocC, or release notes when the relevant Swift package or tool is open source and available there; use official Apple web docs only when the page content is actually readable through a capable source. Generic no-JS web search/open results, snippets, metadata shells, or bare Apple Developer URLs are not enough evidence that Apple docs were read. +- Before proposing an architecture or implementation, state the documented API behavior, lifecycle rule, or workflow requirement being relied on. +- Do not rely on memory, habit, or analogy as the primary source when Apple documentation exists. +- If Apple documentation and the current code disagree, stop and report the conflict before continuing. +- If no relevant Apple documentation can be found, say that explicitly before proceeding. +- Prefer the simplest correct Swift that is easiest to read, reason about, and maintain. +- Treat idiomatic Swift, Cocoa conventions, and modern Swift features as tools in service of readability, not as goals by themselves. +- Do not add ceremony, abstraction, or boilerplate just to make code look more architectural, more generic, or more "Swifty". +- Strongly prefer synthesized, implicit, and framework-provided behavior over custom code. +- Prefer synthesized conformances (`Codable`, `Equatable`, `Hashable`, etc.) whenever they satisfy the actual requirements. +- Prefer memberwise and otherwise synthesized initializers, default property values, and framework defaults over handwritten setup code. +- Do not add `CodingKeys`, manual `Codable` methods, custom initializers, wrappers, helper types, protocols, coordinators, or extra layers unless they are required by a concrete constraint or they make the final code clearly easier to understand. +- Prefer applicable existing framework or platform error types before inventing custom error wrappers or error hierarchies. +- Prefer direct, simple error flows and small focused error enums only when they materially improve understanding. +- Prefer stable, source-of-truth naming across layers when the data and meaning have not changed. +- Treat naming consistency as a reliability feature: if the same data still serves the same purpose, keep the same name. +- Do not rename fields just to match local style conventions when the external schema is already clear and stable. +- Do not use automatic case-conversion strategies such as `.convertFromSnakeCase` or `.convertToSnakeCase` unless the project explicitly wants that behavior and it clearly improves readability overall. +- When an API, cloud service, or wire format already provides clear names, preserve those names directly in Swift models and nearby code unless the meaning actually changes or a concrete collision must be resolved. +- Preserve raw wire and persistence shapes by default; do not add DTO, domain, or view-model conversion layers unless meaning actually changes or a concrete boundary requires it. +- Treat redundant wrappers, rename-and-copy layers, and duplicated logic as anti-patterns by default. +- This guidance is optimized for an advanced Swift reader and may prefer dense but readable modern Swift over beginner-style explicitness. +- Prefer explicit names that are consistent, unambiguous, and easy to scan at the call site. +- For public Swift APIs, treat streamlined, compact, ergonomic call sites as the only acceptable default; do not grow method families, overload sets, or loosely typed entry points when one clear typed API can express the operation. +- Prefer optional parameters with explicit default values over additional methods or overloads whenever the difference is optional behavior on the same operation. +- When a public function, initializer, or method reaches four or more arguments or parameters, strongly prefer a named typed `struct` request, options, or configuration value so call sites stay readable and future additions do not multiply overloads. +- Prefer enums, enum cases with associated values, and narrow typed values over strings, booleans, sentinel values, or parallel parameters whenever the domain has a closed or meaningful set of choices. +- Prefer compact syntax when it improves local reasoning, including shorthand syntax, ternary expressions, trailing closures, enums, `switch`, `map`, `filter`, `forEach`, async iteration, `AsyncSequence`, `AsyncStream`, and `AsyncAlgorithms`. +- Prefer explicit default values at initialization when they reduce optional-handling clutter and keep the code easier to follow. +- When lines, chains, or expressions get long, prefer chopping them down into a clean vertical, top-down structure with straight visual flow. +- Do not force value types by default, protocols at seams, actors by default, or other pattern slogans when a plainer concrete implementation is easier to reason about. +- Keep code compliant with Swift 6 language mode. +- Keep strict concurrency checking enabled. +- Prefer modern structured concurrency (`async`/`await`, task groups, actors) over legacy async patterns when it keeps the flow clearer and more direct. +- Make async code cancellation-aware and keep actor or task boundaries explicit instead of hiding them behind detached tasks or queue wrappers. +- Prefer clear `Sendable` boundaries for values that cross task or actor isolation, and keep unchecked sendability exceptional and justified locally. +- Prefer Swift Testing (`import Testing`) as the default test framework, and use XCTest only when a dependency or platform constraint requires it. +- Prefer Swift Testing for unit-style and package-style test surfaces in modern Xcode projects, including suites, tags, parameterized tests, and direct async tests. +- Use XCTest when the platform surface, dependency graph, or Apple tooling still expects it, and keep XCTest and Swift Testing responsibilities clearly separated when both coexist. +- Use XCUITest for UI automation, and prefer explicit element wait APIs such as `waitForExistence(timeout:)`, `waitForNonExistence(timeout:)`, and related state waits over fixed sleeps. +- Keep `.xctestplan` files versioned when test configurations, diagnostics, sanitizers, locale coverage, or selective plan execution matter, and inspect or run them explicitly with `xcodebuild -showTestPlans` and `xcodebuild -testPlan ...`. +- Prefer normal Xcode and XCTest parallel execution for ordinary Swift Testing, XCTest, and XCUITest runs when the project, scheme, destination, and test plan support it. Do not serialize regular tests just because they use Swift, XCTest, async tests, UI automation, or `.xctestplan` matrices. +- Treat tests that load large local AI or ML models, especially models over 500 million parameters, as heavy system-resource tests. Run those tests sequentially, one at a time, and call `unload_models` on Gale's live TTS service before the heavy run and `reload_models` after it ends, even when the run fails or is interrupted. +- Prefer first-party and top-tier Swift ecosystem packages from Apple, `swiftlang`, the Swift Server Work Group, and similarly trusted core Swift projects when they simplify the code and make it easier to reason about. +- Commonly approved examples include `swift-configuration` and `swift-async-algorithms` when they reduce bespoke code and improve readability. +- For Apple app projects, prefer Apple-native logging facilities first and allow Swift Logging where it makes the project API clearer. +- Prefer Swift OpenTelemetry for telemetry and instrumentation when telemetry is needed, and prefer existing ecosystem integrations over bespoke wrappers. +- Prefer a checked-in repo-root `.swiftformat` file as the default Swift formatting source of truth, and prefer a pre-commit hook that formats staged Swift sources and then verifies them with `swiftformat --lint` before commit. +- Treat SwiftLint as an optional complementary signal layer for clarity, safety, and maintainability after SwiftFormat owns formatting shape. +- Keep automation and CI commands deterministic, non-interactive, and explicit about toolchain, platform, and configuration assumptions. + +## SwiftUI and State Architecture + +- Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. +- Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. +- Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. +- Keep updates to view-driving state minimal and localized. +- Prefer durable identity for types that drive SwiftUI state and view updates. +- Treat `App` as the application entry and scene composition boundary, `Scene` as the container for scene-specific lifecycle and environment, and `View` as the component rendering layer. +- Every native app target must have exactly one app lifecycle entry point: one `@main` app type, one `main.swift`, or the platform-equivalent single launch entry. Do not add alternate app entry points, second `@main` types, duplicate `main.swift` files, target-specific app entry files, or parallel app structs for variants. When launch behavior must differ by platform, configuration, or feature flag, keep the single entry point and use Swift conditional compilation or ordinary runtime conditionals inside that boundary. +- Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. +- Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. +- Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. +- Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. +- Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. +- Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. + +## Xcode Workspace and Project Baseline + +- Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for Apple platform app integration, schemes, build settings, destinations, and target membership. +- Prefer edits through Xcode-aware project structure and keep project file changes intentional and reviewed closely. +- Use the standard top-level Xcode app repository layout when creating or normalizing native app repos: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/`. +- `Sources/` owns the main app target implementation and app-owned resources/support files. `Tests/` owns all test targets. `Shared/` owns reusable source intended to be compiled into the app and extension targets. `Extensions/` owns extension target roots, one folder per extension. `Configurations/` owns `.xcconfig` layers. `Scripts/` owns project-local automation and build helper scripts. `Packages/` owns local Swift packages only when a real package boundary is justified. +- Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. +- Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. +- `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. +- `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. +- Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. +- Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. +- For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. +- When scripts or terminal workflows add files on disk, verify that Xcode project membership, target membership, build-phase membership, and resource-bundle inclusion all match the intended result; files appearing in the directory tree alone are not enough. +- Direct filesystem edits outside `.pbxproj` are generally safe when Xcode is closed or when the current project is not open in Xcode, but still verify that the Xcode project picks up the intended files and memberships afterward. +- Prefer Debug builds for everyday edit-build-test loops, but validate Release builds explicitly when optimization, packaging, launch behavior, watchdog timing, or deployment realism matters. +- Treat tagged releases as a signal to validate both the normal Debug path and a Release artifact path, and when shipping apps or deliverables test the Release behavior without relying on an attached debugger. +- Prefer direct filesystem edits in Xcode-managed scope only when the workflow already accounts for project-file and scheme integrity. +- Never edit `.pbxproj` files directly. If a project-file change is needed and no safe project-aware tool is available, stop and ask for an Xcode-mediated project change instead. When `.pbxproj` is tracked and Xcode, XcodeGen, or another project-aware workflow legitimately changes it, treat that diff as critical project state: review it, stage it, and commit it with the branch before any push, merge, release, or cleanup. + +## XcodeGen and Build Configuration Defaults + +- For new Xcode app, framework, and workspace repositories, prefer an XcodeGen-backed project by default unless the user explicitly asks for a hand-managed Xcode project or the repository has a concrete reason to avoid a generator dependency. +- If the repo contains `project.yml`, `project.yaml`, or clearly named included XcodeGen spec files, treat the XcodeGen spec set as the source of truth for generated project structure. +- For XcodeGen-backed repos, make target membership, resource membership, schemes, Swift package declarations, test-plan references, project references, build configurations, configuration-file wiring, generation options, and project-level settings in the XcodeGen specs instead of editing the generated `.pbxproj`. +- Before running `xcodegen generate`, inspect the current git diff for generated `.xcodeproj` or `.pbxproj` changes. Treat existing project-file diffs as intentional user or Xcode GUI changes by default, not disposable generator drift. +- When Xcode GUI changes added build settings, signing settings, capabilities, `Info.plist` build setting overrides, file membership, scheme changes, or entitlement wiring to `.pbxproj`, preserve the user intent by moving each intentional value to the owning tracked source first: XcodeGen spec for structure, `.xcconfig` for build settings, `.entitlements` for entitlement keys, `Info.plist` for plist keys, `.xcscheme` or scheme spec for scheme behavior, and `.xctestplan` for test-plan content. +- Only regenerate after that promotion is complete, then review the generated project diff to confirm XcodeGen preserved the intended behavior instead of deleting it. If the owning tracked file is ambiguous, stop and ask before regenerating. +- For new XcodeGen-backed app scaffolds, start from the maintained `apple-dev-skills/templates/xcodegen/` templates when available instead of inventing a fresh project-spec shape from memory. +- Keep `minimumXcodeGenVersion` on a recent validated release for new scaffolds. Prefer updating the template and validation together when the repo intentionally raises the baseline. +- For Xcode 16 or newer project formats, prefer XcodeGen `syncedFolder` roots at the broad top-level directory boundary so file creation, deletion, and organization stay synchronized between Xcode and the filesystem without hand-listing every source file in YAML. +- Do not fragment ordinary XcodeGen source roots by subdirectory. A standard app target gets one `Sources` source entry that includes all app source, resource, support, generated plist, entitlement, and nested feature folders, plus one `Shared` source entry when shared app/extension code exists. A standard test target gets one `Tests` source entry that includes all test subdirectories. Extension targets use one `Extensions/` source entry per extension target. If a project has another separate top-level logical root, use one top-level entry for that root, not one entry per child folder. +- Never split `Sources/App`, `Sources/Resources`, `Sources/Support`, feature folders, or `Tests/Tests` into separate XcodeGen source entries unless a specific non-ordinary file or folder truly needs custom compiler flags, build-phase routing, destination filters, or target membership that cannot be represented from the broad root. +- If `syncedFolder` behaves poorly for a repo, fall back to the same broad top-level recursive paths such as `Sources`, `Tests`, or `Resources` with explicit `includes` and `excludes`; do not fall back to subdirectory-level fragmentation or one YAML entry per ordinary source file. +- Keep XcodeGen specs readable as project structure, not as a dumping ground for every build setting. Use `configs`, `configFiles`, `targets`, `schemes`, `packages`, `projectReferences`, `targetTemplates`, and `schemeTemplates` deliberately so future edits have an obvious owner. +- Prefer explicit top-level schemes for app scaffolds once scheme behavior matters. Put build, run, test, profile, analyze, archive, environment variables, command-line arguments, and test-plan references in the scheme spec rather than relying on hidden generated defaults. +- Prefer external `.xcconfig` files as the default home for nontrivial build settings. Keep build settings in XcodeGen inline settings only when they are small, local, and clearer there. +- Use `.xcconfig` files for settings that vary by Debug, Release, CI, local development, signing, bundle identity, compiler flags, Swift settings, deployment variants, or environment-specific behavior. +- Keep configuration layering explicit. Prefer a small shared base config, target-level configs for app/test/extension identity, then per-configuration configs that include the narrower target config and override only what changes. +- In XcodeGen specs, wire build configurations to their matching `.xcconfig` files instead of duplicating the same settings across generated project objects. +- Prefer checked-in external `.entitlements` files for app, extension, and capability-bearing targets, with `CODE_SIGN_ENTITLEMENTS` declared in the owning target's `.xcconfig`. Let Xcode capabilities update the entitlement plist when possible, then review and commit the entitlement diff; keep XcodeGen responsible for wiring the file, not regenerating its contents from inline YAML. +- Do not assume Xcode's Build Settings UI writes edited values back into `.xcconfig` files. When a build setting should remain tracked in `.xcconfig`, inspect the generated project diff after GUI changes and move intentional build-setting overrides from `.pbxproj` back into the owning `.xcconfig` before regenerating. +- Keep secrets, personal team IDs, local machine paths, provisioning profiles, API tokens, and private signing material out of committed `.xcconfig` files. Use build settings only for non-secret configuration values, safe placeholders, references to externally supplied values, or local developer placeholders that are safe to commit. +- Before changing generated project structure, inspect the root spec plus any `include` entries so the edit lands in the owning spec rather than duplicating settings in the wrong file. Remember that included specs merge into the root spec, and local overrides may intentionally replace arrays or maps. +- After changing XcodeGen specs, `.xcconfig` files, or entitlement-file wiring, run `xcodegen generate` from the spec root, or `xcodegen generate --spec ` when the project uses a non-default spec path. +- If the spec uses environment variables or generation hooks, preserve and document the required environment before regenerating so CI and other contributors can reproduce the project. +- Review the spec diff, `.xcconfig` diff, and generated `.xcodeproj` diff after regeneration. Generated `.pbxproj` changes are acceptable output when they come from XcodeGen, but they should still be reviewed for unintended target, scheme, signing, package, build-setting, or file-membership churn. +- Validate regenerated projects with explicit `xcodebuild` commands for the affected scheme, destination or SDK, and configuration. +- For existing hand-managed Xcode projects, do not migrate to XcodeGen or externalize build settings into `.xcconfig` files unless the user explicitly asks for that migration. When they do, treat it as a project-structure migration with before/after validation. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/references/swiftui-integration.md b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/swiftui-integration.md new file mode 100644 index 00000000..0cf59392 --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/references/swiftui-integration.md @@ -0,0 +1,7 @@ +# SwiftUI Integration + +- Attach the container with SwiftUI's `modelContainer` integration. +- Read the environment `modelContext`, use `@Query` for live collections, and pass SwiftData model objects or narrow bindings through the view tree. +- Do not put a repository, store, service mirror, DTO mirror, or view-model cache between SwiftData and SwiftUI. +- A paired `GEAWhateverViewModel` may drive presentation state for `GEAWhateverView`, but it must not mirror or cache SwiftData merely to avoid the framework's direct integration. +- Hand component extraction and view naming to `swiftui-app-architecture-workflow`. diff --git a/plugins/apple-dev-skills/skills/swiftdata-workflow/scripts/customization_config.py b/plugins/apple-dev-skills/skills/swiftdata-workflow/scripts/customization_config.py new file mode 100755 index 00000000..021a927e --- /dev/null +++ b/plugins/apple-dev-skills/skills/swiftdata-workflow/scripts/customization_config.py @@ -0,0 +1,213 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.9" +# dependencies = [ +# "PyYAML>=6.0.2,<7", +# ] +# /// +"""Load and persist per-skill customization state.""" + +from __future__ import annotations + +import argparse +import copy +import os +import re +import sys +from pathlib import Path + +import yaml + +SCHEMA_VERSION = 1 +SKILL_NAME = "swiftdata-workflow" +CONFIG_HOME_ENV = "APPLE_DEV_SKILLS_CONFIG_HOME" +DEFAULT_CONFIG_ROOT = "~/.config/gaelic-ghost/apple-dev-skills" +ALLOWED_TOP_LEVEL = {"schemaVersion", "isCustomized", "settings"} + + +def fail(message: str) -> None: + print(f"ERROR: {message}", file=sys.stderr) + raise SystemExit(1) + + +def quote_string(value: str) -> str: + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +def encode_scalar(value) -> str: + if isinstance(value, bool): + return "true" if value else "false" + if isinstance(value, int): + return str(value) + if value is None: + return quote_string("") + return quote_string(str(value)) + + +def parse_yaml(path: Path) -> dict: + if not path.exists(): + fail(f"Missing YAML file: {path}") + + try: + loaded = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: + fail(f"Invalid YAML in {path}: {exc}") + + if loaded is None: + return {} + if not isinstance(loaded, dict): + fail(f"Top-level YAML document must be a mapping in {path}") + + if isinstance(loaded.get("settings"), dict): + loaded["settings"] = { + key: ("" if value is None else value) for key, value in loaded["settings"].items() + } + + return loaded + + +def validate_config(config: dict, *, allow_partial: bool) -> None: + unknown = set(config.keys()) - ALLOWED_TOP_LEVEL + if unknown: + fail(f"Unknown top-level keys: {', '.join(sorted(unknown))}") + + if not allow_partial: + for required in ("schemaVersion", "isCustomized", "settings"): + if required not in config: + fail(f"Missing required key: {required}") + + if "schemaVersion" in config and config["schemaVersion"] != SCHEMA_VERSION: + fail(f"schemaVersion must be {SCHEMA_VERSION}") + + if "isCustomized" in config and not isinstance(config["isCustomized"], bool): + fail("isCustomized must be boolean") + + if "settings" in config: + if not isinstance(config["settings"], dict): + fail("settings must be a mapping") + for key, value in config["settings"].items(): + if not re.fullmatch(r"[A-Za-z0-9_]+", key): + fail(f"Invalid settings key: {key}") + if isinstance(value, (dict, list)): + fail(f"settings values must be scalar: {key}") + + +def merge_configs(base: dict, overlay: dict) -> dict: + merged = { + "schemaVersion": base.get("schemaVersion", SCHEMA_VERSION), + "isCustomized": base.get("isCustomized", False), + "settings": copy.deepcopy(base.get("settings", {})), + } + + if "schemaVersion" in overlay: + merged["schemaVersion"] = overlay["schemaVersion"] + if "isCustomized" in overlay: + merged["isCustomized"] = overlay["isCustomized"] + if "settings" in overlay: + merged["settings"].update(overlay["settings"]) + + return merged + + +def dump_yaml(config: dict) -> str: + lines = [ + f"schemaVersion: {int(config['schemaVersion'])}", + f"isCustomized: {'true' if config['isCustomized'] else 'false'}", + "settings:", + ] + for key in sorted(config["settings"].keys()): + lines.append(f" {key}: {encode_scalar(config['settings'][key])}") + return "\n".join(lines) + "\n" + + +def template_path() -> Path: + return Path(__file__).resolve().parents[1] / "references" / "customization.template.yaml" + + +def config_root() -> Path: + root = os.environ.get(CONFIG_HOME_ENV, DEFAULT_CONFIG_ROOT) + return Path(root).expanduser() + + +def durable_path() -> Path: + return config_root() / SKILL_NAME / "customization.yaml" + + +def load_template() -> dict: + cfg = parse_yaml(template_path()) + validate_config(cfg, allow_partial=False) + return cfg + + +def load_durable() -> dict: + path = durable_path() + if not path.exists(): + return {} + cfg = parse_yaml(path) + validate_config(cfg, allow_partial=False) + return cfg + + +def cmd_path(_: argparse.Namespace) -> None: + print(durable_path()) + + +def cmd_effective(_: argparse.Namespace) -> None: + effective = merge_configs(load_template(), load_durable()) + validate_config(effective, allow_partial=False) + print(dump_yaml(effective), end="") + + +def cmd_apply(args: argparse.Namespace) -> None: + template = load_template() + current = merge_configs(template, load_durable()) + incoming = parse_yaml(Path(args.input)) + validate_config(incoming, allow_partial=True) + + updated = merge_configs(current, incoming) + updated["schemaVersion"] = SCHEMA_VERSION + updated["isCustomized"] = True + validate_config(updated, allow_partial=False) + + target = durable_path() + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(dump_yaml(updated), encoding="utf-8") + print(target) + + +def cmd_reset(_: argparse.Namespace) -> None: + target = durable_path() + if target.exists(): + target.unlink() + print(target) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Manage per-skill customization config") + subparsers = parser.add_subparsers(dest="command", required=True) + + parser_path = subparsers.add_parser("path", help="Print durable config path") + parser_path.set_defaults(func=cmd_path) + + parser_effective = subparsers.add_parser("effective", help="Print merged effective config") + parser_effective.set_defaults(func=cmd_effective) + + parser_apply = subparsers.add_parser("apply", help="Apply and persist config overrides") + parser_apply.add_argument("--input", required=True, help="Path to YAML overrides") + parser_apply.set_defaults(func=cmd_apply) + + parser_reset = subparsers.add_parser("reset", help="Delete durable config for this skill") + parser_reset.set_defaults(func=cmd_reset) + + return parser + + +def main() -> None: + parser = build_parser() + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/plugins/apple-dev-skills/skills/swiftui-animation-workflow/SKILL.md b/plugins/apple-dev-skills/skills/swiftui-animation-workflow/SKILL.md index 61196737..0bdf680e 100644 --- a/plugins/apple-dev-skills/skills/swiftui-animation-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/swiftui-animation-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide SwiftUI animation design, implementation, repair, and validat # SwiftUI Animation Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill to design, implement, repair, and validate SwiftUI motion that stays state-driven, accessible, and grounded in Apple documentation. The skill owns SwiftUI animation selection, transition decisions, phase/keyframe motion, symbol-effect routing, reduce-motion checks, and preview or runtime validation handoffs. diff --git a/plugins/apple-dev-skills/skills/swiftui-animation-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/swiftui-animation-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/swiftui-animation-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/swiftui-animation-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/SKILL.md b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/SKILL.md index 5bb8fed9..afc146ed 100644 --- a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide SwiftUI app-structure decisions for Apple apps across `App`, # SwiftUI App Architecture Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Provide a docs-first workflow for SwiftUI app-structure decisions in Apple apps. This skill owns ownership-boundary guidance, transport-choice guidance, focused-context guidance, and anti-pattern correction for SwiftUI app composition across scenes, commands, focus, environment, preferences, and reusable view structure. @@ -17,20 +13,20 @@ It is not the Apple-docs router, not the accessibility workflow, and not the Xco ## SwiftUI View File Rule -Each SwiftUI `View` component must live in its own Swift file named for that view, and that file must carry the view's own Xcode SwiftUI preview. Do not group multiple `View` component types in one file, even when the views are small, related, nested, or currently used only by one parent. Split them into separate files so Xcode previews remain discoverable, isolated, and reliable. +Use the project's explicit three-letter prefix for every project-owned view file and declaration. Name a view `GEAWhateverView.swift`, its paired `@Observable final class` view model `GEAWhateverViewModel.swift`, and an extracted custom modifier `GEAWhateverViewModifier.swift`. Never use `+` filenames. -SwiftUI view models are always per-view, with no exceptions. If a SwiftUI view has a view model, that model belongs to exactly that `View` component and must live beside the matching view in the matching `+Model.swift` file. Do not share a SwiftUI view model across multiple views, view families, screens, flows, or view clusters, and do not place multiple SwiftUI view models in one shared model file. +A view component that is complex enough to edit or preview independently must have its own file. Simple private computed view properties and small private helper views may remain in the owning file while they keep that component easy to preview, navigate, and edit. Extract them as soon as they clutter that workflow. -Keep supporting code in explicit paired files instead of bundling extra view types together: use `+Model.swift` for that view's model, `+Modifier.swift` for view-specific modifiers, and other narrowly named support files when needed. A file may contain private helper values or small non-`View` helpers for that one component, but it must not contain another SwiftUI `View` component. +Name an extracted child from its complete composition owner: a toggle card inside `GEASettingsSheetView.swift` becomes `GEASettingsSheetToggleCard.swift`. Continue that complete stem for paired support such as `GEASettingsSheetToggleCardViewModel.swift`. This rule also applies outside views, such as `GEAWhateverServiceAdapter.swift`. -For Xcode app projects, use strict Apple-app MVVM source layout: `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` own UI, `Sources/Models` owns persistence and transfer shapes, and `Sources/Services/Consumed`, `Sources/Services/Internal`, and `Sources/Services/Provided` own app services by direction. App-wide `@Observable` state lives beside the app entry point in `App+ViewModel.swift`, containing `@Observable final class AppViewModel`. UIKit and AppKit view-controller support lives beside the matching view as `+Controller.swift`; do not introduce a root `Controllers/` directory. +Extract a custom `ViewModifier` when a view accumulates more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. Keep view models associated with exactly one view and let them source the presentation data that directly drives that view. Runtime/domain values use bare names such as `GEAWhatever`; persistence `Model` naming belongs to `swiftdata-workflow`. ## When To Use - Use this skill when the user wants help structuring a SwiftUI app across `App`, `Scene`, `WindowGroup`, `Window`, `Settings`, or `DocumentGroup`. - Use this skill when the user wants help deciding where app-level, scene-level, and view-level responsibilities belong. - Use this skill when the user wants help choosing between explicit dependency injection, environment values, focused values, scene-focused values, preference keys, bindings, or local state. -- Use this skill when a SwiftUI app uses SwiftData and the agent needs to keep SwiftData directly driving SwiftUI through Apple's data-driven UI integration instead of adding repositories, stores, mirrored state, or view-model cache layers. +- Hand SwiftData persistence and integration decisions to `swiftdata-workflow` while retaining ownership of the view composition around that data. - Use this skill when the user wants help with `FocusState`, `focusable`, focus scopes, focus sections, default focus, focused objects, or other focused-context design that changes ownership or data-flow choices. - Use this skill when the user wants help with command ownership, command menus, command groups, focused command handling, or desktop-oriented SwiftUI command surfaces. - Use this skill when the user wants help cleaning up giant root views, wrapper-heavy architecture, environment abuse, hidden control flow in modifiers, or state scattering in SwiftUI code. @@ -68,7 +64,7 @@ For Xcode app projects, use strict Apple-app MVVM source layout: `Sources/Views/ - view-tree-level - local view 4. Choose the transport that fits the responsibility: - - SwiftData's direct SwiftUI path: `modelContainer` at the app or scene boundary, environment `modelContext`, `@Query`, SwiftData model objects, and narrow bindings + - the SwiftData path selected by `swiftdata-workflow` - explicit initializer injection - `Binding` - environment value @@ -88,10 +84,10 @@ For Xcode app projects, use strict Apple-app MVVM source layout: `Sources/Views/ - giant root views with unrelated lifecycle, command, and rendering concerns mixed together - wrapper-heavy layers added only to look architectural - control flow hidden in modifiers that obscure who owns the action - - multiple SwiftUI `View` component types grouped into one file, especially when that prevents one file-local Xcode preview per component - - one SwiftUI view model shared across a view cluster or stored outside the matching `+Model.swift` file - - a root `Controllers/` directory used for UIKit or AppKit view-controller support instead of `+Controller.swift` beside the matching view - - app-wide state hidden in a service, leaf view, or shared environment object instead of an app-entry `App+ViewModel.swift` + - independently editable or previewable components buried inside a larger view file + - a view model shared across a view cluster or stored outside its matching `GEAWhateverViewModel.swift` file + - `+` filenames or child-component names that omit their composition owner + - long modifier chains left inline after they obscure the view body 6. Return one recommendation path with: - the ownership boundary - the chosen transport @@ -131,7 +127,7 @@ For Xcode app projects, use strict Apple-app MVVM source layout: `Sources/Views/ ## Guards and Stop Conditions - Do not recommend environment values as a default substitute for explicit dependency flow. -- When SwiftData backs a SwiftUI surface, do not recommend any data-access abstraction between SwiftData and SwiftUI. SwiftData should directly drive SwiftUI through `modelContainer`, environment `modelContext`, `@Query`, model objects, and bindings; separate boundaries are only for non-SwiftUI concerns such as import/export, networking, migration tooling, tests, or server sync. +- Hand SwiftData-specific architecture to `swiftdata-workflow` instead of duplicating its persistence rules here. - Do not recommend preference keys for ordinary downward or lateral data flow. - Do not collapse commands, focus, and scene ownership into a single shared mutable object just because it is easy to wire. - Do not present a giant root view or extra wrapper layer as architectural improvement unless it clearly removes a real ownership problem. diff --git a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/anti-patterns-and-corrections.md b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/anti-patterns-and-corrections.md index 3d2705df..f562ef84 100644 --- a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/anti-patterns-and-corrections.md +++ b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/anti-patterns-and-corrections.md @@ -33,7 +33,7 @@ Correction: - split each SwiftUI `View` component into its own `.swift` file - keep that component's Xcode SwiftUI preview in the same file as the component -- move view-local models, modifiers, and support code into explicit paired files such as `+Model.swift` and `+Modifier.swift` +- move view-local models, modifiers, and support code into explicit paired files such as `GEAWhateverViewModel.swift` and `GEAWhateverViewModifier.swift` ## Shared SwiftUI View Models @@ -45,7 +45,7 @@ Bad shape: Correction: - make SwiftUI view models per-view only -- put the model for `.swift` in `+Model.swift` +- put the view model for `GEAWhateverView.swift` in `GEAWhateverViewModel.swift` - split shared view-model state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer ## Wrapper-Heavy Architecture diff --git a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/swiftui-app-architecture-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/sync-swift-package-guidance/references/snippets/apple-swift-package-core.md b/plugins/apple-dev-skills/skills/sync-swift-package-guidance/references/snippets/apple-swift-package-core.md index a95b7655..260cf479 100644 --- a/plugins/apple-dev-skills/skills/sync-swift-package-guidance/references/snippets/apple-swift-package-core.md +++ b/plugins/apple-dev-skills/skills/sync-swift-package-guidance/references/snippets/apple-swift-package-core.md @@ -57,7 +57,8 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## Swift Package and Tooling Baseline - Use Swift Package Manager as the source of truth for package structure and dependencies outside Xcode-managed app workflows. -- Swift packages that support a SwiftData-backed SwiftUI app must not introduce a repository, store, service, DTO mirror, or view-model cache layer between SwiftData and SwiftUI. Keep reusable package code focused on non-UI boundaries, pure helpers, import/export, migration tooling, sync adapters, or tests while SwiftUI app screens stay directly driven by SwiftData's `ModelContainer`, environment `modelContext`, `@Query`, model objects, and bindings. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift; never use `+` filenames. +- Use the dedicated SwiftData workflow when a package supports SwiftData persistence or a SwiftData-backed SwiftUI app. - Prefer `swift package` subcommands for dependency, target, and manifest-adjacent changes before hand-editing `Package.swift`. - Edit `Package.swift` intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible. - Keep `Package.swift` explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer `swiftLanguageModes: [.v6]` as the default declaration, treat `swiftLanguageVersions` as a legacy alias used only when an older manifest surface requires it, and keep the supported Swift toolchain window focused on the latest stable minor and previous stable minor. Treat Swift `6.2` as the current minimum floor for trait-enabled manifests, not as a ceiling; use newer stable Swift toolchains when available and validated, and refresh this guidance when the maintained floor or window changes. Do not lower `// swift-tools-version:` below `6.2` without an explicit repo policy and a matching guidance update. diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/SKILL.md b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/SKILL.md index 831fdaf9..09cb85fe 100644 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/SKILL.md +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/SKILL.md @@ -5,10 +5,6 @@ description: Sync repo guidance for an existing native Apple app repository mana # Sync Xcode Project Guidance -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Bring an existing Xcode app repository up to the expected guidance baseline without overloading the main Xcode execution skill. This skill owns repo-guidance alignment for existing Apple app repos, including deterministic `AGENTS.md` creation or bounded section append behavior, and runs `maintain-project-repo` with the `xcode-app` profile alongside that guidance. `scripts/run_workflow.py` is the runtime entrypoint, and `scripts/sync_xcode_project_guidance.py` applies the current sync behavior. @@ -62,8 +58,8 @@ This skill can be discovered from a standalone `apple-dev-skills` install, but i - missing `Sources/Views/Shared`, `Sources/Views/macOS`, `Sources/Views/iOS`, `Sources/Models`, `Sources/Services/Consumed`, `Sources/Services/Internal`, or `Sources/Services/Provided` - legacy `Sources/Controllers` - unpaired view-model files - - `+Model.swift` or `+Controller.swift` files outside `Sources/Views` - - missing app-entry `App+ViewModel.swift` + - project-owned Swift files missing the selected three-letter prefix + - any project-owned `+` filename - missing internal app service when the app declares a strict app entry point 6. Apply the sync path: - if `AGENTS.md` is missing, copy `assets/AGENTS.md` diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/AGENTS.md b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/AGENTS.md index ffc8ee3f..3e2f5dee 100644 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/AGENTS.md +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/AGENTS.md @@ -21,12 +21,11 @@ - Keep data flow straight and dependency direction unidirectional. - Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for app integration, schemes, and build settings. - Prefer Xcode-aware tooling or `xcodebuild` over ad hoc filesystem assumptions when project structure or target membership is involved. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` pairs with `WhateverNameApp+ViewModel.swift`, containing `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`; do not create or preserve a root `Controllers/` directory. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations. Exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` filenames. Name views `GEAWhateverView.swift`, paired view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable components their own files; small private helpers may remain while they do not clutter focused editing. +- Prefix child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Use `GEAApp.swift` for the lifecycle entry, `GEA.swift` for the runtime/domain value, and `GEAAppService.swift` for its main service. - Use the standard top-level Xcode app repository layout: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/`. - `Sources/` owns the main app target implementation and app-owned resources/support files. `Tests/` owns all test targets. `Shared/` owns reusable source intended to be compiled into the app and extension targets. `Extensions/` owns extension target roots, one folder per extension. `Configurations/` owns `.xcconfig` layers. `Scripts/` owns project-local automation and build helper scripts. `Packages/` owns local Swift packages only when a real package boundary is justified. - Keep those top-level roots stable; do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos. @@ -34,7 +33,7 @@ - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` for shared, macOS-specific, and iOS/iPadOS-specific UI. - `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and shared transfer or persistence shapes. - `Sources/Services/` owns services by direction: `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. - For new Xcode app, framework, and workspace repositories, prefer XcodeGen plus synced source folders, checked-in `.xcconfig` files, and checked-in entitlement files by default unless there is a concrete reason to avoid that generator dependency. - If this repo is XcodeGen-backed, treat `project.yml`, `project.yaml`, and any included XcodeGen specs as the source of truth for generated targets, schemes, build settings, build configurations, Swift packages, test-plan references, and file membership. - For XcodeGen-backed projects, edit the spec set and rerun `xcodegen generate` instead of hand-editing generated `.pbxproj` files. diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/append-section.md b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/append-section.md index 76817fb2..9f21f408 100644 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/append-section.md +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/assets/append-section.md @@ -19,12 +19,13 @@ - Keep data flow straight and dependency direction unidirectional. - Treat the `.xcworkspace` or `.xcodeproj` as the source of truth for app integration, schemes, and build settings. - Prefer Xcode-aware tooling or `xcodebuild` over ad hoc filesystem assumptions when project structure or target membership is involved. +- Choose and record one explicit three-letter uppercase prefix for project-owned Swift files and declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. - Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. - Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` pairs with `WhateverNameApp+ViewModel.swift`, containing `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`; do not create or preserve a root `Controllers/` directory. +- Use `GEAApp.swift` for the lifecycle entry, `GEA.swift` for the application runtime/domain value, and `GEAAppService.swift` for its main service. +- Put UIKit and AppKit view-controller support beside the matching view with a concatenated prefixed name such as `GEAWhateverViewController.swift`. - Use the standard top-level Xcode app repository layout: `Sources/`, `Tests/`, `Shared/`, `Extensions/`, `Configurations/`, `Scripts/`, and `Packages/`. - `Sources/` owns the main app target implementation and app-owned resources/support files. `Tests/` owns all test targets. `Shared/` owns reusable source intended to be compiled into the app and extension targets. `Extensions/` owns extension target roots, one folder per extension. `Configurations/` owns `.xcconfig` layers. `Scripts/` owns project-local automation and build helper scripts. `Packages/` owns local Swift packages only when a real package boundary is justified. - Keep those top-level roots stable; do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos. diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/run_workflow.py b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/run_workflow.py index e99739e5..b8072248 100755 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/run_workflow.py +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/run_workflow.py @@ -59,7 +59,7 @@ def audit_xcode_app_structure(repo_root: Path) -> dict: { "code": "legacy-controllers-directory", "path": "Sources/Controllers", - "message": "Move UIKit/AppKit controller files beside their matching view under Sources/Views as +Controller.swift.", + "message": "Move UIKit/AppKit controller files beside their matching view under Sources/Views with a concatenated prefixed name such as GEAWhateverViewController.swift.", } ) return { diff --git a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/sync_xcode_project_guidance.py b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/sync_xcode_project_guidance.py index ecdffb8e..003b4d64 100755 --- a/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/sync_xcode_project_guidance.py +++ b/plugins/apple-dev-skills/skills/sync-xcode-project-guidance/scripts/sync_xcode_project_guidance.py @@ -22,9 +22,9 @@ "Scripts/repo-maintenance/sync-shared.sh", "Scripts/repo-maintenance/release.sh", "Sources/Views/Shared", - "Sources/Services/Internal", - "WhateverNameApp+ViewModel.swift", - "+Controller.swift", + "Sources/Services/", + "three-letter uppercase prefix", + "GEAAppService.swift", ] REQUIRED_XCODE_APP_DIRECTORIES = [ @@ -171,59 +171,65 @@ def audit_xcode_app_structure(repo_root: Path) -> dict: { "code": "legacy-controllers-directory", "path": "Sources/Controllers", - "message": "Move UIKit/AppKit controller files beside their matching view under Sources/Views as +Controller.swift.", + "message": "Move UIKit/AppKit controller files beside their matching view under Sources/Views with a concatenated prefixed name such as GEAWhateverViewController.swift.", } ) sources_dir = repo_root / "Sources" if sources_dir.is_dir(): + for swift_path in sorted(sources_dir.rglob("*.swift")): + if len(swift_path.stem) < 3 or not swift_path.stem[:3].isalpha() or not swift_path.stem[:3].isupper(): + relative_path = swift_path.relative_to(repo_root).as_posix() + findings.append({ + "code": "missing-swift-file-prefix", + "path": relative_path, + "message": "Project-owned Swift files must start with the selected three-letter uppercase prefix.", + }) + if "+" in swift_path.name: + relative_path = swift_path.relative_to(repo_root).as_posix() + findings.append({ + "code": "legacy-plus-filename", + "path": relative_path, + "message": "Retire the + filename and concatenate the owner and concern for Xcode-friendly navigation and refactoring.", + }) + for view_model_path in sorted(sources_dir.rglob("*ViewModel.swift")): - if view_model_path.name.endswith("+ViewModel.swift"): + if "Sources/Views/" in view_model_path.relative_to(repo_root).as_posix(): continue relative_path = view_model_path.relative_to(repo_root).as_posix() findings.append( { "code": "unpaired-view-model-file", "path": relative_path, - "message": "View model files should be paired with their owning app or view as +ViewModel.swift or +Model.swift.", + "message": "View models belong beside their owning view under Sources/Views with a name such as GEAWhateverViewModel.swift.", } ) - for model_path in sorted(sources_dir.rglob("*+Model.swift")): - if "Sources/Views/" not in model_path.relative_to(repo_root).as_posix(): - relative_path = model_path.relative_to(repo_root).as_posix() - findings.append( - { - "code": "view-model-outside-views", - "path": relative_path, - "message": "View-local +Model.swift files should live beside their matching view under Sources/Views.", - } - ) - - for controller_path in sorted(sources_dir.rglob("*+Controller.swift")): + for controller_path in sorted(sources_dir.rglob("*ViewController.swift")): if "Sources/Views/" not in controller_path.relative_to(repo_root).as_posix(): relative_path = controller_path.relative_to(repo_root).as_posix() findings.append( { "code": "controller-outside-views", "path": relative_path, - "message": "UIKit/AppKit +Controller.swift files should live beside their matching view under Sources/Views.", + "message": "UIKit/AppKit GEAWhateverViewController.swift files should live beside their matching view under Sources/Views.", } ) app_files = [ path for path in sorted(sources_dir.glob("*App.swift")) - if path.is_file() and not path.name.endswith("+ViewModel.swift") + if path.is_file() ] for app_path in app_files: - paired_model = app_path.with_name(f"{app_path.stem}+ViewModel.swift") - if not paired_model.is_file(): - relative_path = paired_model.relative_to(repo_root).as_posix() + prefix = app_path.stem[:-3] + domain_path = app_path.with_name(f"{prefix}.swift") + if not domain_path.is_file(): + relative_path = domain_path.relative_to(repo_root).as_posix() findings.append( { - "code": "missing-app-view-model", + "code": "missing-app-domain-value", "path": relative_path, - "message": "App-wide @Observable state should live beside the app entry point as App+ViewModel.swift.", + "message": "The app lifecycle entry should pair with its bare runtime/domain value, such as GEAApp.swift and GEA.swift.", } ) @@ -235,7 +241,7 @@ def audit_xcode_app_structure(repo_root: Path) -> dict: { "code": "missing-internal-app-service", "path": "Sources/Services/Internal", - "message": "When the app has a main app-wide service, place it under Sources/Services/Internal as AppService.swift.", + "message": "Place the main app service under Sources/Services/Internal with a name such as GEAAppService.swift.", } ) diff --git a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md index fb7613ff..874eb419 100644 --- a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Compatibility workflow surface for broad or legacy Xcode execution # Xcode App Project Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill as a compatibility surface for older references to `xcode-app-project-workflow` while the repo transitions to narrower Xcode execution skills. The real long-term owners are `xcode-build-run-workflow` for build, run, diagnostics, toolchain, and guarded mutation work and `xcode-testing-workflow` for Swift Testing, XCTest, XCUITest, and `.xctestplan` work. `scripts/run_workflow.py` is intentionally routing-only now: it infers enough workspace context to choose the real owner and preserves the direct `.pbxproj` warning boundary so older flows continue to work during the migration window. diff --git a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/xcode-app-project-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/SKILL.md b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/SKILL.md index 45a87e3d..b9f0ad9b 100644 --- a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide build, run, preview-adjacent, workspace-inspection, diagnosti # Xcode Build Run Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill as the primary execution workflow for non-testing work in or around Xcode-managed projects and workspaces. Keep it focused on workspace inspection, read/search diagnostics, builds, runs, previews, toolchain management, file membership, Release-versus-Debug validation, and the `.pbxproj` warning boundary. `scripts/run_workflow.py` is the runtime entrypoint for MCP-first build/run execution, official CLI fallback planning, and direct `.pbxproj` warning enforcement. diff --git a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/xcode-build-run-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/xcode-coding-intelligence-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/xcode-coding-intelligence-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/xcode-coding-intelligence-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/xcode-coding-intelligence-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/skills/xcode-testing-workflow/SKILL.md b/plugins/apple-dev-skills/skills/xcode-testing-workflow/SKILL.md index 363324f1..561da6aa 100644 --- a/plugins/apple-dev-skills/skills/xcode-testing-workflow/SKILL.md +++ b/plugins/apple-dev-skills/skills/xcode-testing-workflow/SKILL.md @@ -5,10 +5,6 @@ description: Guide Swift Testing, XCTest, XCUITest, XCUIAutomation-oriented mech # Xcode Testing Workflow -## SwiftData And SwiftUI Rule - -When a task combines SwiftData with SwiftUI, keep SwiftData directly coupled to SwiftUI through Apple's data-driven path: `modelContainer`, environment `modelContext`, `@Query`, SwiftData model objects, and bindings. Do not add repositories, stores, service layers, DTO mirrors, view-model caches, wrapper objects, or other abstraction layers between SwiftData and SwiftUI. If this skill is not the right owner for SwiftData-backed SwiftUI work, hand off to `apple-dev-skills:swiftui-app-architecture-workflow` instead of inventing an intermediate data layer. - ## Purpose Use this skill as the primary execution workflow for test-focused work in or around Xcode-managed projects and workspaces. Keep it focused on Swift Testing, XCTest, XCUITest, XCUIAutomation-oriented mechanics, `.xctestplan`, destinations, launch arguments, interruption handling, attachments, accessibility-verification follow-through, Instruments profiling, `xctrace` trace capture, filters, retries, diagnostics, and test-specific Debug/Release validation instead of broad build/run or toolchain work. `scripts/run_workflow.py` is the runtime entrypoint for MCP-first test execution, official CLI fallback planning, and the remaining `.pbxproj` warning boundary when mutation enters project-file territory. diff --git a/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-swift-core.md b/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-swift-core.md index d7e27209..0c35cb18 100644 --- a/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-swift-core.md +++ b/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-swift-core.md @@ -52,7 +52,7 @@ Use this snippet in repository `AGENTS.md` files when you want cross-project Swi ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live in `+Model.swift` and must not be shared with any other SwiftUI view. +- SwiftUI view models are per-view and use concatenated prefixed names such as `GEAWhateverViewModel.swift`; never use `+` filenames. - Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, or view-cluster models. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. diff --git a/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-xcode-project-core.md b/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-xcode-project-core.md index 0ccf5b20..56f3f97a 100644 --- a/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-xcode-project-core.md +++ b/plugins/apple-dev-skills/skills/xcode-testing-workflow/references/snippets/apple-xcode-project-core.md @@ -59,13 +59,12 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard ## SwiftUI and State Architecture - Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom. -- Use a strict Apple-app MVVM shape for Xcode app projects: views own their own view-local state and actions where feasible, view models stay paired with their owning app or view, persistence and transfer shapes live in `Models/`, and app-wide services live under `Sources/Services/`. -- Require one SwiftUI `View` component per file, named for that component, with that component's Xcode SwiftUI preview in the same file. -- Do not group multiple SwiftUI view components into one Swift file, even when the views are small, private, related, nested, or currently used only by one parent. -- SwiftUI view models are always per-view, with no exceptions: the model for `.swift` must live beside the view in `+Model.swift` and must not be shared with any other SwiftUI view. -- Do not create shared SwiftUI view-model files, grouped SwiftUI view-model files, view-cluster models, or unpaired `ViewModel.swift` files. Split shared state into explicit inputs, bindings, environment values, focused values, SwiftData model objects, or a non-SwiftUI boundary when that boundary is genuinely outside the view layer. -- Put app-wide `@Observable` state beside the app entry point: `WhateverNameApp.swift` owns the single app lifecycle entry, and `WhateverNameApp+ViewModel.swift` owns `@Observable final class WhateverNameAppViewModel`. -- Put UIKit and AppKit view-controller support beside the matching view under `Sources/Views/` as `+Controller.swift`. Do not create or preserve a root `Controllers/` directory for ordinary app structure. +- Choose and record one explicit three-letter uppercase prefix for every app or package. Prefix project-owned Swift files and primary declarations; exempt only `Package.swift`, externally generated Swift, and vendored third-party Swift. +- Never use `+` in project-owned Swift filenames. Concatenate the owner and concern so Xcode navigation, rename, and refactoring keep one consistent grammar. +- Name views `GEAWhateverView.swift`, paired `@Observable final class` view models `GEAWhateverViewModel.swift`, and extracted modifiers `GEAWhateverViewModifier.swift`. +- Give independently editable or previewable view components their own files. Small private computed view properties or helper views may remain while they do not clutter focused editing or previews. +- Prefix extracted child components with their complete composition owner, such as `GEASettingsSheetToggleCard.swift`. +- Extract a custom `ViewModifier` after more than eight chained modifiers, or earlier when a coherent chain is reusable or obscures the view body. - Prefer straight, top-down data flow with state owned at the narrowest view, scene, or app boundary that matches the behavior. - Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer. - Keep updates to view-driving state minimal and localized. @@ -75,8 +74,7 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Use app-level lifecycle concerns at the `App` boundary, scene lifecycle concerns at the `Scene` boundary, and view-local active or presentation behavior inside views. - Use `@Binding` to pass a focused writable piece of parent-owned state into a child view. - Use `@Bindable` when working with an observable model that should project bindings to its mutable properties in a view. -- When SwiftData and SwiftUI are used together, keep them directly coupled in the Apple-designed data-driven UI path: attach the `ModelContainer` at the app or scene boundary, read and mutate through SwiftUI's environment `modelContext`, let `@Query` drive view collections that should stay live with the model context, and pass SwiftData model objects or narrow bindings through the view tree. -- Do not insert repositories, stores, service layers, mirrored DTO state, view-model cache layers, or other abstraction layers between SwiftData and SwiftUI. Add a separate boundary only for a real non-SwiftUI concern such as import/export, networking, migration tooling, tests, or server sync, and keep SwiftUI screens driven directly by SwiftData. +- Use the dedicated SwiftData workflow for persistence architecture and its direct SwiftUI integration path. - Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies. - Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable. - Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views. @@ -90,10 +88,10 @@ Use this snippet in repository `AGENTS.md` files when you want baseline standard - Keep those top-level roots stable. Do not invent parallel names such as `AppSources`, `TestSources`, `Config`, `BuildScripts`, or `LocalPackages` for ordinary Xcode app repos unless the existing repo already has a deliberate, documented convention. - Inside `Sources/`, use this strict app structure by default: `Views/`, `Models/`, and `Services/`. Do not create a root `Controllers/` directory. - `Sources/Views/` owns SwiftUI views and UIKit/AppKit view surfaces. Use `Sources/Views/Shared`, `Sources/Views/macOS`, and `Sources/Views/iOS` so shared, macOS-specific, and iOS/iPadOS-specific UI have clear homes. -- View files are named `.swift`. View-local view models are named `+Model.swift`. UIKit and AppKit controller support files are named `+Controller.swift`. Each paired model or controller file lives beside its matching view in `Sources/Views/` or the appropriate platform/shared subdirectory. -- `Sources/Models/` owns Core Data persistence models, SwiftData `@Model` types, app datamodels, DTOs, and transfer or persistence shapes that are shared across the app. Service-private request/response shapes may stay inside the owning service folder only when they are not meaningful outside that integration. +- Use bare prefixed names such as `GEAWhatever.swift` for runtime/domain values. Reserve `GEAWhateverModel.swift` for persistence, and use `GEAWhateverRecord.swift` or `GEAWhateverDTO.swift` only for genuinely additional representations. +- `Sources/Models/` owns Core Data and SwiftData persistence models plus additional record or transfer representations. - `Sources/Services/` owns app-wide and boundary-facing services. Use `Consumed/` for external services the app calls, `Internal/` for services owned only by the app, and `Provided/` for services the app exposes to extensions, helpers, plugins, integrations, or other clients. -- When an app has a single main app-wide service, put it under `Sources/Services/Internal/` as `WhateverNameAppService.swift`. +- Name services `GEAWhateverService.swift`; they manage the matching runtime/domain value `GEAWhatever`. `GEAAppService.swift` may manage `GEA`, while `GEAApp.swift` is the lifecycle-entry special case. - Use `xcodebuild` for Apple platform integration validation, including scheme, destination or SDK, and configuration-specific build or test runs. - Keep `xcodebuild` invocations reproducible in automation by passing explicit schemes, destinations or SDKs, and configurations when relevant. - For Codex GUI worktree-first Xcode repos, use a portable `.codex/environments/*.toml` local environment file when the repo wants shared app setup or action buttons. Start from `apple-dev-skills/templates/codex-local-environments/xcode-project.toml`, keep paths repo-relative, and prefer `-derivedDataPath ./DerivedData` or another ignored repo-local build directory instead of user-global DerivedData. diff --git a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py b/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py index 73fd2d4a..dee98132 100644 --- a/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py +++ b/plugins/apple-dev-skills/tests/test_customization_consolidation_review.py @@ -66,8 +66,8 @@ def test_review_doc_counts_match_live_customization_surface(self) -> None: knob_count = _count_template_knobs() runtime_enforced, policy_only = _count_statuses() - self.assertEqual(template_count, 30) - self.assertEqual(script_count, 30) + self.assertEqual(template_count, 31) + self.assertEqual(script_count, 31) self.assertEqual(knob_count, 21) self.assertEqual(runtime_enforced, 20) self.assertEqual(policy_only, 1) diff --git a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py index 06be12fd..040d9950 100644 --- a/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py +++ b/plugins/apple-dev-skills/tests/test_devicecheck_app_attest_workflow.py @@ -71,7 +71,7 @@ def test_plugin_inventory_includes_devicecheck_workflow(self) -> None: self.assertIn("DeviceCheck", plugin) self.assertIn("App Attest", plugin) self.assertIn("./skills/devicecheck-app-attest-workflow/SKILL.md", validator) - self.assertIn("Expected exactly 30 active skills", validator) + self.assertIn("Expected exactly 31 active skills", validator) self.assertIn("Milestone 53: DeviceCheck and App Attest Workflow - Completed", roadmap) diff --git a/plugins/apple-dev-skills/tests/test_structure_swift_sources_file_headers.py b/plugins/apple-dev-skills/tests/test_structure_swift_sources_file_headers.py index 49352b75..e754f708 100644 --- a/plugins/apple-dev-skills/tests/test_structure_swift_sources_file_headers.py +++ b/plugins/apple-dev-skills/tests/test_structure_swift_sources_file_headers.py @@ -30,7 +30,7 @@ def test_report_counts_compliant_missing_and_malformed_headers(self) -> None: root = Path(tmpdir) (root / "Sources").mkdir() (root / "Sources" / "Compliant.swift").write_text( - "/*\nSampleProject\nCompliant.swift\n© Gale Williams 2026\n\nConcern: Entry-point state and setup.\nPurpose: Explains the feature entry point.\nKey Types: FeatureView, FeatureState\nSee Also: FeatureView+Model.swift\n*/\n\nimport Foundation\n", + "/*\nSampleProject\nCompliant.swift\n© Gale Williams 2026\n\nConcern: Entry-point state and setup.\nPurpose: Explains the feature entry point.\nKey Types: FeatureView, FeatureState\nSee Also: GEAFeatureViewModel.swift\n*/\n\nimport Foundation\n", encoding="utf-8", ) (root / "Sources" / "Missing.swift").write_text("import Foundation\n", encoding="utf-8") @@ -102,7 +102,7 @@ def test_apply_inventory_replaces_existing_structured_header(self) -> None: ' purpose: "Defines the new feature entry point."', ' concern: "Feature state and wiring."', ' key_types: "FeatureView, FeatureState"', - ' see_also: "FeatureView+Model.swift, FeatureView+Modifier.swift"', + ' see_also: "GEAFeatureViewModel.swift, GEAFeatureViewModifier.swift"', ] ) + "\n", @@ -119,7 +119,7 @@ def test_apply_inventory_replaces_existing_structured_header(self) -> None: self.assertIn("Concern: Feature state and wiring.", rewritten) self.assertIn("Purpose: Defines the new feature entry point.", rewritten) self.assertIn("Key Types: FeatureView, FeatureState", rewritten) - self.assertIn("See Also: FeatureView+Model.swift, FeatureView+Modifier.swift", rewritten) + self.assertIn("See Also: GEAFeatureViewModel.swift, GEAFeatureViewModifier.swift", rewritten) self.assertNotIn("Old purpose", rewritten) def test_checked_in_inventory_template_matches_loader_contract(self) -> None: @@ -132,7 +132,7 @@ def test_checked_in_inventory_template_matches_loader_contract(self) -> None: self.assertEqual(entries[0]["key_types"], ["FeatureView", "FeatureState"]) self.assertEqual( entries[0]["see_also"], - ["FeatureView+Model.swift", "FeatureView+Modifier.swift"], + ["GEAFeatureViewModel.swift", "GEAFeatureViewModifier.swift"], ) diff --git a/plugins/apple-dev-skills/tests/test_structure_swift_sources_workflow.py b/plugins/apple-dev-skills/tests/test_structure_swift_sources_workflow.py index 017b2e5d..40f05175 100644 --- a/plugins/apple-dev-skills/tests/test_structure_swift_sources_workflow.py +++ b/plugins/apple-dev-skills/tests/test_structure_swift_sources_workflow.py @@ -120,8 +120,8 @@ def test_swiftui_structure_requires_one_view_and_preview_per_file(self) -> None: self.assertIn("require exactly one SwiftUI `View` component per file", skill_text) self.assertIn("keep that component's Xcode SwiftUI preview in the same file", skill_text) - self.assertIn("Do not group multiple `View` component types in one Swift file", source_rules_text) - self.assertIn("Require one SwiftUI `View` component per file", layout_rules_text) + self.assertIn("Hand SwiftUI component", source_rules_text) + self.assertIn("swiftui-app-architecture-workflow", layout_rules_text) def test_swiftui_view_model_structure_is_per_view_only(self) -> None: skill_text = (ROOT / "skills/structure-swift-sources/SKILL.md").read_text(encoding="utf-8") @@ -132,11 +132,10 @@ def test_swiftui_view_model_structure_is_per_view_only(self) -> None: encoding="utf-8" ) - self.assertIn("require any SwiftUI view model for that component to live in `+Model.swift`", skill_text) - self.assertIn("SwiftUI view models are always per-view", source_rules_text) - self.assertIn("Do not share one SwiftUI view model", source_rules_text) - self.assertIn("must live in `+Model.swift`", layout_rules_text) - self.assertNotIn("When a view has a paired model type", layout_rules_text) + self.assertIn("do not use `+` filenames", skill_text) + self.assertIn("concatenated filename grammar", source_rules_text) + self.assertIn("GEASettingsSheetToggleCard.swift", layout_rules_text) + self.assertIn("GEAWhateverModel.swift", layout_rules_text) if __name__ == "__main__": diff --git a/plugins/apple-dev-skills/tests/test_swiftui_app_architecture_workflow.py b/plugins/apple-dev-skills/tests/test_swiftui_app_architecture_workflow.py index 55a7d4cd..d997eebb 100644 --- a/plugins/apple-dev-skills/tests/test_swiftui_app_architecture_workflow.py +++ b/plugins/apple-dev-skills/tests/test_swiftui_app_architecture_workflow.py @@ -58,8 +58,8 @@ def test_view_components_require_file_local_previews(self) -> None: "skills/swiftui-app-architecture-workflow/references/anti-patterns-and-corrections.md" ) - self.assertIn("Each SwiftUI `View` component must live in its own Swift file", skill_text) - self.assertIn("view's own Xcode SwiftUI preview", skill_text) + self.assertIn("complex enough to edit or preview independently", skill_text) + self.assertIn("small private helper views may remain", skill_text) self.assertIn("Grouped SwiftUI View Files", anti_patterns_text) self.assertIn("keep that component's Xcode SwiftUI preview in the same file", anti_patterns_text) @@ -70,11 +70,11 @@ def test_swiftui_view_models_are_per_view_only(self) -> None: ) shared_snippet_text = self.read("shared/agents-snippets/apple-xcode-project-core.md") - self.assertIn("SwiftUI view models are always per-view, with no exceptions", skill_text) - self.assertIn("matching `+Model.swift` file", skill_text) + self.assertIn("GEAWhateverViewModel.swift", skill_text) + self.assertIn("Never use `+` filenames", skill_text) self.assertIn("Shared SwiftUI View Models", anti_patterns_text) - self.assertIn("put the model for `.swift` in `+Model.swift`", anti_patterns_text) - self.assertIn("SwiftUI view models are always per-view, with no exceptions", shared_snippet_text) + self.assertIn("put the view model for `GEAWhateverView.swift`", anti_patterns_text) + self.assertIn("GEAWhateverViewModel.swift", shared_snippet_text) self.assertNotIn("own matching state for a view or small view cluster", shared_snippet_text) diff --git a/plugins/apple-dev-skills/tests/test_xcode_app_bootstrap_workflow.py b/plugins/apple-dev-skills/tests/test_xcode_app_bootstrap_workflow.py index 736e048f..ff86035b 100644 --- a/plugins/apple-dev-skills/tests/test_xcode_app_bootstrap_workflow.py +++ b/plugins/apple-dev-skills/tests/test_xcode_app_bootstrap_workflow.py @@ -68,7 +68,7 @@ def test_wrapper_injects_runtime_defaults(self) -> None: ) env = dict(os.environ) env["APPLE_DEV_SKILLS_CONFIG_HOME"] = tmpdir - code, payload = self.run_script("--name", "DemoApp", "--dry-run", env=env) + code, payload = self.run_script("--name", "DemoApp", "--file-prefix", "GEA", "--dry-run", env=env) self.assertEqual(code, 0) self.assertEqual(payload["normalized_inputs"]["platform"], "macos") self.assertEqual(payload["normalized_inputs"]["ui_stack"], "swiftui") @@ -80,6 +80,8 @@ def test_blocks_non_app_project_kind(self) -> None: code, payload = self.run_script( "--name", "DemoPkg", + "--file-prefix", + "GEP", "--project-kind", "package", "--platform", @@ -96,6 +98,8 @@ def test_blocks_when_platform_is_unresolved(self) -> None: code, payload = self.run_script( "--name", "DemoApp", + "--file-prefix", + "GEA", "--project-generator", "xcodegen", "--dry-run", @@ -108,6 +112,8 @@ def test_blocks_when_generator_is_ask(self) -> None: code, payload = self.run_script( "--name", "DemoApp", + "--file-prefix", + "GEA", "--platform", "macos", "--project-generator", @@ -121,6 +127,8 @@ def test_xcode_path_is_guided_only(self) -> None: code, payload = self.run_script( "--name", "DemoApp", + "--file-prefix", + "GEA", "--platform", "macos", "--project-generator", @@ -137,6 +145,8 @@ def test_xcodegen_path_blocks_when_xcodegen_is_missing(self) -> None: code, payload = self.run_script( "--name", "DemoApp", + "--file-prefix", + "GEA", "--destination", tmpdir, "--platform", @@ -177,6 +187,8 @@ def test_xcodegen_path_can_succeed_with_fake_tools(self) -> None: code, payload = self.run_script( "--name", "DemoApp", + "--file-prefix", + "GEA", "--destination", tmpdir, "--platform", @@ -223,11 +235,11 @@ def test_xcodegen_path_can_succeed_with_fake_tools(self) -> None: self.assertTrue((target / source_dir).is_dir(), source_dir) for placeholder in ("Shared/.gitkeep", "Extensions/.gitkeep", "Scripts/.gitkeep", "Packages/.gitkeep"): self.assertTrue((target / placeholder).exists(), placeholder) - self.assertTrue((target / "Sources" / "DemoApp.swift").exists()) - self.assertTrue((target / "Sources" / "DemoApp+ViewModel.swift").exists()) - self.assertTrue((target / "Sources" / "Views" / "Shared" / "ContentView.swift").exists()) - self.assertTrue((target / "Sources" / "Views" / "Shared" / "ContentView+Model.swift").exists()) - self.assertTrue((target / "Sources" / "Services" / "Internal" / "DemoAppService.swift").exists()) + self.assertTrue((target / "Sources" / "GEAApp.swift").exists()) + self.assertTrue((target / "Sources" / "GEA.swift").exists()) + self.assertTrue((target / "Sources" / "Views" / "Shared" / "GEAContentView.swift").exists()) + self.assertTrue((target / "Sources" / "Views" / "Shared" / "GEAContentViewModel.swift").exists()) + self.assertTrue((target / "Sources" / "Services" / "Internal" / "GEAAppService.swift").exists()) self.assertFalse((target / "Sources" / "Controllers").exists()) self.assertTrue((target / "Sources" / "Support" / "DemoApp.entitlements").exists()) self.assertTrue((target / "Sources" / "Resources" / "Assets.xcassets" / "Contents.json").exists()) @@ -319,9 +331,9 @@ def test_xcodegen_path_can_succeed_with_fake_tools(self) -> None: self.assertIn("Use the standard top-level Xcode app repository layout", agents_text) self.assertIn("`Shared/` owns reusable source intended to be compiled into the app and extension targets", agents_text) self.assertIn("Sources/Views/Shared", agents_text) - self.assertIn("Sources/Services/Internal", agents_text) - self.assertIn("WhateverNameApp+ViewModel.swift", agents_text) - self.assertIn("+Controller.swift", agents_text) + self.assertIn("`Internal/` for services owned only by the app", agents_text) + self.assertIn("GEAAppService.swift", agents_text) + self.assertIn("GEAWhateverViewModel.swift", agents_text) self.assertIn("A standard app target gets one `Sources` source entry", agents_text) self.assertIn("Every native app target must have exactly one app lifecycle entry point", agents_text) self.assertIn("Keep XcodeGen specs readable as project structure", agents_text) diff --git a/plugins/apple-dev-skills/tests/test_xcode_guidance_sync_workflow.py b/plugins/apple-dev-skills/tests/test_xcode_guidance_sync_workflow.py index 5a5e44e1..143a18be 100644 --- a/plugins/apple-dev-skills/tests/test_xcode_guidance_sync_workflow.py +++ b/plugins/apple-dev-skills/tests/test_xcode_guidance_sync_workflow.py @@ -84,14 +84,12 @@ def test_sync_creates_agents_template(self) -> None: self.assertIn("optional parameters with explicit default values", agents_text) self.assertIn("four or more arguments or parameters", agents_text) self.assertIn("Prefer enums, enum cases with associated values", agents_text) - self.assertIn("Require one SwiftUI `View` component per file", agents_text) - self.assertIn("Xcode SwiftUI preview in the same file", agents_text) - self.assertIn("SwiftUI view models are always per-view, with no exceptions", agents_text) - self.assertIn("+Model.swift", agents_text) + self.assertIn("three-letter uppercase prefix", agents_text) + self.assertIn("GEAWhateverViewModel.swift", agents_text) self.assertIn("Sources/Views/Shared", agents_text) - self.assertIn("Sources/Services/Internal", agents_text) - self.assertIn("WhateverNameApp+ViewModel.swift", agents_text) - self.assertIn("+Controller.swift", agents_text) + self.assertIn("`Internal/` for services owned only by the app", agents_text) + self.assertIn("GEAAppService.swift", agents_text) + self.assertNotIn("+ViewModel.swift", agents_text) self.assertEqual(payload["structure_audit"]["status"], "needs-attention") self.assertTrue( any(finding["path"] == "Sources/Views/Shared" for finding in payload["structure_audit"]["findings"]) @@ -140,14 +138,12 @@ def test_sync_appends_section_to_existing_agents(self) -> None: self.assertIn("optional parameters with explicit default values", agents_text) self.assertIn("four or more arguments or parameters", agents_text) self.assertIn("Prefer enums, enum cases with associated values", agents_text) - self.assertIn("Require one SwiftUI `View` component per file", agents_text) - self.assertIn("Xcode SwiftUI preview in the same file", agents_text) - self.assertIn("SwiftUI view models are always per-view, with no exceptions", agents_text) - self.assertIn("+Model.swift", agents_text) + self.assertIn("three-letter uppercase prefix", agents_text) + self.assertIn("GEAWhateverViewModel.swift", agents_text) self.assertIn("Sources/Views/Shared", agents_text) - self.assertIn("Sources/Services/Internal", agents_text) - self.assertIn("WhateverNameApp+ViewModel.swift", agents_text) - self.assertIn("+Controller.swift", agents_text) + self.assertIn("`Internal/` for services owned only by the app", agents_text) + self.assertIn("GEAAppService.swift", agents_text) + self.assertNotIn("+ViewModel.swift", agents_text) self.assertTrue(Path(tmpdir, ".swiftformat").is_file()) self.assertTrue(Path(tmpdir, "Scripts/repo-maintenance/hooks/pre-commit.sample").is_file()) self.assertTrue(Path(tmpdir, "Scripts/repo-maintenance/release.sh").is_file()) diff --git a/plugins/apple-dev-skills/uv.lock b/plugins/apple-dev-skills/uv.lock index 3b159b9c..29e7fb88 100644 --- a/plugins/apple-dev-skills/uv.lock +++ b/plugins/apple-dev-skills/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.10" [[package]] name = "apple-dev-skills-maintainer" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/cardhop-app/.codex-plugin/plugin.json b/plugins/cardhop-app/.codex-plugin/plugin.json index 373d7591..4d61caec 100644 --- a/plugins/cardhop-app/.codex-plugin/plugin.json +++ b/plugins/cardhop-app/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cardhop-app", - "version": "8.6.0", + "version": "8.7.0", "description": "Cardhop.app workflow guidance plus a bundled local MCP server for contact capture and updates on macOS.", "author": { "name": "Gale", diff --git a/plugins/cardhop-app/mcp/pyproject.toml b/plugins/cardhop-app/mcp/pyproject.toml index ad13fd5e..10468a20 100644 --- a/plugins/cardhop-app/mcp/pyproject.toml +++ b/plugins/cardhop-app/mcp/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cardhop-app-mcp" -version = "8.6.0" +version = "8.7.0" requires-python = ">=3.13" dependencies = [ "fastmcp>=3.0.2", diff --git a/plugins/cardhop-app/mcp/uv.lock b/plugins/cardhop-app/mcp/uv.lock index 6e856503..c57e9d1e 100644 --- a/plugins/cardhop-app/mcp/uv.lock +++ b/plugins/cardhop-app/mcp/uv.lock @@ -138,7 +138,7 @@ wheels = [ [[package]] name = "cardhop-app-mcp" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } dependencies = [ { name = "fastmcp" }, diff --git a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json index 02d5bd43..706cbf12 100644 --- a/plugins/cloud-deployment-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-deployment-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-deployment-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for routing cloud deployment work through official provider plugins, MCP servers, CLIs, and Socket-owned deployment guidance.", "author": { "name": "Gale", diff --git a/plugins/cloud-inference-skills/.codex-plugin/plugin.json b/plugins/cloud-inference-skills/.codex-plugin/plugin.json index d03c8740..c9a7df33 100644 --- a/plugins/cloud-inference-skills/.codex-plugin/plugin.json +++ b/plugins/cloud-inference-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "cloud-inference-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Cloud AI inference workflow skills for routing model serving, training, conversion, and GPU infrastructure work across Runpod, Hugging Face, AWS, Vast.ai, CoreWeave, and similar providers.", "author": { "name": "Gale", diff --git a/plugins/dotnet-skills/.codex-plugin/plugin.json b/plugins/dotnet-skills/.codex-plugin/plugin.json index a2bd91d6..8ed68184 100644 --- a/plugins/dotnet-skills/.codex-plugin/plugin.json +++ b/plugins/dotnet-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "dotnet-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for choosing, bootstrapping, building, testing, packaging, diagnosing, and maintaining .NET projects with F# and C# as equal first-party languages.", "author": { "name": "Gale", diff --git a/plugins/game-dev-skills/.codex-plugin/plugin.json b/plugins/game-dev-skills/.codex-plugin/plugin.json index b806e533..0c7ba628 100644 --- a/plugins/game-dev-skills/.codex-plugin/plugin.json +++ b/plugins/game-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "game-dev-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Apple platform game development workflow skills for SpriteKit, SceneKit, GameplayKit, controller input, haptics, profiling, and game-stack routing.", "author": { "name": "Gale", diff --git a/plugins/network-protocol-skills/.codex-plugin/plugin.json b/plugins/network-protocol-skills/.codex-plugin/plugin.json index 9757a085..b823f901 100644 --- a/plugins/network-protocol-skills/.codex-plugin/plugin.json +++ b/plugins/network-protocol-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "network-protocol-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for choosing, planning, implementing, and diagnosing modern application transports and real-time networking protocols, including QUIC, HTTP/3, WebRTC, Media over QUIC, WebTransport-adjacent handoffs, protocol maturity checks, and stack-specific implementation routing.", "author": { "name": "Gale", diff --git a/plugins/productivity-skills/.codex-plugin/plugin.json b/plugins/productivity-skills/.codex-plugin/plugin.json index e90a920b..02d33558 100644 --- a/plugins/productivity-skills/.codex-plugin/plugin.json +++ b/plugins/productivity-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "productivity-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Broadly useful productivity workflows for Codex.", "author": { "name": "Gale", diff --git a/plugins/productivity-skills/pyproject.toml b/plugins/productivity-skills/pyproject.toml index 6d02c41d..2d0e27fe 100644 --- a/plugins/productivity-skills/pyproject.toml +++ b/plugins/productivity-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "productivity-skills-maintenance" -version = "8.6.0" +version = "8.7.0" description = "Maintainer-only Python tooling baseline for productivity-skills." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/productivity-skills/uv.lock b/plugins/productivity-skills/uv.lock index d8c3ef8e..7a2e3e27 100644 --- a/plugins/productivity-skills/uv.lock +++ b/plugins/productivity-skills/uv.lock @@ -228,7 +228,7 @@ wheels = [ [[package]] name = "productivity-skills-maintenance" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/python-skills/.codex-plugin/plugin.json b/plugins/python-skills/.codex-plugin/plugin.json index b47ef13c..b3b178c4 100644 --- a/plugins/python-skills/.codex-plugin/plugin.json +++ b/plugins/python-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "python-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Bundled Python-focused Codex skills for uv bootstrapping, project implementation, diagnostics, packaging, tooling, CI, upgrades, FastAPI, FastMCP, and pytest workflows.", "author": { "name": "Gale", diff --git a/plugins/python-skills/pyproject.toml b/plugins/python-skills/pyproject.toml index a847ca76..22522bf9 100644 --- a/plugins/python-skills/pyproject.toml +++ b/plugins/python-skills/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "python-skills-maintainer" -version = "8.6.0" +version = "8.7.0" description = "Maintainer tooling for the python-skills repository" requires-python = ">=3.11" dependencies = [] diff --git a/plugins/python-skills/uv.lock b/plugins/python-skills/uv.lock index 0da65ab8..0dac1b54 100644 --- a/plugins/python-skills/uv.lock +++ b/plugins/python-skills/uv.lock @@ -251,7 +251,7 @@ wheels = [ [[package]] name = "python-skills-maintainer" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json index c610d292..f75ce2d6 100644 --- a/plugins/reverse-engineering-skills/.codex-plugin/plugin.json +++ b/plugins/reverse-engineering-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "reverse-engineering-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Workflow skills for reverse engineering, decompilation, disassembly, symbol, and artifact-analysis tasks.", "skills": "./skills/", "author": { diff --git a/plugins/rust-skills/.codex-plugin/plugin.json b/plugins/rust-skills/.codex-plugin/plugin.json index 3a8731b7..df587a48 100644 --- a/plugins/rust-skills/.codex-plugin/plugin.json +++ b/plugins/rust-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "rust-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Rust, Cargo, rustup, crate, workspace, CLI, library, package, CI, testing, linting, and formatting workflow skills.", "skills": "./skills/", "author": { diff --git a/plugins/server-side-jvm/.codex-plugin/plugin.json b/plugins/server-side-jvm/.codex-plugin/plugin.json index 19b64b5b..7137a2c3 100644 --- a/plugins/server-side-jvm/.codex-plugin/plugin.json +++ b/plugins/server-side-jvm/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-jvm", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for choosing, building, testing, and maintaining server-side JVM backend projects with Java and Scala as equal first-party languages and future Clojure support planned.", "author": { "name": "Gale", diff --git a/plugins/server-side-swift/.codex-plugin/plugin.json b/plugins/server-side-swift/.codex-plugin/plugin.json index c49662ab..4f5d9ea4 100644 --- a/plugins/server-side-swift/.codex-plugin/plugin.json +++ b/plugins/server-side-swift/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "server-side-swift", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for bootstrapping, syncing, building, running, containerizing, deploying, and maintaining server-side Swift services, including Vapor, Hummingbird, hb, persistence, Swift OpenAPI, RPC-fit decisions, SwiftNIO, observability, auth, app sync, Docker, Apple Containerization, Fly.io, and SwiftPM-first workflows.", "author": { "name": "Gale", diff --git a/plugins/spotify/.codex-plugin/plugin.json b/plugins/spotify/.codex-plugin/plugin.json index e17a6f73..445ab84b 100644 --- a/plugins/spotify/.codex-plugin/plugin.json +++ b/plugins/spotify/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "spotify", - "version": "8.6.0", + "version": "8.7.0", "description": "Placeholder plugin repository for future Spotify-focused Codex workflows.", "author": { "name": "Gale", diff --git a/plugins/swift-lang/.codex-plugin/plugin.json b/plugins/swift-lang/.codex-plugin/plugin.json index 8c49ddae..70f45043 100644 --- a/plugins/swift-lang/.codex-plugin/plugin.json +++ b/plugins/swift-lang/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swift-lang", - "version": "8.6.0", + "version": "8.7.0", "description": "Shared Swift language skills for API style, error handling, functional pipelines, formatting, source organization, and modernization cleanup.", "skills": "./skills/", "author": { diff --git a/plugins/swiftasb-skills/.codex-plugin/plugin.json b/plugins/swiftasb-skills/.codex-plugin/plugin.json index 28369c4b..d1ad2ddc 100644 --- a/plugins/swiftasb-skills/.codex-plugin/plugin.json +++ b/plugins/swiftasb-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "swiftasb-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for explaining SwiftASB and building SwiftUI, AppKit, and Swift package integrations on top of it.", "author": { "name": "Gale", diff --git a/plugins/things-app/.codex-plugin/plugin.json b/plugins/things-app/.codex-plugin/plugin.json index bb065544..c8e56b7d 100644 --- a/plugins/things-app/.codex-plugin/plugin.json +++ b/plugins/things-app/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "things-app", - "version": "8.6.0", + "version": "8.7.0", "description": "Things.app skills and a bundled local MCP server for reminders, planning digests, and structured task workflows.", "author": { "name": "Gale", diff --git a/plugins/things-app/mcp/pyproject.toml b/plugins/things-app/mcp/pyproject.toml index 418420bf..2d587601 100644 --- a/plugins/things-app/mcp/pyproject.toml +++ b/plugins/things-app/mcp/pyproject.toml @@ -7,7 +7,7 @@ packages = ["app"] [project] name = "things-mcp" -version = "8.6.0" +version = "8.7.0" requires-python = ">=3.13" dependencies = [ "fastmcp>=3.0.2", diff --git a/plugins/things-app/mcp/uv.lock b/plugins/things-app/mcp/uv.lock index 45b85c92..8f77554a 100644 --- a/plugins/things-app/mcp/uv.lock +++ b/plugins/things-app/mcp/uv.lock @@ -1241,7 +1241,7 @@ wheels = [ [[package]] name = "things-mcp" -version = "8.6.0" +version = "8.7.0" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/plugins/things-app/pyproject.toml b/plugins/things-app/pyproject.toml index e7e9710d..972bb77d 100644 --- a/plugins/things-app/pyproject.toml +++ b/plugins/things-app/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "things-app-maintenance" -version = "8.6.0" +version = "8.7.0" description = "Maintainer-only Python tooling baseline for things-app skills and plugin packaging." requires-python = ">=3.11" dependencies = [] diff --git a/plugins/things-app/uv.lock b/plugins/things-app/uv.lock index c076137a..19408295 100644 --- a/plugins/things-app/uv.lock +++ b/plugins/things-app/uv.lock @@ -120,7 +120,7 @@ wheels = [ [[package]] name = "things-app-maintenance" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies] diff --git a/plugins/web-dev-skills/.codex-plugin/plugin.json b/plugins/web-dev-skills/.codex-plugin/plugin.json index cb43b0ec..052cbe1a 100644 --- a/plugins/web-dev-skills/.codex-plugin/plugin.json +++ b/plugins/web-dev-skills/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "web-dev-skills", - "version": "8.6.0", + "version": "8.7.0", "description": "Codex skills for focused web and Expo native-boundary workflows.", "author": { "name": "Gale", diff --git a/pyproject.toml b/pyproject.toml index b48c99fa..5ff2bd4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "socket-maintenance" -version = "8.6.0" +version = "8.7.0" description = "Root uv tooling baseline for the socket superproject." requires-python = ">=3.11" dependencies = [] diff --git a/uv.lock b/uv.lock index 2f1ad563..30f824f4 100644 --- a/uv.lock +++ b/uv.lock @@ -286,7 +286,7 @@ wheels = [ [[package]] name = "socket-maintenance" -version = "8.6.0" +version = "8.7.0" source = { virtual = "." } [package.dev-dependencies]