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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +9 -0
- package/README.md +50 -43
- package/bin/reis-mobile.mjs +24 -16
- package/core/install/codex-plugin.mjs +66 -0
- package/core/install/init.mjs +46 -0
- package/package.json +1 -1
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "AI agents for mobile engineering",
|
|
8
|
-
"version": "0.3.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
73
|
-
| **npm** | The `reis-mobile` CLI: `init
|
|
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
|
|
76
|
+
Requirements: **Node.js 22+**, and **[Claude Code](https://claude.com/claude-code)**, the **Codex CLI**, or both.
|
|
76
77
|
|
|
77
|
-
### Claude Code
|
|
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
|
|
82
|
+
reis-mobile init eng
|
|
88
83
|
```
|
|
89
84
|
|
|
90
|
-
|
|
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
|
-
```
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
```text
|
|
88
|
+
â Claude Code installed
|
|
89
|
+
â Codex installed
|
|
90
|
+
â Language en (~/.config/reis-mobile/config.json)
|
|
95
91
|
```
|
|
96
92
|
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
135
|
-
reis-mobile init
|
|
136
|
-
reis-mobile lang
|
|
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
|
|
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
|
-
|
|
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
|
|
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`:
|
|
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
|
|
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
|
|
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.
|
package/bin/reis-mobile.mjs
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
|
101
|
-
? uninstallClaudePlugin({ scope })
|
|
102
|
-
: installClaudePlugin({ scope, ...(local && { source: PLUGIN_ROOT }) });
|
|
99
|
+
const { ok, results } = runInit({ uninstall, scope, ...(local && { source: PLUGIN_ROOT }) });
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
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('
|
|
111
|
-
return 0;
|
|
114
|
+
console.log(`â ${'Language'.padEnd(12)}saved setting removed`);
|
|
115
|
+
return ok ? 0 : 1;
|
|
112
116
|
}
|
|
113
|
-
if (requested
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
}
|