opencode-skills-antigravity 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/bundled-skills/app-store-changelog/SKILL.md +75 -0
  2. package/bundled-skills/app-store-changelog/agents/openai.yaml +4 -0
  3. package/bundled-skills/app-store-changelog/references/release-notes-guidelines.md +34 -0
  4. package/bundled-skills/app-store-changelog/scripts/collect_release_changes.sh +33 -0
  5. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  6. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  7. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  8. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  9. package/bundled-skills/docs/sources/sources.md +10 -0
  10. package/bundled-skills/docs/users/bundles.md +9 -1
  11. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  12. package/bundled-skills/docs/users/faq.md +36 -0
  13. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  14. package/bundled-skills/docs/users/getting-started.md +1 -1
  15. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  16. package/bundled-skills/docs/users/usage.md +14 -4
  17. package/bundled-skills/docs/users/visual-guide.md +4 -4
  18. package/bundled-skills/github/SKILL.md +76 -0
  19. package/bundled-skills/github/agents/openai.yaml +4 -0
  20. package/bundled-skills/ios-debugger-agent/SKILL.md +59 -0
  21. package/bundled-skills/ios-debugger-agent/agents/openai.yaml +4 -0
  22. package/bundled-skills/macos-menubar-tuist-app/SKILL.md +109 -0
  23. package/bundled-skills/macos-menubar-tuist-app/agents/openai.yaml +4 -0
  24. package/bundled-skills/macos-spm-app-packaging/SKILL.md +105 -0
  25. package/bundled-skills/macos-spm-app-packaging/agents/openai.yaml +4 -0
  26. package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Package.swift +17 -0
  27. package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/Resources/.keep +0 -0
  28. package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift +11 -0
  29. package/bundled-skills/macos-spm-app-packaging/assets/templates/bootstrap/version.env +2 -0
  30. package/bundled-skills/macos-spm-app-packaging/assets/templates/build_icon.sh +49 -0
  31. package/bundled-skills/macos-spm-app-packaging/assets/templates/compile_and_run.sh +63 -0
  32. package/bundled-skills/macos-spm-app-packaging/assets/templates/launch.sh +28 -0
  33. package/bundled-skills/macos-spm-app-packaging/assets/templates/make_appcast.sh +82 -0
  34. package/bundled-skills/macos-spm-app-packaging/assets/templates/package_app.sh +206 -0
  35. package/bundled-skills/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +52 -0
  36. package/bundled-skills/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +52 -0
  37. package/bundled-skills/macos-spm-app-packaging/assets/templates/version.env +2 -0
  38. package/bundled-skills/macos-spm-app-packaging/references/packaging.md +17 -0
  39. package/bundled-skills/macos-spm-app-packaging/references/release.md +32 -0
  40. package/bundled-skills/macos-spm-app-packaging/references/scaffold.md +79 -0
  41. package/bundled-skills/orchestrate-batch-refactor/SKILL.md +97 -0
  42. package/bundled-skills/orchestrate-batch-refactor/agents/openai.yaml +4 -0
  43. package/bundled-skills/orchestrate-batch-refactor/references/agent-prompt-templates.md +53 -0
  44. package/bundled-skills/orchestrate-batch-refactor/references/work-packet-template.md +31 -0
  45. package/bundled-skills/project-skill-audit/SKILL.md +190 -0
  46. package/bundled-skills/project-skill-audit/agents/openai.yaml +4 -0
  47. package/bundled-skills/react-component-performance/SKILL.md +135 -0
  48. package/bundled-skills/react-component-performance/agents/openai.yaml +4 -0
  49. package/bundled-skills/react-component-performance/references/examples.md +88 -0
  50. package/bundled-skills/simplify-code/SKILL.md +179 -0
  51. package/bundled-skills/snowflake-development/SKILL.md +5 -0
  52. package/bundled-skills/swift-concurrency-expert/SKILL.md +113 -0
  53. package/bundled-skills/swift-concurrency-expert/agents/openai.yaml +4 -0
  54. package/bundled-skills/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
  55. package/bundled-skills/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
  56. package/bundled-skills/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
  57. package/bundled-skills/swiftui-liquid-glass/SKILL.md +98 -0
  58. package/bundled-skills/swiftui-liquid-glass/agents/openai.yaml +4 -0
  59. package/bundled-skills/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  60. package/bundled-skills/swiftui-performance-audit/SKILL.md +114 -0
  61. package/bundled-skills/swiftui-performance-audit/agents/openai.yaml +4 -0
  62. package/bundled-skills/swiftui-performance-audit/references/code-smells.md +150 -0
  63. package/bundled-skills/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  64. package/bundled-skills/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  65. package/bundled-skills/swiftui-performance-audit/references/profiling-intake.md +44 -0
  66. package/bundled-skills/swiftui-performance-audit/references/report-template.md +47 -0
  67. package/bundled-skills/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  68. package/bundled-skills/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  69. package/bundled-skills/swiftui-ui-patterns/SKILL.md +103 -0
  70. package/bundled-skills/swiftui-ui-patterns/agents/openai.yaml +4 -0
  71. package/bundled-skills/swiftui-ui-patterns/references/app-wiring.md +201 -0
  72. package/bundled-skills/swiftui-ui-patterns/references/async-state.md +96 -0
  73. package/bundled-skills/swiftui-ui-patterns/references/components-index.md +50 -0
  74. package/bundled-skills/swiftui-ui-patterns/references/controls.md +57 -0
  75. package/bundled-skills/swiftui-ui-patterns/references/deeplinks.md +66 -0
  76. package/bundled-skills/swiftui-ui-patterns/references/focus.md +90 -0
  77. package/bundled-skills/swiftui-ui-patterns/references/form.md +97 -0
  78. package/bundled-skills/swiftui-ui-patterns/references/grids.md +71 -0
  79. package/bundled-skills/swiftui-ui-patterns/references/haptics.md +71 -0
  80. package/bundled-skills/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  81. package/bundled-skills/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  82. package/bundled-skills/swiftui-ui-patterns/references/list.md +86 -0
  83. package/bundled-skills/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  84. package/bundled-skills/swiftui-ui-patterns/references/macos-settings.md +71 -0
  85. package/bundled-skills/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  86. package/bundled-skills/swiftui-ui-patterns/references/media.md +73 -0
  87. package/bundled-skills/swiftui-ui-patterns/references/menu-bar.md +101 -0
  88. package/bundled-skills/swiftui-ui-patterns/references/navigationstack.md +159 -0
  89. package/bundled-skills/swiftui-ui-patterns/references/overlay.md +45 -0
  90. package/bundled-skills/swiftui-ui-patterns/references/performance.md +62 -0
  91. package/bundled-skills/swiftui-ui-patterns/references/previews.md +48 -0
  92. package/bundled-skills/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  93. package/bundled-skills/swiftui-ui-patterns/references/scrollview.md +87 -0
  94. package/bundled-skills/swiftui-ui-patterns/references/searchable.md +71 -0
  95. package/bundled-skills/swiftui-ui-patterns/references/sheets.md +155 -0
  96. package/bundled-skills/swiftui-ui-patterns/references/split-views.md +72 -0
  97. package/bundled-skills/swiftui-ui-patterns/references/tabview.md +114 -0
  98. package/bundled-skills/swiftui-ui-patterns/references/theming.md +71 -0
  99. package/bundled-skills/swiftui-ui-patterns/references/title-menus.md +93 -0
  100. package/bundled-skills/swiftui-ui-patterns/references/top-bar.md +49 -0
  101. package/bundled-skills/swiftui-view-refactor/SKILL.md +210 -0
  102. package/bundled-skills/swiftui-view-refactor/agents/openai.yaml +4 -0
  103. package/bundled-skills/swiftui-view-refactor/references/mv-patterns.md +161 -0
  104. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ # Title menus
2
+
3
+ ## Intent
4
+
5
+ Use a title menu in the navigation bar to provide context‑specific filtering or quick actions without adding extra chrome.
6
+
7
+ ## Core patterns
8
+
9
+ - Use `ToolbarTitleMenu` to attach a menu to the navigation title.
10
+ - Keep the menu content compact and grouped with dividers.
11
+
12
+ ## Example: title menu for filters
13
+
14
+ ```swift
15
+ @ToolbarContentBuilder
16
+ private var toolbarView: some ToolbarContent {
17
+ ToolbarTitleMenu {
18
+ Button("Latest") { timeline = .latest }
19
+ Button("Resume") { timeline = .resume }
20
+ Divider()
21
+ Button("Local") { timeline = .local }
22
+ Button("Federated") { timeline = .federated }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Example: attach to a view
28
+
29
+ ```swift
30
+ NavigationStack {
31
+ TimelineView()
32
+ .toolbar {
33
+ toolbarView
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Example: title + menu together
39
+
40
+ ```swift
41
+ struct TimelineScreen: View {
42
+ @State private var timeline: TimelineFilter = .home
43
+
44
+ var body: some View {
45
+ NavigationStack {
46
+ TimelineView()
47
+ .toolbar {
48
+ ToolbarItem(placement: .principal) {
49
+ VStack(spacing: 2) {
50
+ Text(timeline.title)
51
+ .font(.headline)
52
+ Text(timeline.subtitle)
53
+ .font(.caption)
54
+ .foregroundStyle(.secondary)
55
+ }
56
+ }
57
+
58
+ ToolbarTitleMenu {
59
+ Button("Home") { timeline = .home }
60
+ Button("Local") { timeline = .local }
61
+ Button("Federated") { timeline = .federated }
62
+ }
63
+ }
64
+ .navigationBarTitleDisplayMode(.inline)
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Example: title + subtitle with menu
71
+
72
+ ```swift
73
+ ToolbarItem(placement: .principal) {
74
+ VStack(spacing: 2) {
75
+ Text(title)
76
+ .font(.headline)
77
+ Text(subtitle)
78
+ .font(.caption)
79
+ .foregroundStyle(.secondary)
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Design choices to keep
85
+
86
+ - Only show the title menu when filtering or context switching is available.
87
+ - Keep the title readable; avoid long labels that truncate.
88
+ - Use secondary text below the title if extra context is needed.
89
+
90
+ ## Pitfalls
91
+
92
+ - Don’t overload the menu with too many options.
93
+ - Avoid using title menus for destructive actions.
@@ -0,0 +1,49 @@
1
+ # Top bar overlays (iOS 26+ and fallback)
2
+
3
+ ## Intent
4
+
5
+ Provide a custom top selector or pill row that sits above scroll content, using `safeAreaBar(.top)` on iOS 26 and a compatible fallback on earlier OS versions.
6
+
7
+ ## iOS 26+ approach
8
+
9
+ Use `safeAreaBar(edge: .top)` to attach the view to the safe area bar.
10
+
11
+ ```swift
12
+ if #available(iOS 26.0, *) {
13
+ content
14
+ .safeAreaBar(edge: .top) {
15
+ TopSelectorView()
16
+ .padding(.horizontal, .layoutPadding)
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## Fallback for earlier iOS
22
+
23
+ Use `.safeAreaInset(edge: .top)` and hide the toolbar background to avoid double layers.
24
+
25
+ ```swift
26
+ content
27
+ .toolbarBackground(.hidden, for: .navigationBar)
28
+ .safeAreaInset(edge: .top, spacing: 0) {
29
+ VStack(spacing: 0) {
30
+ TopSelectorView()
31
+ .padding(.vertical, 8)
32
+ .padding(.horizontal, .layoutPadding)
33
+ .background(Color.primary.opacity(0.06))
34
+ .background(Material.ultraThin)
35
+ Divider()
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Design choices to keep
41
+
42
+ - Use `safeAreaBar` when available; it integrates better with the navigation bar.
43
+ - Use a subtle background + divider in the fallback to keep separation from content.
44
+ - Keep the selector height compact to avoid pushing content too far down.
45
+
46
+ ## Pitfalls
47
+
48
+ - Don’t stack multiple top insets; it can create extra padding.
49
+ - Avoid heavy, opaque backgrounds that fight the navigation bar.
@@ -0,0 +1,210 @@
1
+ ---
2
+ name: swiftui-view-refactor
3
+ description: Refactor SwiftUI views into smaller components with stable, explicit data flow.
4
+ risk: safe
5
+ source: "Dimillian/Skills (MIT)"
6
+ date_added: "2026-03-25"
7
+ ---
8
+
9
+ # SwiftUI View Refactor
10
+
11
+ ## Overview
12
+ Refactor SwiftUI views toward small, explicit, stable view types. Default to vanilla SwiftUI: local state in the view, shared dependencies in the environment, business logic in services/models, and view models only when the request or existing code clearly requires one.
13
+
14
+ ## When to Use
15
+
16
+ - When cleaning up a large SwiftUI view or splitting long `body` implementations.
17
+ - When you need smaller subviews, explicit dependency injection, or better Observation usage.
18
+
19
+ ## Core Guidelines
20
+
21
+ ### 1) View ordering (top → bottom)
22
+ - Enforce this ordering unless the existing file has a stronger local convention you must preserve.
23
+ - Environment
24
+ - `private`/`public` `let`
25
+ - `@State` / other stored properties
26
+ - computed `var` (non-view)
27
+ - `init`
28
+ - `body`
29
+ - computed view builders / other view helpers
30
+ - helper / async functions
31
+
32
+ ### 2) Default to MV, not MVVM
33
+ - Views should be lightweight state expressions and orchestration points, not containers for business logic.
34
+ - Favor `@State`, `@Environment`, `@Query`, `.task`, `.task(id:)`, and `onChange` before reaching for a view model.
35
+ - Inject services and shared models via `@Environment`; keep domain logic in services/models, not in the view body.
36
+ - Do not introduce a view model just to mirror local view state or wrap environment dependencies.
37
+ - If a screen is getting large, split the UI into subviews before inventing a new view model layer.
38
+
39
+ ### 3) Strongly prefer dedicated subview types over computed `some View` helpers
40
+ - Flag `body` properties that are longer than roughly one screen or contain multiple logical sections.
41
+ - Prefer extracting dedicated `View` types for non-trivial sections, especially when they have state, async work, branching, or deserve their own preview.
42
+ - Keep computed `some View` helpers rare and small. Do not build an entire screen out of `private var header: some View`-style fragments.
43
+ - Pass small, explicit inputs (data, bindings, callbacks) into extracted subviews instead of handing down the entire parent state.
44
+ - If an extracted subview becomes reusable or independently meaningful, move it to its own file.
45
+
46
+ Prefer:
47
+
48
+ ```swift
49
+ var body: some View {
50
+ List {
51
+ HeaderSection(title: title, subtitle: subtitle)
52
+ FilterSection(
53
+ filterOptions: filterOptions,
54
+ selectedFilter: $selectedFilter
55
+ )
56
+ ResultsSection(items: filteredItems)
57
+ FooterSection()
58
+ }
59
+ }
60
+
61
+ private struct HeaderSection: View {
62
+ let title: String
63
+ let subtitle: String
64
+
65
+ var body: some View {
66
+ VStack(alignment: .leading, spacing: 6) {
67
+ Text(title).font(.title2)
68
+ Text(subtitle).font(.subheadline)
69
+ }
70
+ }
71
+ }
72
+
73
+ private struct FilterSection: View {
74
+ let filterOptions: [FilterOption]
75
+ @Binding var selectedFilter: FilterOption
76
+
77
+ var body: some View {
78
+ ScrollView(.horizontal, showsIndicators: false) {
79
+ HStack {
80
+ ForEach(filterOptions, id: \.self) { option in
81
+ FilterChip(option: option, isSelected: option == selectedFilter)
82
+ .onTapGesture { selectedFilter = option }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ Avoid:
91
+
92
+ ```swift
93
+ var body: some View {
94
+ List {
95
+ header
96
+ filters
97
+ results
98
+ footer
99
+ }
100
+ }
101
+
102
+ private var header: some View {
103
+ VStack(alignment: .leading, spacing: 6) {
104
+ Text(title).font(.title2)
105
+ Text(subtitle).font(.subheadline)
106
+ }
107
+ }
108
+ ```
109
+
110
+ ### 3b) Extract actions and side effects out of `body`
111
+ - Do not keep non-trivial button actions inline in the view body.
112
+ - Do not bury business logic inside `.task`, `.onAppear`, `.onChange`, or `.refreshable`.
113
+ - Prefer calling small private methods from the view, and move real business logic into services/models.
114
+ - The body should read like UI, not like a view controller.
115
+
116
+ ```swift
117
+ Button("Save", action: save)
118
+ .disabled(isSaving)
119
+
120
+ .task(id: searchText) {
121
+ await reload(for: searchText)
122
+ }
123
+
124
+ private func save() {
125
+ Task { await saveAsync() }
126
+ }
127
+
128
+ private func reload(for searchText: String) async {
129
+ guard !searchText.isEmpty else {
130
+ results = []
131
+ return
132
+ }
133
+ await searchService.search(searchText)
134
+ }
135
+ ```
136
+
137
+ ### 4) Keep a stable view tree (avoid top-level conditional view swapping)
138
+ - Avoid `body` or computed views that return completely different root branches via `if/else`.
139
+ - Prefer a single stable base view with conditions inside sections/modifiers (`overlay`, `opacity`, `disabled`, `toolbar`, etc.).
140
+ - Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.
141
+
142
+ Prefer:
143
+
144
+ ```swift
145
+ var body: some View {
146
+ List {
147
+ documentsListContent
148
+ }
149
+ .toolbar {
150
+ if canEdit {
151
+ editToolbar
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ Avoid:
158
+
159
+ ```swift
160
+ var documentsListView: some View {
161
+ if canEdit {
162
+ editableDocumentsList
163
+ } else {
164
+ readOnlyDocumentsList
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### 5) View model handling (only if already present or explicitly requested)
170
+ - Treat view models as a legacy or explicit-need pattern, not the default.
171
+ - Do not introduce a view model unless the request or existing code clearly calls for one.
172
+ - If a view model exists, make it non-optional when possible.
173
+ - Pass dependencies to the view via `init`, then create the view model in the view's `init`.
174
+ - Avoid `bootstrapIfNeeded` patterns and other delayed setup workarounds.
175
+
176
+ Example (Observation-based):
177
+
178
+ ```swift
179
+ @State private var viewModel: SomeViewModel
180
+
181
+ init(dependency: Dependency) {
182
+ _viewModel = State(initialValue: SomeViewModel(dependency: dependency))
183
+ }
184
+ ```
185
+
186
+ ### 6) Observation usage
187
+ - For `@Observable` reference types on iOS 17+, store them as `@State` in the owning view.
188
+ - Pass observables down explicitly; avoid optional state unless the UI genuinely needs it.
189
+ - If the deployment target includes iOS 16 or earlier, use `@StateObject` at the owner and `@ObservedObject` when injecting legacy observable models.
190
+
191
+ ## Workflow
192
+
193
+ 1. Reorder the view to match the ordering rules.
194
+ 2. Remove inline actions and side effects from `body`; move business logic into services/models and keep only thin orchestration in the view.
195
+ 3. Shorten long bodies by extracting dedicated subview types; avoid rebuilding the screen out of many computed `some View` helpers.
196
+ 4. Ensure stable view structure: avoid top-level `if`-based branch swapping; move conditions to localized sections/modifiers.
197
+ 5. If a view model exists or is explicitly required, replace optional view models with a non-optional `@State` view model initialized in `init`.
198
+ 6. Confirm Observation usage: `@State` for root `@Observable` models on iOS 17+, legacy wrappers only when the deployment target requires them.
199
+ 7. Keep behavior intact: do not change layout or business logic unless requested.
200
+
201
+ ## Notes
202
+
203
+ - Prefer small, explicit view types over large conditional blocks and large computed `some View` properties.
204
+ - Keep computed view builders below `body` and non-view computed vars above `init`.
205
+ - A good SwiftUI refactor should make the view read top-to-bottom as data flow plus layout, not as mixed layout and imperative logic.
206
+ - For MV-first guidance and rationale, see `references/mv-patterns.md`.
207
+
208
+ ## Large-view handling
209
+
210
+ When a SwiftUI view file exceeds ~300 lines, split it aggressively. Extract meaningful sections into dedicated `View` types instead of hiding complexity in many computed properties. Use `private` extensions with `// MARK: -` comments for actions and helpers, but do not treat extensions as a substitute for breaking a giant screen into smaller view types. If an extracted subview is reused or independently meaningful, move it into its own file.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "SwiftUI View Refactor"
3
+ short_description: "Refactor large SwiftUI view files"
4
+ default_prompt: "Use $swiftui-view-refactor to clean up and split this SwiftUI view without changing its behavior."
@@ -0,0 +1,161 @@
1
+ # MV Patterns Reference
2
+
3
+ Distilled guidance for deciding whether a SwiftUI feature should stay as plain MV or introduce a view model.
4
+
5
+ Inspired by the user's provided source, "SwiftUI in 2025: Forget MVVM" (Thomas Ricouard), but rewritten here as a practical refactoring reference.
6
+
7
+ ## Default stance
8
+
9
+ - Default to MV: views are lightweight state expressions and orchestration points.
10
+ - Prefer `@State`, `@Environment`, `@Query`, `.task`, `.task(id:)`, and `onChange` before reaching for a view model.
11
+ - Keep business logic in services, models, or domain types, not in the view body.
12
+ - Split large screens into smaller view types before inventing a view model layer.
13
+ - Avoid manual fetching or state plumbing that duplicates SwiftUI or SwiftData mechanisms.
14
+ - Test services, models, and transformations first; views should stay simple and declarative.
15
+
16
+ ## When to avoid a view model
17
+
18
+ Do not introduce a view model when it would mostly:
19
+ - mirror local view state,
20
+ - wrap values already available through `@Environment`,
21
+ - duplicate `@Query`, `@State`, or `Binding`-based data flow,
22
+ - exist only because the view body is too long,
23
+ - hold one-off async loading logic that can live in `.task` plus local view state.
24
+
25
+ In these cases, simplify the view and data flow instead of adding indirection.
26
+
27
+ ## When a view model may be justified
28
+
29
+ A view model can be reasonable when at least one of these is true:
30
+ - the user explicitly asks for one,
31
+ - the codebase already standardizes on a view model pattern for that feature,
32
+ - the screen needs a long-lived reference model with behavior that does not fit naturally in services alone,
33
+ - the feature is adapting a non-SwiftUI API that needs a dedicated bridge object,
34
+ - multiple views share the same presentation-specific state and that state is not better modeled as app-level environment data.
35
+
36
+ Even then, keep the view model small, explicit, and non-optional when possible.
37
+
38
+ ## Preferred pattern: local state plus environment
39
+
40
+ ```swift
41
+ struct FeedView: View {
42
+ @Environment(BlueSkyClient.self) private var client
43
+
44
+ enum ViewState {
45
+ case loading
46
+ case error(String)
47
+ case loaded([Post])
48
+ }
49
+
50
+ @State private var viewState: ViewState = .loading
51
+
52
+ var body: some View {
53
+ List {
54
+ switch viewState {
55
+ case .loading:
56
+ ProgressView("Loading feed...")
57
+ case .error(let message):
58
+ ErrorStateView(message: message, retryAction: { await loadFeed() })
59
+ case .loaded(let posts):
60
+ ForEach(posts) { post in
61
+ PostRowView(post: post)
62
+ }
63
+ }
64
+ }
65
+ .task { await loadFeed() }
66
+ }
67
+
68
+ private func loadFeed() async {
69
+ do {
70
+ let posts = try await client.getFeed()
71
+ viewState = .loaded(posts)
72
+ } catch {
73
+ viewState = .error(error.localizedDescription)
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Why this is preferred:
80
+ - state stays close to the UI that renders it,
81
+ - dependencies come from the environment instead of a wrapper object,
82
+ - the view coordinates UI flow while the service owns the real work.
83
+
84
+ ## Preferred pattern: use modifiers as lightweight orchestration
85
+
86
+ ```swift
87
+ .task(id: searchText) {
88
+ guard !searchText.isEmpty else {
89
+ results = []
90
+ return
91
+ }
92
+ await searchFeed(query: searchText)
93
+ }
94
+
95
+ .onChange(of: isInSearch, initial: false) {
96
+ guard !isInSearch else { return }
97
+ Task { await fetchSuggestedFeed() }
98
+ }
99
+ ```
100
+
101
+ Use view lifecycle modifiers for simple, local orchestration. Do not convert these into a view model by default unless the behavior clearly outgrows the view.
102
+
103
+ ## SwiftData note
104
+
105
+ SwiftData is a strong argument for keeping data flow inside the view when possible.
106
+
107
+ Prefer:
108
+
109
+ ```swift
110
+ struct BookListView: View {
111
+ @Query private var books: [Book]
112
+ @Environment(\.modelContext) private var modelContext
113
+
114
+ var body: some View {
115
+ List {
116
+ ForEach(books) { book in
117
+ BookRowView(book: book)
118
+ .swipeActions {
119
+ Button("Delete", role: .destructive) {
120
+ modelContext.delete(book)
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ Avoid adding a view model that manually fetches and mirrors the same state unless the feature has an explicit reason to do so.
130
+
131
+ ## Testing guidance
132
+
133
+ Prefer to test:
134
+ - services and business rules,
135
+ - models and state transformations,
136
+ - async workflows at the service layer,
137
+ - UI behavior with previews or higher-level UI tests.
138
+
139
+ Do not introduce a view model primarily to make a simple SwiftUI view "testable." That usually adds ceremony without improving the architecture.
140
+
141
+ ## Refactor checklist
142
+
143
+ When refactoring toward MV:
144
+ - Remove view models that only wrap environment dependencies or local view state.
145
+ - Replace optional or delayed-initialized view models when plain view state is enough.
146
+ - Pull business logic out of the view body and into services/models.
147
+ - Keep the view as a thin coordinator of UI state, navigation, and user actions.
148
+ - Split large bodies into smaller view types before adding new layers of indirection.
149
+
150
+ ## Bottom line
151
+
152
+ Treat view models as the exception, not the default.
153
+
154
+ In modern SwiftUI, the default stack is:
155
+ - `@State` for local state,
156
+ - `@Environment` for shared dependencies,
157
+ - `@Query` for SwiftData-backed collections,
158
+ - lifecycle modifiers for lightweight orchestration,
159
+ - services and models for business logic.
160
+
161
+ Reach for a view model only when the feature clearly needs one.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-skills-antigravity",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "OpenCode CLI plugin that automatically downloads and keeps Antigravity Awesome Skills up to date.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",