@mmerterden/multi-agent-pipeline 20.35.0 → 20.37.0

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 (207) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +6 -5
  3. package/README.tr.md +6 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +4 -4
  7. package/install/catalog-history.json +1 -1
  8. package/manifest.json +210 -136
  9. package/package.json +5 -1
  10. package/pipeline/commands/multi-agent/SKILL.md +33 -154
  11. package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
  12. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
  13. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
  14. package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
  15. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
  16. package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
  17. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
  18. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
  19. package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
  20. package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
  21. package/pipeline/commands/multi-agent/office-status/SKILL.md +1 -1
  22. package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
  23. package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
  24. package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
  25. package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
  26. package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
  27. package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
  28. package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
  29. package/pipeline/commands/multi-agent/sync/SKILL.md +38 -167
  30. package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
  31. package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
  32. package/pipeline/contract/CHANGELOG.md +9 -0
  33. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  34. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  35. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  36. package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
  37. package/pipeline/contract/fixtures/runs-empty.json +1 -1
  38. package/pipeline/contract/fixtures/runs-failed.json +1 -1
  39. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
  40. package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
  41. package/pipeline/contract/fixtures/runs-running.json +1 -1
  42. package/pipeline/contract/manifest.json +5 -5
  43. package/pipeline/contract/types/index.d.ts +3 -2
  44. package/pipeline/lib/qr-code.mjs +26 -0
  45. package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
  46. package/pipeline/multi-agent-refs/channels/generator.md +14 -0
  47. package/pipeline/multi-agent-refs/channels/preview.md +13 -0
  48. package/pipeline/multi-agent-refs/channels/reference.md +46 -0
  49. package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
  50. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  51. package/pipeline/multi-agent-refs/design-check/export.md +1 -0
  52. package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
  53. package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
  54. package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
  55. package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
  56. package/pipeline/multi-agent-refs/help/en.md +1 -0
  57. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  58. package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
  59. package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
  60. package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
  61. package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
  62. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
  63. package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
  64. package/pipeline/multi-agent-refs/resume/details.md +115 -2
  65. package/pipeline/multi-agent-refs/review/inputs.md +33 -1
  66. package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
  67. package/pipeline/multi-agent-refs/review/panel.md +75 -0
  68. package/pipeline/multi-agent-refs/review/valven.md +5 -1
  69. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
  70. package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
  71. package/pipeline/multi-agent-refs/stack/background.md +72 -0
  72. package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
  73. package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
  74. package/pipeline/multi-agent-refs/sync/office.md +10 -0
  75. package/pipeline/multi-agent-refs/sync/overview.md +61 -0
  76. package/pipeline/multi-agent-refs/sync/repo.md +91 -0
  77. package/pipeline/multi-agent-refs/update/details.md +83 -0
  78. package/pipeline/schemas/runs-index.schema.json +2 -1
  79. package/pipeline/scripts/contract-server.mjs +1 -1
  80. package/pipeline/scripts/office-pair.mjs +40 -9
  81. package/pipeline/scripts/runs-index.mjs +34 -7
  82. package/pipeline/scripts/status-overview.mjs +401 -0
  83. package/pipeline/skills/.skill-manifest.json +69 -65
  84. package/pipeline/skills/.skills-index.json +15 -4
  85. package/pipeline/skills/shared/README.md +4 -3
  86. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -71
  87. package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
  88. package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
  89. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +11 -73
  90. package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
  91. package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
  92. package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
  93. package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
  94. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
  95. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
  96. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
  97. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
  98. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
  99. package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
  100. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +1 -1
  101. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
  102. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
  103. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
  104. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
  105. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +75 -219
  106. package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
  107. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
  108. package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
  109. package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
  110. package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
  111. package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
  112. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
  113. package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
  114. package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
  115. package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
  116. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
  117. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
  118. package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
  119. package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
  120. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
  121. package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
  122. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
  123. package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
  124. package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
  125. package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
  126. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
  127. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
  128. package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
  129. package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
  130. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
  131. package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
  132. package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
  133. package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
  134. package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
  135. package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
  136. package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
  137. package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
  138. package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
  139. package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
  140. package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
  141. package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
  142. package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
  143. package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
  144. package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
  145. package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
  146. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
  147. package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
  148. package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
  149. package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
  150. package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
  151. package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
  152. package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
  153. package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
  154. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
  155. package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
  156. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
  157. package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
  158. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
  159. package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
  160. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
  161. package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
  162. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
  163. package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
  164. package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
  165. package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
  166. package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
  167. package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
  168. package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
  169. package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
  170. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
  171. package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
  172. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
  173. package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
  174. package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
  175. package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
  176. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
  177. package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
  178. package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
  179. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
  180. package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
  181. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
  182. package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
  183. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
  184. package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
  185. package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
  186. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
  187. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
  188. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
  189. package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
  190. package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
  191. package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
  192. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
  193. package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
  194. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
  195. package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
  196. package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
  197. package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
  198. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
  199. package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
  200. package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
  201. package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
  202. package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
  203. package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
  204. package/pipeline/skills/skills-index.md +2 -1
  205. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
  206. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
  207. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
@@ -0,0 +1,257 @@
1
+ # AlarmKit code
2
+
3
+ <!-- toc -->
4
+ - [Authorization](#authorization)
5
+ - [Countdown timers](#countdown-timers)
6
+ - [Live Activity integration](#live-activity-integration)
7
+ <!-- /toc -->
8
+
9
+ Code for authorization, scheduling, countdown timers, presentations, metadata and the Live Activity widget. SKILL.md keeps the rules, lifecycle and review guidance.
10
+
11
+ ## Authorization
12
+
13
+ The user must grant access. The app may ask up front, or AlarmKit will prompt
14
+ the first time something is scheduled. If neither prompt ends in approval, no
15
+ alarm is scheduled and nothing will ring.
16
+
17
+ ```swift
18
+ import AlarmKit
19
+
20
+ func ensureAlarmAccess() async -> Bool {
21
+ let center = AlarmManager.shared
22
+ switch center.authorizationState {
23
+ case .authorized:
24
+ return true
25
+ case .denied:
26
+ return false
27
+ case .notDetermined:
28
+ let answer = try? await center.requestAuthorization()
29
+ return answer == .authorized
30
+ @unknown default:
31
+ return false
32
+ }
33
+ }
34
+
35
+ func watchAlarmAccess(onChange: @escaping (Bool) -> Void) async {
36
+ for await state in AlarmManager.shared.authorizationUpdates {
37
+ switch state {
38
+ case .authorized: onChange(true)
39
+ case .denied, .notDetermined: onChange(false)
40
+ @unknown default: onChange(false)
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ `AlarmManager.AuthorizationState` has three cases: `.notDetermined`,
47
+ `.denied` and `.authorized`. `authorizationState` is a synchronous read;
48
+ `authorizationUpdates` is an async sequence of changes.
49
+
50
+ ### Configuring and scheduling
51
+
52
+ ```swift
53
+ import AlarmKit
54
+ import SwiftUI
55
+
56
+ struct ShiftInfo: AlarmMetadata {
57
+ var shiftName: String
58
+ }
59
+
60
+ @available(iOS 26.1, *)
61
+ func scheduleShiftAlarm() async throws -> Alarm {
62
+ let dawn = Alarm.Schedule.Relative.Time(hour: 5, minute: 30)
63
+ let alert = AlarmPresentation.Alert(
64
+ title: "Early shift",
65
+ secondaryButton: AlarmButton(text: "Later", textColor: .white, systemImageName: "zzz"),
66
+ secondaryButtonBehavior: .countdown
67
+ )
68
+ let attributes = AlarmAttributes(
69
+ presentation: AlarmPresentation(alert: alert),
70
+ metadata: ShiftInfo(shiftName: "Warehouse A"),
71
+ tintColor: .teal
72
+ )
73
+ let config = AlarmManager.AlarmConfiguration(
74
+ countdownDuration: Alarm.CountdownDuration(preAlert: nil, postAlert: 9 * 60),
75
+ schedule: .relative(.init(time: dawn, repeats: .never)),
76
+ attributes: attributes,
77
+ sound: .default
78
+ )
79
+ let center = AlarmManager.shared
80
+ return try await center.schedule(id: UUID(), configuration: config)
81
+ }
82
+ ```
83
+
84
+ Here `postAlert` is what makes the secondary button a snooze: pressing it
85
+ starts a nine-minute countdown and then rings again.
86
+
87
+ `stopIntent` and `secondaryIntent` both default to `nil`, and that is usually
88
+ right:
89
+
90
+ - Leave `stopIntent` out when the system Stop is all you need. Pass one only
91
+ when stopping must also clean up app state or trigger some side effect.
92
+ - Leave `secondaryIntent` out for a plain snooze or repeat, which is
93
+ `secondaryButtonBehavior: .countdown` plus `CountdownDuration.postAlert`.
94
+ Pass one only with `.custom` behavior or when the app must do its own work.
95
+
96
+ ## Countdown timers
97
+
98
+ A timer rings once a duration has passed and always shows its countdown.
99
+
100
+ ```swift
101
+ import AlarmKit
102
+ import SwiftUI
103
+
104
+ struct BrewInfo: AlarmMetadata {
105
+ var blend: String
106
+ }
107
+
108
+ @available(iOS 26.1, *)
109
+ func startSteepTimer() async throws {
110
+ let presentation = AlarmPresentation(
111
+ alert: .init(title: "Tea is ready"),
112
+ countdown: .init(title: "Steeping"),
113
+ paused: .init(title: "Steeping paused",
114
+ resumeButton: AlarmButton(text: "Resume", textColor: .white, systemImageName: "play.fill"))
115
+ )
116
+ let attributes = AlarmAttributes(presentation: presentation,
117
+ metadata: BrewInfo(blend: "Sencha"),
118
+ tintColor: .green)
119
+ let threeMinutes = AlarmManager.AlarmConfiguration.timer(duration: 180, attributes: attributes, sound: .default)
120
+ let center = AlarmManager.shared
121
+ _ = try await center.schedule(id: UUID(), configuration: threeMinutes)
122
+ }
123
+ ```
124
+
125
+ ### AlarmPresentation
126
+
127
+ `AlarmPresentation` describes the text and buttons per state. The system draws
128
+ the alerting UI from it; the widget extension may draw countdown and paused.
129
+
130
+ | Type | State | Required |
131
+ |---|---|---|
132
+ | `AlarmPresentation.Alert(title:secondaryButton:secondaryButtonBehavior:)` | alerting | yes |
133
+ | `AlarmPresentation.Countdown(title:pauseButton:)` | counting down | no |
134
+ | `AlarmPresentation.Paused(title:resumeButton:)` | paused | no |
135
+
136
+ Combine them with `AlarmPresentation(alert:countdown:paused:)`:
137
+
138
+ ```swift
139
+ import AlarmKit
140
+ import SwiftUI
141
+
142
+ @available(iOS 26.1, *)
143
+ func workoutPresentation() -> AlarmPresentation {
144
+ let again = AlarmButton(text: "Again", textColor: .white, systemImageName: "repeat")
145
+ let hold = AlarmButton(text: "Hold", textColor: .orange, systemImageName: "pause.fill")
146
+ let go = AlarmButton(text: "Go", textColor: .orange, systemImageName: "play.fill")
147
+ return AlarmPresentation(
148
+ alert: .init(title: "Rest over", secondaryButton: again, secondaryButtonBehavior: .countdown),
149
+ countdown: .init(title: "Resting", pauseButton: hold),
150
+ paused: .init(title: "Rest on hold", resumeButton: go)
151
+ )
152
+ }
153
+ ```
154
+
155
+ ### Metadata
156
+
157
+ ```swift
158
+ import AlarmKit
159
+ import SwiftUI
160
+
161
+ struct PlantInfo: AlarmMetadata { var plantName: String; var litres: Double }
162
+ struct NoAlarmData: AlarmMetadata {}
163
+
164
+ @available(iOS 26.1, *)
165
+ func plantAttributes() -> (AlarmAttributes<PlantInfo>, AlarmAttributes<NoAlarmData>) {
166
+ let look = AlarmPresentation(alert: .init(title: "Water the fern"))
167
+ let withData = AlarmAttributes(presentation: look, metadata: PlantInfo(plantName: "Fern", litres: 0.5), tintColor: .mint)
168
+ let plain = AlarmAttributes<NoAlarmData>(presentation: look, metadata: nil, tintColor: .mint)
169
+ return (withData, plain)
170
+ }
171
+ ```
172
+
173
+ When there is nothing to carry, declare an empty type that conforms to
174
+ `AlarmMetadata` and pass `metadata: nil`.
175
+
176
+ ## Live Activity integration
177
+
178
+ A ringing alarm is a Live Activity that the system renders on the Lock Screen,
179
+ in the Dynamic Island, in StandBy and on a paired Apple Watch; the app has no
180
+ say over that screen. Countdown and paused are different: the app supplies a widget
181
+ extension whose `ActivityConfiguration` names the same
182
+ `AlarmAttributes<Metadata>` type used at scheduling.
183
+
184
+ - Plan on the extension whenever countdown presentation is used. The metadata
185
+ type must be compiled into both the app and the extension, and it should stay
186
+ small.
187
+ - Leave the extension out and an alarm can vanish early or never ring. The
188
+ system only draws a fallback countdown in narrow cases, such as after a
189
+ restart before the first unlock.
190
+
191
+ AlarmKit is responsible for: permission to schedule alarms, `AlarmManager` scheduling and state,
192
+ `AlarmAttributes`, `AlarmPresentation`, `AlarmPresentationState`, the sound,
193
+ and the system Stop, Repeat and Open App actions. It is not a general surface
194
+ for custom notification UI.
195
+
196
+ When describing where alarms appear, name Lock Screen, Dynamic Island, StandBy
197
+ and paired Apple Watch together. Smart Stack is not an AlarmKit surface.
198
+
199
+ Hand-offs:
200
+
201
+ - Widgets on the Home Screen or in Smart Stack, `WidgetFamily`, timelines,
202
+ `WidgetCenter` reloads: `widgetkit`.
203
+ - Non-alarm Live Activities (`Activity.request`, `update`, `end`),
204
+ push-to-start and per-activity update tokens, and remote `content-state`
205
+ payloads: `live-activities`.
206
+ - APNs, `UNUserNotificationCenter`, notification categories and actions,
207
+ custom notification UI: `push-notifications`, unless the code ends up
208
+ calling `AlarmManager`.
209
+
210
+ Setup stays with what Apple documents: `NSAlarmKitUsageDescription` and
211
+ `AlarmManager` authorization. Do not require extra keys or a
212
+ `com.apple.developer.alarmkit` entitlement unless a current Apple source says so.
213
+
214
+ ```swift
215
+ import ActivityKit
216
+ import AlarmKit
217
+ import SwiftUI
218
+ import WidgetKit
219
+
220
+ struct CoachInfo: AlarmMetadata {
221
+ var drill: String
222
+ }
223
+
224
+ struct CoachAlarmWidget: Widget {
225
+ var body: some WidgetConfiguration {
226
+ ActivityConfiguration(for: AlarmAttributes<CoachInfo>.self) { context in
227
+ Text(context.attributes.metadata?.drill ?? "Timer")
228
+ .padding()
229
+ } dynamicIsland: { context in
230
+ DynamicIsland {
231
+ DynamicIslandExpandedRegion(.center) {
232
+ let heading = context.attributes.presentation.alert.title
233
+ Text(heading)
234
+ }
235
+ DynamicIslandExpandedRegion(.bottom) {
236
+ if case .countdown(let run) = context.state.mode {
237
+ Text(timerInterval: Date.now...run.fireDate, countsDown: true)
238
+ }
239
+ }
240
+ } compactLeading: {
241
+ Image(systemName: "alarm.fill")
242
+ } compactTrailing: {
243
+ Image(systemName: "alarm.fill")
244
+ } minimal: {
245
+ Image(systemName: "alarm.fill")
246
+ }
247
+ }
248
+ }
249
+ }
250
+
251
+ @main
252
+ struct CoachWidgets: WidgetBundle {
253
+ var body: some Widget {
254
+ CoachAlarmWidget()
255
+ }
256
+ }
257
+ ```
@@ -179,32 +179,9 @@ The data layer implements repository interfaces. It coordinates between remote
179
179
 
180
180
  ### Repository Implementation
181
181
 
182
- ```kotlin
183
- class OrderRepositoryImpl @Inject constructor(
184
- private val remoteDataSource: OrderRemoteDataSource,
185
- private val localDataSource: OrderLocalDataSource,
186
- private val orderMapper: OrderMapper,
187
- ) : OrderRepository {
188
-
189
- override fun getOrders(store: String, region: String): Flow<List<Order>> =
190
- localDataSource.getOrders(store, region)
191
- .map { entities -> entities.map(orderMapper::toDomain) }
192
-
193
- override fun searchOrders(query: String, date: LocalDate): Flow<List<Order>> = flow {
194
- val dtos = remoteDataSource.searchOrders(query, date.toString())
195
- localDataSource.insertOrders(dtos.map(orderMapper::toEntity))
196
- emitAll(
197
- localDataSource.getOrders(query, query)
198
- .map { entities -> entities.map(orderMapper::toDomain) }
199
- )
200
- }
182
+ The implementation is injected, maps entities to domain models through the mapper on every read, exposes reads as `Flow`, and wraps suspend writes in `Result` with `runCatching`.
201
183
 
202
- override suspend fun payOrder(orderId: String): Result<Payment> = runCatching {
203
- val response = remoteDataSource.payOrder(orderId)
204
- orderMapper.toPaymentDomain(response)
205
- }
206
- }
207
- ```
184
+ Code: [Repository Implementation](references/code-examples.md#repository-implementation).
208
185
 
209
186
  ### Data Sources
210
187
 
@@ -394,39 +371,7 @@ depending on every `impl`. Full host injection and per-feature contribution:
394
371
 
395
372
  Mappers isolate layer boundaries. Each layer has its own model type.
396
373
 
397
- ```kotlin
398
- class OrderMapper @Inject constructor() {
399
-
400
- fun toDomain(entity: OrderEntity): Order = Order(
401
- id = entity.id,
402
- store = Location(entity.storeCode, entity.storeName),
403
- region = Location(entity.regionCode, entity.regionName),
404
- placedAt = Instant.parse(entity.placedAt),
405
- deliveredAt = Instant.parse(entity.deliveredAt),
406
- price = Price(entity.priceAmount, entity.priceCurrency),
407
- status = OrderStatus.valueOf(entity.status),
408
- )
409
-
410
- fun toEntity(dto: OrderDto): OrderEntity = OrderEntity(
411
- id = dto.id,
412
- storeCode = dto.store.code,
413
- storeName = dto.store.name,
414
- regionCode = dto.region.code,
415
- regionName = dto.region.name,
416
- placedAt = dto.placedAt,
417
- deliveredAt = dto.deliveredAt,
418
- priceAmount = dto.price.amount,
419
- priceCurrency = dto.price.currency,
420
- status = dto.status,
421
- )
422
-
423
- fun toPaymentDomain(response: PaymentResponse): Payment = Payment(
424
- id = response.paymentId,
425
- confirmationCode = response.confirmationCode,
426
- status = PaymentStatus.valueOf(response.status),
427
- )
428
- }
429
- ```
374
+ Code: [Mapper Pattern](references/code-examples.md#mapper-pattern).
430
375
 
431
376
  ## Do's and Don'ts
432
377
 
@@ -487,3 +432,7 @@ class OrderMapper @Inject constructor() {
487
432
  - [ ] Feature and data internals are `internal`
488
433
  - [ ] Coroutine dispatchers are injectable via `DispatcherProvider`
489
434
  - [ ] No `GlobalScope` usage; all coroutines use structured concurrency
435
+
436
+ ## References
437
+
438
+ - [Code examples](references/code-examples.md): open for the full code behind Repository Implementation, Mapper Pattern.
@@ -0,0 +1,68 @@
1
+ # Android Architecture Code Examples
2
+
3
+ Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
4
+
5
+ ## Repository Implementation
6
+
7
+ ```kotlin
8
+ class OrderRepositoryImpl @Inject constructor(
9
+ private val remoteDataSource: OrderRemoteDataSource,
10
+ private val localDataSource: OrderLocalDataSource,
11
+ private val orderMapper: OrderMapper,
12
+ ) : OrderRepository {
13
+
14
+ override fun getOrders(store: String, region: String): Flow<List<Order>> =
15
+ localDataSource.getOrders(store, region)
16
+ .map { entities -> entities.map(orderMapper::toDomain) }
17
+
18
+ override fun searchOrders(query: String, date: LocalDate): Flow<List<Order>> = flow {
19
+ val dtos = remoteDataSource.searchOrders(query, date.toString())
20
+ localDataSource.insertOrders(dtos.map(orderMapper::toEntity))
21
+ emitAll(
22
+ localDataSource.getOrders(query, query)
23
+ .map { entities -> entities.map(orderMapper::toDomain) }
24
+ )
25
+ }
26
+
27
+ override suspend fun payOrder(orderId: String): Result<Payment> = runCatching {
28
+ val response = remoteDataSource.payOrder(orderId)
29
+ orderMapper.toPaymentDomain(response)
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Mapper Pattern
35
+
36
+ ```kotlin
37
+ class OrderMapper @Inject constructor() {
38
+
39
+ fun toDomain(entity: OrderEntity): Order = Order(
40
+ id = entity.id,
41
+ store = Location(entity.storeCode, entity.storeName),
42
+ region = Location(entity.regionCode, entity.regionName),
43
+ placedAt = Instant.parse(entity.placedAt),
44
+ deliveredAt = Instant.parse(entity.deliveredAt),
45
+ price = Price(entity.priceAmount, entity.priceCurrency),
46
+ status = OrderStatus.valueOf(entity.status),
47
+ )
48
+
49
+ fun toEntity(dto: OrderDto): OrderEntity = OrderEntity(
50
+ id = dto.id,
51
+ storeCode = dto.store.code,
52
+ storeName = dto.store.name,
53
+ regionCode = dto.region.code,
54
+ regionName = dto.region.name,
55
+ placedAt = dto.placedAt,
56
+ deliveredAt = dto.deliveredAt,
57
+ priceAmount = dto.price.amount,
58
+ priceCurrency = dto.price.currency,
59
+ status = dto.status,
60
+ )
61
+
62
+ fun toPaymentDomain(response: PaymentResponse): Payment = Payment(
63
+ id = response.paymentId,
64
+ confirmationCode = response.confirmationCode,
65
+ status = PaymentStatus.valueOf(response.status),
66
+ )
67
+ }
68
+ ```
@@ -60,29 +60,7 @@ The system builds the intent with `init()`, fills in parameters, then calls
60
60
  `perform()`. A static `title` (a `LocalizedStringResource`) and a
61
61
  `parameterSummary` shape how Shortcuts shows it.
62
62
 
63
- ```swift
64
- import AppIntents
65
-
66
- struct WaterPlantIntent: AppIntent {
67
- static let title: LocalizedStringResource = "Water Plant"
68
- static let description = IntentDescription("Logs a watering for one of your plants.")
69
-
70
- @Parameter(title: "Plant")
71
- var plant: PlantEntity
72
-
73
- @Parameter(title: "Cups", default: 1)
74
- var cups: Int
75
-
76
- static var parameterSummary: some ParameterSummary {
77
- Summary("Water \(\.$plant)") { \.$cups }
78
- }
79
-
80
- func perform() async throws -> some IntentResult & ProvidesDialog {
81
- try await CareLog.shared.recordWatering(plantID: plant.id, cups: cups)
82
- return .result(dialog: "Logged \(cups) cups for \(plant.name).")
83
- }
84
- }
85
- ```
63
+ Code: [AppIntent](references/code-examples.md#appintent).
86
64
 
87
65
  Optional members:
88
66
 
@@ -330,28 +308,7 @@ Adopt `IndexedEntity` to make entities searchable. `indexingKey` ties a
330
308
  property to a `CSSearchableItemAttributeSet` key path: on `@Property` from
331
309
  iOS 18.4, on `@ComputedProperty` from iOS 26.
332
310
 
333
- ```swift
334
- import CoreSpotlight
335
-
336
- struct CareGuideEntity: IndexedEntity {
337
- static let defaultQuery = CareGuideQuery()
338
- static let typeDisplayRepresentation: TypeDisplayRepresentation = "Care Guide"
339
-
340
- let id: UUID
341
-
342
- @Property(title: "Title", indexingKey: \.title)
343
- var title: String
344
-
345
- var steps: [String]
346
-
347
- @ComputedProperty(indexingKey: \.contentDescription)
348
- var overview: String { steps.prefix(2).joined(separator: " ") }
349
-
350
- var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(title)") }
351
- }
352
-
353
- // try await CSSearchableIndex(name: "guides").indexAppEntities(guides)
354
- ```
311
+ Code: [Spotlight and IndexedEntity (iOS 18+)](references/code-examples.md#spotlight-and-indexedentity-ios-18).
355
312
 
356
313
  - IDs must be stable: a UUID or slug, never a database row ID that can be
357
314
  reused.
@@ -445,3 +402,4 @@ The [advanced guide](references/appintents-advanced.md) covers every
445
402
  `@Parameter` form, `EntityPropertyQuery`, assistant schemas, Focus filters,
446
403
  SiriKit migration, errors and dialogs, confirmation, authentication, URL
447
404
  representations and Spotlight indexing.
405
+ - [Code examples](references/code-examples.md): open for the full code behind AppIntent, Spotlight and IndexedEntity (iOS 18+).
@@ -0,0 +1,54 @@
1
+ # App Intents Code Examples
2
+
3
+ Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
4
+
5
+ ## AppIntent
6
+
7
+ ```swift
8
+ import AppIntents
9
+
10
+ struct WaterPlantIntent: AppIntent {
11
+ static let title: LocalizedStringResource = "Water Plant"
12
+ static let description = IntentDescription("Logs a watering for one of your plants.")
13
+
14
+ @Parameter(title: "Plant")
15
+ var plant: PlantEntity
16
+
17
+ @Parameter(title: "Cups", default: 1)
18
+ var cups: Int
19
+
20
+ static var parameterSummary: some ParameterSummary {
21
+ Summary("Water \(\.$plant)") { \.$cups }
22
+ }
23
+
24
+ func perform() async throws -> some IntentResult & ProvidesDialog {
25
+ try await CareLog.shared.recordWatering(plantID: plant.id, cups: cups)
26
+ return .result(dialog: "Logged \(cups) cups for \(plant.name).")
27
+ }
28
+ }
29
+ ```
30
+
31
+ ## Spotlight and IndexedEntity (iOS 18+)
32
+
33
+ ```swift
34
+ import CoreSpotlight
35
+
36
+ struct CareGuideEntity: IndexedEntity {
37
+ static let defaultQuery = CareGuideQuery()
38
+ static let typeDisplayRepresentation: TypeDisplayRepresentation = "Care Guide"
39
+
40
+ let id: UUID
41
+
42
+ @Property(title: "Title", indexingKey: \.title)
43
+ var title: String
44
+
45
+ var steps: [String]
46
+
47
+ @ComputedProperty(indexingKey: \.contentDescription)
48
+ var overview: String { steps.prefix(2).joined(separator: " ") }
49
+
50
+ var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(title)") }
51
+ }
52
+
53
+ // try await CSSearchableIndex(name: "guides").indexAppEntities(guides)
54
+ ```
@@ -206,41 +206,7 @@ Keep the brief as text beside the frames so a new locale or size reuses it.
206
206
 
207
207
  ## Asking for reviews in the app
208
208
 
209
- Use the SwiftUI environment value `RequestReviewAction` or, from UIKit,
210
- `AppStore.requestReview(in:)`. The full StoreKit surface is covered in
211
- `storekit`.
212
-
213
- ```swift
214
- import StoreKit
215
- import SwiftUI
216
-
217
- struct TripSummaryView: View {
218
- @Environment(\.requestReview) private var requestReview
219
- let completedTrips: Int
220
-
221
- var body: some View {
222
- TripSummaryContent()
223
- .onAppear {
224
- if completedTrips == 5 { requestReview() }
225
- }
226
- }
227
- }
228
- ```
229
-
230
- - The system shows the prompt at most three times in 365 days per device for
231
- someone who has not rated the app.
232
- - Calling the API is a request; StoreKit decides whether anything appears.
233
- - Development builds always show it; TestFlight builds never do.
234
- - Good moments: a meaningful task just finished, a milestone or streak, a
235
- visible success, several active sessions.
236
- - Bad moments: the very first launch, the onboarding flow, any error or crash,
237
- a purchase that failed, or directly after a button tap.
238
- - Do not put a "Do you like this app?" dialog in front of the system prompt.
239
- Apple discourages intercepting it and may reject apps that do.
240
-
241
- For a permanent "Write a review" row in settings, open
242
- `https://apps.apple.com/app/id{APP_ID}?action=write-review`. It goes straight
243
- to the review sheet and does not count against the three-per-year limit.
209
+ Use `RequestReviewAction` (or `AppStore.requestReview(in:)`) after a meaningful success, never on first launch, onboarding, errors or a failed purchase, and never behind a pre-prompt; the system caps it at three prompts a year. Code, timing rules and the write-review link: [Reviews, events and localized metadata](references/engagement-and-localization.md).
244
210
 
245
211
  ## Custom Product Pages
246
212
 
@@ -267,24 +233,7 @@ URL format, channel mapping and maintenance are in the
267
233
 
268
234
  ## In-App Events
269
235
 
270
- Events are shown in search, on Today and in recommendations tailored to
271
- each user. They raise visibility while they run and can bring lapsed
272
- users back.
273
-
274
- - Badges (seven): Challenge, Competition, Live Event, Major Update, New
275
- Season, Premiere, Special Event. Pick the one that matches what actually happens in
276
- the app, and never create an event without real time-bound content.
277
- - Text limits in characters: 30 for the name, 50 for the short description,
278
- 120 for the long one.
279
- - Media: an event card image or video at 16:9 (1920 x 1080) and an event
280
- details page image or video at 9:16 (1080 x 1920).
281
- - Time them with feature releases, seasonal moments and content drops.
282
- - Write the name and short description for someone scanning search results;
283
- they appear on the event cards in Search and Today.
284
- - The card is shown on the product page as well, and a well-timed card lifts
285
- tap-through.
286
- - Line events up so a new one starts as the previous one ends, keeping a card
287
- in search at all times.
236
+ Seven badges, only for real time-bound content; name 30, short description 50, long description 120 characters; 16:9 card and 9:16 details media; keep one event live at all times. Full rules: [Reviews, events and localized metadata](references/engagement-and-localization.md).
288
237
 
289
238
  ## Product Page Optimization
290
239
 
@@ -313,37 +262,11 @@ default product page.
313
262
 
314
263
  ## Ratings and reviews
315
264
 
316
- Star ratings and written reviews are visible on the product page and in
317
- search, and they move both ranking and conversion.
318
-
319
- - Earn more recent positive reviews: prompt after successes and answer
320
- complaints constructively.
321
- - Answer critical reviews from App Store Connect. A calm, specific reply
322
- improves how the app looks even when the rating does not change: acknowledge
323
- the problem, say what is fixed or in progress, keep the tone neutral.
324
- - Read reviews as research into what users want, what frustrates them and the
325
- words they use, then feed that into metadata and captions.
326
- - Submitting a new version offers the option of resetting the summary rating. Do it only
327
- when the shown rating is far below current quality after major fixes. Keep a
328
- good rating that reflects the app fairly, skip the reset for routine fixes, and after
329
- a risky redesign wait until it has settled.
265
+ Earn recent positive reviews, answer critical ones calmly and specifically, mine reviews for wording, and reset the summary rating only after major fixes. Details: [Reviews, events and localized metadata](references/engagement-and-localization.md).
330
266
 
331
267
  ## Localized Metadata Optimization
332
268
 
333
- A localized listing is a separate job from translating the app's screens.
334
- Keywords, descriptions and captions are researched and written per market.
335
-
336
- - Research keywords in each market. The term people use to find a photo editor
337
- in Japanese is not a word-for-word translation of the English one.
338
- - Rewrite the description per locale, adapting opening lines, features, proof
339
- and close to local expectations.
340
- - Localize every surface (description, keywords, previews, screenshots), and do
341
- not count on literal translations or on one locale's keywords being indexed
342
- elsewhere.
343
- - Caption screenshots in the market's language; native captions convert better
344
- than English ones.
345
- - Localizing strings inside the app (String Catalogs, `FormatStyle`,
346
- right-to-left layout) is covered by `ios-localization`.
269
+ Research keywords and rewrite descriptions and captions per market instead of translating; in-app string localization belongs to `ios-localization`. Details: [Reviews, events and localized metadata](references/engagement-and-localization.md).
347
270
 
348
271
  ## Mistakes to catch
349
272
 
@@ -383,6 +306,7 @@ Keywords, descriptions and captions are researched and written per market.
383
306
 
384
307
  ## References
385
308
 
309
+ - [Reviews, events and localized metadata](references/engagement-and-localization.md): review prompt timing and API, In-App Event badges and limits, replying to reviews and rating resets, and per-market metadata.
386
310
  - [Keyword research](references/keyword-research-methodology.md):
387
311
  what search indexes, the research process, scoring, slot allocation, seasonal
388
312
  rotation, before and after examples.