mobile-ai-agents 1.0.23
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/AGENTS.md +228 -0
- package/CLAUDE.md +236 -0
- package/LICENSE +21 -0
- package/README.md +652 -0
- package/agents/android/android-crash-analyzer/README.md +134 -0
- package/agents/android/android-crash-analyzer/agent.md +206 -0
- package/agents/android/android-crash-analyzer/example-input.md +138 -0
- package/agents/android/android-crash-analyzer/example-output.md +225 -0
- package/agents/android/anr-investigation/README.md +78 -0
- package/agents/android/anr-investigation/agent.md +117 -0
- package/agents/android/anr-investigation/example-input.md +62 -0
- package/agents/android/anr-investigation/example-output.md +103 -0
- package/agents/android/axiom/agent.md +197 -0
- package/agents/android/code-reviewer/README.md +108 -0
- package/agents/android/code-reviewer/agent.md +210 -0
- package/agents/android/code-reviewer/example-input.md +116 -0
- package/agents/android/code-reviewer/example-output.md +115 -0
- package/agents/android/compose-navigation/README.md +76 -0
- package/agents/android/compose-navigation/agent.md +121 -0
- package/agents/android/compose-navigation/example-input.md +49 -0
- package/agents/android/compose-navigation/example-output.md +325 -0
- package/agents/android/compose-screen-builder/README.md +95 -0
- package/agents/android/compose-screen-builder/agent.md +170 -0
- package/agents/android/compose-screen-builder/example-input.md +40 -0
- package/agents/android/compose-screen-builder/example-output.md +568 -0
- package/agents/android/compose-ui-reviewer/README.md +73 -0
- package/agents/android/compose-ui-reviewer/agent.md +253 -0
- package/agents/android/compose-ui-reviewer/example-input.md +84 -0
- package/agents/android/compose-ui-reviewer/example-output.md +102 -0
- package/agents/android/crash-analyzer/README.md +53 -0
- package/agents/android/crash-analyzer/agent.md +295 -0
- package/agents/android/memory-leak-analyzer/README.md +79 -0
- package/agents/android/memory-leak-analyzer/agent.md +116 -0
- package/agents/android/memory-leak-analyzer/example-input.md +78 -0
- package/agents/android/memory-leak-analyzer/example-output.md +128 -0
- package/agents/cross-platform/accessibility-auditor/README.md +47 -0
- package/agents/cross-platform/accessibility-auditor/agent.md +268 -0
- package/agents/cross-platform/accessibility-auditor/example-input.md +84 -0
- package/agents/cross-platform/accessibility-auditor/example-output.md +132 -0
- package/agents/cross-platform/appforge/agent.md +303 -0
- package/agents/cross-platform/ci-cd-generator/README.md +65 -0
- package/agents/cross-platform/ci-cd-generator/agent.md +422 -0
- package/agents/cross-platform/ci-cd-generator/example-input.md +69 -0
- package/agents/cross-platform/ci-cd-generator/example-output.md +261 -0
- package/agents/cross-platform/crasher/agent.md +221 -0
- package/agents/cross-platform/figma/agent.md +247 -0
- package/agents/cross-platform/launchpad/agent.md +330 -0
- package/agents/cross-platform/mobile-harness/agent.md +422 -0
- package/agents/cross-platform/mobile-memory/agent.md +429 -0
- package/agents/cross-platform/perf/agent.md +261 -0
- package/agents/cross-platform/pipeline/agent.md +288 -0
- package/agents/cross-platform/release-notes-generator/README.md +63 -0
- package/agents/cross-platform/release-notes-generator/agent.md +218 -0
- package/agents/cross-platform/release-notes-generator/example-input.md +71 -0
- package/agents/cross-platform/release-notes-generator/example-output.md +96 -0
- package/agents/cross-platform/scribe/agent.md +180 -0
- package/agents/cross-platform/security-scanner/README.md +61 -0
- package/agents/cross-platform/security-scanner/agent.md +287 -0
- package/agents/cross-platform/security-scanner/example-input.md +77 -0
- package/agents/cross-platform/security-scanner/example-output.md +141 -0
- package/agents/cross-platform/sentinel/agent.md +217 -0
- package/agents/cross-platform/store-listing-writer/README.md +58 -0
- package/agents/cross-platform/store-listing-writer/agent.md +380 -0
- package/agents/cross-platform/store-listing-writer/example-input.md +67 -0
- package/agents/cross-platform/store-listing-writer/example-output.md +131 -0
- package/agents/flutter/bloc-feature-builder/README.md +101 -0
- package/agents/flutter/bloc-feature-builder/agent.md +225 -0
- package/agents/flutter/bloc-feature-builder/example-input.md +47 -0
- package/agents/flutter/bloc-feature-builder/example-output.md +838 -0
- package/agents/flutter/dart/agent.md +223 -0
- package/agents/flutter/widget-generator/README.md +66 -0
- package/agents/flutter/widget-generator/agent.md +264 -0
- package/agents/flutter/widget-generator/example-input.md +71 -0
- package/agents/flutter/widget-generator/example-output.md +163 -0
- package/agents/gaming/forge/agent.md +197 -0
- package/agents/gaming/unreal/agent.md +200 -0
- package/agents/ios/crash-analyzer/README.md +69 -0
- package/agents/ios/crash-analyzer/agent.md +300 -0
- package/agents/ios/crash-analyzer/example-input.md +83 -0
- package/agents/ios/crash-analyzer/example-output.md +126 -0
- package/agents/ios/swift/agent.md +206 -0
- package/agents/ios/swift-reviewer/README.md +68 -0
- package/agents/ios/swift-reviewer/agent.md +231 -0
- package/agents/ios/swift-reviewer/example-input.md +100 -0
- package/agents/ios/swift-reviewer/example-output.md +127 -0
- package/agents/react-native/bridge/agent.md +212 -0
- package/agents/react-native/performance-optimizer/README.md +81 -0
- package/agents/react-native/performance-optimizer/agent.md +284 -0
- package/agents/react-native/performance-optimizer/example-input.md +104 -0
- package/agents/react-native/performance-optimizer/example-output.md +132 -0
- package/agents/unity/shader-generator/README.md +77 -0
- package/agents/unity/shader-generator/agent.md +270 -0
- package/agents/unity/shader-generator/example-input.md +70 -0
- package/agents/unity/shader-generator/example-output.md +160 -0
- package/agents/unreal/blueprint-advisor/README.md +71 -0
- package/agents/unreal/blueprint-advisor/agent.md +311 -0
- package/agents/unreal/blueprint-advisor/example-input.md +73 -0
- package/agents/unreal/blueprint-advisor/example-output.md +217 -0
- package/cli/index.js +1091 -0
- package/docs/agent-guide.md +138 -0
- package/docs/chatgpt-integration.md +219 -0
- package/docs/core-loops.md +143 -0
- package/docs/cursor-integration.md +143 -0
- package/docs/getting-started.md +132 -0
- package/docs/index.html +711 -0
- package/docs/installation.md +271 -0
- package/docs/mobile-harness.md +160 -0
- package/docs/mobile-mcp.md +172 -0
- package/docs/mobile-memory.md +349 -0
- package/docs/release-process.md +85 -0
- package/docs/releases/release-v1.0.0.md +165 -0
- package/docs/roadmap/github-issues.md +239 -0
- package/docs/skill-categories.md +125 -0
- package/docs/vscode-copilot-integration.md +104 -0
- package/docs/wiki.md +615 -0
- package/install.sh +507 -0
- package/mobile-karpathy.md +79 -0
- package/package.json +57 -0
- package/skills/android/README.md +31 -0
- package/skills/android/android-tdd.md +89 -0
- package/skills/android/anr-investigation.md +251 -0
- package/skills/android/code-review.md +98 -0
- package/skills/android/compose-migration.md +48 -0
- package/skills/android/compose-review.md +78 -0
- package/skills/android/kotlin-modernize.md +51 -0
- package/skills/android/memory-leak-investigation.md +256 -0
- package/skills/android/proguard-rules.md +48 -0
- package/skills/cross-platform/accessibility-audit.md +63 -0
- package/skills/cross-platform/api-versioning.md +59 -0
- package/skills/cross-platform/clean-code-audit.md +239 -0
- package/skills/cross-platform/crash-triage.md +53 -0
- package/skills/cross-platform/deeplink-debug.md +57 -0
- package/skills/cross-platform/feature-slice.md +48 -0
- package/skills/cross-platform/grill-mobile.md +64 -0
- package/skills/cross-platform/mobile-mcp-qa.md +193 -0
- package/skills/cross-platform/mobile-memory-graph.md +179 -0
- package/skills/cross-platform/mobile-memory-save.md +210 -0
- package/skills/cross-platform/mobile-memory-search.md +154 -0
- package/skills/cross-platform/perf-audit.md +61 -0
- package/skills/cross-platform/prd-verification.md +324 -0
- package/skills/cross-platform/release-prep.md +64 -0
- package/skills/cross-platform/security-audit.md +281 -0
- package/skills/cross-platform/store-listing.md +75 -0
- package/skills/flutter/README.md +32 -0
- package/skills/flutter/dart-modernize.md +47 -0
- package/skills/flutter/flutter-review.md +59 -0
- package/skills/flutter/flutter-tdd.md +73 -0
- package/skills/flutter/widget-extract.md +41 -0
- package/skills/flutter/widget-gen.md +131 -0
- package/skills/gaming/blueprint-to-cpp.md +51 -0
- package/skills/gaming/game-perf.md +54 -0
- package/skills/gaming/shader-gen.md +44 -0
- package/skills/gaming/unity-tdd.md +85 -0
- package/skills/ios/README.md +45 -0
- package/skills/ios/data-persistence.md +151 -0
- package/skills/ios/ios-tdd.md +90 -0
- package/skills/ios/networking.md +139 -0
- package/skills/ios/performance.md +122 -0
- package/skills/ios/swift-concurrency.md +57 -0
- package/skills/ios/swift-review.md +124 -0
- package/skills/ios/swiftui-review.md +49 -0
- package/skills/ios/swiftui-state.md +122 -0
- package/skills/ios/unit-testing.md +144 -0
- package/skills/ios/xcode-warnings.md +37 -0
- package/skills/react-native/README.md +42 -0
- package/skills/react-native/bridge-audit.md +104 -0
- package/skills/react-native/expo-optimize.md +55 -0
- package/skills/react-native/new-arch-migrate.md +53 -0
- package/skills/react-native/performance.md +124 -0
- package/skills/react-native/rn-review.md +53 -0
- package/skills/react-native/rn-tdd.md +76 -0
- package/skills/shared/README.md +46 -0
- package/skills/shared/accessibility-audit.md +132 -0
- package/skills/shared/crash-analysis.md +110 -0
- package/skills/shared/security-scan.md +149 -0
- package/skills/unity/README.md +31 -0
- package/skills/unity/shader-review.md +110 -0
- package/templates/agent-template.md +88 -0
- package/templates/prompt-template.md +50 -0
- package/templates/skill-template.md +64 -0
- package/templates/workflow-template.md +48 -0
- package/workflows/app-launch.md +66 -0
- package/workflows/appforge-workflow.md +232 -0
- package/workflows/ci-setup.md +77 -0
- package/workflows/crash-to-fix.md +71 -0
- package/workflows/feature-ship.md +61 -0
- package/workflows/game-level.md +72 -0
- package/workflows/mobile-harness.md +319 -0
- package/workflows/mobile-mcp-qa.md +228 -0
- package/workflows/mobile-memory-workflow.md +243 -0
- package/workflows/new-project-setup.md +93 -0
- package/workflows/new-screen.md +68 -0
- package/workflows/perf-sprint.md +78 -0
- package/workflows/release-cycle.md +71 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Example Output — CI/CD Pipeline Generator
|
|
2
|
+
|
|
3
|
+
Full agent response to the input in [`example-input.md`](example-input.md).
|
|
4
|
+
|
|
5
|
+
Three complete GitHub Actions workflow files are generated. Key sections shown below — see `agent.md` for the full output of all three files, which matches what the agent produces.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## File: .github/workflows/pr-check.yml
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
name: PR Check
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
branches: [main, develop]
|
|
17
|
+
|
|
18
|
+
concurrency:
|
|
19
|
+
group: pr-check-${{ github.ref }}
|
|
20
|
+
cancel-in-progress: true
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
lint-and-test:
|
|
24
|
+
name: Lint & Unit Tests
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Set up JDK 21
|
|
32
|
+
uses: actions/setup-java@v4
|
|
33
|
+
with:
|
|
34
|
+
java-version: '21'
|
|
35
|
+
distribution: 'temurin'
|
|
36
|
+
|
|
37
|
+
- name: Cache Gradle
|
|
38
|
+
uses: actions/cache@v4
|
|
39
|
+
with:
|
|
40
|
+
path: |
|
|
41
|
+
~/.gradle/caches
|
|
42
|
+
~/.gradle/wrapper
|
|
43
|
+
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
44
|
+
restore-keys: gradle-
|
|
45
|
+
|
|
46
|
+
- name: Grant gradlew permission
|
|
47
|
+
run: chmod +x gradlew
|
|
48
|
+
|
|
49
|
+
- name: Run Lint
|
|
50
|
+
run: ./gradlew lintDebug
|
|
51
|
+
|
|
52
|
+
- name: Run Unit Tests
|
|
53
|
+
run: ./gradlew testDebugUnitTest
|
|
54
|
+
|
|
55
|
+
- name: Upload Lint Report (on failure)
|
|
56
|
+
if: failure()
|
|
57
|
+
uses: actions/upload-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: lint-report
|
|
60
|
+
path: app/build/reports/lint-results-debug.html
|
|
61
|
+
|
|
62
|
+
- name: Upload Test Results
|
|
63
|
+
if: always()
|
|
64
|
+
uses: actions/upload-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
name: test-results
|
|
67
|
+
path: app/build/reports/tests/testDebugUnitTest/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## File: .github/workflows/staging-build.yml
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
name: Staging Build
|
|
76
|
+
|
|
77
|
+
on:
|
|
78
|
+
push:
|
|
79
|
+
branches: [develop]
|
|
80
|
+
workflow_dispatch:
|
|
81
|
+
|
|
82
|
+
jobs:
|
|
83
|
+
build-and-distribute:
|
|
84
|
+
name: Build & Deploy to Firebase
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
|
|
87
|
+
steps:
|
|
88
|
+
- name: Checkout
|
|
89
|
+
uses: actions/checkout@v4
|
|
90
|
+
|
|
91
|
+
- name: Set up JDK 21
|
|
92
|
+
uses: actions/setup-java@v4
|
|
93
|
+
with:
|
|
94
|
+
java-version: '21'
|
|
95
|
+
distribution: 'temurin'
|
|
96
|
+
|
|
97
|
+
- name: Cache Gradle
|
|
98
|
+
uses: actions/cache@v4
|
|
99
|
+
with:
|
|
100
|
+
path: |
|
|
101
|
+
~/.gradle/caches
|
|
102
|
+
~/.gradle/wrapper
|
|
103
|
+
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
104
|
+
restore-keys: gradle-
|
|
105
|
+
|
|
106
|
+
- name: Grant gradlew permission
|
|
107
|
+
run: chmod +x gradlew
|
|
108
|
+
|
|
109
|
+
- name: Run Unit Tests
|
|
110
|
+
run: ./gradlew testDebugUnitTest
|
|
111
|
+
|
|
112
|
+
- name: Decode Keystore
|
|
113
|
+
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
|
|
114
|
+
|
|
115
|
+
- name: Build Signed APK
|
|
116
|
+
run: |
|
|
117
|
+
./gradlew assembleRelease \
|
|
118
|
+
-Pandroid.injected.signing.store.file=keystore.jks \
|
|
119
|
+
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
|
|
120
|
+
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
|
|
121
|
+
-Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
|
|
122
|
+
|
|
123
|
+
- name: Install Firebase CLI
|
|
124
|
+
run: npm install -g firebase-tools
|
|
125
|
+
|
|
126
|
+
- name: Upload to Firebase App Distribution
|
|
127
|
+
run: |
|
|
128
|
+
firebase appdistribution:distribute \
|
|
129
|
+
app/build/outputs/apk/release/app-release.apk \
|
|
130
|
+
--app "${{ secrets.FIREBASE_APP_ID }}" \
|
|
131
|
+
--token "${{ secrets.FIREBASE_TOKEN }}" \
|
|
132
|
+
--groups "internal-testers" \
|
|
133
|
+
--release-notes "Staging build from ${{ github.sha }}"
|
|
134
|
+
|
|
135
|
+
- name: Notify Slack — Success
|
|
136
|
+
if: success()
|
|
137
|
+
uses: slackapi/slack-github-action@v1.27.0
|
|
138
|
+
with:
|
|
139
|
+
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
140
|
+
webhook-type: incoming-webhook
|
|
141
|
+
payload: |
|
|
142
|
+
{"text": "✅ *Staging deployed* — ${{ github.repository }} @ `${{ github.sha }}`"}
|
|
143
|
+
|
|
144
|
+
- name: Notify Slack — Failure
|
|
145
|
+
if: failure()
|
|
146
|
+
uses: slackapi/slack-github-action@v1.27.0
|
|
147
|
+
with:
|
|
148
|
+
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
149
|
+
webhook-type: incoming-webhook
|
|
150
|
+
payload: |
|
|
151
|
+
{"text": "❌ *Staging FAILED* — ${{ github.repository }} @ `${{ github.sha }}`\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run>"}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## File: .github/workflows/production-release.yml
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
name: Production Release
|
|
160
|
+
|
|
161
|
+
on:
|
|
162
|
+
push:
|
|
163
|
+
tags:
|
|
164
|
+
- 'v*.*.*'
|
|
165
|
+
|
|
166
|
+
jobs:
|
|
167
|
+
release:
|
|
168
|
+
name: Build, Sign & Publish
|
|
169
|
+
runs-on: ubuntu-latest
|
|
170
|
+
|
|
171
|
+
steps:
|
|
172
|
+
- name: Checkout
|
|
173
|
+
uses: actions/checkout@v4
|
|
174
|
+
|
|
175
|
+
- name: Set up JDK 21
|
|
176
|
+
uses: actions/setup-java@v4
|
|
177
|
+
with:
|
|
178
|
+
java-version: '21'
|
|
179
|
+
distribution: 'temurin'
|
|
180
|
+
|
|
181
|
+
- name: Cache Gradle
|
|
182
|
+
uses: actions/cache@v4
|
|
183
|
+
with:
|
|
184
|
+
path: |
|
|
185
|
+
~/.gradle/caches
|
|
186
|
+
~/.gradle/wrapper
|
|
187
|
+
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
188
|
+
restore-keys: gradle-
|
|
189
|
+
|
|
190
|
+
- name: Grant gradlew permission
|
|
191
|
+
run: chmod +x gradlew
|
|
192
|
+
|
|
193
|
+
- name: Run Lint
|
|
194
|
+
run: ./gradlew lintRelease
|
|
195
|
+
|
|
196
|
+
- name: Run Unit Tests
|
|
197
|
+
run: ./gradlew testReleaseUnitTest
|
|
198
|
+
|
|
199
|
+
- name: Decode Keystore
|
|
200
|
+
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
|
|
201
|
+
|
|
202
|
+
- name: Build Signed AAB
|
|
203
|
+
run: |
|
|
204
|
+
./gradlew bundleRelease \
|
|
205
|
+
-Pandroid.injected.signing.store.file=keystore.jks \
|
|
206
|
+
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
|
|
207
|
+
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
|
|
208
|
+
-Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}
|
|
209
|
+
|
|
210
|
+
- name: Create GitHub Release
|
|
211
|
+
uses: softprops/action-gh-release@v2
|
|
212
|
+
with:
|
|
213
|
+
files: app/build/outputs/bundle/release/app-release.aab
|
|
214
|
+
generate_release_notes: true
|
|
215
|
+
|
|
216
|
+
- name: Upload to Play Store (Internal Track)
|
|
217
|
+
uses: r0adkll/upload-google-play@v1.1.3
|
|
218
|
+
with:
|
|
219
|
+
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
|
220
|
+
packageName: com.example.yourapp # ← update this
|
|
221
|
+
releaseFiles: app/build/outputs/bundle/release/app-release.aab
|
|
222
|
+
track: internal
|
|
223
|
+
status: completed
|
|
224
|
+
|
|
225
|
+
- name: Notify Slack — Released
|
|
226
|
+
if: success()
|
|
227
|
+
uses: slackapi/slack-github-action@v1.27.0
|
|
228
|
+
with:
|
|
229
|
+
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
230
|
+
webhook-type: incoming-webhook
|
|
231
|
+
payload: |
|
|
232
|
+
{"text": "🚀 *${{ github.ref_name }} submitted to Play Store* — ${{ github.repository }}"}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Secrets Reference
|
|
238
|
+
|
|
239
|
+
| Secret | Where to Get It | Where to Set It |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `KEYSTORE_BASE64` | `base64 -i release.jks \| pbcopy` | GitHub → Settings → Secrets |
|
|
242
|
+
| `KEYSTORE_PASSWORD` | Your keystore password | GitHub → Settings → Secrets |
|
|
243
|
+
| `KEY_ALIAS` | From `keytool` when creating the keystore | GitHub → Settings → Secrets |
|
|
244
|
+
| `KEY_PASSWORD` | Your key password | GitHub → Settings → Secrets |
|
|
245
|
+
| `FIREBASE_APP_ID` | Firebase Console → Project Settings → Your Apps | GitHub → Settings → Secrets |
|
|
246
|
+
| `FIREBASE_TOKEN` | `firebase login:ci` in your terminal | GitHub → Settings → Secrets |
|
|
247
|
+
| `SLACK_WEBHOOK_URL` | Slack App → Incoming Webhooks | GitHub → Settings → Secrets |
|
|
248
|
+
| `PLAY_SERVICE_ACCOUNT_JSON` | Google Play Console → API access → Service accounts → JSON key | GitHub → Settings → Secrets |
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Setup Guide
|
|
253
|
+
|
|
254
|
+
1. Generate a release keystore: `keytool -genkey -v -keystore release.jks -alias my-key -keyalg RSA -keysize 2048 -validity 10000`
|
|
255
|
+
2. Base64-encode it: `base64 -i release.jks | pbcopy` — paste as `KEYSTORE_BASE64` secret
|
|
256
|
+
3. Add all secrets from the table above in GitHub → repository → Settings → Secrets and variables → Actions
|
|
257
|
+
4. Create a Firebase project and register your Android app to get `FIREBASE_APP_ID`
|
|
258
|
+
5. Run `firebase login:ci` locally to get `FIREBASE_TOKEN`
|
|
259
|
+
6. Create a Google Play service account with Release Manager role and download the JSON key
|
|
260
|
+
7. For production releases, push a version tag: `git tag v1.2.0 && git push origin v1.2.0`
|
|
261
|
+
8. Update `packageName: com.example.yourapp` in the production workflow to match your app's package
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# CRASHER — Crash Investigator
|
|
2
|
+
|
|
3
|
+
**Platform:** Cross-Platform (Android / iOS / Flutter / React Native)
|
|
4
|
+
**Personality:** Forensic investigator. Nothing escapes. Has a wall covered in stacktraces and string.
|
|
5
|
+
**Category:** Debugging / Reliability
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Parses mobile crash logs and stacktraces from any platform, identifies the root cause, explains the failure path, and provides a concrete fix with a regression test strategy.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Input Format
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
PLATFORM: <Android | iOS | Flutter | React Native>
|
|
19
|
+
SOURCE: <Firebase Crashlytics | Sentry | Bugsnag | raw logcat | Xcode organizer | other>
|
|
20
|
+
APP_VERSION: <e.g. 2.3.1>
|
|
21
|
+
CRASH_LOG:
|
|
22
|
+
<paste the full stacktrace or crash report>
|
|
23
|
+
CONTEXT: <optional: what the user was doing, recent code changes, reproduction rate>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Output Format
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
CRASHER REPORT
|
|
32
|
+
==============
|
|
33
|
+
Platform: <platform>
|
|
34
|
+
Crash Type: <NullPointerException | EXC_BAD_ACCESS | ANR | OOM | assertion | ...>
|
|
35
|
+
Severity: <P0 | P1 | P2>
|
|
36
|
+
Reproduction Rate: <if known>
|
|
37
|
+
|
|
38
|
+
ROOT CAUSE
|
|
39
|
+
----------
|
|
40
|
+
<2–4 sentences: exactly what failed, why, and under what conditions>
|
|
41
|
+
|
|
42
|
+
FAILURE PATH
|
|
43
|
+
------------
|
|
44
|
+
1. <First thing that went wrong>
|
|
45
|
+
2. <What it triggered>
|
|
46
|
+
3. <The crash point>
|
|
47
|
+
|
|
48
|
+
CRASH LOCATION
|
|
49
|
+
--------------
|
|
50
|
+
File: <file>
|
|
51
|
+
Line: <line if determinable>
|
|
52
|
+
Function: <function>
|
|
53
|
+
|
|
54
|
+
THE FIX
|
|
55
|
+
-------
|
|
56
|
+
<Concrete code change to prevent the crash>
|
|
57
|
+
|
|
58
|
+
```<language>
|
|
59
|
+
<corrected code snippet>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
REGRESSION TEST
|
|
63
|
+
---------------
|
|
64
|
+
<How to write a test that would have caught this>
|
|
65
|
+
|
|
66
|
+
PREVENTION
|
|
67
|
+
----------
|
|
68
|
+
<1–3 patterns or checks that would have prevented this class of crash>
|
|
69
|
+
|
|
70
|
+
VERDICT: FIXED | NEEDS MORE CONTEXT | FLAKY (non-deterministic)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## System Prompt
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
You are CRASHER — a forensic crash investigator who has analyzed thousands of production
|
|
79
|
+
stacktraces across Android, iOS, Flutter, and React Native. You read crash logs the way a
|
|
80
|
+
detective reads a crime scene. Every frame in the stacktrace is a clue. You do not stop
|
|
81
|
+
at the surface symptom — you trace the failure to its origin.
|
|
82
|
+
|
|
83
|
+
Given a crash log:
|
|
84
|
+
1. Identify the crash type: NullPointerException, EXC_BAD_ACCESS, ANR, OOM, assertion
|
|
85
|
+
failure, unhandled exception, stack overflow, or other.
|
|
86
|
+
2. Assign priority:
|
|
87
|
+
- P0: affects all users, data loss, app unusable
|
|
88
|
+
- P1: affects a significant % of users on common flows
|
|
89
|
+
- P2: edge case, rare condition
|
|
90
|
+
3. Trace the failure path from the top frame back to the origin. The first frame is
|
|
91
|
+
usually the symptom; the root cause is typically 3–8 frames deeper.
|
|
92
|
+
4. Provide a concrete fix — not "add a null check" but the exact code change.
|
|
93
|
+
5. Write the regression test that would have caught this.
|
|
94
|
+
6. Identify the class of bug so the team can audit for similar patterns.
|
|
95
|
+
|
|
96
|
+
Platform-specific knowledge:
|
|
97
|
+
- Android: distinguish fatal vs ANR vs native crash; read Kotlin/Java stack frames; know
|
|
98
|
+
common coroutine crash patterns (ViewModelScope + UI access on wrong thread).
|
|
99
|
+
- iOS: read EXC_BAD_ACCESS addresses; distinguish Swift vs ObjC frames; identify
|
|
100
|
+
retain cycle + deallocation crashes.
|
|
101
|
+
- Flutter: read Dart stack frames; identify framework vs app code; know common BuildContext
|
|
102
|
+
across async gap patterns.
|
|
103
|
+
- React Native: distinguish JS bundle errors from native crashes; read Metro source maps.
|
|
104
|
+
|
|
105
|
+
Output MUST follow the exact format specified. Do not add extra sections or omit any section.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Example
|
|
111
|
+
|
|
112
|
+
### Input
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
PLATFORM: Android
|
|
116
|
+
SOURCE: Firebase Crashlytics
|
|
117
|
+
APP_VERSION: 3.2.0
|
|
118
|
+
CRASH_LOG:
|
|
119
|
+
Fatal Exception: java.lang.NullPointerException
|
|
120
|
+
at com.example.app.profile.ProfileFragment.onViewCreated(ProfileFragment.kt:47)
|
|
121
|
+
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
|
|
122
|
+
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
|
|
123
|
+
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1409)
|
|
124
|
+
at com.example.app.MainActivity.navigateToProfile(MainActivity.kt:89)
|
|
125
|
+
at com.example.app.home.HomeViewModel$loadDashboard$1.invokeSuspend(HomeViewModel.kt:62)
|
|
126
|
+
|
|
127
|
+
CONTEXT: Happens when user taps profile from the home screen. Reproduction rate: 8%.
|
|
128
|
+
Recent change: added async profile prefetch in HomeViewModel.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Output
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
CRASHER REPORT
|
|
135
|
+
==============
|
|
136
|
+
Platform: Android
|
|
137
|
+
Crash Type: NullPointerException
|
|
138
|
+
Severity: P1
|
|
139
|
+
Reproduction Rate: 8%
|
|
140
|
+
|
|
141
|
+
ROOT CAUSE
|
|
142
|
+
----------
|
|
143
|
+
navigateToProfile() is called from inside a coroutine (HomeViewModel.loadDashboard)
|
|
144
|
+
after an asynchronous operation. By the time the coroutine resumes, the Fragment's
|
|
145
|
+
view may not exist yet — or ProfileFragment.onViewCreated accesses a ViewModel or
|
|
146
|
+
binding object that is null because the Fragment lifecycle hasn't completed initialization.
|
|
147
|
+
The 8% rate suggests a race between async completion and Fragment lifecycle state.
|
|
148
|
+
|
|
149
|
+
FAILURE PATH
|
|
150
|
+
------------
|
|
151
|
+
1. HomeViewModel.loadDashboard suspends on an async call (line 62).
|
|
152
|
+
2. On resumption, it calls MainActivity.navigateToProfile (line 89).
|
|
153
|
+
3. Navigation triggers Fragment creation and onViewCreated fires.
|
|
154
|
+
4. ProfileFragment.onViewCreated accesses a null reference at line 47.
|
|
155
|
+
|
|
156
|
+
CRASH LOCATION
|
|
157
|
+
--------------
|
|
158
|
+
File: profile/ProfileFragment.kt
|
|
159
|
+
Line: 47
|
|
160
|
+
Function: onViewCreated
|
|
161
|
+
|
|
162
|
+
THE FIX
|
|
163
|
+
-------
|
|
164
|
+
Two changes needed:
|
|
165
|
+
1. Never navigate from inside a ViewModel coroutine directly. Use a SingleLiveEvent
|
|
166
|
+
or SharedFlow<NavigationEvent> that the Fragment/Activity observes safely.
|
|
167
|
+
2. In ProfileFragment.onViewCreated, guard the null access:
|
|
168
|
+
|
|
169
|
+
```kotlin
|
|
170
|
+
// HomeViewModel.kt — replace direct navigation with event
|
|
171
|
+
private val _navEvent = MutableSharedFlow<NavEvent>(extraBufferCapacity = 1)
|
|
172
|
+
val navEvent: SharedFlow<NavEvent> = _navEvent.asSharedFlow()
|
|
173
|
+
|
|
174
|
+
// In loadDashboard:
|
|
175
|
+
_navEvent.emit(NavEvent.ToProfile(userId))
|
|
176
|
+
|
|
177
|
+
// MainActivity.kt — observe in lifecycle scope
|
|
178
|
+
lifecycleScope.launch {
|
|
179
|
+
viewModel.navEvent.collect { event ->
|
|
180
|
+
when (event) {
|
|
181
|
+
is NavEvent.ToProfile -> navigateToProfile(event.userId)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
REGRESSION TEST
|
|
188
|
+
---------------
|
|
189
|
+
```kotlin
|
|
190
|
+
@Test
|
|
191
|
+
fun `navigateToProfile not called directly from coroutine`() {
|
|
192
|
+
// Verify HomeViewModel emits NavEvent rather than calling navigation directly.
|
|
193
|
+
// Use Turbine to collect SharedFlow and assert NavEvent.ToProfile is emitted.
|
|
194
|
+
val viewModel = HomeViewModel(fakeRepository)
|
|
195
|
+
val events = mutableListOf<NavEvent>()
|
|
196
|
+
val job = testScope.launch { viewModel.navEvent.toList(events) }
|
|
197
|
+
viewModel.loadDashboard()
|
|
198
|
+
testScope.advanceUntilIdle()
|
|
199
|
+
assertTrue(events.any { it is NavEvent.ToProfile })
|
|
200
|
+
job.cancel()
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
PREVENTION
|
|
205
|
+
----------
|
|
206
|
+
1. Never call navigation or UI methods directly from ViewModel coroutines. Use event channels.
|
|
207
|
+
2. In Fragment.onViewCreated, use requireView() and viewLifecycleOwner — never access views
|
|
208
|
+
or binding outside the view lifecycle.
|
|
209
|
+
3. Add a lint rule (DetektRule or custom) that flags Activity/Fragment method calls from ViewModel.
|
|
210
|
+
|
|
211
|
+
VERDICT: FIXED
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Notes
|
|
217
|
+
|
|
218
|
+
- Provide as much of the stacktrace as available — truncated traces reduce accuracy.
|
|
219
|
+
- Include CONTEXT (what user was doing, reproduction rate) — this dramatically improves root cause analysis.
|
|
220
|
+
- For obfuscated stacktraces, de-obfuscate first using your ProGuard/R8 mapping file.
|
|
221
|
+
- Tested with: Claude Sonnet 4.6.
|