reis-mobile 0.3.5

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 (105) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/CHANGELOG.md +103 -0
  4. package/LICENSE +21 -0
  5. package/README.md +631 -0
  6. package/THIRD_PARTY_NOTICES.md +105 -0
  7. package/agents/flutter-architect.md +92 -0
  8. package/agents/flutter-performance-engineer.md +103 -0
  9. package/agents/flutter-test-engineer.md +100 -0
  10. package/agents/lead-mobile.md +107 -0
  11. package/agents/mobile-code-reviewer.md +66 -0
  12. package/agents/mobile-staff-engineer.md +85 -0
  13. package/agents/plugin-native-expert.md +97 -0
  14. package/bin/reis-mobile.mjs +256 -0
  15. package/commands/debate.md +135 -0
  16. package/commands/doctor.md +23 -0
  17. package/commands/reis-mobile.md +50 -0
  18. package/commands/review.md +53 -0
  19. package/core/config/language.mjs +83 -0
  20. package/core/context/context-engine.mjs +113 -0
  21. package/core/detection/project-probe.mjs +104 -0
  22. package/core/detection/stack-detector.mjs +204 -0
  23. package/core/diagnostics/doctor.mjs +181 -0
  24. package/core/install/claude-plugin.mjs +76 -0
  25. package/core/paths.mjs +4 -0
  26. package/core/registry/frontmatter.mjs +81 -0
  27. package/core/registry/registry.mjs +140 -0
  28. package/core/router/intent-detector.mjs +75 -0
  29. package/core/router/intents.mjs +91 -0
  30. package/core/router/router.mjs +83 -0
  31. package/core/security/redact.mjs +24 -0
  32. package/package.json +35 -0
  33. package/skills/architecture-feature-first/SKILL.md +116 -0
  34. package/skills/bloc/SKILL.md +354 -0
  35. package/skills/code-review/SKILL.md +89 -0
  36. package/skills/dart-3-updates/SKILL.md +223 -0
  37. package/skills/dart-add-unit-test/SKILL.md +126 -0
  38. package/skills/dart-collect-coverage/SKILL.md +145 -0
  39. package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
  40. package/skills/dart-generate-test-mocks/SKILL.md +159 -0
  41. package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
  42. package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
  43. package/skills/dart-run-static-analysis/SKILL.md +108 -0
  44. package/skills/dart-use-pattern-matching/SKILL.md +240 -0
  45. package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
  46. package/skills/detect-mobile-stack/SKILL.md +33 -0
  47. package/skills/effective-dart/SKILL.md +187 -0
  48. package/skills/firebase-ai/SKILL.md +93 -0
  49. package/skills/firebase-analytics/SKILL.md +129 -0
  50. package/skills/firebase-app-check/SKILL.md +124 -0
  51. package/skills/firebase-auth/SKILL.md +224 -0
  52. package/skills/firebase-cloud-firestore/SKILL.md +165 -0
  53. package/skills/firebase-cloud-functions/SKILL.md +148 -0
  54. package/skills/firebase-crashlytics/SKILL.md +182 -0
  55. package/skills/firebase-data-connect/SKILL.md +80 -0
  56. package/skills/firebase-database/SKILL.md +224 -0
  57. package/skills/firebase-in-app-messaging/SKILL.md +111 -0
  58. package/skills/firebase-messaging/SKILL.md +201 -0
  59. package/skills/firebase-remote-config/SKILL.md +131 -0
  60. package/skills/firebase-storage/SKILL.md +158 -0
  61. package/skills/flutter-add-integration-test/SKILL.md +167 -0
  62. package/skills/flutter-add-widget-preview/SKILL.md +149 -0
  63. package/skills/flutter-add-widget-test/SKILL.md +158 -0
  64. package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
  65. package/skills/flutter-animating-apps/SKILL.md +185 -0
  66. package/skills/flutter-app-architecture/SKILL.md +80 -0
  67. package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
  68. package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
  69. package/skills/flutter-building-forms/SKILL.md +125 -0
  70. package/skills/flutter-building-layouts/SKILL.md +131 -0
  71. package/skills/flutter-building-plugins/SKILL.md +209 -0
  72. package/skills/flutter-caching-data/SKILL.md +171 -0
  73. package/skills/flutter-change-notifier/SKILL.md +138 -0
  74. package/skills/flutter-embedding-native-views/SKILL.md +200 -0
  75. package/skills/flutter-errors/SKILL.md +126 -0
  76. package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
  77. package/skills/flutter-handling-concurrency/SKILL.md +182 -0
  78. package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
  79. package/skills/flutter-improving-accessibility/SKILL.md +113 -0
  80. package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
  81. package/skills/flutter-login-usecase/SKILL.md +38 -0
  82. package/skills/flutter-managing-state/SKILL.md +206 -0
  83. package/skills/flutter-project-audit/SKILL.md +66 -0
  84. package/skills/flutter-reducing-app-size/SKILL.md +103 -0
  85. package/skills/flutter-secure-token-store/SKILL.md +35 -0
  86. package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
  87. package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
  88. package/skills/flutter-setup-localization/SKILL.md +214 -0
  89. package/skills/flutter-theming-apps/SKILL.md +163 -0
  90. package/skills/flutter-use-http-package/SKILL.md +178 -0
  91. package/skills/flutter-widget-review/SKILL.md +54 -0
  92. package/skills/flutter-working-with-databases/SKILL.md +203 -0
  93. package/skills/flutterfire-configure/SKILL.md +143 -0
  94. package/skills/mobile-security-audit/SKILL.md +101 -0
  95. package/skills/mockito/SKILL.md +149 -0
  96. package/skills/mocktail/SKILL.md +122 -0
  97. package/skills/patrol-e2e-testing/SKILL.md +224 -0
  98. package/skills/provider/SKILL.md +139 -0
  99. package/skills/riverpod/SKILL.md +246 -0
  100. package/skills/testing/SKILL.md +50 -0
  101. package/stacks/android/stack.json +8 -0
  102. package/stacks/flutter/stack.json +8 -0
  103. package/stacks/ios/stack.json +8 -0
  104. package/stacks/kotlin-multiplatform/stack.json +8 -0
  105. package/stacks/react-native/stack.json +8 -0
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: plugin-native-expert
3
+ description: Use this agent for Flutter plugin development and native platform integration — MethodChannel, EventChannel and BasicMessageChannel design, Pigeon, Android Kotlin and iOS Swift bridges, threading and lifecycle bugs between Flutter and native code, and deprecated native APIs. Typical triggers are building a plugin for a device capability, reviewing a MethodChannel handler and an EventChannel stream crashing when the app goes to background.
4
+ model: inherit
5
+ color: magenta
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
7
+ routing: manual
8
+ stacks: [flutter]
9
+ ---
10
+
11
+ You are a Flutter plugin and native integration specialist with deep expertise in bridging Flutter/Dart with Android (Kotlin) and iOS (Swift).
12
+
13
+ ## When to invoke
14
+
15
+ - **New plugin or native feature.** Design the Dart API, the channel contract and both native implementations.
16
+ - **Channel code review.** Check threading, lifecycle, error handling and API choices.
17
+ - **Bridge bugs.** Crashes, missing callbacks or leaks between Flutter and native code.
18
+
19
+ ## Expertise
20
+
21
+ - Federated plugin structure and `plugin_platform_interface`
22
+ - `MethodChannel`: async call patterns and argument encoding
23
+ - `EventChannel`: stream lifecycle, sink management, cancellation
24
+ - `BasicMessageChannel` for high-frequency messaging
25
+ - Pigeon for type-safe channel generation, when the project accepts code generation
26
+ - Android: `FlutterPlugin`, `ActivityAware`, `ActivityPluginBinding`, coroutines, Activity Result APIs
27
+ - iOS: `FlutterPlugin`, `FlutterMethodChannel`, `FlutterStreamHandler`, Swift concurrency
28
+
29
+ ## Review dimensions
30
+
31
+ 1. **Threading**
32
+ - Channel results and events are delivered on the platform main thread (Android main `Looper`, iOS main queue).
33
+ - Heavy work runs off the main thread (Android `Dispatchers.IO`, iOS background tasks) and returns to the main thread before replying.
34
+ - No UI work off the main thread.
35
+
36
+ 2. **Lifecycle**
37
+ - Android: registration in `onAttachedToEngine`, activity-scoped work in `onAttachedToActivity`, cleanup in `onDetachedFromActivity`/`onDetachedFromEngine`.
38
+ - iOS: registration in `register(with:)`, cleanup of observers and stream handlers.
39
+ - No retained channels, activities or unclosed event sinks after detach.
40
+
41
+ 3. **Error handling**
42
+ - Every call ends in exactly one of `success`, `error` or `notImplemented`.
43
+ - Failures return `FlutterError`/`result.error` with meaningful codes and messages.
44
+ - `EventSink` errors and `endOfStream` handled; sink cleared in `onCancel`.
45
+ - Native exceptions never escape and crash the app.
46
+
47
+ 4. **Modernization**
48
+ - Deprecated Android APIs (for example `startActivityForResult`) replaced by Activity Result APIs.
49
+ - Completion handlers replaced by async/await where the minimum iOS version allows.
50
+
51
+ 5. **Contract design**
52
+ - The right channel type for the traffic pattern.
53
+ - Typed `StandardMessageCodec` values instead of JSON strings.
54
+ - Null safety consistent on both sides; the Dart interface does not leak platform details.
55
+
56
+ ## Writing new integration code
57
+
58
+ **Dart**
59
+ ```dart
60
+ abstract interface class BatteryPlatform {
61
+ Future<int?> batteryLevel();
62
+ Stream<BatteryState> get states;
63
+ }
64
+
65
+ const _methods = MethodChannel('dev.example.battery/methods');
66
+ const _events = EventChannel('dev.example.battery/events');
67
+ ```
68
+
69
+ Use the project's own package name for channel names.
70
+
71
+ **Android (Kotlin)**
72
+ - Scope activity work through `ActivityPluginBinding`.
73
+ - Capture `MethodChannel.Result` before suspending and reply exactly once.
74
+
75
+ **iOS (Swift)**
76
+ - `guard let` for argument unwrapping from `FlutterMethodCall`.
77
+ - `FlutterMethodNotImplemented` for unknown methods.
78
+ - Main actor for UI work from async contexts.
79
+
80
+ When the native feature feeds app data, expose it through the project's existing service or data source layer, not through channel calls in widgets.
81
+
82
+ ## Output
83
+
84
+ 1. **Critical issues**: threading, crashes, leaks
85
+ 2. **Correctness issues**: lifecycle, error handling
86
+ 3. **Modernization suggestions**
87
+ 4. **Corrected code** for each issue, not just descriptions
88
+
89
+ ## Self-check
90
+
91
+ - [ ] Channel replies and events on the correct thread
92
+ - [ ] Lifecycle callbacks implemented and cleaned up
93
+ - [ ] Each call replied to exactly once
94
+ - [ ] Event sink cleared on cancel
95
+ - [ ] No deprecated APIs
96
+ - [ ] Errors carry meaningful codes and messages
97
+ - [ ] Dart interface free of platform details
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { parseArgs } from 'node:util';
5
+
6
+ import { LANGUAGES, configPath, removeConfig, resolveLanguage, saveLanguage } from '../core/config/language.mjs';
7
+ import { collectReviewContext } from '../core/context/context-engine.mjs';
8
+ import { detectStack } from '../core/detection/stack-detector.mjs';
9
+ import { runDoctor } from '../core/diagnostics/doctor.mjs';
10
+ import { installClaudePlugin, uninstallClaudePlugin } from '../core/install/claude-plugin.mjs';
11
+ import { PLUGIN_ROOT } from '../core/paths.mjs';
12
+ import { loadRegistry, validateRegistry } from '../core/registry/registry.mjs';
13
+ import { INTENT_IDS } from '../core/router/intents.mjs';
14
+ import { route } from '../core/router/router.mjs';
15
+
16
+ const USAGE = `reis-mobile — AI agents for mobile engineering
17
+
18
+ Usage: reis-mobile <command> [options]
19
+
20
+ Commands:
21
+ init [en|pt] Install the reis-mobile plugin into Claude Code, optionally setting the language
22
+ lang [en|pt] Show or set the language the /reis-mobile commands answer in
23
+ detect Detect the mobile stack of a project
24
+ doctor Diagnose the environment for the detected stack
25
+ route <prompt...> Show which intent, agent and skills a prompt resolves to
26
+ review [prompt...] Route a code review and collect the git context for it
27
+ agents | skills | stacks List registered components
28
+ validate Validate the plugin's agents, skills and stacks
29
+
30
+ Options:
31
+ --dir <path> Project directory (default: current directory)
32
+ --intent <id> Force an intent (${INTENT_IDS.join(', ')})
33
+ --base <ref> review: compare <ref>...HEAD instead of the working tree
34
+ --all doctor: check every tool, not only the relevant ones
35
+ --strict doctor: exit with code 1 when there are warnings
36
+ --local init: install the plugin from this installation instead of GitHub
37
+ --scope <scope> init: user (default), project or local
38
+ --uninstall init: remove the plugin, its marketplace and the saved language
39
+ --lang <en|pt> Override the saved language for this run
40
+ --json Machine-readable output
41
+ -v, --version Print the version
42
+ -h, --help Show this help`;
43
+
44
+ const OPTIONS = {
45
+ dir: { type: 'string' },
46
+ intent: { type: 'string' },
47
+ base: { type: 'string' },
48
+ all: { type: 'boolean' },
49
+ strict: { type: 'boolean' },
50
+ local: { type: 'boolean' },
51
+ scope: { type: 'string' },
52
+ uninstall: { type: 'boolean' },
53
+ lang: { type: 'string' },
54
+ json: { type: 'boolean' },
55
+ version: { type: 'boolean', short: 'v' },
56
+ help: { type: 'boolean', short: 'h' },
57
+ };
58
+
59
+ const COMMANDS = {
60
+ init: commandInit,
61
+ lang: commandLang,
62
+ detect: commandDetect,
63
+ doctor: commandDoctor,
64
+ route: commandRoute,
65
+ review: commandReview,
66
+ agents: (options) => commandList('agents', options),
67
+ skills: (options) => commandList('skills', options),
68
+ stacks: (options) => commandList('stacks', options),
69
+ validate: commandValidate,
70
+ };
71
+
72
+ async function main(argv) {
73
+ const { values: options, positionals } = parseArgs({ args: argv, options: OPTIONS, allowPositionals: true });
74
+ const [name, ...rest] = positionals;
75
+
76
+ if (options.version) {
77
+ const pkg = JSON.parse(await readFile(join(PLUGIN_ROOT, 'package.json'), 'utf8'));
78
+ console.log(`reis-mobile ${pkg.version}`);
79
+ return 0;
80
+ }
81
+ if (options.help || !name) {
82
+ console.log(USAGE);
83
+ return name || options.help ? 0 : 1;
84
+ }
85
+
86
+ const command = COMMANDS[name];
87
+ if (!command) {
88
+ console.error(`Unknown command "${name}".\n\n${USAGE}`);
89
+ return 1;
90
+ }
91
+ return (await command({ ...options, dir: options.dir ?? process.cwd(), prompt: rest.join(' ') })) ?? 0;
92
+ }
93
+
94
+ async function commandInit({ local, scope, uninstall, lang, prompt }) {
95
+ // The installers pass the language through REIS_MOBILE_LANG.
96
+ const requested = lang ?? (prompt || process.env.REIS_MOBILE_LANG || undefined);
97
+ // Validate before touching Claude Code, so a typo does not leave a half-done install.
98
+ if (requested && !uninstall) await resolveLanguage({ flag: requested, env: {} });
99
+
100
+ const result = uninstall
101
+ ? uninstallClaudePlugin({ scope })
102
+ : installClaudePlugin({ scope, ...(local && { source: PLUGIN_ROOT }) });
103
+
104
+ if (!result.ok) {
105
+ console.error(`✗ ${result.failedStep} failed (exit ${result.status})`);
106
+ return 1;
107
+ }
108
+ if (uninstall) {
109
+ await removeConfig();
110
+ console.log('✓ reis-mobile removed from Claude Code');
111
+ return 0;
112
+ }
113
+ if (requested) console.log(`✓ Language: ${await saveLanguage(requested)} (${configPath()})`);
114
+ console.log('✓ reis-mobile installed in Claude Code. Restart Claude Code, then run /reis-mobile:doctor');
115
+ return 0;
116
+ }
117
+
118
+ async function commandLang({ prompt, json }) {
119
+ if (prompt) {
120
+ const language = await saveLanguage(prompt);
121
+ if (json) return print({ language, path: configPath() });
122
+ console.log(`✓ Language: ${language} (${LANGUAGES[language]}) · saved to ${configPath()}`);
123
+ return 0;
124
+ }
125
+ const language = await resolveLanguage();
126
+ if (json) return print({ language, path: configPath() });
127
+ console.log(language ? `${language} (${LANGUAGES[language]})` : 'not set: the commands answer in the language of each request');
128
+ return 0;
129
+ }
130
+
131
+ async function commandDetect({ dir, json, lang }) {
132
+ const detection = await detectStack(dir);
133
+ const language = await resolveLanguage({ flag: lang });
134
+ if (json) return print({ ...detection, language });
135
+
136
+ console.log(`Stack ${detection.stack}${detection.variant ? ` (${detection.variant})` : ''}`);
137
+ console.log(`Languages ${detection.languages.join(', ') || '-'}`);
138
+ console.log(`Platforms ${detection.platforms.join(', ') || '-'}`);
139
+ for (const evidence of detection.evidence) console.log(` · ${evidence}`);
140
+ const others = detection.candidates.slice(1).map((candidate) => candidate.stack);
141
+ if (others.length) console.log(`Also matches ${others.join(', ')}`);
142
+ printLanguage(language);
143
+ return detection.stack === 'unknown' ? 2 : 0;
144
+ }
145
+
146
+ async function commandDoctor({ dir, all, strict, json, lang }) {
147
+ const report = await runDoctor({ projectDir: dir, all });
148
+ const language = await resolveLanguage({ flag: lang });
149
+ if (json) {
150
+ print({ ...report, language });
151
+ } else {
152
+ console.log('reis-mobile doctor\n');
153
+ for (const [section, title] of [['environment', 'Environment'], ['project', 'Project'], ['integrations', 'Integrations']]) {
154
+ const checks = report.checks.filter((check) => check.section === section);
155
+ if (!checks.length) continue;
156
+ console.log(`${title}\n${'-'.repeat(title.length)}`);
157
+ for (const check of checks) console.log(`${check.label.padEnd(16)}${SYMBOLS[check.status]} ${check.detail ?? ''}`.trimEnd());
158
+ console.log('');
159
+ }
160
+ console.log(report.warnings ? `${report.warnings} warning(s)` : 'No issues found');
161
+ printLanguage(language);
162
+ }
163
+ return strict && report.warnings ? 1 : 0;
164
+ }
165
+
166
+ const SYMBOLS = { ok: '✓', warning: '✗', info: '-', skipped: '-' };
167
+
168
+ async function commandRoute({ dir, intent, prompt, json, lang }) {
169
+ if (!prompt && !intent) {
170
+ console.error('route needs a prompt or --intent.');
171
+ return 1;
172
+ }
173
+ const result = await route({ prompt, intent, projectDir: dir });
174
+ const language = await resolveLanguage({ flag: lang });
175
+ if (json) return print({ ...summarizeRoute(result), language });
176
+ printRoute(result);
177
+ printLanguage(language);
178
+ return 0;
179
+ }
180
+
181
+ async function commandReview({ dir, base, prompt, json, lang }) {
182
+ const result = await route({ prompt, intent: 'review', projectDir: dir });
183
+ const context = collectReviewContext({ projectDir: dir, base });
184
+ const language = await resolveLanguage({ flag: lang });
185
+ if (json) return print({ ...summarizeRoute(result), language, context });
186
+
187
+ printRoute(result);
188
+ printLanguage(language);
189
+ console.log(`\nContext ${context.mode}${context.base ? ` (${context.base}...HEAD)` : ''}`);
190
+ if (context.note) console.log(` ${context.note}`);
191
+ for (const file of context.files) console.log(` ${file.status.padEnd(10)} ${file.path}`);
192
+ if (context.diff) console.log(`\n${context.diff}`);
193
+ return 0;
194
+ }
195
+
196
+ async function commandList(kind, { json }) {
197
+ const registry = await loadRegistry();
198
+ const items = registry[kind];
199
+ if (json) return print(items);
200
+ for (const item of items) {
201
+ if (kind === 'stacks') {
202
+ console.log(`${item.id.padEnd(24)}${item.name} · ${item.platforms.join(', ')}`);
203
+ } else {
204
+ const routing = item.routed ? `${item.intents.join(',')} @ ${item.stacks.join(',')}` : 'manual';
205
+ console.log(`${item.name.padEnd(28)}${routing}`);
206
+ }
207
+ }
208
+ return 0;
209
+ }
210
+
211
+ async function commandValidate({ json }) {
212
+ const registry = await loadRegistry();
213
+ const errors = validateRegistry(registry);
214
+ if (json) {
215
+ print({ valid: errors.length === 0, errors });
216
+ } else if (errors.length) {
217
+ for (const error of errors) console.error(`✗ ${error}`);
218
+ } else {
219
+ console.log(`✓ ${registry.stacks.length} stacks, ${registry.agents.length} agents, ${registry.skills.length} skills`);
220
+ }
221
+ return errors.length ? 1 : 0;
222
+ }
223
+
224
+ function summarizeRoute(result) {
225
+ const component = (item) => item && { name: item.name, description: item.description, path: item.path };
226
+ return { ...result, agent: component(result.agent), skills: result.skills.map(component) };
227
+ }
228
+
229
+ function printRoute(result) {
230
+ const confidence = result.intentSource === 'explicit' ? 'explicit' : `confidence ${result.confidence}`;
231
+ console.log(`Intent ${result.intent ?? '-'} (${confidence})${result.area ? ` · area ${result.area}` : ''}`);
232
+ console.log(`Stack ${result.stack}${result.platformFocus.length ? ` · focus ${result.platformFocus.join(', ')}` : ''}`);
233
+ console.log(`Agent ${result.agent?.name ?? '-'}`);
234
+ console.log(`Skills ${result.skills.map((skill) => skill.name).join(', ') || '-'}`);
235
+ for (const warning of result.warnings) console.log(`! ${warning}`);
236
+ }
237
+
238
+ /** `-` means no saved language: the commands follow the language of the request. */
239
+ function printLanguage(language) {
240
+ console.log(`Language ${language ?? '-'}`);
241
+ }
242
+
243
+ function print(value) {
244
+ console.log(JSON.stringify(value, null, 2));
245
+ return 0;
246
+ }
247
+
248
+ main(process.argv.slice(2)).then(
249
+ (code) => {
250
+ process.exitCode = code;
251
+ },
252
+ (error) => {
253
+ console.error(`reis-mobile: ${error.message}`);
254
+ process.exitCode = 1;
255
+ },
256
+ );
@@ -0,0 +1,135 @@
1
+ ---
2
+ description: Structured debate between the mobile specialists — each agent defends its perspective, rebuts the others, and lead-mobile decides
3
+ argument-hint: "[--rounds N] [--agents a,b,c] [--external] <question>"
4
+ allowed-tools: ["Bash(node:*)", "Bash(command:*)", "Bash(mkdir:*)", "Bash(codex:*)", "Bash(gemini:*)", "Agent", "Read", "Write", "Grep", "Glob"]
5
+ ---
6
+
7
+ # reis-mobile debate
8
+
9
+ Arguments received: `$ARGUMENTS`
10
+
11
+ Use this command for architecture decisions with real trade-offs, not for routine questions: every debate costs several agents across two rounds. If the question has a single answer that can be verified in the code, answer it directly and tell the user why it was not worth a debate.
12
+
13
+ ## 1. Parse the arguments
14
+
15
+ | Flag | Default | Effect |
16
+ |---|---|---|
17
+ | `--rounds N` | 2 | Number of rounds, from 1 to 3. Above 3, warn and use 3. |
18
+ | `--agents a,b,c` | automatic | Explicit participants, using the names from the table in step 3. |
19
+ | `--external` | off | Adds Codex and Gemini to the debate, when installed. |
20
+
21
+ Whatever remains is the question, as free text. Without a question, explain the usage and stop.
22
+
23
+ ## 2. Gather the context
24
+
25
+ ```bash
26
+ node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect --dir "$PWD" --json
27
+ ```
28
+
29
+ The JSON carries the stack and the `language` for the whole debate: `en` English, `pt` Brazilian Portuguese, `null` the language of the user's question. Use it in the announcements, the briefings, every round file and the synthesis, so participants write in it too. Code, identifiers and file paths stay as they are.
30
+
31
+ If the question mentions files or directories, resolve the paths and read them now: participants receive the content in the briefing, not the path. If it fails, show the error and stop.
32
+
33
+ ## 3. Pick the participants
34
+
35
+ Choose **three** agents whose priorities genuinely conflict on the question. A debate between roles that agree produces no decision.
36
+
37
+ | Agent | Defends | Model |
38
+ |---|---|---|
39
+ | `reis-mobile:flutter-architect` | Layer boundaries, modularization, long-term maintenance cost | `opus` |
40
+ | `reis-mobile:flutter-performance-engineer` | Frames, rebuilds, memory, startup time — hostile to indirection | `sonnet` |
41
+ | `reis-mobile:flutter-test-engineer` | Testability, injection seams, coverage cost | `sonnet` |
42
+ | `reis-mobile:plugin-native-expert` | Flutter–native boundary, platform channels, lifecycle | `opus` |
43
+ | `reis-mobile:mobile-staff-engineer` | Build, release, migration, cross-platform trade-offs | `opus` |
44
+ | `reis-mobile:mobile-code-reviewer` | Concrete risk in the code that already exists | `sonnet` |
45
+
46
+ Pass the `model` from the table on every `Agent` call. It overrides the `model: inherit` the agents declare, and that is what keeps the debate from turning into a monologue: with everyone on the same model, participants inherit the same biases and the same blind spots, and the disagreement stays on the surface of the role. The table pairs model strength with role — `opus` where the question is a long-term trade-off, `sonnet` where it is concrete implementation detail.
47
+
48
+ Automatic selection must end with **at least two different models** among the participants. If the question leads to three agents on the same model, swap one of them for the most relevant adjacent agent that uses the other model, and tell the user you did so.
49
+
50
+ Selection rules:
51
+
52
+ - The detected stack rules. The `flutter-*` agents only join Flutter projects; in native Android or iOS, use `mobile-staff-engineer` and `mobile-code-reviewer`.
53
+ - Without native code in the project, do not call `plugin-native-expert`.
54
+ - `--agents` overrides the automatic choice, but not the models: each agent keeps the one from the table. Unknown name: warn and stop.
55
+ - `reis-mobile:lead-mobile` never debates — it moderates in step 7, always on `opus`, because synthesizing conflicting positions is the heaviest work in the flow.
56
+
57
+ Before starting, tell the user in one line the stack, the participants with their models and the number of rounds.
58
+
59
+ ## 4. Prepare the folder
60
+
61
+ ```bash
62
+ mkdir -p ".reis-mobile/debates/<NNN-question-slug>/rounds"
63
+ ```
64
+
65
+ `NNN` is sequential within `.reis-mobile/debates/`. Write `context.md` in the folder with the question, the stack, the language, the participants, the flags and the context read in step 2.
66
+
67
+ ## 5. Round 1 — blind positions
68
+
69
+ Launch the participants **in parallel**, each with `Agent(subagent_type: "<agent>", model: "<model from the table>", run_in_background: true)`, in a single tool block. Nobody sees anyone else's position in this round: that is what keeps the first to answer from anchoring the rest.
70
+
71
+ Each briefing, self-contained:
72
+
73
+ ```text
74
+ You are taking part in a technical debate as <agent role>.
75
+
76
+ QUESTION: <question>
77
+ STACK: <detected stack>
78
+ LANGUAGE: <language — write your position in it>
79
+ CONTEXT: <content of the relevant files>
80
+
81
+ Defend the position your specialty supports, in at most 400 words.
82
+ Cover: your recommendation, the evidence in the code that supports it, what you
83
+ give up by choosing it, and the condition that would change your mind.
84
+ Ground every claim in file:line. Do not write what you have not verified.
85
+
86
+ Write your position to: .reis-mobile/debates/<id>/rounds/r1_<agent>.md
87
+ ```
88
+
89
+ ### 5.1 External providers (only with `--external`)
90
+
91
+ Check before calling, and carry on without them if they are missing — their absence is informative, not an error:
92
+
93
+ ```bash
94
+ command -v codex >/dev/null 2>&1 && echo codex
95
+ command -v gemini >/dev/null 2>&1 && echo gemini
96
+ ```
97
+
98
+ If present, call them in parallel with the agents, writing to `r1_codex.md` and `r1_gemini.md`:
99
+
100
+ ```bash
101
+ codex exec --full-auto "<briefing>"
102
+ printf '%s' "<briefing>" | gemini -p "" -o text --approval-mode yolo
103
+ ```
104
+
105
+ They do not have the plugin skills and do not know the project: give the entire context in the briefing and treat the output as an outside opinion, to be checked against the code before it enters the synthesis. State at the opening who joined and who was missing.
106
+
107
+ ## 6. Round 2 — rebuttal
108
+
109
+ For every round after the first, launch the same participants again, in parallel and with the same models as round 1 — changing a participant's model mid-debate changes the debater, and the rebuttal is no longer theirs. Now each one receives the others' positions:
110
+
111
+ ```text
112
+ These are the other participants' positions from the previous round:
113
+
114
+ <content of each r<N-1>_*.md>
115
+
116
+ Attack the specific points you disagree with, citing who said them and
117
+ verifying the claim in the code. Where they are right and you were wrong,
118
+ say so. At most 300 words, in the same language as round 1.
119
+
120
+ Write to: .reis-mobile/debates/<id>/rounds/r<N>_<agent>.md
121
+ ```
122
+
123
+ Rebutting the generic thesis does not count: every rebuttal cites a concrete claim from another participant. If a participant only repeats the previous round without engaging, record that and discount their weight in the synthesis.
124
+
125
+ ## 7. Synthesis
126
+
127
+ Invoke `reis-mobile:lead-mobile` with all rounds, following the **Debate moderation** section of `${CLAUDE_PLUGIN_ROOT}/agents/lead-mobile.md`. It resolves the disagreements and produces the action plan.
128
+
129
+ Before accepting the synthesis, confirm any decisive claim in the code yourself: participants do not verify each other. Write the result to `.reis-mobile/debates/<id>/synthesis.md`.
130
+
131
+ ## 8. Present
132
+
133
+ Show the synthesis in the chat, in this order: the decision first, then the disagreements that produced it, then the action plan. Close with the path to the debate folder.
134
+
135
+ Do not apply any code change. The debate ends in a recommendation; implementing it is a new request.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Diagnoses the mobile environment (Flutter, Android SDK, Java, Xcode, CocoaPods, Node...) and the configuration of the detected project
3
+ argument-hint: "[--all] [--dir <path>]"
4
+ allowed-tools: ["Bash(node:*)", "Read"]
5
+ ---
6
+
7
+ # reis-mobile doctor
8
+
9
+ Diagnostic result:
10
+
11
+ !`node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" doctor $ARGUMENTS`
12
+
13
+ ## Instructions
14
+
15
+ 1. Show the report above to the user exactly as it came, inside a code block.
16
+ 2. For each line marked with `✗`, explain in one sentence the likely cause and the command to fix it, taking the operating system and the detected stack into account. Examples:
17
+ - `pubspec.lock missing` → `flutter pub get`
18
+ - `Podfile.lock missing` → `cd ios && pod install`
19
+ - `Android SDK not found` → install it through Android Studio and export `ANDROID_HOME`
20
+ - `Java` failing in a Flutter project → `flutter doctor -v` shows which JDK Flutter uses; the `java` on the PATH may be a different one
21
+ 3. Do not run install or fix commands unless the user asks.
22
+ 4. If there is no `✗`, just say the environment is ready for the detected stack.
23
+ 5. Write the explanations in the language on the report's `Language` line (`en` English, `pt` Brazilian Portuguese). With `-`, use the language of the user's request. The report itself stays exactly as it came.
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: reis-mobile entry point — lists the commands or forwards to doctor, review, debate or the right agent from a free-form request
3
+ argument-hint: "[doctor | review | debate | free-form request]"
4
+ allowed-tools: ["Bash(node:*)", "Bash(git:*)", "Read", "Grep", "Glob"]
5
+ ---
6
+
7
+ # reis-mobile
8
+
9
+ Arguments received: `$ARGUMENTS`
10
+
11
+ Pick the case by the first word of the arguments.
12
+
13
+ ## Language
14
+
15
+ The CLI output has a `Language` line (`language` in JSON). Write everything the user reads — the answer, report headings and text, syntheses and files you create — in that language: `en` is English, `pt` is Brazilian Portuguese. With `-` or `null`, use the language of the user's request. Code, identifiers, file paths, commands and quoted tool output stay as they are.
16
+
17
+ ## No arguments
18
+
19
+ Reply only with this list. The only thing to run is `node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" lang`, to find the language:
20
+
21
+ ```text
22
+ /reis-mobile:doctor Environment and project: SDKs, Xcode, CocoaPods, Gradle wrapper, lock files
23
+ /reis-mobile:review [--base <ref>] Code review of the changes (or the whole project)
24
+ /reis-mobile:debate <question> Debate between the specialists, with a decision by lead-mobile
25
+ /reis-mobile <free-form request> Forwards the request to the agent and skills for the detected stack
26
+ ```
27
+
28
+ When the language is `pt`, translate the descriptions and keep the commands as they are. When it is not set, use English.
29
+
30
+ ## `doctor [options]`
31
+
32
+ 1. Run via Bash: `node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" doctor --dir "$PWD" <options>`.
33
+ 2. Read `${CLAUDE_PLUGIN_ROOT}/commands/doctor.md` and follow its **Instructions** section on that output.
34
+
35
+ ## `review [options] [focus]`
36
+
37
+ Read `${CLAUDE_PLUGIN_ROOT}/commands/review.md` and follow all of it, treating the rest of the arguments (without the word `review`) as `$ARGUMENTS`.
38
+
39
+ ## `debate [options] <question>`
40
+
41
+ Read `${CLAUDE_PLUGIN_ROOT}/commands/debate.md` and follow all of it, treating the rest of the arguments (without the word `debate`) as `$ARGUMENTS`.
42
+
43
+ ## Any other text
44
+
45
+ It is a free-form request, in English or Portuguese.
46
+
47
+ 1. Run via Bash, with correct quoting: `node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" route --dir "$PWD" -- "<request>"`. If it fails, show the error and stop.
48
+ 2. Read `${CLAUDE_PLUGIN_ROOT}/agents/<Agent>.md` and, in order, `${CLAUDE_PLUGIN_ROOT}/skills/<skill>/SKILL.md` for each listed skill.
49
+ 3. Handle the request following the agent's process and the skills' checklists. Confirm in the code everything you claim.
50
+ 4. On the first line of the answer, state the stack, the agent and the skills used. If the router emitted warnings (lines starting with `!`), mention them.
@@ -0,0 +1,53 @@
1
+ ---
2
+ description: Mobile code review — detects the stack, selects the right agent and skills, and reviews the changes (or the whole project)
3
+ argument-hint: "[--base <ref>] [review focus]"
4
+ allowed-tools: ["Bash(node:*)", "Bash(git:*)", "Read", "Grep", "Glob"]
5
+ ---
6
+
7
+ # reis-mobile review
8
+
9
+ Arguments received: `$ARGUMENTS`
10
+
11
+ ## 1. Route and gather context
12
+
13
+ Extract the `--base <ref>` option from the arguments (if present). The rest is the review focus, as free text. Run via Bash, with correct quoting:
14
+
15
+ ```bash
16
+ node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" review --dir "$PWD" [--base <ref>] -- "<review focus>"
17
+ ```
18
+
19
+ The output reports:
20
+
21
+ - `Intent`, `Stack` and `focus` (native platforms mentioned in the focus);
22
+ - `Agent`: the responsible agent;
23
+ - `Skills`: the skills to apply, in priority order;
24
+ - `Language`: the language to answer in (see **Language**);
25
+ - `Context`: `working-tree`, `range` or `project`, with the list of changed files and the diff (secrets already masked).
26
+
27
+ If the command fails, show the error to the user and stop.
28
+
29
+ ## Language
30
+
31
+ The CLI output has a `Language` line (`language` in JSON). Write everything the user reads — the answer, report headings and text, syntheses and files you create — in that language: `en` is English, `pt` is Brazilian Portuguese. With `-` or `null`, use the language of the user's request. Code, identifiers, file paths, commands and quoted tool output stay as they are.
32
+
33
+ ## 2. Load the instructions
34
+
35
+ Read with the Read tool, in this order:
36
+
37
+ 1. `${CLAUDE_PLUGIN_ROOT}/agents/<Agent>.md`: role, process, rules and report format;
38
+ 2. `${CLAUDE_PLUGIN_ROOT}/skills/<skill>/SKILL.md` for each listed skill.
39
+
40
+ ## 3. Review
41
+
42
+ Follow the agent's process, applying the skills' checklists:
43
+
44
+ - **working-tree / range:** review the changed files. Read the full file when the diff does not give enough context. `untracked` files do not appear in the diff: read them directly. If the diff was truncated, read the remaining files.
45
+ - **project:** there are no changes; audit the project starting from the entry points and the data layer.
46
+
47
+ Confirm every finding in the code before reporting it. Do not modify files: this is a review.
48
+
49
+ ## 4. Report
50
+
51
+ Answer in the format defined by the agent (Summary, Critical, Bugs, Architecture, Security, Performance, Maintainability, Suggested changes), translating the headings when the language is not English. On the first line of the Summary, state the stack, the agent and the skills used, so the routing is visible.
52
+
53
+ If the router emitted warnings (lines starting with `!`), mention them in the Summary.