reis-mobile 0.3.5 → 0.3.6

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "AI agents for mobile engineering",
8
- "version": "0.3.5"
8
+ "version": "0.3.6"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "reis-mobile",
13
13
  "source": "./",
14
14
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development.",
15
- "version": "0.3.5",
15
+ "version": "0.3.6",
16
16
  "author": {
17
17
  "name": "Warleson Reis"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development. Run /reis-mobile:doctor, then /reis-mobile:review.",
5
5
  "author": {
6
6
  "name": "Warleson Reis"
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [0.3.6] - 2026-09-16
6
+
7
+ ### Changed
8
+
9
+ - `reis-mobile init` installs the plugin in Claude Code **and Codex**, in whichever of the two is on the `PATH`, and reports each one as installed, skipped or failed. A failure in one tool does not stop the other. In Codex it adds the GitHub marketplace, refreshes its snapshot and adds `reis-mobile@reis-mobile`, which also fixes `plugin reis-mobile was not found in marketplace reis-mobile` on an outdated snapshot.
10
+ - `reis-mobile init --uninstall` removes the plugin and the marketplace from both tools, and only removes a Codex marketplace that is configured, since Codex treats removing a missing one as an error.
11
+ - `init` exits with an error when neither Claude Code nor Codex is found, without saving the language.
12
+ - The README uses `reis-mobile init eng` in its examples.
13
+
5
14
  ## [0.3.5] - 2026-09-16
6
15
 
7
16
  ### Changed
package/README.md CHANGED
@@ -6,7 +6,7 @@ A mobile app is not a generic project. A code review that does not know about `B
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/wrsilva/reis-mobile/actions/workflows/ci.yml"><img src="https://github.com/wrsilva/reis-mobile/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
9
- <img src="https://img.shields.io/badge/Version-0.3.5-blue" alt="Version 0.3.5">
9
+ <img src="https://img.shields.io/badge/Version-0.3.6-blue" alt="Version 0.3.6">
10
10
  <img src="https://img.shields.io/badge/Claude_Code-plugin-blueviolet" alt="Claude Code plugin">
11
11
  <img src="https://img.shields.io/badge/Node.js-22+-339933?logo=node.js&logoColor=white" alt="Node.js 22+">
12
12
  <img src="https://img.shields.io/badge/Dependencies-0-brightgreen" alt="Zero dependencies">
@@ -25,7 +25,7 @@ A mobile app is not a generic project. A code review that does not know about `B
25
25
 
26
26
  ðŸ‘Ĩ **7 agents and 67 mobile skills.** Flutter architect, performance and test engineers, Flutter/Android/iOS staff engineer, native plugin specialist, code reviewer and a lead that coordinates them all, plus skills for BLoC, Riverpod, Firebase, testing, layout, plugins and security.
27
27
 
28
- 🌐 **Answers in English or Portuguese.** `reis-mobile init pt` or `reis-mobile init en` sets the language of reports, explanations and debates.
28
+ 🌐 **Answers in English or Portuguese.** `reis-mobile init eng` or `reis-mobile init pt` sets the language of reports, explanations and debates.
29
29
 
30
30
  🧭 **Routes to the right specialist.** Describe the problem in English or Portuguese, and the router identifies the intent, the stack and the platform in focus. "The Android build of my Flutter app broke" loads Flutter **and** Android context.
31
31
 
@@ -35,7 +35,7 @@ A mobile app is not a generic project. A code review that does not know about `B
35
35
 
36
36
  🔒 **Secrets never reach the model.** The diff sent for review goes through a layer that masks API keys, tokens, JWTs, private keys and keystore passwords.
37
37
 
38
- ðŸŠķ **No dependencies.** `npx reis-mobile init`, zero npm dependencies, no hooks, no external provider.
38
+ ðŸŠķ **No dependencies.** `npx reis-mobile init eng` installs it in Claude Code and Codex, with zero npm dependencies, no hooks and no external provider.
39
39
 
40
40
  ---
41
41
 
@@ -52,7 +52,8 @@ A mobile app is not a generic project. A code review that does not know about `B
52
52
 
53
53
  | Version | Highlights |
54
54
  |--------|-----------|
55
- | **v0.3.5** (current) | Installs through npm (`npx reis-mobile init`) and the Claude Code and Codex plugins. Homebrew and the curl/PowerShell installers are discontinued. |
55
+ | **v0.3.6** (current) | `reis-mobile init eng` installs the plugin in Claude Code and Codex at once, skipping whichever is not installed; `init --uninstall` removes it from both. |
56
+ | **v0.3.5** | Installs through npm (`npx reis-mobile init`) and the Claude Code and Codex plugins. Homebrew and the curl/PowerShell installers are discontinued. |
56
57
  | **v0.3.4** | Back to the `reis-mobile` name: `reis-mobile` CLI and `/reis-mobile:*` commands, undoing the `mobile` renames of v0.3.2 and v0.3.3. |
57
58
  | **v0.3.1** | Answers in English or Portuguese: `reis-mobile init pt\|en` and `reis-mobile lang` save the language for reports, doctor explanations and debates. Agents, skills, commands and docs are now written in English. |
58
59
  | **v0.3.0** | `/reis-mobile:debate` command: three specialists with conflicting priorities debate over two rounds and `lead-mobile` decides. Participants vary in role and model; `--external` adds Codex and Gemini. Brings forward the `council` planned for v0.8.0. |
@@ -69,32 +70,29 @@ reis-mobile ships through three channels:
69
70
  | Channel | What you get |
70
71
  |---|---|
71
72
  | **Claude Code plugin** | Everything: 7 agents, 67 skills and the `/reis-mobile:*` commands |
72
- | **Codex plugin** | The skills, plus the CLI if you install it from npm |
73
- | **npm** | The `reis-mobile` CLI: `init`, `doctor`, `route`, `review`, `lang`, and the router the commands rely on |
73
+ | **Codex plugin** | The skills |
74
+ | **npm** | The `reis-mobile` CLI: `init` installs both plugins, plus `doctor`, `route`, `review` and `lang` |
74
75
 
75
- Requirements: **Node.js 22+**. For the plugins, **[Claude Code](https://claude.com/claude-code)** or the **Codex CLI**.
76
+ Requirements: **Node.js 22+**, and **[Claude Code](https://claude.com/claude-code)**, the **Codex CLI**, or both.
76
77
 
77
- ### Claude Code (recommended)
78
-
79
- ```bash
80
- npx reis-mobile init pt # registers the plugin and answers in Portuguese; use "en" for English
81
- ```
82
-
83
- Or install the CLI globally and keep it around for `doctor`, `route` and `review`:
78
+ ### Quick install (Claude Code and Codex)
84
79
 
85
80
  ```bash
86
81
  npm install -g reis-mobile
87
- reis-mobile init pt
82
+ reis-mobile init eng
88
83
  ```
89
84
 
90
- Without Node.js tooling, add the plugin straight from GitHub:
85
+ `reis-mobile init` finds which of `claude` and `codex` are on your `PATH` and installs the plugin in each, skipping the missing one:
91
86
 
92
- ```bash
93
- claude plugin marketplace add https://github.com/wrsilva/reis-mobile.git
94
- claude plugin install reis-mobile@reis-mobile
87
+ ```text
88
+ ✓ Claude Code installed
89
+ ✓ Codex installed
90
+ ✓ Language en (~/.config/reis-mobile/config.json)
95
91
  ```
96
92
 
97
- Restart Claude Code and run, in your app folder:
93
+ Running it again is safe, and it also updates an existing install. Without a global install, use `npx reis-mobile init eng`.
94
+
95
+ Restart Claude Code and Codex. In Claude Code, run in your app folder:
98
96
 
99
97
  ```text
100
98
  /reis-mobile
@@ -102,23 +100,29 @@ Restart Claude Code and run, in your app folder:
102
100
  /reis-mobile:review
103
101
  ```
104
102
 
105
- ### Codex
103
+ In Codex, type `$` in the message field to search for skills, or use `/skills`. See the [official Codex skills documentation](https://learn.chatgpt.com/docs/build-skills#how-chatgpt-and-codex-use-skills).
106
104
 
107
- With the Codex CLI installed and supporting `codex plugin`:
105
+ ### Manual install, without npm
106
+
107
+ The plugins install straight from GitHub. In each tool, the first command registers the marketplace and only needs to run once per machine:
108
108
 
109
109
  ```bash
110
+ # Claude Code
111
+ claude plugin marketplace add https://github.com/wrsilva/reis-mobile.git
112
+ claude plugin install reis-mobile@reis-mobile
113
+
114
+ # Codex
110
115
  codex plugin marketplace add https://github.com/wrsilva/reis-mobile.git
111
116
  codex plugin add reis-mobile@reis-mobile
112
117
  ```
113
118
 
114
- The first command registers the marketplace with GitHub as its source; the second installs the plugin into the Codex cache. Check it with `codex plugin list --json`: look for `reis-mobile@reis-mobile` with `installed: true` and `enabled: true`.
119
+ If Codex answers `plugin reis-mobile was not found in marketplace reis-mobile`, the marketplace is either not registered or an old snapshot: run `codex plugin marketplace add` again, or `codex plugin marketplace upgrade reis-mobile`, and repeat `codex plugin add`.
115
120
 
116
- Restart Codex and open a conversation in your mobile app folder. In the CLI or the IDE extension, type `$` in the message field to search for skills, or use `/skills`. See the [official Codex skills documentation](https://learn.chatgpt.com/docs/build-skills#how-chatgpt-and-codex-use-skills).
121
+ ### Using the CLI from Codex
117
122
 
118
- The plugin gives Codex the skills; the agents and the `/reis-mobile:*` commands are Claude Code formats and have not been validated in Codex. For routing and review context, install the CLI from npm and run it in the app folder:
123
+ The Codex plugin gives Codex the skills; the agents and the `/reis-mobile:*` commands are Claude Code formats and have not been validated in Codex. For routing and review context, run the CLI in the app folder:
119
124
 
120
125
  ```bash
121
- npm install -g reis-mobile
122
126
  reis-mobile doctor
123
127
  reis-mobile route "review architecture" --json
124
128
  reis-mobile review --json
@@ -131,9 +135,9 @@ You can also ask Codex: *"Run `reis-mobile review --json`, read the instructions
131
135
  Agents, skills and commands are written in English, and that does not change. What you choose is the language reis-mobile **answers** in: `/reis-mobile:review` reports, `/reis-mobile:doctor` explanations, and debate rounds and syntheses.
132
136
 
133
137
  ```bash
134
- reis-mobile init pt # install the plugin and answer in Brazilian Portuguese
135
- reis-mobile init en # install the plugin and answer in English (also: eng)
136
- reis-mobile lang pt # change the language later, without reinstalling
138
+ reis-mobile init eng # install the plugins and answer in English (also: en)
139
+ reis-mobile init pt # install the plugins and answer in Brazilian Portuguese
140
+ reis-mobile lang eng # change the language later, without reinstalling
137
141
  reis-mobile lang # show the current language
138
142
  ```
139
143
 
@@ -143,11 +147,8 @@ The choice is saved to `~/.config/reis-mobile/config.json` (`%APPDATA%\reis-mobi
143
147
  <summary>Uninstall</summary>
144
148
 
145
149
  ```bash
146
- reis-mobile init --uninstall # Claude Code plugin, its marketplace and the saved language
147
- npm uninstall -g reis-mobile # CLI
148
-
149
- codex plugin remove reis-mobile@reis-mobile
150
- codex plugin marketplace remove reis-mobile
150
+ reis-mobile init --uninstall # plugin and marketplace in Claude Code and Codex, and the saved language
151
+ npm uninstall -g reis-mobile # CLI (the -g matters: without it npm looks in the current folder)
151
152
  ```
152
153
  </details>
153
154
 
@@ -192,15 +193,20 @@ After editing agents, skills or commands, restart the Claude Code session.
192
193
  To see what changed, check the [CHANGELOG](CHANGELOG.md) or the [releases](https://github.com/wrsilva/reis-mobile/releases).
193
194
 
194
195
  ```bash
195
- # Claude Code plugin
196
+ npm update -g reis-mobile
197
+ reis-mobile init eng # refreshes the marketplace and the plugin in Claude Code and Codex
198
+ ```
199
+
200
+ Without npm, update each tool by hand:
201
+
202
+ ```bash
203
+ # Claude Code
196
204
  claude plugin marketplace update reis-mobile
197
205
  claude plugin update reis-mobile@reis-mobile
198
206
 
199
- # Codex plugin
207
+ # Codex
200
208
  codex plugin marketplace upgrade reis-mobile
201
-
202
- # CLI
203
- npm update -g reis-mobile
209
+ codex plugin add reis-mobile@reis-mobile
204
210
  ```
205
211
 
206
212
  Then **restart Claude Code or Codex**. The plugin update only happens when a new version is out; if you are already on the latest, Claude Code answers `already at the latest version`.
@@ -212,7 +218,7 @@ reis-mobile --version
212
218
  claude plugin list | grep -A1 reis-mobile
213
219
  ```
214
220
 
215
- > **Installed v0.3.2 or v0.3.3?** Those versions renamed the plugin and the CLI to `mobile`; v0.3.4 goes back to `reis-mobile`. Run `reis-mobile init`: it installs `reis-mobile@reis-mobile` and removes `mobile@reis-mobile`. In Codex: `codex plugin add reis-mobile@reis-mobile` and `codex plugin remove mobile@reis-mobile`.
221
+ > **Installed v0.3.2 or v0.3.3?** Those versions renamed the plugin and the CLI to `mobile`; v0.3.4 goes back to `reis-mobile`. Run `reis-mobile init eng`: in Claude Code and Codex it installs `reis-mobile@reis-mobile` and removes `mobile@reis-mobile`.
216
222
 
217
223
  <details>
218
224
  <summary>Update troubleshooting</summary>
@@ -222,7 +228,7 @@ claude plugin list | grep -A1 reis-mobile
222
228
  | `claude plugin update` says it is already on the latest version, but the release is newer | Run `claude plugin marketplace update reis-mobile` before `update` |
223
229
  | New agents or skills do not show up | Restart Claude Code: plugins only reload in a new session |
224
230
  | `reis-mobile --version` is still old after `npm update -g` | Another `reis-mobile` comes first on the `PATH`, often from Homebrew or the old installers; check with `which -a reis-mobile` (`where.exe reis-mobile` on Windows) |
225
- | Plugin broken after updating | Reinstall: `reis-mobile init --uninstall` and then `reis-mobile init` |
231
+ | Plugin broken after updating | Reinstall: `reis-mobile init --uninstall` and then `reis-mobile init eng` |
226
232
 
227
233
  </details>
228
234
 
@@ -537,7 +543,7 @@ Third-party skills keep their original name and license. See [THIRD_PARTY_NOTICE
537
543
 
538
544
  **Own namespace.** Commands live under `/reis-mobile:*` and do not conflict with the built-in `/review` or `/security-review`.
539
545
 
540
- **Clean uninstall.** `reis-mobile init --uninstall` removes the plugin, its marketplace and the saved language without leaving configuration behind.
546
+ **Clean uninstall.** `reis-mobile init --uninstall` removes the plugin and its marketplace from Claude Code and Codex, and the saved language, without leaving configuration behind.
541
547
 
542
548
  ---
543
549
 
@@ -552,6 +558,7 @@ Third-party skills keep their original name and license. See [THIRD_PARTY_NOTICE
552
558
  | v0.3.1 | Answer language (`reis-mobile init pt\|en`, `reis-mobile lang`) and project content in English | ✅ |
553
559
  | v0.3.4 | Back to the `reis-mobile` name after the `mobile` renames of v0.3.2 and v0.3.3 | ✅ |
554
560
  | v0.3.5 | Distribution through npm and the Claude Code and Codex plugins only | ✅ |
561
+ | v0.3.6 | `reis-mobile init` installs in Claude Code and Codex | ✅ |
555
562
  | v0.4.0 | Native Android and iOS skills, `.reis-mobile/config.yaml` and `/reis-mobile:debug` (Gradle, Xcode, CocoaPods, Flutter) | âģ |
556
563
  | v0.5.0 | `/reis-mobile:test` and native tests (XCTest, Espresso) | âģ |
557
564
  | v0.6.0 | `/reis-mobile:release` with quality gates | âģ |
@@ -577,7 +584,7 @@ Detection, the doctor, the security skill and the `mobile-code-reviewer`, `mobil
577
584
  The plugin's skills live in the `reis-mobile:` namespace and do not conflict, but Claude Code loads both descriptions. To save context, remove the global copies the plugin already covers.
578
585
 
579
586
  **Does it work in Codex?**
580
- The marketplace and plugin `0.2.1` can be installed straight from GitHub with `codex plugin`. See [installation and usage in Codex](#codex). Installation does not confirm parity with the Claude Code agents and commands; the `reis-mobile` CLI can also be run by Codex to get diagnostics and context.
587
+ The marketplace and plugin `0.2.1` can be installed straight from GitHub with `codex plugin`. See [installation](#quick-install-claude-code-and-codex) and [using the CLI from Codex](#using-the-cli-from-codex). Installation does not confirm parity with the Claude Code agents and commands; the `reis-mobile` CLI can also be run by Codex to get diagnostics and context.
581
588
 
582
589
  **Does it work in Cursor?**
583
590
  The Cursor integration has not been validated yet. The MCP server remains on the roadmap for v0.8.0.
@@ -7,7 +7,7 @@ import { LANGUAGES, configPath, removeConfig, resolveLanguage, saveLanguage } fr
7
7
  import { collectReviewContext } from '../core/context/context-engine.mjs';
8
8
  import { detectStack } from '../core/detection/stack-detector.mjs';
9
9
  import { runDoctor } from '../core/diagnostics/doctor.mjs';
10
- import { installClaudePlugin, uninstallClaudePlugin } from '../core/install/claude-plugin.mjs';
10
+ import { runInit } from '../core/install/init.mjs';
11
11
  import { PLUGIN_ROOT } from '../core/paths.mjs';
12
12
  import { loadRegistry, validateRegistry } from '../core/registry/registry.mjs';
13
13
  import { INTENT_IDS } from '../core/router/intents.mjs';
@@ -18,7 +18,7 @@ const USAGE = `reis-mobile — AI agents for mobile engineering
18
18
  Usage: reis-mobile <command> [options]
19
19
 
20
20
  Commands:
21
- init [en|pt] Install the reis-mobile plugin into Claude Code, optionally setting the language
21
+ init [en|pt] Install the reis-mobile plugin into Claude Code and Codex, optionally setting the language
22
22
  lang [en|pt] Show or set the language the /reis-mobile commands answer in
23
23
  detect Detect the mobile stack of a project
24
24
  doctor Diagnose the environment for the detected stack
@@ -34,8 +34,8 @@ Options:
34
34
  --all doctor: check every tool, not only the relevant ones
35
35
  --strict doctor: exit with code 1 when there are warnings
36
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
37
+ --scope <scope> init: Claude Code scope, user (default), project or local
38
+ --uninstall init: remove the plugin and its marketplace from Claude Code and Codex, and the saved language
39
39
  --lang <en|pt> Override the saved language for this run
40
40
  --json Machine-readable output
41
41
  -v, --version Print the version
@@ -92,27 +92,35 @@ async function main(argv) {
92
92
  }
93
93
 
94
94
  async function commandInit({ local, scope, uninstall, lang, prompt }) {
95
- // The installers pass the language through REIS_MOBILE_LANG.
96
95
  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.
96
+ // Validate before touching Claude Code or Codex, so a typo does not leave a half-done install.
98
97
  if (requested && !uninstall) await resolveLanguage({ flag: requested, env: {} });
99
98
 
100
- const result = uninstall
101
- ? uninstallClaudePlugin({ scope })
102
- : installClaudePlugin({ scope, ...(local && { source: PLUGIN_ROOT }) });
99
+ const { ok, results } = runInit({ uninstall, scope, ...(local && { source: PLUGIN_ROOT }) });
103
100
 
104
- if (!result.ok) {
105
- console.error(`✗ ${result.failedStep} failed (exit ${result.status})`);
101
+ console.log('');
102
+ for (const result of results) {
103
+ if (result.skipped) console.log(`- ${result.name.padEnd(12)}not found, skipped`);
104
+ else if (result.ok) console.log(`✓ ${result.name.padEnd(12)}${uninstall ? 'removed' : 'installed'}`);
105
+ else console.error(`✗ ${result.name.padEnd(12)}${result.failedStep} failed (exit ${result.status})`);
106
+ }
107
+
108
+ if (!results.some((result) => !result.skipped)) {
109
+ console.error('\nNeither Claude Code nor Codex was found in PATH. Install one of them and run `reis-mobile init` again.');
106
110
  return 1;
107
111
  }
108
112
  if (uninstall) {
109
113
  await removeConfig();
110
- console.log('✓ reis-mobile removed from Claude Code');
111
- return 0;
114
+ console.log(`✓ ${'Language'.padEnd(12)}saved setting removed`);
115
+ return ok ? 0 : 1;
112
116
  }
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;
117
+ if (requested && results.some((result) => result.ok)) {
118
+ console.log(`✓ ${'Language'.padEnd(12)}${await saveLanguage(requested)} (${configPath()})`);
119
+ }
120
+ if (results.some((result) => result.ok)) {
121
+ console.log('\nRestart Claude Code or Codex. In Claude Code, run /reis-mobile:doctor.');
122
+ }
123
+ return ok ? 0 : 1;
116
124
  }
117
125
 
118
126
  async function commandLang({ prompt, json }) {
@@ -0,0 +1,66 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ import { LEGACY_PLUGIN_IDS, MARKETPLACE_NAME, MARKETPLACE_URL, PLUGIN_ID } from './claude-plugin.mjs';
4
+
5
+ /**
6
+ * Registers the reis-mobile marketplace and installs the plugin through the `codex` CLI.
7
+ *
8
+ * `marketplace add` and `plugin add` are idempotent in Codex. `marketplace upgrade` only
9
+ * accepts Git marketplaces, so it is skipped for a local `source`; it refreshes a snapshot
10
+ * that predates the latest release, which otherwise fails with "not found in marketplace".
11
+ */
12
+ export function installCodexPlugin({ source = MARKETPLACE_URL, run = runCodex, list = listCodexPlugins } = {}) {
13
+ const result = runSteps(run, [
14
+ ['plugin', 'marketplace', 'add', source],
15
+ ...(isGitSource(source) ? [['plugin', 'marketplace', 'upgrade', MARKETPLACE_NAME]] : []),
16
+ ['plugin', 'add', PLUGIN_ID],
17
+ ]);
18
+ if (!result.ok) return result;
19
+ const installed = list().plugins;
20
+ return runSteps(run, LEGACY_PLUGIN_IDS.filter((id) => installed.includes(id)).map((id) => ['plugin', 'remove', id]));
21
+ }
22
+
23
+ /** `marketplace remove` fails when the marketplace is not configured, so it only runs when it is. */
24
+ export function uninstallCodexPlugin({ run = runCodex, list = listCodexPlugins } = {}) {
25
+ const { plugins, marketplaces } = list();
26
+ return runSteps(run, [
27
+ ...[PLUGIN_ID, ...LEGACY_PLUGIN_IDS].filter((id) => plugins.includes(id)).map((id) => ['plugin', 'remove', id]),
28
+ ...(marketplaces.includes(MARKETPLACE_NAME) ? [['plugin', 'marketplace', 'remove', MARKETPLACE_NAME]] : []),
29
+ ]);
30
+ }
31
+
32
+ function isGitSource(source) {
33
+ return /^(https?:\/\/|git@|ssh:\/\/)/.test(source);
34
+ }
35
+
36
+ function runSteps(run, steps) {
37
+ for (const args of steps) {
38
+ const status = run(args);
39
+ if (status !== 0) return { ok: false, failedStep: `codex ${args.join(' ')}`, status };
40
+ }
41
+ return { ok: true };
42
+ }
43
+
44
+ function runCodex(args) {
45
+ const result = spawnSync('codex', args, { stdio: 'inherit', shell: process.platform === 'win32' });
46
+ if (result.error) throw result.error;
47
+ return result.status ?? 1;
48
+ }
49
+
50
+ /** Installed plugin ids and configured marketplace names. Unreadable output means neither. */
51
+ function listCodexPlugins() {
52
+ return {
53
+ plugins: readJson(['plugin', 'list', '--json'], (data) => data.installed.map((plugin) => plugin.pluginId)),
54
+ marketplaces: readJson(['plugin', 'marketplace', 'list', '--json'], (data) => data.marketplaces.map((marketplace) => marketplace.name)),
55
+ };
56
+ }
57
+
58
+ function readJson(args, pick) {
59
+ const result = spawnSync('codex', args, { encoding: 'utf8', shell: process.platform === 'win32' });
60
+ if (result.status !== 0) return [];
61
+ try {
62
+ return pick(JSON.parse(result.stdout));
63
+ } catch {
64
+ return [];
65
+ }
66
+ }
@@ -0,0 +1,46 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ import { installClaudePlugin, uninstallClaudePlugin } from './claude-plugin.mjs';
4
+ import { installCodexPlugin, uninstallCodexPlugin } from './codex-plugin.mjs';
5
+
6
+ /** Every tool `reis-mobile init` registers the plugin in, when its CLI is on the PATH. */
7
+ export const TARGETS = [
8
+ {
9
+ id: 'claude',
10
+ name: 'Claude Code',
11
+ bin: 'claude',
12
+ install: ({ source, scope }) => installClaudePlugin({ source, scope }),
13
+ uninstall: ({ scope }) => uninstallClaudePlugin({ scope }),
14
+ },
15
+ {
16
+ id: 'codex',
17
+ name: 'Codex',
18
+ bin: 'codex',
19
+ install: ({ source }) => installCodexPlugin({ source }),
20
+ uninstall: () => uninstallCodexPlugin(),
21
+ },
22
+ ];
23
+
24
+ /**
25
+ * Installs (or removes) the plugin in each tool that is available, and never stops at the
26
+ * first failure: one broken tool must not keep the plugin out of the other.
27
+ *
28
+ * Returns one entry per target: `skipped` when its CLI is missing, otherwise `ok` plus the
29
+ * failing step. `ok` is false overall when a tool failed or none was found.
30
+ */
31
+ export function runInit({ uninstall = false, source, scope = 'user', targets = TARGETS, exists = commandExists } = {}) {
32
+ const results = targets.map((target) => {
33
+ if (!exists(target.bin)) return { id: target.id, name: target.name, skipped: true };
34
+ const options = { scope, ...(source && { source }) };
35
+ const result = uninstall ? target.uninstall(options) : target.install(options);
36
+ return { id: target.id, name: target.name, ...result };
37
+ });
38
+ const ran = results.filter((result) => !result.skipped);
39
+ return { ok: ran.length > 0 && ran.every((result) => result.ok), results };
40
+ }
41
+
42
+ export function commandExists(bin) {
43
+ const result = spawnSync(bin, ['--version'], { stdio: 'ignore', shell: process.platform === 'win32' });
44
+ // Through a Windows shell a missing command is exit code 1 (cmd) instead of ENOENT.
45
+ return !result.error && !(process.platform === 'win32' && result.status !== 0);
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development.",
5
5
  "type": "module",
6
6
  "license": "MIT",