@kata-sh/cli 0.1.0 → 0.1.2
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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +56 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +95 -0
- package/dist/resource-loader.d.ts +18 -0
- package/dist/resource-loader.js +50 -0
- package/dist/wizard.d.ts +15 -0
- package/dist/wizard.js +159 -0
- package/package.json +50 -21
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +45 -0
- package/src/resources/AGENTS.md +108 -0
- package/src/resources/KATA-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +200 -0
- package/src/resources/extensions/bg-shell/index.ts +2758 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4916 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/github/formatters.ts +207 -0
- package/src/resources/extensions/github/gh-api.ts +537 -0
- package/src/resources/extensions/github/index.ts +778 -0
- package/src/resources/extensions/kata/activity-log.ts +88 -0
- package/src/resources/extensions/kata/auto.ts +2786 -0
- package/src/resources/extensions/kata/commands.ts +355 -0
- package/src/resources/extensions/kata/crash-recovery.ts +85 -0
- package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
- package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
- package/src/resources/extensions/kata/doctor.ts +683 -0
- package/src/resources/extensions/kata/files.ts +730 -0
- package/src/resources/extensions/kata/gitignore.ts +165 -0
- package/src/resources/extensions/kata/guided-flow.ts +976 -0
- package/src/resources/extensions/kata/index.ts +556 -0
- package/src/resources/extensions/kata/metrics.ts +397 -0
- package/src/resources/extensions/kata/observability-validator.ts +408 -0
- package/src/resources/extensions/kata/package.json +11 -0
- package/src/resources/extensions/kata/paths.ts +346 -0
- package/src/resources/extensions/kata/preferences.ts +695 -0
- package/src/resources/extensions/kata/prompt-loader.ts +50 -0
- package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
- package/src/resources/extensions/kata/prompts/discuss.md +151 -0
- package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
- package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
- package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
- package/src/resources/extensions/kata/prompts/queue.md +85 -0
- package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
- package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
- package/src/resources/extensions/kata/prompts/system.md +341 -0
- package/src/resources/extensions/kata/session-forensics.ts +550 -0
- package/src/resources/extensions/kata/skill-discovery.ts +137 -0
- package/src/resources/extensions/kata/state.ts +509 -0
- package/src/resources/extensions/kata/templates/context.md +76 -0
- package/src/resources/extensions/kata/templates/decisions.md +8 -0
- package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/kata/templates/plan.md +133 -0
- package/src/resources/extensions/kata/templates/preferences.md +15 -0
- package/src/resources/extensions/kata/templates/project.md +31 -0
- package/src/resources/extensions/kata/templates/reassessment.md +28 -0
- package/src/resources/extensions/kata/templates/requirements.md +81 -0
- package/src/resources/extensions/kata/templates/research.md +46 -0
- package/src/resources/extensions/kata/templates/roadmap.md +118 -0
- package/src/resources/extensions/kata/templates/slice-context.md +58 -0
- package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
- package/src/resources/extensions/kata/templates/state.md +19 -0
- package/src/resources/extensions/kata/templates/task-plan.md +52 -0
- package/src/resources/extensions/kata/templates/task-summary.md +57 -0
- package/src/resources/extensions/kata/templates/uat.md +54 -0
- package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
- package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
- package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
- package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
- package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
- package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
- package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
- package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
- package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
- package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
- package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
- package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
- package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
- package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
- package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
- package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
- package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
- package/src/resources/extensions/kata/types.ts +159 -0
- package/src/resources/extensions/kata/unit-runtime.ts +163 -0
- package/src/resources/extensions/kata/workspace-index.ts +203 -0
- package/src/resources/extensions/kata/worktree.ts +182 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +68 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +822 -0
- package/src/resources/extensions/shared/next-action-ui.ts +235 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +92 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1293 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/dist/commands/task.d.ts +0 -9
- package/dist/commands/task.d.ts.map +0 -1
- package/dist/commands/task.js +0 -129
- package/dist/commands/task.js.map +0 -1
- package/dist/commands/task.test.d.ts +0 -2
- package/dist/commands/task.test.d.ts.map +0 -1
- package/dist/commands/task.test.js +0 -169
- package/dist/commands/task.test.js.map +0 -1
- package/dist/e2e/task-e2e.test.d.ts +0 -2
- package/dist/e2e/task-e2e.test.d.ts.map +0 -1
- package/dist/e2e/task-e2e.test.js +0 -173
- package/dist/e2e/task-e2e.test.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -93
- package/dist/index.js.map +0 -1
- package/dist/slug.d.ts +0 -2
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js +0 -12
- package/dist/slug.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -32
- package/dist/slug.test.js.map +0 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<overview>
|
|
2
|
+
SwiftUI wraps UIKit on iOS and AppKit on macOS. Interoperability enables using UIKit/AppKit features not yet available in SwiftUI, and incrementally adopting SwiftUI in existing projects.
|
|
3
|
+
|
|
4
|
+
**Bridging patterns:**
|
|
5
|
+
- **SwiftUI → UIKit/AppKit**: UIViewRepresentable, NSViewRepresentable, UIViewControllerRepresentable
|
|
6
|
+
- **UIKit/AppKit → SwiftUI**: UIHostingController, NSHostingController/NSHostingView
|
|
7
|
+
- **Coordinator pattern**: Bridge delegates and target-action patterns to SwiftUI
|
|
8
|
+
|
|
9
|
+
**When to read this:**
|
|
10
|
+
- Wrapping UIKit views not available in SwiftUI
|
|
11
|
+
- Embedding SwiftUI in existing UIKit apps
|
|
12
|
+
- Handling delegate-based APIs
|
|
13
|
+
</overview>
|
|
14
|
+
|
|
15
|
+
<uiview_representable>
|
|
16
|
+
## UIViewRepresentable
|
|
17
|
+
|
|
18
|
+
**Basic structure:**
|
|
19
|
+
```swift
|
|
20
|
+
struct CustomTextField: UIViewRepresentable {
|
|
21
|
+
@Binding var text: String
|
|
22
|
+
|
|
23
|
+
func makeUIView(context: Context) -> UITextField {
|
|
24
|
+
let textField = UITextField()
|
|
25
|
+
textField.delegate = context.coordinator
|
|
26
|
+
return textField
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func updateUIView(_ uiView: UITextField, context: Context) {
|
|
30
|
+
if uiView.text != text {
|
|
31
|
+
uiView.text = text
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func makeCoordinator() -> Coordinator {
|
|
36
|
+
Coordinator(self)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class Coordinator: NSObject, UITextFieldDelegate {
|
|
40
|
+
var parent: CustomTextField
|
|
41
|
+
|
|
42
|
+
init(_ parent: CustomTextField) {
|
|
43
|
+
self.parent = parent
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func textFieldDidChangeSelection(_ textField: UITextField) {
|
|
47
|
+
parent.text = textField.text ?? ""
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Lifecycle:**
|
|
54
|
+
- `makeUIView` - called once when created
|
|
55
|
+
- `updateUIView` - called when SwiftUI state changes
|
|
56
|
+
- `dismantleUIView` - optional cleanup
|
|
57
|
+
</uiview_representable>
|
|
58
|
+
|
|
59
|
+
<uiviewcontroller_representable>
|
|
60
|
+
## UIViewControllerRepresentable
|
|
61
|
+
|
|
62
|
+
```swift
|
|
63
|
+
struct ImagePicker: UIViewControllerRepresentable {
|
|
64
|
+
@Binding var image: UIImage?
|
|
65
|
+
@Environment(\.dismiss) var dismiss
|
|
66
|
+
|
|
67
|
+
func makeUIViewController(context: Context) -> UIImagePickerController {
|
|
68
|
+
let picker = UIImagePickerController()
|
|
69
|
+
picker.delegate = context.coordinator
|
|
70
|
+
return picker
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
|
|
74
|
+
|
|
75
|
+
func makeCoordinator() -> Coordinator {
|
|
76
|
+
Coordinator(self)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
80
|
+
let parent: ImagePicker
|
|
81
|
+
|
|
82
|
+
init(_ parent: ImagePicker) {
|
|
83
|
+
self.parent = parent
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
87
|
+
parent.image = info[.originalImage] as? UIImage
|
|
88
|
+
parent.dismiss()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
</uiviewcontroller_representable>
|
|
94
|
+
|
|
95
|
+
<nsview_representable>
|
|
96
|
+
## NSViewRepresentable (macOS)
|
|
97
|
+
|
|
98
|
+
Same pattern as UIViewRepresentable:
|
|
99
|
+
|
|
100
|
+
```swift
|
|
101
|
+
struct ColorWell: NSViewRepresentable {
|
|
102
|
+
@Binding var color: NSColor
|
|
103
|
+
|
|
104
|
+
func makeNSView(context: Context) -> NSColorWell {
|
|
105
|
+
let colorWell = NSColorWell()
|
|
106
|
+
colorWell.target = context.coordinator
|
|
107
|
+
colorWell.action = #selector(Coordinator.colorDidChange(_:))
|
|
108
|
+
return colorWell
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func updateNSView(_ nsView: NSColorWell, context: Context) {
|
|
112
|
+
nsView.color = color
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func makeCoordinator() -> Coordinator {
|
|
116
|
+
Coordinator(self)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class Coordinator: NSObject {
|
|
120
|
+
var parent: ColorWell
|
|
121
|
+
|
|
122
|
+
init(_ parent: ColorWell) {
|
|
123
|
+
self.parent = parent
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@objc func colorDidChange(_ sender: NSColorWell) {
|
|
127
|
+
parent.color = sender.color
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
</nsview_representable>
|
|
133
|
+
|
|
134
|
+
<hosting_controller>
|
|
135
|
+
## UIHostingController
|
|
136
|
+
|
|
137
|
+
**Embedding SwiftUI in UIKit:**
|
|
138
|
+
```swift
|
|
139
|
+
class MainViewController: UIViewController {
|
|
140
|
+
override func viewDidLoad() {
|
|
141
|
+
super.viewDidLoad()
|
|
142
|
+
|
|
143
|
+
let swiftUIView = MySwiftUIView()
|
|
144
|
+
let hostingController = UIHostingController(rootView: swiftUIView)
|
|
145
|
+
|
|
146
|
+
addChild(hostingController)
|
|
147
|
+
view.addSubview(hostingController.view)
|
|
148
|
+
|
|
149
|
+
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
150
|
+
NSLayoutConstraint.activate([
|
|
151
|
+
hostingController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
|
152
|
+
hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
153
|
+
hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
154
|
+
hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
|
|
155
|
+
])
|
|
156
|
+
|
|
157
|
+
hostingController.didMove(toParent: self)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
</hosting_controller>
|
|
162
|
+
|
|
163
|
+
<coordinator_pattern>
|
|
164
|
+
## Coordinator Pattern
|
|
165
|
+
|
|
166
|
+
**When to use:**
|
|
167
|
+
- Handling delegate callbacks
|
|
168
|
+
- Managing target-action patterns
|
|
169
|
+
- Bridging imperative events to SwiftUI
|
|
170
|
+
|
|
171
|
+
**Structure:**
|
|
172
|
+
```swift
|
|
173
|
+
func makeCoordinator() -> Coordinator {
|
|
174
|
+
Coordinator(self)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
class Coordinator: NSObject, SomeDelegate {
|
|
178
|
+
var parent: ParentView
|
|
179
|
+
|
|
180
|
+
init(_ parent: ParentView) {
|
|
181
|
+
self.parent = parent
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
</coordinator_pattern>
|
|
186
|
+
|
|
187
|
+
<decision_tree>
|
|
188
|
+
## When to Use Interop
|
|
189
|
+
|
|
190
|
+
**Use UIKit/AppKit when:**
|
|
191
|
+
- SwiftUI lacks the feature
|
|
192
|
+
- Performance critical scenarios
|
|
193
|
+
- Integrating existing code
|
|
194
|
+
|
|
195
|
+
**Stay with pure SwiftUI when:**
|
|
196
|
+
- SwiftUI has native support
|
|
197
|
+
- Xcode Previews matter
|
|
198
|
+
- Cross-platform code needed
|
|
199
|
+
</decision_tree>
|
|
200
|
+
|
|
201
|
+
<anti_patterns>
|
|
202
|
+
## What NOT to Do
|
|
203
|
+
|
|
204
|
+
<anti_pattern name="UIKit by default">
|
|
205
|
+
**Problem:** Using UIViewRepresentable when SwiftUI works
|
|
206
|
+
**Instead:** Check if SwiftUI added the feature
|
|
207
|
+
</anti_pattern>
|
|
208
|
+
|
|
209
|
+
<anti_pattern name="Skipping Coordinator">
|
|
210
|
+
**Problem:** Handling delegates without Coordinator
|
|
211
|
+
**Instead:** Always use Coordinator for delegate patterns
|
|
212
|
+
</anti_pattern>
|
|
213
|
+
|
|
214
|
+
<anti_pattern name="Memory leaks in hosting">
|
|
215
|
+
**Problem:** Not managing child view controller properly
|
|
216
|
+
**Instead:** addChild → addSubview → didMove(toParent:)
|
|
217
|
+
</anti_pattern>
|
|
218
|
+
</anti_patterns>
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<required_reading>
|
|
2
|
+
**Read these reference files NOW before starting:**
|
|
3
|
+
1. `../macos-apps/references/cli-workflow.md` - Build, run, test from CLI
|
|
4
|
+
2. `references/architecture.md` - App structure, MVVM patterns
|
|
5
|
+
3. `references/state-management.md` - Property wrappers, @Observable
|
|
6
|
+
</required_reading>
|
|
7
|
+
|
|
8
|
+
<process>
|
|
9
|
+
## Step 1: Understand Existing Codebase
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
find . -name "*.swift" -type f | head -20
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Identify:**
|
|
16
|
+
- App architecture (MVVM, TCA, etc.)
|
|
17
|
+
- Existing patterns and conventions
|
|
18
|
+
- Navigation approach
|
|
19
|
+
- Dependency injection method
|
|
20
|
+
|
|
21
|
+
## Step 2: Plan Feature Integration
|
|
22
|
+
|
|
23
|
+
**Define scope:**
|
|
24
|
+
- What views needed?
|
|
25
|
+
- What state must be managed?
|
|
26
|
+
- Does it need persistence (SwiftData)?
|
|
27
|
+
- Does it need network calls?
|
|
28
|
+
- How does it connect to existing features?
|
|
29
|
+
|
|
30
|
+
## Step 3: Create Feature Module
|
|
31
|
+
|
|
32
|
+
Follow existing organization:
|
|
33
|
+
```
|
|
34
|
+
Features/
|
|
35
|
+
YourFeature/
|
|
36
|
+
Views/
|
|
37
|
+
YourFeatureView.swift
|
|
38
|
+
ViewModels/
|
|
39
|
+
YourFeatureViewModel.swift
|
|
40
|
+
Models/
|
|
41
|
+
YourFeatureModel.swift
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Step 4: Implement View Model
|
|
45
|
+
|
|
46
|
+
```swift
|
|
47
|
+
@Observable
|
|
48
|
+
final class YourFeatureViewModel {
|
|
49
|
+
var items: [YourModel] = []
|
|
50
|
+
var isLoading = false
|
|
51
|
+
var errorMessage: String?
|
|
52
|
+
|
|
53
|
+
private let dataService: DataService
|
|
54
|
+
|
|
55
|
+
init(dataService: DataService) {
|
|
56
|
+
self.dataService = dataService
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func loadData() async {
|
|
60
|
+
isLoading = true
|
|
61
|
+
defer { isLoading = false }
|
|
62
|
+
|
|
63
|
+
do {
|
|
64
|
+
items = try await dataService.fetchItems()
|
|
65
|
+
} catch {
|
|
66
|
+
errorMessage = error.localizedDescription
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Step 5: Implement Views
|
|
73
|
+
|
|
74
|
+
```swift
|
|
75
|
+
struct YourFeatureView: View {
|
|
76
|
+
@State private var viewModel: YourFeatureViewModel
|
|
77
|
+
|
|
78
|
+
init(viewModel: YourFeatureViewModel) {
|
|
79
|
+
self.viewModel = viewModel
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var body: some View {
|
|
83
|
+
List(viewModel.items) { item in
|
|
84
|
+
NavigationLink(value: item) {
|
|
85
|
+
YourItemRow(item: item)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
.navigationTitle("Feature Title")
|
|
89
|
+
.navigationDestination(for: YourModel.self) { item in
|
|
90
|
+
YourFeatureDetailView(item: item)
|
|
91
|
+
}
|
|
92
|
+
.task {
|
|
93
|
+
await viewModel.loadData()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Step 6: Wire Up Navigation
|
|
100
|
+
|
|
101
|
+
**NavigationStack routing:**
|
|
102
|
+
```swift
|
|
103
|
+
NavigationLink(value: NavigationDestination.yourFeature) {
|
|
104
|
+
Text("Go to Feature")
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.navigationDestination(for: NavigationDestination.self) { destination in
|
|
108
|
+
switch destination {
|
|
109
|
+
case .yourFeature:
|
|
110
|
+
YourFeatureView(viewModel: viewModel)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Sheet presentation:**
|
|
116
|
+
```swift
|
|
117
|
+
@State private var showingFeature = false
|
|
118
|
+
|
|
119
|
+
Button("Show") { showingFeature = true }
|
|
120
|
+
.sheet(isPresented: $showingFeature) {
|
|
121
|
+
NavigationStack { YourFeatureView(viewModel: viewModel) }
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Step 7: Build and Verify
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# 1. Build
|
|
129
|
+
xcodebuild -scheme AppName build 2>&1 | xcsift
|
|
130
|
+
|
|
131
|
+
# 2. Run tests
|
|
132
|
+
xcodebuild -scheme AppName test 2>&1 | xcsift
|
|
133
|
+
|
|
134
|
+
# 3. Launch and monitor
|
|
135
|
+
# macOS:
|
|
136
|
+
open ./build/Build/Products/Debug/AppName.app
|
|
137
|
+
log stream --predicate 'subsystem == "com.yourcompany.appname"' --level debug
|
|
138
|
+
|
|
139
|
+
# iOS Simulator:
|
|
140
|
+
xcrun simctl boot "iPhone 15 Pro" 2>/dev/null || true
|
|
141
|
+
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/AppName.app
|
|
142
|
+
xcrun simctl launch booted com.yourcompany.appname
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Report to user:
|
|
146
|
+
- "Build: ✓"
|
|
147
|
+
- "Tests: X pass, 0 fail"
|
|
148
|
+
- "Feature added. Ready for you to test [navigation path to feature]"
|
|
149
|
+
|
|
150
|
+
**User verifies:**
|
|
151
|
+
- Navigate to feature from all entry points
|
|
152
|
+
- Test interactions
|
|
153
|
+
- Check loading/error states
|
|
154
|
+
- Verify light and dark mode
|
|
155
|
+
</process>
|
|
156
|
+
|
|
157
|
+
<anti_patterns>
|
|
158
|
+
## Avoid These Mistakes
|
|
159
|
+
|
|
160
|
+
**Not following existing patterns:**
|
|
161
|
+
- Creating new navigation when project has established pattern
|
|
162
|
+
- Using different naming conventions
|
|
163
|
+
- Introducing new DI when project has standard
|
|
164
|
+
|
|
165
|
+
**Overengineering:**
|
|
166
|
+
- Adding abstraction that doesn't exist elsewhere
|
|
167
|
+
- Creating generic solutions for specific problems
|
|
168
|
+
- Breaking single view into dozens of tiny files prematurely
|
|
169
|
+
|
|
170
|
+
**Tight coupling:**
|
|
171
|
+
- Accessing other features' view models directly
|
|
172
|
+
- Hardcoding dependencies
|
|
173
|
+
- Circular dependencies between features
|
|
174
|
+
|
|
175
|
+
**Breaking existing functionality:**
|
|
176
|
+
- Modifying shared view models without checking all callers
|
|
177
|
+
- Changing navigation state structure
|
|
178
|
+
- Removing @Environment values other views depend on
|
|
179
|
+
</anti_patterns>
|
|
180
|
+
|
|
181
|
+
<success_criteria>
|
|
182
|
+
This workflow is complete when:
|
|
183
|
+
- [ ] Feature matches existing architecture patterns
|
|
184
|
+
- [ ] Views compose with existing navigation
|
|
185
|
+
- [ ] State management follows project conventions
|
|
186
|
+
- [ ] Dependency injection consistent with existing code
|
|
187
|
+
- [ ] All existing tests pass
|
|
188
|
+
- [ ] No compiler warnings introduced
|
|
189
|
+
- [ ] Error states handled gracefully
|
|
190
|
+
- [ ] Code follows existing naming conventions
|
|
191
|
+
</success_criteria>
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
<required_reading>
|
|
2
|
+
**Read these reference files NOW before starting:**
|
|
3
|
+
1. `../macos-apps/references/project-scaffolding.md` - XcodeGen templates and file structure
|
|
4
|
+
2. `../macos-apps/references/cli-workflow.md` - Build/run/test from CLI
|
|
5
|
+
3. `references/architecture.md` - MVVM patterns and project structure
|
|
6
|
+
4. `references/state-management.md` - Property wrappers
|
|
7
|
+
</required_reading>
|
|
8
|
+
|
|
9
|
+
<process>
|
|
10
|
+
## Step 1: Clarify Requirements
|
|
11
|
+
|
|
12
|
+
Ask the user:
|
|
13
|
+
- What does the app do? (core functionality)
|
|
14
|
+
- Which platform? (iOS, macOS, or both)
|
|
15
|
+
- Any specific features needed? (persistence, networking, system integration)
|
|
16
|
+
|
|
17
|
+
## Step 2: Scaffold Project with XcodeGen
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Create directory structure
|
|
21
|
+
mkdir -p AppName/Sources AppName/Tests AppName/Resources
|
|
22
|
+
cd AppName
|
|
23
|
+
|
|
24
|
+
# Create project.yml (see ../macos-apps/references/project-scaffolding.md for full template)
|
|
25
|
+
cat > project.yml << 'EOF'
|
|
26
|
+
name: AppName
|
|
27
|
+
options:
|
|
28
|
+
bundleIdPrefix: com.yourcompany
|
|
29
|
+
deploymentTarget:
|
|
30
|
+
iOS: "17.0"
|
|
31
|
+
macOS: "14.0"
|
|
32
|
+
xcodeVersion: "15.0"
|
|
33
|
+
createIntermediateGroups: true
|
|
34
|
+
|
|
35
|
+
targets:
|
|
36
|
+
AppName:
|
|
37
|
+
type: application
|
|
38
|
+
platform: iOS # or macOS, or [iOS, macOS] for multi-platform
|
|
39
|
+
sources: [Sources]
|
|
40
|
+
resources: [Resources]
|
|
41
|
+
settings:
|
|
42
|
+
base:
|
|
43
|
+
PRODUCT_BUNDLE_IDENTIFIER: com.yourcompany.appname
|
|
44
|
+
DEVELOPMENT_TEAM: YOURTEAMID
|
|
45
|
+
SWIFT_VERSION: "5.9"
|
|
46
|
+
|
|
47
|
+
AppNameTests:
|
|
48
|
+
type: bundle.unit-test
|
|
49
|
+
platform: iOS
|
|
50
|
+
sources: [Tests]
|
|
51
|
+
dependencies:
|
|
52
|
+
- target: AppName
|
|
53
|
+
|
|
54
|
+
schemes:
|
|
55
|
+
AppName:
|
|
56
|
+
build:
|
|
57
|
+
targets:
|
|
58
|
+
AppName: all
|
|
59
|
+
AppNameTests: [test]
|
|
60
|
+
test:
|
|
61
|
+
targets: [AppNameTests]
|
|
62
|
+
EOF
|
|
63
|
+
|
|
64
|
+
# Generate xcodeproj
|
|
65
|
+
xcodegen generate
|
|
66
|
+
|
|
67
|
+
# Verify
|
|
68
|
+
xcodebuild -list -project AppName.xcodeproj
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Step 3: Create Source Files
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Sources/
|
|
75
|
+
├── AppNameApp.swift # App entry point
|
|
76
|
+
├── ContentView.swift # Main view
|
|
77
|
+
├── Models/
|
|
78
|
+
├── ViewModels/
|
|
79
|
+
├── Views/
|
|
80
|
+
│ ├── Screens/
|
|
81
|
+
│ └── Components/
|
|
82
|
+
├── Services/
|
|
83
|
+
└── Info.plist
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Step 4: Configure App Entry Point
|
|
87
|
+
|
|
88
|
+
```swift
|
|
89
|
+
import SwiftUI
|
|
90
|
+
|
|
91
|
+
@main
|
|
92
|
+
struct YourAppNameApp: App {
|
|
93
|
+
var body: some Scene {
|
|
94
|
+
WindowGroup {
|
|
95
|
+
ContentView()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Step 5: Create Base Navigation
|
|
102
|
+
|
|
103
|
+
**Tab-based app:**
|
|
104
|
+
```swift
|
|
105
|
+
struct MainTabView: View {
|
|
106
|
+
var body: some View {
|
|
107
|
+
TabView {
|
|
108
|
+
HomeView()
|
|
109
|
+
.tabItem { Label("Home", systemImage: "house") }
|
|
110
|
+
SettingsView()
|
|
111
|
+
.tabItem { Label("Settings", systemImage: "gear") }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Stack-based navigation:**
|
|
118
|
+
```swift
|
|
119
|
+
struct RootView: View {
|
|
120
|
+
var body: some View {
|
|
121
|
+
NavigationStack {
|
|
122
|
+
HomeView()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Step 6: Implement First View Model
|
|
129
|
+
|
|
130
|
+
```swift
|
|
131
|
+
import Foundation
|
|
132
|
+
import Observation
|
|
133
|
+
|
|
134
|
+
@Observable
|
|
135
|
+
final class HomeViewModel {
|
|
136
|
+
var items: [Item] = []
|
|
137
|
+
var isLoading = false
|
|
138
|
+
var errorMessage: String?
|
|
139
|
+
|
|
140
|
+
func loadData() async {
|
|
141
|
+
isLoading = true
|
|
142
|
+
defer { isLoading = false }
|
|
143
|
+
|
|
144
|
+
do {
|
|
145
|
+
// items = try await service.fetchItems()
|
|
146
|
+
} catch {
|
|
147
|
+
errorMessage = error.localizedDescription
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Step 7: Create Main View
|
|
154
|
+
|
|
155
|
+
```swift
|
|
156
|
+
struct HomeView: View {
|
|
157
|
+
@State private var viewModel = HomeViewModel()
|
|
158
|
+
|
|
159
|
+
var body: some View {
|
|
160
|
+
List(viewModel.items) { item in
|
|
161
|
+
Text(item.name)
|
|
162
|
+
}
|
|
163
|
+
.navigationTitle("Home")
|
|
164
|
+
.overlay {
|
|
165
|
+
if viewModel.isLoading { ProgressView() }
|
|
166
|
+
}
|
|
167
|
+
.task {
|
|
168
|
+
await viewModel.loadData()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#Preview {
|
|
174
|
+
NavigationStack { HomeView() }
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Step 8: Wire Up Dependencies
|
|
179
|
+
|
|
180
|
+
```swift
|
|
181
|
+
@Observable
|
|
182
|
+
final class AppDependencies {
|
|
183
|
+
let apiService: APIService
|
|
184
|
+
|
|
185
|
+
static let shared = AppDependencies()
|
|
186
|
+
|
|
187
|
+
private init() {
|
|
188
|
+
self.apiService = APIService()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Inject in App:
|
|
194
|
+
```swift
|
|
195
|
+
@main
|
|
196
|
+
struct YourAppNameApp: App {
|
|
197
|
+
@State private var dependencies = AppDependencies.shared
|
|
198
|
+
|
|
199
|
+
var body: some Scene {
|
|
200
|
+
WindowGroup {
|
|
201
|
+
ContentView()
|
|
202
|
+
.environment(dependencies)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Step 9: Build and Verify
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Build with error parsing
|
|
212
|
+
xcodebuild -scheme AppName -destination 'platform=iOS Simulator,name=iPhone 15 Pro' build 2>&1 | xcsift
|
|
213
|
+
|
|
214
|
+
# Boot simulator and install
|
|
215
|
+
xcrun simctl boot "iPhone 15 Pro" 2>/dev/null || true
|
|
216
|
+
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/AppName.app
|
|
217
|
+
|
|
218
|
+
# Launch and stream logs
|
|
219
|
+
xcrun simctl launch booted com.yourcompany.appname
|
|
220
|
+
log stream --predicate 'subsystem == "com.yourcompany.appname"' --level debug
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
For macOS apps:
|
|
224
|
+
```bash
|
|
225
|
+
xcodebuild -scheme AppName build 2>&1 | xcsift
|
|
226
|
+
open ./build/Build/Products/Debug/AppName.app
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Report to user:
|
|
230
|
+
- "Build: ✓"
|
|
231
|
+
- "App installed on simulator, launching now"
|
|
232
|
+
- "Ready for you to check [specific functionality]"
|
|
233
|
+
</process>
|
|
234
|
+
|
|
235
|
+
<anti_patterns>
|
|
236
|
+
## Avoid These Mistakes
|
|
237
|
+
|
|
238
|
+
**Using NavigationView:**
|
|
239
|
+
```swift
|
|
240
|
+
// DON'T
|
|
241
|
+
NavigationView { ContentView() }
|
|
242
|
+
|
|
243
|
+
// DO
|
|
244
|
+
NavigationStack { ContentView() }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Using ObservableObject for new code:**
|
|
248
|
+
```swift
|
|
249
|
+
// DON'T
|
|
250
|
+
class ViewModel: ObservableObject {
|
|
251
|
+
@Published var data = []
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// DO
|
|
255
|
+
@Observable
|
|
256
|
+
final class ViewModel {
|
|
257
|
+
var data = []
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Massive views:**
|
|
262
|
+
```swift
|
|
263
|
+
// DON'T
|
|
264
|
+
struct HomeView: View {
|
|
265
|
+
var body: some View {
|
|
266
|
+
VStack { /* 300 lines */ }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// DO
|
|
271
|
+
struct HomeView: View {
|
|
272
|
+
var body: some View {
|
|
273
|
+
VStack {
|
|
274
|
+
HeaderComponent()
|
|
275
|
+
ContentList()
|
|
276
|
+
FooterActions()
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Missing previews:**
|
|
283
|
+
```swift
|
|
284
|
+
// Always add previews for iteration
|
|
285
|
+
#Preview { HomeView() }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Business logic in views:**
|
|
289
|
+
```swift
|
|
290
|
+
// Move to view model
|
|
291
|
+
struct ProductView: View {
|
|
292
|
+
@State private var viewModel = ProductViewModel()
|
|
293
|
+
|
|
294
|
+
var body: some View {
|
|
295
|
+
Button("Buy") { Task { await viewModel.purchase() } }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
</anti_patterns>
|
|
300
|
+
|
|
301
|
+
<success_criteria>
|
|
302
|
+
This workflow is complete when:
|
|
303
|
+
- [ ] Project builds without errors
|
|
304
|
+
- [ ] Folder structure matches MVVM pattern
|
|
305
|
+
- [ ] Navigation set up with NavigationStack or TabView
|
|
306
|
+
- [ ] At least one @Observable view model exists
|
|
307
|
+
- [ ] Dependencies injected via @Environment
|
|
308
|
+
- [ ] No deprecated APIs (NavigationView, ObservableObject)
|
|
309
|
+
- [ ] SwiftUI previews render correctly
|
|
310
|
+
- [ ] App launches without warnings
|
|
311
|
+
</success_criteria>
|