mobile-ai-agents 1.0.34 → 1.0.36
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 +1 -1
- package/README.md +30 -14
- package/agents/cross-platform/mobile-harness/agent.md +91 -24
- package/cli/commands/doctor.js +182 -0
- package/cli/commands/mcp.js +330 -0
- package/cli/commands/router.js +32 -0
- package/cli/index.js +24 -37
- package/cli/lib/options.js +24 -0
- package/docs/android-device-setup.md +138 -0
- package/docs/getting-started.md +8 -0
- package/docs/index.html +6 -1
- package/docs/installation.md +8 -0
- package/docs/mobile-harness.md +44 -0
- package/docs/mobile-mcp.md +14 -0
- package/docs/release-process.md +1 -0
- package/docs/wiki.md +10 -0
- package/package.json +4 -1
- package/skills/cross-platform/mobile-mcp-qa.md +14 -0
- package/templates/android-device-config.json +16 -0
- package/workflows/mobile-harness.md +54 -33
package/AGENTS.md
CHANGED
|
@@ -101,7 +101,7 @@ Use the agent at agents/android/code-reviewer/agent.md to review this file:
|
|
|
101
101
|
| Release Notes Generator | `agents/cross-platform/release-notes-generator/` | All | Git commits → user-facing release notes |
|
|
102
102
|
| CI/CD Pipeline Generator | `agents/cross-platform/ci-cd-generator/` | All | Generates GitHub Actions / Bitrise / Fastlane configs |
|
|
103
103
|
| Store Listing Writer | `agents/cross-platform/store-listing-writer/` | All | Play Store / App Store descriptions optimised for ASO |
|
|
104
|
-
| Mobile Harness | `agents/cross-platform/mobile-harness/` | All |
|
|
104
|
+
| Mobile Harness | `agents/cross-platform/mobile-harness/` | All | Guide beginners from idea and stack choice through one-task implementation, verification, device proof, and memory |
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Android · iOS · Flutter · React Native · Kotlin Multiplatform · Unity · Un
|
|
|
11
11
|
[](https://github.com/salmanashraf/mobile-agency/stargazers)
|
|
12
12
|
[](#core-loops-and-platform-plugins)
|
|
13
13
|
|
|
14
|
-
[Installation Guide](docs/installation.md) · [Core Loops](docs/core-loops.md) · [mobile-karpathy](mobile-karpathy.md) · [Wiki](https://github.com/salmanashraf/mobile-
|
|
14
|
+
[Installation Guide](docs/installation.md) · [Android Device Setup](docs/android-device-setup.md) · [Core Loops](docs/core-loops.md) · [mobile-karpathy](mobile-karpathy.md) · [Wiki](https://github.com/salmanashraf/mobile-ai-agents/wiki) · [Getting Started](docs/getting-started.md) · [Contributing](CONTRIBUTING.md)
|
|
15
15
|
|
|
16
16
|
> AI agents, skills, and Loop Engineering workflows for building, testing, reviewing, and shipping Android, iOS, Flutter, React Native, Unity, and Unreal apps.
|
|
17
17
|
|
|
@@ -83,7 +83,7 @@ Mobile AI Agents turns AI coding from one-off prompts into a repeatable mobile e
|
|
|
83
83
|
| 1. Start | Turn a raw idea into starter PRD, design, task, roadmap, and memory docs. | `npx mobile-ai-agents start` |
|
|
84
84
|
| 2. Install | Add the agents, skills, and workflows to your AI coding tool. | `npx mobile-ai-agents install` |
|
|
85
85
|
| 3. Open your app | Use it inside an Android, iOS, Flutter, React Native, KMP, Unity, or Unreal project. | Claude Code · Cursor · Windsurf · Copilot · Codex |
|
|
86
|
-
| 4. Run the loop | Tell Mobile Harness to continue from the generated docs.
|
|
86
|
+
| 4. Run the loop | Tell Mobile Harness to continue from the generated docs. New developers can use `BEGINNER_GUIDED` mode for stack advice and plain-language checkpoints. | `/mobile-harness` |
|
|
87
87
|
| 5. Build with gates | The loop plans, implements, reviews architecture, checks security, audits performance, verifies UI, and runs QA. | `@MOBILE-HARNESS` |
|
|
88
88
|
| 6. Continue later | Project context, decisions, reports, and next tasks are saved so work can continue across sessions. | Mobile Memory |
|
|
89
89
|
|
|
@@ -174,7 +174,9 @@ Not sure where to begin? Pick your situation:
|
|
|
174
174
|
| Build, test, and verify a feature | `@MOBILE-HARNESS` + approved PRD/design/tasks |
|
|
175
175
|
| Prepare a release | `/release-prep` |
|
|
176
176
|
| Generate release notes | `@SCRIBE` + paste your git log |
|
|
177
|
-
|
|
|
177
|
+
| Plan an app without implementing it | `@APPFORGE` + answer the discovery questions |
|
|
178
|
+
| Build a first app with guided stack selection | `@MOBILE-HARNESS` in `BEGINNER_GUIDED` mode |
|
|
179
|
+
| Build an approved app plan through device proof | `@MOBILE-HARNESS` |
|
|
178
180
|
|
|
179
181
|
---
|
|
180
182
|
|
|
@@ -396,6 +398,14 @@ Mobile MCP fits the QA stage next: use it for emulator, simulator, or real-devic
|
|
|
396
398
|
|
|
397
399
|
Mobile MCP gives Mobile AI Agents a device automation layer for iOS and Android simulators, emulators, and real devices.
|
|
398
400
|
|
|
401
|
+
Prepare an Android project with environment diagnostics and settings-preserving MCP setup:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
npx mobile-ai-agents doctor --platform android
|
|
405
|
+
npx mobile-ai-agents mcp setup --client claude
|
|
406
|
+
npx mobile-ai-agents mcp config init --app-id com.example.app
|
|
407
|
+
```
|
|
408
|
+
|
|
399
409
|
```
|
|
400
410
|
/mobile-mcp-qa
|
|
401
411
|
1. List devices
|
|
@@ -406,7 +416,7 @@ Mobile MCP gives Mobile AI Agents a device automation layer for iOS and Android
|
|
|
406
416
|
6. Produce MOBILE_MCP_QA_REPORT.md
|
|
407
417
|
```
|
|
408
418
|
|
|
409
|
-
Use it inside APPFORGE Stage 7 Full QA, UI match review, launch readiness checks, and screenshot validation.
|
|
419
|
+
Use it inside APPFORGE Stage 7 Full QA, UI match review, launch readiness checks, and screenshot validation. Guides: [Android Device QA Setup](docs/android-device-setup.md), [wiki guide](https://github.com/salmanashraf/mobile-ai-agents/wiki/Android-Device-QA-Setup), and [Mobile MCP Integration](docs/mobile-mcp.md).
|
|
410
420
|
|
|
411
421
|
### Device Proof Reports
|
|
412
422
|
|
|
@@ -431,20 +441,26 @@ Mobile Harness is the Loop Engineering orchestrator for Mobile AI Agents. It can
|
|
|
431
441
|
|
|
432
442
|
Use it when you want to define the goal once and have the system keep moving through the app-building loop without repeating the same manual prompts at every stage.
|
|
433
443
|
|
|
444
|
+
Use `BEGINNER_GUIDED` mode when this is your first mobile app or you are unsure about the platform or stack. Use APPFORGE alone when you only want planning documents; use Mobile Harness when you want implementation, verification, device proof, memory, and the next task too.
|
|
445
|
+
|
|
434
446
|
### What It Runs
|
|
435
447
|
|
|
436
448
|
```text
|
|
437
449
|
Goal
|
|
438
|
-
->
|
|
439
|
-
->
|
|
440
|
-
->
|
|
441
|
-
->
|
|
442
|
-
->
|
|
443
|
-
->
|
|
444
|
-
->
|
|
445
|
-
->
|
|
446
|
-
|
|
447
|
-
|
|
450
|
+
-> PLAN: choose profile/stack and create or read PRD.md + ROADMAP.md
|
|
451
|
+
-> DESIGN: approve DESIGN.md
|
|
452
|
+
-> TASK: select one small item from TASKS.md
|
|
453
|
+
-> IMPLEMENT: change only that task
|
|
454
|
+
-> VERIFY: review, build, test, and check the PRD
|
|
455
|
+
-> DEVICE PROOF: run Mobile MCP QA when available
|
|
456
|
+
-> MEMORY: update MOBILE_MEMORY.md and the report
|
|
457
|
+
-> NEXT ACTION: continue, fix, or stop at a human gate
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Beginner prompt:
|
|
461
|
+
|
|
462
|
+
```text
|
|
463
|
+
Use MOBILE-HARNESS in BEGINNER_GUIDED mode. I have an app idea but I am not sure which platform or stack to use. Recommend one with reasons, create PRD.md, DESIGN.md, TASKS.md, DEPENDENCIES.md, ROADMAP.md, and MOBILE_MEMORY.md, then stop for approval before coding.
|
|
448
464
|
```
|
|
449
465
|
|
|
450
466
|
### First Questions It Should Ask
|
|
@@ -29,8 +29,12 @@ Use MOBILE-HARNESS when the user wants one system to manage:
|
|
|
29
29
|
- Performance and accessibility checks
|
|
30
30
|
- Play Store launch preparation
|
|
31
31
|
|
|
32
|
+
Use `BEGINNER_GUIDED` mode when the user has a rough idea, is new to mobile development, does not know which platform or stack to choose, or wants each step explained in plain language. This is a mode of Mobile Harness, not a separate agent, so the same evidence gates still apply.
|
|
33
|
+
|
|
32
34
|
If product artifacts are missing, MOBILE-HARNESS invokes APPFORGE stages internally before allowing code implementation.
|
|
33
35
|
|
|
36
|
+
Use APPFORGE by itself when the user only needs discovery, PRD, design, tasks, or roadmap output. Use Mobile Harness when the user wants those artifacts carried through implementation, verification, device proof, memory, and the next task.
|
|
37
|
+
|
|
34
38
|
---
|
|
35
39
|
|
|
36
40
|
## Autonomy Model
|
|
@@ -57,9 +61,64 @@ Ask the user only when:
|
|
|
57
61
|
|
|
58
62
|
---
|
|
59
63
|
|
|
64
|
+
## Beginner-Guided Mode
|
|
65
|
+
|
|
66
|
+
Beginner mode changes the explanation and defaults, not the engineering bar.
|
|
67
|
+
|
|
68
|
+
Start with only the questions that cannot be inferred:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
1. What app do you want to build, and who is it for?
|
|
72
|
+
2. Do you want iPhone, Android, or both? "Not sure" is valid.
|
|
73
|
+
3. Is this your first mobile app, and which languages do you already know?
|
|
74
|
+
4. Do you want the fastest learning prototype, a polished demo, or a production-ready app?
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Inspect an existing repository before recommending anything. For a new app, recommend one stack and explain the reason in two plain-language sentences:
|
|
78
|
+
|
|
79
|
+
| Situation | Default Recommendation | Reason |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| Android only | Kotlin + Jetpack Compose | Native Android tooling and direct platform access |
|
|
82
|
+
| iPhone/iPad only | Swift + SwiftUI | Native Apple tooling and direct platform access |
|
|
83
|
+
| Android + iOS, user knows TypeScript/JavaScript | React Native + Expo + TypeScript | Fast cross-platform start with familiar language and managed tooling |
|
|
84
|
+
| Android + iOS, user accepts Dart and wants consistent custom UI | Flutter + Dart | One UI codebase with strong cross-platform rendering |
|
|
85
|
+
| Existing app | Existing stack | Avoid an unnecessary rewrite and preserve project conventions |
|
|
86
|
+
|
|
87
|
+
Document the recommendation, alternatives considered, and tradeoff in `PRD.md` and `ROADMAP.md`. Never imply there is one universally best stack.
|
|
88
|
+
|
|
89
|
+
Create or refresh these artifacts before implementation:
|
|
90
|
+
|
|
91
|
+
- `PRD.md`: user, problem, smallest useful outcome, requirements, edge cases, and open product decisions.
|
|
92
|
+
- `DESIGN.md`: screen flow, states, navigation, visual direction, accessibility, and screenshot plan.
|
|
93
|
+
- `TASKS.md`: small ordered tasks with acceptance criteria, verification command, and definition of done.
|
|
94
|
+
- `DEPENDENCIES.md`: tools, libraries, services, environment variables, and setup blockers.
|
|
95
|
+
- `ROADMAP.md`: milestones, task order, deferred scope, and release checkpoints.
|
|
96
|
+
- `MOBILE_MEMORY.md`: decisions, completed work, current task, blockers, and one next action.
|
|
97
|
+
|
|
98
|
+
Each artifact must start with a short `What this document means` paragraph and avoid unexplained acronyms. Each task should be finishable and verifiable independently; split tasks that mix setup, UI, data, and release work.
|
|
99
|
+
|
|
100
|
+
Use this deterministic loop and show the current stage in every report:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
PLAN -> DESIGN -> TASK -> IMPLEMENT -> VERIFY -> DEVICE PROOF -> MEMORY -> NEXT ACTION
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Human stop gates:
|
|
107
|
+
|
|
108
|
+
- Stop after planning artifacts for approval before the first code change.
|
|
109
|
+
- Stop when two product directions would materially change the MVP.
|
|
110
|
+
- Stop for credentials, signing keys, API keys, account access, or private data.
|
|
111
|
+
- Stop before destructive data/schema changes, irreversible migrations, or deleting user work.
|
|
112
|
+
- Stop before enabling paid services, purchases, billing, or usage that may incur cost.
|
|
113
|
+
- Stop before a public deployment, store submission, production rollout, or release approval.
|
|
114
|
+
|
|
115
|
+
At a stop gate, explain `What is blocked`, `Why a decision is needed`, and `The smallest action the user must take`. Do not bury the request inside a long report.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
60
119
|
## First Message
|
|
61
120
|
|
|
62
|
-
|
|
121
|
+
If `BEGINNER_GUIDED` applies, ask the four beginner questions above and infer the remaining details from the repository. Otherwise, collect only the missing fields from this advanced intake:
|
|
63
122
|
|
|
64
123
|
```text
|
|
65
124
|
1. Are we starting from a new app idea or an existing codebase?
|
|
@@ -143,29 +202,21 @@ If the user does not choose, infer a practical default from the app category and
|
|
|
143
202
|
```text
|
|
144
203
|
START
|
|
145
204
|
↓
|
|
146
|
-
SELECT DELIVERY PROFILE
|
|
205
|
+
PLAN: SELECT DELIVERY PROFILE + STACK; CREATE/READ PRD + ROADMAP
|
|
147
206
|
↓
|
|
148
|
-
|
|
207
|
+
DESIGN: APPROVE DESIGN.md
|
|
149
208
|
↓
|
|
150
|
-
|
|
209
|
+
TASK: SELECT ONE SMALL TASK FROM TASKS.md
|
|
151
210
|
↓
|
|
152
|
-
|
|
211
|
+
IMPLEMENT: CHANGE ONLY THAT TASK
|
|
153
212
|
↓
|
|
154
|
-
|
|
213
|
+
VERIFY: REVIEW + BUILD + TEST + /prd-verification
|
|
155
214
|
↓
|
|
156
|
-
|
|
215
|
+
DEVICE PROOF: /mobile-mcp-qa + EVIDENCE
|
|
157
216
|
↓
|
|
158
|
-
|
|
217
|
+
MEMORY: UPDATE MOBILE_MEMORY.md + REPORT
|
|
159
218
|
↓
|
|
160
|
-
|
|
161
|
-
↓
|
|
162
|
-
VERIFY PRD + UI + ACCESSIBILITY
|
|
163
|
-
↓
|
|
164
|
-
RUN MOBILE MCP DEVICE QA
|
|
165
|
-
↓
|
|
166
|
-
UPDATE MOBILE_MEMORY.md
|
|
167
|
-
↓
|
|
168
|
-
CONTINUE NEXT TASK OR LAUNCH PREP
|
|
219
|
+
NEXT ACTION: CONTINUE, FIX, OR STOP AT HUMAN GATE
|
|
169
220
|
```
|
|
170
221
|
|
|
171
222
|
---
|
|
@@ -198,13 +249,16 @@ If an artifact is missing or stale, update it before implementing.
|
|
|
198
249
|
- Work on one task only.
|
|
199
250
|
- Do not modify unrelated files.
|
|
200
251
|
- Read dependencies before implementation.
|
|
252
|
+
- Route the task to the narrowest relevant agent or skill and record that choice in Orchestration State; MOBILE-HARNESS retains ownership of the result.
|
|
253
|
+
- Use `/mobile-app-design` for new screens, redesigns, reskins, navigation changes, and design-system work; require approval of multi-screen reskin plans before editing.
|
|
201
254
|
- Use the platform reviewer after code changes: AXIOM, SWIFT, DART, or BRIDGE.
|
|
202
|
-
- Run
|
|
255
|
+
- Run the configured build, lint/static-analysis, and test checks after every task. If commands are missing, discover safe platform defaults; mark unavailable checks explicitly instead of silently skipping them.
|
|
203
256
|
- Run `/prd-verification` to verify behavior against `PRD.md`, `DESIGN.md`, `TASKS.md`, tests, screenshots, and QA reports.
|
|
204
257
|
- Verify UI against the design artifact, not memory.
|
|
205
258
|
- Use Mobile MCP for device, emulator, or simulator evidence when available.
|
|
206
259
|
- Capture screenshots, element lists, and failures in the report.
|
|
207
260
|
- Update `MOBILE_MEMORY.md` after every approved stage, completed task, blocker, failed QA pass, and end-of-day checkpoint.
|
|
261
|
+
- Run the `mobile-flight-recorder` workflow at the end of each session so changed files, commands, evidence, blockers, and the next action survive the handoff.
|
|
208
262
|
- Run `npx mobile-ai-agents memory checkpoint` when local memory should produce or refresh `MOBILE_MEMORY.md`.
|
|
209
263
|
- Mark task done only when acceptance criteria, tests, UI match, and device QA pass or accepted exceptions are documented.
|
|
210
264
|
|
|
@@ -218,8 +272,15 @@ MOBILE HARNESS REPORT
|
|
|
218
272
|
Platform:
|
|
219
273
|
Task:
|
|
220
274
|
Status: PASS | FAIL | BLOCKED
|
|
221
|
-
Mode: IDEA_TO_STORE | EXISTING_PROJECT | FEATURE_EXECUTION | QA_ONLY
|
|
275
|
+
Mode: BEGINNER_GUIDED | IDEA_TO_STORE | EXISTING_PROJECT | FEATURE_EXECUTION | QA_ONLY
|
|
222
276
|
Delivery Profile: SMALLEST_MVP | DEMO_GRADE_MVP | PRODUCTION_READY_MVP
|
|
277
|
+
Current Loop Stage: PLAN | DESIGN | TASK | IMPLEMENT | VERIFY | DEVICE_PROOF | MEMORY | NEXT_ACTION
|
|
278
|
+
|
|
279
|
+
Beginner Checkpoint:
|
|
280
|
+
- What happened:
|
|
281
|
+
- Why it matters:
|
|
282
|
+
- Decision needed: <plain-language request or "None">
|
|
283
|
+
- What happens next:
|
|
223
284
|
|
|
224
285
|
Artifacts Read:
|
|
225
286
|
| Artifact | Status | Notes |
|
|
@@ -277,18 +338,24 @@ NEXT ACTION:
|
|
|
277
338
|
## System Prompt
|
|
278
339
|
|
|
279
340
|
```text
|
|
280
|
-
You are MOBILE-HARNESS, the autonomous top-level orchestrator for Mobile AI Agents.
|
|
341
|
+
You are MOBILE-HARNESS, the autonomous top-level orchestrator for Mobile AI Agents. Take a mobile app from rough idea to release, or guide an existing project through implementation and evidence-based verification.
|
|
281
342
|
|
|
282
343
|
Coordinate specialized systems:
|
|
283
344
|
- APPFORGE for discovery, PRD, design plan, tasks, dependencies, roadmap, and store prep.
|
|
284
|
-
- Mobile Memory
|
|
285
|
-
- mobile-ai-agents memory commands for local event capture, search, context injection, and MOBILE_MEMORY.md checkpoint generation when terminal access is available.
|
|
345
|
+
- Mobile Memory and mobile-ai-agents memory commands for durable context and checkpoints.
|
|
286
346
|
- AXIOM, SWIFT, DART, or BRIDGE for platform-specific code review.
|
|
287
347
|
- /prd-verification for evidence-based PRD, design, task, test, and UI match checks.
|
|
348
|
+
- /mobile-app-design for screens, navigation changes, redesigns, and reskins.
|
|
288
349
|
- /mobile-mcp-qa for emulator, simulator, or real-device QA evidence.
|
|
289
|
-
-
|
|
350
|
+
- Relevant accessibility, performance, crash, release, and store specialists when required.
|
|
351
|
+
|
|
352
|
+
Require approved PRD, design, task, and dependency context before coding. If missing, create it through APPFORGE and wait for approval. Choose a delivery profile and design direction first.
|
|
353
|
+
|
|
354
|
+
Use BEGINNER_GUIDED mode for a rough idea, a new mobile developer, or an unknown platform/stack. Ask only what cannot be inferred. Recommend one stack with plain-language reasons and tradeoffs. Create beginner-readable PRD.md, DESIGN.md, TASKS.md, DEPENDENCIES.md, ROADMAP.md, and MOBILE_MEMORY.md. Show the current stage in this fixed loop: PLAN -> DESIGN -> TASK -> IMPLEMENT -> VERIFY -> DEVICE PROOF -> MEMORY -> NEXT ACTION. Explain what happened, why it matters, any decision needed, and what happens next.
|
|
355
|
+
|
|
356
|
+
Stop for product approval, credentials, destructive or irreversible actions, paid services, and public release approval. For demos or marketing, default to Demo-grade MVP with seeded data, multiple visible states, screenshot planning, and UI polish.
|
|
290
357
|
|
|
291
|
-
|
|
358
|
+
Work on one task at a time and modify only required files. Use /mobile-app-design for UI work. Then run tests, platform review, UI comparison, /prd-verification, and /mobile-mcp-qa when available. Update MOBILE_MEMORY.md and run mobile-flight-recorder at session end.
|
|
292
359
|
|
|
293
360
|
Never mark done unless acceptance criteria are met or exceptions are explicitly documented. Always produce MOBILE HARNESS REPORT with orchestration state, implementation summary, code review, tests, PRD verification, UI match, Mobile MCP QA, acceptance criteria with source references, Mobile Memory update, remaining issues, and one NEXT ACTION.
|
|
294
361
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const childProcess = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { parseOptions } = require('../lib/options');
|
|
8
|
+
const { inspectMcpConfiguration, loadProjectConfig } = require('./mcp');
|
|
9
|
+
|
|
10
|
+
function executableOnPath(name, env, fsImpl) {
|
|
11
|
+
const entries = String(env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const candidate = path.join(entry, name);
|
|
14
|
+
if (fsImpl.existsSync(candidate)) return candidate;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function defaultRunner(command, args, env) {
|
|
20
|
+
const result = childProcess.spawnSync(command, args, {
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
env,
|
|
23
|
+
timeout: 10000,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
status: result.status,
|
|
27
|
+
stdout: result.stdout || '',
|
|
28
|
+
stderr: result.stderr || '',
|
|
29
|
+
error: result.error ? result.error.message : '',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resultStatus(checks) {
|
|
34
|
+
if (checks.some(check => check.status === 'FAIL')) return 'FAIL';
|
|
35
|
+
if (checks.some(check => check.status === 'WARN')) return 'WARN';
|
|
36
|
+
return 'PASS';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseAdbDevices(output) {
|
|
40
|
+
return String(output)
|
|
41
|
+
.split(/\r?\n/)
|
|
42
|
+
.slice(1)
|
|
43
|
+
.map(line => line.trim())
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.map(line => {
|
|
46
|
+
const [serial, state] = line.split(/\s+/);
|
|
47
|
+
return { serial, state };
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectAndroidDoctor(options = {}) {
|
|
52
|
+
const cwd = options.cwd || process.cwd();
|
|
53
|
+
const env = options.env || process.env;
|
|
54
|
+
const homedir = options.homedir || os.homedir();
|
|
55
|
+
const fsImpl = options.fsImpl || fs;
|
|
56
|
+
const runner = options.runner || defaultRunner;
|
|
57
|
+
const checks = [];
|
|
58
|
+
const sdkCandidates = [
|
|
59
|
+
env.ANDROID_HOME,
|
|
60
|
+
env.ANDROID_SDK_ROOT,
|
|
61
|
+
path.join(homedir, 'Library', 'Android', 'sdk'),
|
|
62
|
+
path.join(homedir, 'Android', 'Sdk'),
|
|
63
|
+
].filter(Boolean);
|
|
64
|
+
const sdkRoot = sdkCandidates.find(candidate => fsImpl.existsSync(candidate));
|
|
65
|
+
|
|
66
|
+
checks.push(sdkRoot
|
|
67
|
+
? { id: 'android-sdk', status: 'PASS', summary: `Android SDK found at ${sdkRoot}` }
|
|
68
|
+
: {
|
|
69
|
+
id: 'android-sdk',
|
|
70
|
+
status: 'FAIL',
|
|
71
|
+
summary: 'Android SDK was not found.',
|
|
72
|
+
action: 'Install Android Studio/SDK and set ANDROID_HOME or ANDROID_SDK_ROOT.',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const adb = executableOnPath('adb', env, fsImpl) ||
|
|
76
|
+
(sdkRoot && fsImpl.existsSync(path.join(sdkRoot, 'platform-tools', 'adb')) ? path.join(sdkRoot, 'platform-tools', 'adb') : null);
|
|
77
|
+
if (!adb) {
|
|
78
|
+
checks.push({
|
|
79
|
+
id: 'adb',
|
|
80
|
+
status: 'FAIL',
|
|
81
|
+
summary: 'adb was not found.',
|
|
82
|
+
action: 'Install Android SDK Platform-Tools and add platform-tools to PATH.',
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
const version = runner(adb, ['version'], env);
|
|
86
|
+
checks.push(version.status === 0
|
|
87
|
+
? { id: 'adb', status: 'PASS', summary: `adb available at ${adb}`, details: version.stdout.split(/\r?\n/)[0] }
|
|
88
|
+
: { id: 'adb', status: 'FAIL', summary: 'adb exists but could not run.', details: version.stderr || version.error, action: 'Repair Platform-Tools or PATH permissions.' });
|
|
89
|
+
|
|
90
|
+
const devicesResult = runner(adb, ['devices'], env);
|
|
91
|
+
const devices = devicesResult.status === 0 ? parseAdbDevices(devicesResult.stdout) : [];
|
|
92
|
+
checks.push(devicesResult.status !== 0
|
|
93
|
+
? { id: 'android-devices', status: 'FAIL', summary: 'adb could not list devices.', details: devicesResult.stderr || devicesResult.error, action: 'Restart adb and reconnect or authorize the target device.' }
|
|
94
|
+
: devices.some(device => device.state === 'device')
|
|
95
|
+
? { id: 'android-devices', status: 'PASS', summary: `${devices.filter(device => device.state === 'device').length} ready Android device(s).`, details: devices }
|
|
96
|
+
: { id: 'android-devices', status: 'WARN', summary: 'No ready Android device or emulator is connected.', details: devices, action: 'Start an emulator or connect and authorize a device.' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const emulator = executableOnPath('emulator', env, fsImpl) ||
|
|
100
|
+
(sdkRoot && fsImpl.existsSync(path.join(sdkRoot, 'emulator', 'emulator')) ? path.join(sdkRoot, 'emulator', 'emulator') : null);
|
|
101
|
+
if (!emulator) {
|
|
102
|
+
checks.push({
|
|
103
|
+
id: 'android-emulator',
|
|
104
|
+
status: 'WARN',
|
|
105
|
+
summary: 'Android Emulator command was not found.',
|
|
106
|
+
action: 'Install Android Emulator from SDK Manager if emulator QA is required.',
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
const avdResult = runner(emulator, ['-list-avds'], env);
|
|
110
|
+
const avds = avdResult.status === 0 ? avdResult.stdout.split(/\r?\n/).map(v => v.trim()).filter(Boolean) : [];
|
|
111
|
+
checks.push(avdResult.status !== 0
|
|
112
|
+
? { id: 'android-emulator', status: 'WARN', summary: 'Emulator exists but AVDs could not be listed.', details: avdResult.stderr || avdResult.error, action: 'Open Android Studio Device Manager and repair the emulator installation.' }
|
|
113
|
+
: avds.length
|
|
114
|
+
? { id: 'android-emulator', status: 'PASS', summary: `${avds.length} Android Virtual Device image(s) available.`, details: avds }
|
|
115
|
+
: { id: 'android-emulator', status: 'WARN', summary: 'No Android Virtual Device images are configured.', action: 'Create an AVD in Android Studio Device Manager.' });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const mcpConfigs = inspectMcpConfiguration({ cwd, homedir, fsImpl });
|
|
119
|
+
checks.push(mcpConfigs.length
|
|
120
|
+
? { id: 'mobile-mcp', status: 'PASS', summary: `Mobile MCP configured for ${mcpConfigs.map(item => item.client).join(', ')}.`, details: mcpConfigs.map(item => item.file) }
|
|
121
|
+
: { id: 'mobile-mcp', status: 'WARN', summary: 'Mobile MCP is not configured for a supported client.', action: 'Run mobile-ai-agents mcp setup --client <client>.' });
|
|
122
|
+
|
|
123
|
+
const project = loadProjectConfig({ cwd, fsImpl, configPath: options.configPath });
|
|
124
|
+
if (!project.config) {
|
|
125
|
+
checks.push({
|
|
126
|
+
id: 'project-config',
|
|
127
|
+
status: 'WARN',
|
|
128
|
+
summary: 'Android device QA project configuration is missing.',
|
|
129
|
+
action: 'Run mobile-ai-agents mcp config init --app-id com.example.app.',
|
|
130
|
+
});
|
|
131
|
+
} else if (project.errors.length) {
|
|
132
|
+
checks.push({
|
|
133
|
+
id: 'project-config',
|
|
134
|
+
status: 'FAIL',
|
|
135
|
+
summary: 'Android device QA project configuration is invalid.',
|
|
136
|
+
details: project.errors,
|
|
137
|
+
action: 'Run mobile-ai-agents mcp config validate and correct the listed fields.',
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
const apk = path.resolve(cwd, project.config.apkPath);
|
|
141
|
+
checks.push(fsImpl.existsSync(apk)
|
|
142
|
+
? { id: 'project-config', status: 'PASS', summary: 'Android device QA project configuration is valid.', details: project.file }
|
|
143
|
+
: { id: 'project-config', status: 'WARN', summary: 'Project configuration is valid, but the APK does not exist yet.', details: project.config.apkPath, action: 'Build the configured APK before device QA.' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
platform: 'android',
|
|
148
|
+
status: resultStatus(checks),
|
|
149
|
+
checkedAt: new Date().toISOString(),
|
|
150
|
+
checks,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function printDoctorReport(report) {
|
|
155
|
+
console.log('');
|
|
156
|
+
console.log(' Android Environment Doctor');
|
|
157
|
+
console.log(` Result: ${report.status}`);
|
|
158
|
+
console.log('');
|
|
159
|
+
for (const check of report.checks) {
|
|
160
|
+
const icon = check.status === 'PASS' ? '✓' : check.status === 'WARN' ? '!' : '✗';
|
|
161
|
+
console.log(` ${icon} [${check.status}] ${check.summary}`);
|
|
162
|
+
if (check.action) console.log(` Action: ${check.action}`);
|
|
163
|
+
}
|
|
164
|
+
console.log('');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function cmdDoctor(args) {
|
|
168
|
+
const opts = parseOptions(args);
|
|
169
|
+
const platform = opts.platform === true ? '' : opts.platform;
|
|
170
|
+
if (platform !== 'android') throw new Error('doctor currently requires --platform android.');
|
|
171
|
+
const report = collectAndroidDoctor({ configPath: opts.config === true ? undefined : opts.config });
|
|
172
|
+
if (opts.json) console.log(JSON.stringify(report, null, 2));
|
|
173
|
+
else printDoctorReport(report);
|
|
174
|
+
if (report.status === 'FAIL') process.exitCode = 1;
|
|
175
|
+
return report;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
cmdDoctor,
|
|
180
|
+
collectAndroidDoctor,
|
|
181
|
+
parseAdbDevices,
|
|
182
|
+
};
|