reis-mobile 0.7.0 â 0.7.1
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 +12 -2
- package/CHANGELOG.md +12 -0
- package/README.md +29 -8
- package/bin/reis-mobile.mjs +15 -0
- package/bin/update-check.mjs +14 -0
- package/core/update/check.mjs +119 -0
- package/hooks/hooks.json +17 -0
- package/package.json +4 -2
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "AI agents for mobile engineering",
|
|
8
|
-
"version": "0.7.
|
|
8
|
+
"version": "0.7.1"
|
|
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.7.
|
|
15
|
+
"version": "0.7.1",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Warleson Reis"
|
|
18
18
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reis-mobile",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development. Use /reis-mobile:doctor, /reis-mobile:review and /reis-mobile:test.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Warleson Reis"
|
|
@@ -8,5 +8,15 @@
|
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": "https://github.com/wrsilva/reis-mobile",
|
|
10
10
|
"homepage": "https://github.com/wrsilva/reis-mobile",
|
|
11
|
-
"keywords": [
|
|
11
|
+
"keywords": [
|
|
12
|
+
"mobile",
|
|
13
|
+
"flutter",
|
|
14
|
+
"android",
|
|
15
|
+
"ios",
|
|
16
|
+
"react-native",
|
|
17
|
+
"kotlin-multiplatform",
|
|
18
|
+
"code-review",
|
|
19
|
+
"agents",
|
|
20
|
+
"skills"
|
|
21
|
+
]
|
|
12
22
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The pr
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.7.1] - 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- A `SessionStart` hook for Claude Code and Codex that warns when the installed plugin is behind npm's latest release. It caches registry results for 24 hours, stays silent offline and can be disabled with `REIS_MOBILE_UPDATE_CHECK=0`.
|
|
12
|
+
- `reis-mobile update-check [--force]` for manually checking the CLI version.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `package.json` is the only editable version source. CI synchronizes the plugin manifests and the Release workflow tests, tags and publishes a new version after it reaches `main`.
|
|
17
|
+
- Version synchronization now promotes authored `[Unreleased]` notes to a dated changelog section and refreshes the README's latest-release summary without changing historical entries.
|
|
18
|
+
|
|
7
19
|
## [0.7.0] - 2026-09-17
|
|
8
20
|
|
|
9
21
|
### Added
|
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/
|
|
9
|
+
<img src="https://img.shields.io/npm/v/reis-mobile" alt="Latest npm version">
|
|
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">
|
|
@@ -35,12 +35,29 @@ 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
|
-
ðŠķ **Two commands to install.** `npm install -g reis-mobile` and `reis-mobile init eng` set it up in Claude Code and Codex, with zero npm dependencies
|
|
38
|
+
ðŠķ **Two commands to install.** `npm install -g reis-mobile` and `reis-mobile init eng` set it up in Claude Code and Codex, with zero npm dependencies and no external provider.
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
42
|
## What's new
|
|
43
43
|
|
|
44
|
+
<!-- reis-mobile:latest-release:start -->
|
|
45
|
+
### v0.7.1
|
|
46
|
+
|
|
47
|
+
#### Added
|
|
48
|
+
|
|
49
|
+
- A `SessionStart` hook for Claude Code and Codex that warns when the installed plugin is behind npm's latest release. It caches registry results for 24 hours, stays silent offline and can be disabled with `REIS_MOBILE_UPDATE_CHECK=0`.
|
|
50
|
+
- `reis-mobile update-check [--force]` for manually checking the CLI version.
|
|
51
|
+
|
|
52
|
+
#### Changed
|
|
53
|
+
|
|
54
|
+
- `package.json` is the only editable version source. CI synchronizes the plugin manifests and the Release workflow tests, tags and publishes a new version after it reaches `main`.
|
|
55
|
+
- Version synchronization now promotes authored `[Unreleased]` notes to a dated changelog section and refreshes the README's latest-release summary without changing historical entries.
|
|
56
|
+
|
|
57
|
+
<!-- reis-mobile:latest-release:end -->
|
|
58
|
+
|
|
59
|
+
### Earlier highlights
|
|
60
|
+
|
|
44
61
|
**v0.7.0:** `/reis-mobile:test` runs, writes, audits or fixes tests using the app's existing setup, including XCTest/XCUITest and Espresso. The 18 agents now use a shared project brief and role-specific investigations tied to actual files, symbols and constraints.
|
|
45
62
|
|
|
46
63
|
> ð **v0.4.0: every mobile stack.** Native Android, native iOS and React Native get their own architect, performance and test specialists, and `/reis-mobile:debug` finds the cause of a failing build with the toolchain versions in hand.
|
|
@@ -54,7 +71,8 @@ A mobile app is not a generic project. A code review that does not know about `B
|
|
|
54
71
|
|
|
55
72
|
| Version | Highlights |
|
|
56
73
|
|--------|-----------|
|
|
57
|
-
| **v0.7.
|
|
74
|
+
| **v0.7.1** | Update notices on Claude Code and Codex session start, plus a single-source version and CI-driven release process. |
|
|
75
|
+
| **v0.7.0** | `/reis-mobile:test`, native XCTest and Espresso guidance, and project-specific contracts for the 18 specialists. |
|
|
58
76
|
| **v0.6.0** | `mobile-accessibility-auditor` and `mobile-release-engineer` agents with the new `mobile-accessibility` and `mobile-release` skills: accessibility audits on every stack, release readiness with a go/no-go verdict, release notes and store copy, CI/CD. `mobile-debug` gains guides for ANRs, Android memory leaks and deep links. |
|
|
59
77
|
| **v0.5.0** | 11 skills instead of 60: topic skills with a guide per platform (`mobile-architecture`, `mobile-code-review`, `mobile-debug`, `mobile-test`, `mobile-security`, `mobile-firebase`) and platform skills (`mobile-flutter`, `mobile-android`, and the new `mobile-ios` and `mobile-rn`). Half the fixed context cost. |
|
|
60
78
|
| **v0.4.0** | Native Android, iOS and React Native: architect, performance and test agents for each; `/reis-mobile:debug` for Gradle, Xcode, CocoaPods and Flutter failures; `mobile-test` and `mobile-firebase` with a guide per platform; the Android team's skills; `.reis-mobile/config.yaml` for monorepos. Skills now use stack prefixes. |
|
|
@@ -104,6 +122,8 @@ reis-mobile init eng
|
|
|
104
122
|
|
|
105
123
|
Then restart Claude Code and Codex. See what changed in the [CHANGELOG](CHANGELOG.md).
|
|
106
124
|
|
|
125
|
+
The plugin checks the npm `latest` tag when a Claude Code or Codex session starts or resumes. If a newer release exists, it shows an update notice; it never installs anything automatically. The registry result is cached for 24 hours, the request times out after 1.5 seconds, and offline failures are silent in the hook. Codex requires you to trust the new hook through `/hooks` before it runs. To check the CLI manually, run `reis-mobile update-check --force`; set `REIS_MOBILE_UPDATE_CHECK=0` to disable the automatic check. Versions released before this hook cannot display the notice until they are updated once.
|
|
126
|
+
|
|
107
127
|
### Uninstall
|
|
108
128
|
|
|
109
129
|
```bash
|
|
@@ -397,17 +417,17 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
|
|
|
397
417
|
|
|
398
418
|
**Secrets masked.** The diff goes through [`core/security/redact.mjs`](core/security/redact.mjs) before reaching the model. Lock files and generated code (`*.g.dart`, `*.freezed.dart`, `*.pbxproj`) stay out of the diff. Redaction is a protection layer, not a guarantee. See [SECURITY.md](SECURITY.md).
|
|
399
419
|
|
|
400
|
-
**No telemetry.** `detect`, `doctor`, `route`, `review`, `debug` and `test` make no network calls.
|
|
420
|
+
**No telemetry.** `detect`, `doctor`, `route`, `review`, `debug` and `test` make no network calls. The update hook and `update-check` request the public npm package metadata; they send no project files or identifiers. Installation downloads the package and registers the plugin. The Google Play policy guide in `mobile-android` runs Python scripts that download your app's public Google Play listing when the model uses it. The AI analysis uses the model of the session you are working in, such as Claude Code or Codex.
|
|
401
421
|
|
|
402
|
-
**Exception: `/reis-mobile:debate --external`.** This flag, and only this flag, sends the debate context â including excerpts of the files mentioned in the question â to the `codex` and `gemini` CLIs, which are third-party and have their own data policies. Without the flag,
|
|
422
|
+
**Exception: `/reis-mobile:debate --external`.** This flag, and only this flag, sends the debate context â including excerpts of the files mentioned in the question â to the `codex` and `gemini` CLIs, which are third-party and have their own data policies. Without the flag, the debate context stays in your session. The `review` diff is masked by `redact.mjs`, but the context you cite in a debate question does not go through that layer: check what you are sending before using `--external`.
|
|
403
423
|
|
|
404
424
|
**Context cost.** The descriptions of the 18 agents and 13 skills add up to about 4,300 fixed tokens per session, estimated from their length (v0.3 measured about 4,800 with `claude plugin details reis-mobile`). The full content of each skill is only loaded when it is used.
|
|
405
425
|
|
|
406
|
-
**
|
|
426
|
+
**One session hook.** On session startup or resume, the plugin checks for a newer release and warns only when the installed plugin is outdated. It does not read the project or modify the plugin installation.
|
|
407
427
|
|
|
408
428
|
**Own namespace.** Commands live under `/reis-mobile:*` and do not conflict with the built-in `/review` or `/security-review`.
|
|
409
429
|
|
|
410
|
-
**Clean uninstall.** `reis-mobile init --uninstall` removes the plugin and its marketplace from Claude Code and Codex,
|
|
430
|
+
**Clean uninstall.** `reis-mobile init --uninstall` removes the plugin and its marketplace from Claude Code and Codex, the saved language and the update-check cache.
|
|
411
431
|
|
|
412
432
|
---
|
|
413
433
|
|
|
@@ -427,6 +447,7 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
|
|
|
427
447
|
| v0.5.0 | Topic and platform skills (`mobile-*`), with guides for Flutter, Android, iOS and React Native in each; new `mobile-ios` and `mobile-rn` | â
|
|
|
428
448
|
| v0.6.0 | Accessibility auditor and release engineer agents, `mobile-accessibility` and `mobile-release` skills, ANR, memory leak and deep link debugging guides | â
|
|
|
429
449
|
| v0.7.0 | `/reis-mobile:test`, native tests (XCTest, Espresso) and project-specific specialist contracts | â
|
|
|
450
|
+
| v0.7.1 | Update notices, single-source version and release automation | â
|
|
|
430
451
|
| v0.8.0 | `/reis-mobile:release` with quality gates | âģ |
|
|
431
452
|
| v0.9.0 | MCP server | âģ |
|
|
432
453
|
| v0.10.0 | Multi-provider (OpenAI, Gemini, OpenRouter, Ollama) beyond the debate's `--external` | âģ |
|
|
@@ -461,7 +482,7 @@ Yes: `reis-mobile init eng` installs the plugin in Codex too. See [installation]
|
|
|
461
482
|
The Cursor integration has not been validated yet. The MCP server remains on the roadmap for v0.8.0.
|
|
462
483
|
|
|
463
484
|
**What does reis-mobile send outside my machine?**
|
|
464
|
-
The
|
|
485
|
+
The project analysis commands do not make network requests. The update hook and `reis-mobile update-check` request the public npm package metadata without sending project files or identifiers. What the model reads during `/reis-mobile:review` follows the same rules as any Claude Code session.
|
|
465
486
|
|
|
466
487
|
---
|
|
467
488
|
|
package/bin/reis-mobile.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { PLUGIN_ROOT } from '../core/paths.mjs';
|
|
|
13
13
|
import { loadRegistry, validateRegistry } from '../core/registry/registry.mjs';
|
|
14
14
|
import { INTENT_IDS } from '../core/router/intents.mjs';
|
|
15
15
|
import { route } from '../core/router/router.mjs';
|
|
16
|
+
import { checkForUpdate, clearUpdateCache } from '../core/update/check.mjs';
|
|
16
17
|
|
|
17
18
|
const USAGE = `reis-mobile â AI agents for mobile engineering
|
|
18
19
|
|
|
@@ -29,6 +30,7 @@ Commands:
|
|
|
29
30
|
test [request...] Prepare test routing, git context and toolchain checks (does not run tests)
|
|
30
31
|
agents | skills | stacks List registered components
|
|
31
32
|
validate Validate the plugin's agents, skills and stacks
|
|
33
|
+
update-check Check npm for a newer reis-mobile release
|
|
32
34
|
|
|
33
35
|
Options:
|
|
34
36
|
--dir <path> Project directory (default: current directory, or "app" in .reis-mobile/config.yaml)
|
|
@@ -39,6 +41,7 @@ Options:
|
|
|
39
41
|
--local init: install the plugin from this installation instead of GitHub
|
|
40
42
|
--scope <scope> init: Claude Code scope, user (default), project or local
|
|
41
43
|
--uninstall init: remove the plugin and its marketplace from Claude Code and Codex, and the saved language
|
|
44
|
+
--force update-check: ignore the cached registry result
|
|
42
45
|
--lang <en|pt> Override the saved language for this run
|
|
43
46
|
--json Machine-readable output
|
|
44
47
|
-v, --version Print the version
|
|
@@ -53,6 +56,7 @@ const OPTIONS = {
|
|
|
53
56
|
local: { type: 'boolean' },
|
|
54
57
|
scope: { type: 'string' },
|
|
55
58
|
uninstall: { type: 'boolean' },
|
|
59
|
+
force: { type: 'boolean' },
|
|
56
60
|
lang: { type: 'string' },
|
|
57
61
|
json: { type: 'boolean' },
|
|
58
62
|
version: { type: 'boolean', short: 'v' },
|
|
@@ -72,6 +76,7 @@ const COMMANDS = {
|
|
|
72
76
|
skills: (options) => commandList('skills', options),
|
|
73
77
|
stacks: (options) => commandList('stacks', options),
|
|
74
78
|
validate: commandValidate,
|
|
79
|
+
'update-check': commandUpdateCheck,
|
|
75
80
|
};
|
|
76
81
|
|
|
77
82
|
async function main(argv) {
|
|
@@ -103,6 +108,15 @@ async function main(argv) {
|
|
|
103
108
|
|
|
104
109
|
const PROJECT_COMMANDS = new Set(['detect', 'doctor', 'route', 'review', 'debug', 'test']);
|
|
105
110
|
|
|
111
|
+
async function commandUpdateCheck({ force, json }) {
|
|
112
|
+
const result = await checkForUpdate({ force });
|
|
113
|
+
if (json) return print(result);
|
|
114
|
+
if (result.status === 'outdated') console.log(`reis-mobile ${result.current} is outdated; ${result.latest} is available. Run npm update -g reis-mobile && reis-mobile init.`);
|
|
115
|
+
else if (result.status === 'current') console.log(`reis-mobile ${result.current} is up to date (latest: ${result.latest}).`);
|
|
116
|
+
else console.error('Could not check the latest reis-mobile release. Try again with --force when online.');
|
|
117
|
+
return result.status === 'unavailable' ? 1 : 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
106
120
|
async function commandInit({ local, scope, uninstall, lang, prompt }) {
|
|
107
121
|
const requested = lang ?? (prompt || process.env.REIS_MOBILE_LANG || undefined);
|
|
108
122
|
// Validate before touching Claude Code or Codex, so a typo does not leave a half-done install.
|
|
@@ -123,6 +137,7 @@ async function commandInit({ local, scope, uninstall, lang, prompt }) {
|
|
|
123
137
|
}
|
|
124
138
|
if (uninstall) {
|
|
125
139
|
await removeConfig();
|
|
140
|
+
await clearUpdateCache();
|
|
126
141
|
console.log(`â ${'Language'.padEnd(12)}saved setting removed`);
|
|
127
142
|
return ok ? 0 : 1;
|
|
128
143
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { checkForUpdate, updateMessage } from '../core/update/check.mjs';
|
|
3
|
+
|
|
4
|
+
if (process.env.REIS_MOBILE_UPDATE_CHECK !== '0') {
|
|
5
|
+
try {
|
|
6
|
+
const result = await checkForUpdate();
|
|
7
|
+
if (result.status === 'outdated') {
|
|
8
|
+
const tool = process.env.PLUGIN_ROOT ? 'codex' : 'claude';
|
|
9
|
+
process.stdout.write(`${JSON.stringify({ systemMessage: updateMessage(result, tool) })}\n`);
|
|
10
|
+
}
|
|
11
|
+
} catch {
|
|
12
|
+
// Update checks are advisory and must never fail a session.
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { readFile, mkdir, rm, rmdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { PLUGIN_ROOT } from '../paths.mjs';
|
|
6
|
+
|
|
7
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/reis-mobile/latest';
|
|
8
|
+
const SUCCESS_TTL_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
const FAILURE_TTL_MS = 60 * 60 * 1000;
|
|
10
|
+
|
|
11
|
+
export function updateCachePath(env = process.env) {
|
|
12
|
+
const base = process.platform === 'win32'
|
|
13
|
+
? env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
14
|
+
: env.XDG_CACHE_HOME || join(homedir(), '.cache');
|
|
15
|
+
return join(base, 'reis-mobile', 'update-check.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function clearUpdateCache(path = updateCachePath()) {
|
|
19
|
+
try {
|
|
20
|
+
await rm(path, { force: true });
|
|
21
|
+
await rmdir(dirname(path));
|
|
22
|
+
} catch {
|
|
23
|
+
// Cache cleanup must not prevent uninstalling the plugin.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function installedVersion(root = PLUGIN_ROOT) {
|
|
28
|
+
return JSON.parse(await readFile(join(root, 'package.json'), 'utf8')).version;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function compareVersions(left, right) {
|
|
32
|
+
const parse = (value) => {
|
|
33
|
+
if (typeof value !== 'string') return null;
|
|
34
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
|
|
35
|
+
if (!match) return null;
|
|
36
|
+
const numbers = match.slice(1, 4).map(Number);
|
|
37
|
+
return numbers.every(Number.isSafeInteger) ? { numbers, prerelease: match[4] } : null;
|
|
38
|
+
};
|
|
39
|
+
const a = parse(left);
|
|
40
|
+
const b = parse(right);
|
|
41
|
+
if (!a || !b) return null;
|
|
42
|
+
for (let index = 0; index < 3; index++) {
|
|
43
|
+
if (a.numbers[index] !== b.numbers[index]) return Math.sign(a.numbers[index] - b.numbers[index]);
|
|
44
|
+
}
|
|
45
|
+
if (a.prerelease === b.prerelease) return 0;
|
|
46
|
+
if (!a.prerelease) return 1;
|
|
47
|
+
if (!b.prerelease) return -1;
|
|
48
|
+
// The package is published under the stable npm dist-tag. An unknown prerelease
|
|
49
|
+
// ordering should never generate an update warning for the same base version.
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function fetchLatestVersion(fetcher = fetch) {
|
|
54
|
+
const response = await fetcher(REGISTRY_URL, {
|
|
55
|
+
headers: { accept: 'application/json' },
|
|
56
|
+
signal: AbortSignal.timeout(1500),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) throw new Error(`Registry returned ${response.status}`);
|
|
59
|
+
const { version } = await response.json();
|
|
60
|
+
if (typeof version !== 'string' || compareVersions(version, version) === null) {
|
|
61
|
+
throw new Error('Registry returned an invalid version');
|
|
62
|
+
}
|
|
63
|
+
return version;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function checkForUpdate({
|
|
67
|
+
current,
|
|
68
|
+
cachePath = updateCachePath(),
|
|
69
|
+
now = Date.now(),
|
|
70
|
+
force = false,
|
|
71
|
+
fetchVersion = fetchLatestVersion,
|
|
72
|
+
} = {}) {
|
|
73
|
+
const version = current ?? await installedVersion();
|
|
74
|
+
let cached;
|
|
75
|
+
try {
|
|
76
|
+
cached = JSON.parse(await readFile(cachePath, 'utf8'));
|
|
77
|
+
} catch {
|
|
78
|
+
cached = null;
|
|
79
|
+
}
|
|
80
|
+
const cachedLatest = compareVersions(cached?.latest, cached?.latest) === 0 ? cached.latest : null;
|
|
81
|
+
const age = now - cached?.checkedAt;
|
|
82
|
+
const ttl = cachedLatest ? SUCCESS_TTL_MS : FAILURE_TTL_MS;
|
|
83
|
+
let latest = cachedLatest;
|
|
84
|
+
if (force || !Number.isFinite(age) || age < 0 || age >= ttl) {
|
|
85
|
+
try {
|
|
86
|
+
latest = await fetchVersion();
|
|
87
|
+
if (compareVersions(latest, latest) === null) throw new Error('Invalid version');
|
|
88
|
+
} catch {
|
|
89
|
+
latest = cachedLatest;
|
|
90
|
+
if (!latest) {
|
|
91
|
+
await saveCache(cachePath, { checkedAt: now, latest: null });
|
|
92
|
+
return { status: 'unavailable', current: version };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
await saveCache(cachePath, { checkedAt: now, latest });
|
|
96
|
+
}
|
|
97
|
+
if (!latest || compareVersions(version, latest) === null) return { status: 'unavailable', current: version };
|
|
98
|
+
return {
|
|
99
|
+
status: compareVersions(version, latest) < 0 ? 'outdated' : 'current',
|
|
100
|
+
current: version,
|
|
101
|
+
latest,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function saveCache(path, value) {
|
|
106
|
+
try {
|
|
107
|
+
await mkdir(dirname(path), { recursive: true });
|
|
108
|
+
await writeFile(path, JSON.stringify(value), { mode: 0o600 });
|
|
109
|
+
} catch {
|
|
110
|
+
// A read-only home must not interrupt a session.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function updateMessage({ current, latest }, tool = 'claude') {
|
|
115
|
+
const update = tool === 'codex'
|
|
116
|
+
? 'codex plugin marketplace upgrade reis-mobile && codex plugin add reis-mobile@reis-mobile'
|
|
117
|
+
: 'claude plugin marketplace update reis-mobile && claude plugin update reis-mobile@reis-mobile';
|
|
118
|
+
return `reis-mobile ${current} is outdated; ${latest} is available. Update the plugin: ${update}. If you installed the CLI with npm, also run npm update -g reis-mobile.`;
|
|
119
|
+
}
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Warn when a newer reis-mobile release is available.",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "startup|resume",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/bin/update-check.mjs\"",
|
|
11
|
+
"timeout": 3
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reis-mobile",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "AI agents and skills for mobile development â Flutter, Android, iOS and React Native â as a plugin for Claude Code and Codex: code review, debugging, tests, architecture, security and Firebase.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"commands",
|
|
20
20
|
"core",
|
|
21
21
|
"docs/agent-context.md",
|
|
22
|
+
"hooks",
|
|
22
23
|
"skills",
|
|
23
24
|
"stacks",
|
|
24
25
|
"THIRD_PARTY_NOTICES.md",
|
|
@@ -30,7 +31,8 @@
|
|
|
30
31
|
"scripts": {
|
|
31
32
|
"test": "node --test",
|
|
32
33
|
"validate": "node bin/reis-mobile.mjs validate",
|
|
33
|
-
"check": "npm run validate && npm test"
|
|
34
|
+
"check": "npm run validate && npm test",
|
|
35
|
+
"version:sync": "node scripts/versions.mjs --sync"
|
|
34
36
|
},
|
|
35
37
|
"keywords": [
|
|
36
38
|
"flutter",
|