@1aboveio/skills 1.0.17 → 1.0.19
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/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +175 -141
- package/README.md +1 -1
- package/package.json +5 -4
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +14 -12
- package/skills/app-dev/android-development/references/diagnosis.md +4 -6
- package/skills/app-dev/android-development/references/gotchas.md +3 -9
- package/skills/app-dev/android-development/references/init.md +5 -2
- package/skills/app-dev/android-development/references/setup.md +2 -1
- package/skills/app-dev/android-development/references/ui-journeys.md +12 -127
- package/skills/app-dev/android-development/scripts/failure_capture.py +23 -84
- package/skills/app-dev/android-development/scripts/init.mjs +2 -1
- package/skills/app-dev/android-development/scripts/maestro_failure.py +43 -387
- package/skills/app-dev/android-development/scripts/test_maestro_failure.py +113 -424
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/maestro/LICENSE +3 -0
- package/skills/app-dev/maestro/SKILL.md +112 -0
- package/skills/app-dev/maestro/references/android-connection.md +54 -0
- package/skills/app-dev/maestro/references/authoring.md +113 -0
- package/skills/app-dev/maestro/references/diagnosis.md +50 -0
- package/skills/app-dev/maestro/references/evidence.md +74 -0
- package/skills/app-dev/maestro/references/journeys.md +100 -0
- package/skills/app-dev/maestro/references/migration.md +35 -0
- package/skills/app-dev/maestro/release-check.mjs +95 -0
- package/skills/app-dev/maestro/scripts/failure_record.py +110 -0
- package/skills/app-dev/maestro/scripts/maestro_failure.py +481 -0
- package/skills/app-dev/maestro/scripts/test_maestro_failure.py +710 -0
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/agent-catalog.mjs +44 -11
- package/skills/engineering/harness-runtime/contracts.md +1 -1
- package/skills/engineering/resolve-issues/SKILL.md +4 -2
- package/skills/engineering/resolve-issues/references/preflight.md +17 -3
- package/skills/engineering/resolve-issues/references/retention.md +162 -0
- package/skills/engineering/resolve-issues/references/workspaces.md +40 -10
- package/skills/engineering/resolve-issues/scripts/daily-cleanup.mjs +169 -0
- package/skills/engineering/resolve-issues/scripts/resource-formats.mjs +625 -0
- package/skills/engineering/resolve-issues/scripts/retention-lock.mjs +548 -0
- package/skills/engineering/resolve-issues/scripts/retention-plans.mjs +57 -0
- package/skills/engineering/resolve-issues/scripts/retention.mjs +2314 -0
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maestro
|
|
3
|
+
description: "Design, run, debug and migrate mobile E2E journeys with Maestro: complete flows and subflows, selectors, parameters, exclusive UI-driver ownership, non-UI authentication/backend helpers and failure evidence. Use for Maestro YAML/CLI/MCP work, slow fragmented mobile journeys, UiAutomation driver collisions, or replacing a hybrid Python/uiautomator2 UI harness. Android-first; use platform-neutral flow guidance for iOS only with its verified device/runtime contract. NOT for Android provisioning/builds (android-development), iOS setup (ios-foundation), browser Playwright tests (e2e-test), or CI workflow YAML (github-actions)."
|
|
4
|
+
release-version: 0.49.1
|
|
5
|
+
---
|
|
6
|
+
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
7
|
+
# Maestro
|
|
8
|
+
|
|
9
|
+
Make the journey, not each tap, the unit of UI execution. Maestro owns ordinary app UI;
|
|
10
|
+
the existing runner owns resources and cleanup; existing non-UI helpers establish backend
|
|
11
|
+
and platform facts. Preserve the repository's acceptance criteria and selected checkpoint.
|
|
12
|
+
|
|
13
|
+
## Read the assignment
|
|
14
|
+
|
|
15
|
+
Read applicable `AGENTS.md`, `CONTRIBUTING.md`, test guidance and relevant ADRs from the
|
|
16
|
+
assigned workspace. Resolve the actual case, caller, source, app artifact, served code,
|
|
17
|
+
device identifier and runtime owner before execution. Follow nested guidance for touched
|
|
18
|
+
paths. Existing policy wins; missing guidance uses the defaults here, while unreadable
|
|
19
|
+
guidance or an unresolved ownership conflict is reported before affected operations.
|
|
20
|
+
|
|
21
|
+
This skill writes requested flows, helpers and tests, not project conventions or agent
|
|
22
|
+
settings on discovery. It has no `init` command and installs no tools automatically.
|
|
23
|
+
For missing Android workflow conventions, offer `android-development init` once without
|
|
24
|
+
blocking the task; that skill owns its authorized writes. A task override lasts only for
|
|
25
|
+
the assignment and survives context reload, not as a saved policy change.
|
|
26
|
+
|
|
27
|
+
## Work loop
|
|
28
|
+
|
|
29
|
+
1. **Select proof.** Name the user journey and its required observations. Map each to a
|
|
30
|
+
UI assertion, non-UI helper or specifically justified native case using
|
|
31
|
+
[the test split](references/journeys.md#split-by-responsibility). Reuse sufficient
|
|
32
|
+
existing proof; do not add a matrix or gate because Maestro is being used.
|
|
33
|
+
2. **Use the owned runtime.** Consume the existing runner's device assignment, app/code
|
|
34
|
+
identity, private endpoints and disposable fixture contract. For Android environment
|
|
35
|
+
work use `android-development`; for iOS setup use `ios-foundation`. Neither a new flow
|
|
36
|
+
nor this skill authorizes another emulator, dependency install or fixture lifecycle.
|
|
37
|
+
3. **Compose the journey.** Prefer one top-level flow invoking reusable `runFlow` subflows
|
|
38
|
+
within one Maestro invocation. Group contiguous UI work; split only for a real external
|
|
39
|
+
or native checkpoint. Read [journeys](references/journeys.md) before authoring or running.
|
|
40
|
+
4. **Verify the real path.** Test actual case dispatch, caller parameters, production
|
|
41
|
+
selectors and fixture composition before allocating a device when host checks suffice.
|
|
42
|
+
Then run the selected case through the maintained local/CI caller on the identified app.
|
|
43
|
+
Syntax checks, mocks, a readiness probe or YAML files alone are not execution proof.
|
|
44
|
+
5. **Diagnose and correct.** At failure, identify the earliest failed stage and retain its
|
|
45
|
+
evidence before teardown. Use [diagnosis](references/diagnosis.md); confirm the original
|
|
46
|
+
reproduction after the correction and repeat only invalidated checks.
|
|
47
|
+
6. **Report and finish.** Name executed cases/assertions, source/app identities, results,
|
|
48
|
+
remaining requirements and cleanup or explicit runtime handoff. Follow
|
|
49
|
+
[evidence](references/evidence.md); a converter's success is not a journey pass.
|
|
50
|
+
|
|
51
|
+
For an authorized existing-harness conversion, read [migration](references/migration.md).
|
|
52
|
+
An installed Maestro CLI or a broken harness alone does not authorize a migration.
|
|
53
|
+
|
|
54
|
+
## One UI driver
|
|
55
|
+
|
|
56
|
+
A Maestro-only case starts no uiautomator2 client in its parent, child, observer or failure
|
|
57
|
+
capture. A reader that acquires Android UI automation is still a competing driver.
|
|
58
|
+
Use Maestro's hierarchy/artifacts and suitable non-conflicting platform probes instead.
|
|
59
|
+
|
|
60
|
+
During migration, a necessary handoff goes through the process that actually launched the
|
|
61
|
+
old server: verify ownership, stop and confirm release, run Maestro to completion, then
|
|
62
|
+
reclaim only if the next native phase needs it. A joined child client cannot necessarily
|
|
63
|
+
stop the parent's server. Keep the existing security/currentness checks and never stop a
|
|
64
|
+
peer. Treat this as transitional, not the default around every action.
|
|
65
|
+
|
|
66
|
+
## Framework responsibilities
|
|
67
|
+
|
|
68
|
+
| Responsibility | Owner | Rule |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Setup, source/app identity, timeouts and cleanup | Existing runner | One authoritative resource owner per device; retain its emulator/ADB/Metro/backend and fixture lifecycle |
|
|
71
|
+
| Ordinary app interactions and UI assertions | Maestro | No uiautomator2 server starts in this case's parent, child, observer or failure capture |
|
|
72
|
+
| Financial and backend verification | Existing backend test code | Verify real records, amounts, request counts and isolation, not just success text; preserve required checkpoint order |
|
|
73
|
+
| Specialized native assertions requiring another UI driver | Existing native test code | Separate exclusive case/phase; Maestro is stopped and its registration released before the native driver starts |
|
|
74
|
+
| Logic and module tests | Existing host/JVM tests | Run without an emulator where sufficient; do not convert these tests to UI journeys |
|
|
75
|
+
|
|
76
|
+
The runner may use Python or another existing language. It orchestrates resources and
|
|
77
|
+
case/checkpoint boundaries, not individual taps. Backend helpers remain non-UI; a reader
|
|
78
|
+
that acquires UI automation belongs in the exclusive native path. Suitable non-conflicting
|
|
79
|
+
platform probes may run during a Maestro case, but do not silently substitute them for
|
|
80
|
+
required native proof. Grouping UI actions never moves a financial guard past its action.
|
|
81
|
+
|
|
82
|
+
This skill owns the Maestro side of that split. `android-development` owns Android runtime
|
|
83
|
+
and build work; `ios-foundation` owns iOS setup without Android addressing assumptions.
|
|
84
|
+
Use `test-quality` for test honesty, `e2e-test` for browser journeys and `github-actions`
|
|
85
|
+
for workflow implementation. This is a test-framework contract, not an agent-role policy.
|
|
86
|
+
|
|
87
|
+
Maestro supports focused-element assertions; focus alone is not a reason to keep another
|
|
88
|
+
driver. App stop/relaunch does not automatically prove OS process death, reboot, continuous
|
|
89
|
+
focus retention, physical hardware or payment-provider acceptance. Match each claim to its
|
|
90
|
+
observation. A multi-device coordinator may orchestrate separate Maestro journeys, but
|
|
91
|
+
independent shards do not establish an ordered cross-device interaction.
|
|
92
|
+
|
|
93
|
+
The skill ships no Maestro binary and no resource runner. Reuse the repo-pinned CLI and
|
|
94
|
+
check its actual `--help`/version for flags and artifact shape. Shared flow guidance can
|
|
95
|
+
apply to iOS, but this extraction does not certify iOS provisioning, transport or native
|
|
96
|
+
assertions. Keep that evidence gap explicit.
|
|
97
|
+
|
|
98
|
+
## References
|
|
99
|
+
|
|
100
|
+
- [Journeys and helpers](references/journeys.md): test split, state, selectors and invocation.
|
|
101
|
+
- [Flow authoring](references/authoring.md): a complete flow/subflow example and parameter checks.
|
|
102
|
+
- [Diagnosis](references/diagnosis.md): driver startup, action failures and useful comparisons.
|
|
103
|
+
- [Android connection](references/android-connection.md): direct adbd versus ADB-server routing and JVM key lookup.
|
|
104
|
+
- [Evidence](references/evidence.md): reports, converter and original-status preservation.
|
|
105
|
+
- [Migration](references/migration.md): replace hybrid UI paths without losing assertions.
|
|
106
|
+
|
|
107
|
+
## Sources
|
|
108
|
+
|
|
109
|
+
- [Nested flows](https://docs.maestro.dev/maestro-flows/flow-control-and-logic/nested-flows)
|
|
110
|
+
- [State selectors](https://docs.maestro.dev/reference/selectors/state-selectors)
|
|
111
|
+
- [JavaScript HTTP requests](https://docs.maestro.dev/advanced/javascript/make-http-requests)
|
|
112
|
+
- [Launch behavior](https://docs.maestro.dev/reference/commands-available/launchapp)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Android connection and driver startup
|
|
2
|
+
|
|
3
|
+
Read this when ADB sees the device but Maestro cannot find/authenticate it, or when the
|
|
4
|
+
driver never reaches its first flow command. The runtime owner supplies the assigned
|
|
5
|
+
serial, server/guest endpoints and authorized credentials. This reference does not
|
|
6
|
+
provision devices or change global ADB state.
|
|
7
|
+
|
|
8
|
+
## Distinguish the endpoints
|
|
9
|
+
|
|
10
|
+
The Maestro 2.12.0 connection implementation has separate direct-adbd and ADB-server
|
|
11
|
+
factories. Discovery/by-ID paths and explicit endpoint paths are not interchangeable.
|
|
12
|
+
Do not assume `ADB_SERVER_SOCKET`, `ANDROID_ADB_SERVER_PORT`, `ADB_VENDOR_KEYS` or the
|
|
13
|
+
fact that the host `adb` works means the JVM followed the same route.
|
|
14
|
+
|
|
15
|
+
Inspect the actual pinned CLI `--help`, launcher and bundled connection implementation
|
|
16
|
+
to establish which route the invocation selects. Use supported flags through the existing
|
|
17
|
+
adapter. For a conventional Android emulator the console and adbd transport ports differ;
|
|
18
|
+
the console port is not an adbd endpoint. Read the owner's actual mapping instead of
|
|
19
|
+
guessing from another session's serial. Keep peer/default-server endpoints untouched.
|
|
20
|
+
|
|
21
|
+
## Check the effective JVM credentials
|
|
22
|
+
|
|
23
|
+
The Dadb `AdbKeyPair.readDefault()` implementation reads `.android/adbkey` and
|
|
24
|
+
`.android/adbkey.pub` under Java's `user.home`. If the private key is absent it generates
|
|
25
|
+
a new pair there. A private shell HOME, `ADB_VENDOR_KEYS`, or a successful host ADB
|
|
26
|
+
connection does not by itself establish that Java used the guest-authorized pair.
|
|
27
|
+
|
|
28
|
+
Verify that behavior in the pinned dependency before applying it to another version.
|
|
29
|
+
Compare the launcher's effective JVM home and public-key identity with the runtime
|
|
30
|
+
owner's authorized key. Use the owner's supported JVM-home/credential configuration;
|
|
31
|
+
do not copy another owner's key, print private key material, approve an unexpected USB
|
|
32
|
+
dialog or alter a shared server to work around failed authentication. Bound the probe
|
|
33
|
+
and record missing credential authority rather than retrying the whole journey.
|
|
34
|
+
|
|
35
|
+
## Separate authentication from instrumentation
|
|
36
|
+
|
|
37
|
+
On Android, a session installs/starts Maestro's `dev.mobile.maestro` and
|
|
38
|
+
`dev.mobile.maestro.test` driver packages and launches instrumentation. A working adbd
|
|
39
|
+
connection does not prove this succeeded. Retain installation output, instrumentation
|
|
40
|
+
errors and the guest log interval covering startup before cleanup.
|
|
41
|
+
|
|
42
|
+
If instrumentation reports an existing UiAutomation registration, inspect its real
|
|
43
|
+
owner as described in [diagnosis](diagnosis.md). If no commands ran, do not debug a UI
|
|
44
|
+
selector yet. Once startup is corrected, the selected case still owes its UI/backend
|
|
45
|
+
assertions; driver readiness alone is not acceptance.
|
|
46
|
+
|
|
47
|
+
## Sources and version limits
|
|
48
|
+
|
|
49
|
+
- [Maestro 2.12.0 AndroidDeviceConnection](https://github.com/mobile-dev-inc/Maestro/blob/cli-2.12.0/maestro-client/src/main/java/maestro/android/AndroidDeviceConnection.kt)
|
|
50
|
+
- [Dadb default key lookup](https://github.com/mobile-dev-inc/dadb/blob/master/dadb/src/main/kotlin/dadb/AdbKeyPair.kt)
|
|
51
|
+
|
|
52
|
+
The Dadb source link follows its branch and is explanatory evidence, not a dependency pin.
|
|
53
|
+
Recheck the installed implementation; these sources do not certify every consumer's CLI,
|
|
54
|
+
transport or launcher configuration.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Author a flow, not a CLI loop
|
|
2
|
+
|
|
3
|
+
Start from the repo's existing selectors, copies and fixtures. The following is a structural
|
|
4
|
+
example for a warm History filter case, not runnable proof against an arbitrary app.
|
|
5
|
+
Replace the IDs and assertion with the actual production controls and required result;
|
|
6
|
+
keep its existing caller and `APP_ID`/`FILTER_DATE` input validation.
|
|
7
|
+
|
|
8
|
+
```yaml
|
|
9
|
+
# maestro/flows/history-filter.yaml
|
|
10
|
+
appId: ${APP_ID}
|
|
11
|
+
name: History filter
|
|
12
|
+
---
|
|
13
|
+
- launchApp:
|
|
14
|
+
stopApp: false
|
|
15
|
+
- runFlow:
|
|
16
|
+
file: ../shared/open-history.yaml
|
|
17
|
+
- tapOn:
|
|
18
|
+
id: history_filters
|
|
19
|
+
- tapOn:
|
|
20
|
+
id: history_filter_from
|
|
21
|
+
- assertVisible:
|
|
22
|
+
id: history_filter_from
|
|
23
|
+
focused: true
|
|
24
|
+
- inputText: ${FILTER_DATE}
|
|
25
|
+
- tapOn:
|
|
26
|
+
id: history_filter_apply
|
|
27
|
+
- assertNotVisible:
|
|
28
|
+
id: history_filter_apply
|
|
29
|
+
- assertVisible:
|
|
30
|
+
id: history_filter_active
|
|
31
|
+
text: ${FILTER_DATE}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
# maestro/shared/open-history.yaml
|
|
36
|
+
appId: ${APP_ID}
|
|
37
|
+
---
|
|
38
|
+
- tapOn:
|
|
39
|
+
id: navigation_history
|
|
40
|
+
- assertVisible:
|
|
41
|
+
id: history_list
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
One CLI invocation executes the top-level flow and its subflow. The subflow does not
|
|
45
|
+
restart, clear state or grant permissions behind the caller's back. An ordinary focused
|
|
46
|
+
selector is useful at a checkpoint, not a replacement for a required continuous hold.
|
|
47
|
+
|
|
48
|
+
Bind the expected value to the intended control, as `id` plus `text` above. Separate
|
|
49
|
+
assertions for a badge and a date anywhere on screen can pass when another row contains
|
|
50
|
+
that date. If the real renderer puts the value on a child, use a verified parent/descendant
|
|
51
|
+
relationship (for example `containsDescendants`) instead of dropping that binding.
|
|
52
|
+
|
|
53
|
+
## Parameters and input
|
|
54
|
+
|
|
55
|
+
Pass required values through the existing caller and `-e NAME=value`/subflow `env` route
|
|
56
|
+
supported by the pinned version. Prefer required inputs over hidden defaults. If a default
|
|
57
|
+
is actually needed, preserve undefined versus empty/false/zero and verify its expansion
|
|
58
|
+
with the real CLI before adopting it. Do not put the caller's input name in a competing
|
|
59
|
+
flow-level default block and assume CLI precedence.
|
|
60
|
+
|
|
61
|
+
Assert the exact resulting value when text entry is part of the requirement. A successful
|
|
62
|
+
`inputText` command does not prove the entire string reached the intended field. Current
|
|
63
|
+
Maestro docs flag Android Unicode input limitations: verify the selected CLI/input route
|
|
64
|
+
for non-ASCII data, and use an existing supported alternative if required. Do not claim
|
|
65
|
+
Chinese input proof from ASCII-only data, silently change locale, or add sleeps/retries
|
|
66
|
+
without identifying the failed operation.
|
|
67
|
+
|
|
68
|
+
Use existing syntax/admission checks before allocating the device, then run the actual
|
|
69
|
+
selected journey. Syntax validation does not check that an ID is in the real app, that
|
|
70
|
+
the caller supplied the right parameter, or that the destination rendered.
|
|
71
|
+
|
|
72
|
+
## Consume an existing helper result
|
|
73
|
+
|
|
74
|
+
When the case requires real Login-code delivery and an authorized helper interface already
|
|
75
|
+
exists, a flow can retrieve that result without restarting Maestro. The following fragments
|
|
76
|
+
assume the existing helper returns JSON with a string `code` scoped to this invocation and
|
|
77
|
+
the exact preceding Login request. They are not permission to build an endpoint.
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
- runScript:
|
|
81
|
+
file: ../scripts/read-auth-code.js
|
|
82
|
+
- tapOn:
|
|
83
|
+
id: authentication_code
|
|
84
|
+
- inputText: ${output.authCode}
|
|
85
|
+
- tapOn:
|
|
86
|
+
id: authentication_verify
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// maestro/scripts/read-auth-code.js; use the existing helper's real contract.
|
|
91
|
+
var response = http.get(AUTH_HELPER_URL, {
|
|
92
|
+
headers: { Authorization: 'Bearer ' + AUTH_HELPER_TOKEN }
|
|
93
|
+
});
|
|
94
|
+
if (response.status !== 200) throw new Error('Authentication helper failed');
|
|
95
|
+
var body = json(response.body);
|
|
96
|
+
if (typeof body.code !== 'string' || body.code.length === 0) {
|
|
97
|
+
throw new Error('Authentication code unavailable');
|
|
98
|
+
}
|
|
99
|
+
output.authCode = body.code;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The caller supplies the already-authorized URL/token without logging them, and the existing
|
|
103
|
+
owner bounds helper waits and closes any owned endpoint. Scope/authorization belong to the
|
|
104
|
+
helper, not a guessed query parameter. Sanitize code-entry artifacts before publishing. For
|
|
105
|
+
an approved non-Login fixture, keep that fixture rather than adding a code-delivery step.
|
|
106
|
+
|
|
107
|
+
## Sources
|
|
108
|
+
|
|
109
|
+
- [runFlow](https://docs.maestro.dev/reference/commands-available/runFlow)
|
|
110
|
+
- [State selectors](https://docs.maestro.dev/reference/selectors/state-selectors)
|
|
111
|
+
- [inputText](https://docs.maestro.dev/reference/commands-available/inputText)
|
|
112
|
+
- [runScript output](https://docs.maestro.dev/reference/commands-available/runScript)
|
|
113
|
+
- [Relational selectors](https://docs.maestro.dev/reference/selectors/relational-selectors)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Diagnose the failing stage
|
|
2
|
+
|
|
3
|
+
Preserve the first failure and inspect the actual operation before another device run.
|
|
4
|
+
Use the assigned source and runtime; do not substitute a newer checkout or relabel an old
|
|
5
|
+
APK. Keep uncertainty when the retained evidence cannot distinguish causes.
|
|
6
|
+
|
|
7
|
+
| Earliest failed stage | Smallest useful evidence/question |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| CLI/flow admission | Real arguments, supported selector, syntax and selected flow count |
|
|
10
|
+
| Device discovery/authentication | Actual endpoint, owned serial, authorized key and JVM home; did a command reach the intended guest? |
|
|
11
|
+
| Driver startup | Installation/instrumentation output and guest startup log interval before teardown; which process owns UI automation? |
|
|
12
|
+
| UI action/selector | Failing command, hierarchy, screenshot, actual foreground state and caller parameters |
|
|
13
|
+
| Business assertion | Real record IDs, fixture and backend/provider readback at the required checkpoint |
|
|
14
|
+
| Teardown | Cleanup status separately from the original failed stage |
|
|
15
|
+
|
|
16
|
+
For `UiAutomationService ... already registered!`, identify the registration holder. In a
|
|
17
|
+
parent/child harness, a child's uiautomator2 client may have joined the parent's server;
|
|
18
|
+
stopping that client releases nothing. Ask the verified owner to stop and confirm exit for
|
|
19
|
+
a necessary transitional handoff. For a migrated Maestro-only path, remove competing setup
|
|
20
|
+
and observers once equivalent observations are in place. Never use broad kills, another
|
|
21
|
+
namespace or longer timeouts as a substitute for correct ownership.
|
|
22
|
+
|
|
23
|
+
A correct transport and key do not prove driver readiness. Capture installation,
|
|
24
|
+
instrumentation and crash logs covering startup, not only a tail after the driver failed.
|
|
25
|
+
For endpoint modes, JVM `user.home` key lookup and driver packages, read
|
|
26
|
+
[Android connection](android-connection.md) before changing private transport settings.
|
|
27
|
+
A ready driver does not prove the app used the expected Metro/backend endpoint. Compare
|
|
28
|
+
with an existing known-working caller on the same relevant contract before inventing a
|
|
29
|
+
new transport or collector; explain differences instead of claiming all callers are alike.
|
|
30
|
+
|
|
31
|
+
For UI failures, test the actual producer/consumer where a host reproduction suffices:
|
|
32
|
+
localized labels with country suffixes, undefined-versus-empty parameters, exact selected
|
|
33
|
+
case, fixture prerequisites and executable path. Preserve original gesture coverage such
|
|
34
|
+
as a deliberate double tap; a single successful submit does not replace it.
|
|
35
|
+
|
|
36
|
+
When comparing scroll adapters, content DOWN requires an upward finger swipe; content UP
|
|
37
|
+
requires a downward swipe. A regression fixture must keep the target offscreen until the
|
|
38
|
+
correct gesture, rather than reveal it after an arbitrary gesture count. Prefer Maestro's
|
|
39
|
+
existing scroll command over adding a comparison adapter.
|
|
40
|
+
|
|
41
|
+
Use bounded searches over named files and installed module paths. An unbounded recursive
|
|
42
|
+
search through workspaces/caches can outlast the journey being debugged. Every experiment
|
|
43
|
+
answers a stated question and has an execution/cleanup bound. Correct the supported cause,
|
|
44
|
+
confirm the original reproduction, then rerun invalidated checks; neither a full unchanged
|
|
45
|
+
campaign nor a new worker is required for each small correction.
|
|
46
|
+
|
|
47
|
+
Measure case wall time, invocation count and time inside Maestro separately. Invocation
|
|
48
|
+
time includes UI execution and is not all removable startup cost. Use existing logs;
|
|
49
|
+
do not promise a speedup, increase sensitive assertion windows or build a metrics system
|
|
50
|
+
solely to explain one slow case.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Journey results and failure evidence
|
|
2
|
+
|
|
3
|
+
Record selected cases and executed assertions, source/native artifact/served code identity,
|
|
4
|
+
device/OS and relevant configuration, original status, evidence paths and cleanup outcome.
|
|
5
|
+
The existing runner owns deadlines and cleanup. Successful rendering is not a packaged-app
|
|
6
|
+
or release claim unless that checkpoint's artifact/source requirements were satisfied.
|
|
7
|
+
|
|
8
|
+
Use the pinned Maestro's per-flow results/JUnit report or the existing runner's honest PASS
|
|
9
|
+
markers. A zero exit with no selected flows is not a pass. Capture passing checkpoints with
|
|
10
|
+
`takeScreenshot` when required; ordinary passing flows have no automatic screenshots.
|
|
11
|
+
|
|
12
|
+
The bundled `scripts/maestro_failure.py` maps retained Maestro artifacts to `failure.json`:
|
|
13
|
+
`case`, `step`, `error`, `screenshot`, `uiDump`, `logcatTail`, `evidenceDir`, `cleanupErrors`.
|
|
14
|
+
It prints one `NATIVE_FAILURE` JSON line with `case`, `step`, `error`, `evidenceDir`. The
|
|
15
|
+
first existing failure wins; later capture or cleanup failures never replace its cause.
|
|
16
|
+
Artifact fields are paths relative to the evidence directory or null with a capture reason.
|
|
17
|
+
The converter reads retained files and writes only under the selected evidence directory;
|
|
18
|
+
it starts no device or UI-automation client. Absolute/escaping artifact paths and symlinks
|
|
19
|
+
outside the Flow or destination are rejected as capture gaps, preserving the original error.
|
|
20
|
+
|
|
21
|
+
Confirm the pinned CLI's actual output layout first. The converter understands session/flow
|
|
22
|
+
folders with `commands.json`, failing-step screenshots/hierarchies and device logs. When a
|
|
23
|
+
version uses a different shape, retain its original output and adapt/test the real reader;
|
|
24
|
+
do not assume missing converter output means a passing case. iOS log-name fallbacks in the
|
|
25
|
+
reader are not an observed iOS-device certification.
|
|
26
|
+
|
|
27
|
+
Malformed command entries or unsupported statuses are conversion errors, not empty passing
|
|
28
|
+
flows. If part of a session is unreadable, the CLI retains an available failed record,
|
|
29
|
+
reports the omitted Flow(s) and returns 2 for incomplete conversion. Its selection is only
|
|
30
|
+
the earliest readable failure, not proof of the session's first failure. An explicitly
|
|
31
|
+
empty command list produces no record and still does not prove a selected journey ran.
|
|
32
|
+
|
|
33
|
+
Nested `runFlow` output may include a failed parent summary as well as a failed child.
|
|
34
|
+
The reader's timestamp/entry selection is mechanical; deepest-child/root-cause attribution
|
|
35
|
+
is not certified without a retained nested-output fixture from the pinned CLI. Keep the
|
|
36
|
+
raw command sequence and inspect it when that distinction matters.
|
|
37
|
+
|
|
38
|
+
Give every case/attempt a new owned `LOG_DIR`, shared with its launcher. Reusing a runtime
|
|
39
|
+
does not reuse its failure-record destination. After finalized CLI output, convert before
|
|
40
|
+
teardown with the original status preserved. Inside the existing owner's bounded command:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
maestro_status=0
|
|
44
|
+
maestro --device "$DEVICE_ID" test --test-output-dir "$LOG_DIR/maestro" \
|
|
45
|
+
--format junit --output "$LOG_DIR/report.xml" ./maestro/flows/checkout.yaml \
|
|
46
|
+
|| maestro_status=$?
|
|
47
|
+
conversion_status=0
|
|
48
|
+
python3 <skill-base-dir>/scripts/maestro_failure.py \
|
|
49
|
+
--maestro-dir "$LOG_DIR/maestro" --evidence-dir "$LOG_DIR" --case "$DEVICE_CASE" \
|
|
50
|
+
|| conversion_status=$?
|
|
51
|
+
printf 'maestro_exit=%s conversion_exit=%s\n' "$maestro_status" "$conversion_status"
|
|
52
|
+
if [ "$maestro_status" -ne 0 ]; then
|
|
53
|
+
exit "$maestro_status"
|
|
54
|
+
fi
|
|
55
|
+
exit "$conversion_status"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Here `<skill-base-dir>` is the installed **maestro** directory. Retain the repo's actual
|
|
59
|
+
qualified invocation/transport in place of the example. Converter exit zero means evidence
|
|
60
|
+
processing succeeded, not that the journey passed: a recorded failure also returns zero.
|
|
61
|
+
|
|
62
|
+
If startup leaves no step report, retain CLI logs and the actual driver-start interval.
|
|
63
|
+
Have the existing owner write its partial failure record before teardown, preserving the
|
|
64
|
+
original exit/error. Capture with Maestro artifacts or non-conflicting probes; a fallback
|
|
65
|
+
`uiautomator dump` can itself acquire UI automation, so it must not run alongside Maestro.
|
|
66
|
+
Report missing capture as a gap, not permission to start a competing client.
|
|
67
|
+
|
|
68
|
+
Keep raw credentials, authentication codes and payment data private. Publish only sanitized
|
|
69
|
+
findings. Report build time, device time and total worker time separately; one cannot be
|
|
70
|
+
inferred from another.
|
|
71
|
+
|
|
72
|
+
## Source
|
|
73
|
+
|
|
74
|
+
- [Reports and artifacts](https://docs.maestro.dev/maestro-flows/workspace-management/test-reports-and-artifacts)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Complete journeys
|
|
2
|
+
|
|
3
|
+
## Split by responsibility
|
|
4
|
+
|
|
5
|
+
| Required observation | Default location |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Navigation, form entry, scrolling, visible results, enabled/selected/focused state | Maestro flow and assertions |
|
|
8
|
+
| Exact records, amounts, duplicate-call counts, authorization isolation and recovery identity | Existing backend/database helper |
|
|
9
|
+
| Authentication/fixtures required by this case | Existing approved helper; for real Login-code acceptance, the code produced by that request |
|
|
10
|
+
| Device setup, installation, Metro, fixture creation and cleanup | Existing runtime owner |
|
|
11
|
+
| OS/security/accessibility fact not exposed with equivalent fidelity by Maestro | Non-conflicting platform probe or named exclusive native case |
|
|
12
|
+
|
|
13
|
+
Use the cheapest observation that proves the requirement. An on-screen success is not an
|
|
14
|
+
exact Sale assertion; conversely, a database row does not prove the intended screen rendered.
|
|
15
|
+
Preserve the repository-approved authentication fixture: an unrelated authenticated-screen
|
|
16
|
+
test may use an approved seeded session or test-account service. When acceptance includes
|
|
17
|
+
Login or code delivery, exercise that real path and retrieve its actual code through the
|
|
18
|
+
approved helper (for example disposable PostgreSQL). Do not substitute a hardcoded OTP or
|
|
19
|
+
injected session for required authentication proof. Provider doubles remain only where
|
|
20
|
+
repo policy allows them; no production fixture endpoint or replay of an Unknown payment
|
|
21
|
+
to get a green journey.
|
|
22
|
+
|
|
23
|
+
Separate helpers from UI clients. Run before/after checks directly through the existing
|
|
24
|
+
runner. Where ordering needs a mid-flow result, first reuse an existing supported helper
|
|
25
|
+
interface. Maestro `runScript` can issue HTTP requests, but a new bridge is a scoped design
|
|
26
|
+
change, not required infrastructure: get authorization, reuse the same fixture/authority,
|
|
27
|
+
limit it to owned disposable resources and required operations, keep credentials private,
|
|
28
|
+
bound waits, and close it through the existing owner. It must not become arbitrary command
|
|
29
|
+
execution or a public production test route. Record a necessary split if no safe interface
|
|
30
|
+
exists instead of inventing one or omitting an assertion.
|
|
31
|
+
|
|
32
|
+
## Starting state
|
|
33
|
+
|
|
34
|
+
Declare the required app state, permissions, locale, window and fixture in the existing
|
|
35
|
+
case setup. Inspect hooks and subflows for resets too.
|
|
36
|
+
|
|
37
|
+
- For a warm app, keep it running or use `launchApp: {stopApp: false}` to foreground it.
|
|
38
|
+
Plain `launchApp` stops and relaunches by default.
|
|
39
|
+
- `clearState` clears app data, not installs an APK or proves upgrade behavior. Clear only
|
|
40
|
+
owned disposable state and restore debug endpoint preferences when the reset removes them.
|
|
41
|
+
- Set permissions explicitly for denied/unset/prompt cases: `launchApp` grants all by default.
|
|
42
|
+
- Configure lifecycle-changing size/density settings before starting an observation whose
|
|
43
|
+
Activity must survive. Preserve real restart/process-death checks where assigned.
|
|
44
|
+
|
|
45
|
+
## Flow structure and selectors
|
|
46
|
+
|
|
47
|
+
Read [flow authoring](authoring.md) for a complete top-level/subflow example and the
|
|
48
|
+
required-input/text-entry checks before writing a new journey.
|
|
49
|
+
|
|
50
|
+
Keep one top-level business case with subflows for reusable Login, context selection or
|
|
51
|
+
editor actions. A subflow call is not another CLI process. Host code should not decide every
|
|
52
|
+
tap or reacquire UI automation between subflows. Do not combine independent stateful cases
|
|
53
|
+
merely to reduce process count; each retains its starting state, result and failure record.
|
|
54
|
+
|
|
55
|
+
Prefer existing stable accessibility/resource IDs. Otherwise derive complete, unambiguous
|
|
56
|
+
labels from the real renderer and metadata, including localization and suffixes. Maestro
|
|
57
|
+
text selectors use regular expressions; escape literal dynamic values as appropriate.
|
|
58
|
+
Confirm destination-only state after navigation, and use bounded `scrollUntilVisible` for
|
|
59
|
+
offscreen controls. A fake exposing every control cannot prove scrolling works.
|
|
60
|
+
Bind a dynamic expected value to its intended field/row/container, using the verified
|
|
61
|
+
hierarchy when needed. A separate screen-wide text match may come from an unrelated row
|
|
62
|
+
and cannot establish that a filter, receipt or selected item has the expected value.
|
|
63
|
+
|
|
64
|
+
Verify parameter precedence against the pinned CLI and actual caller. Supply defaults only
|
|
65
|
+
when an input is undefined, preserving intentional empty/false/zero values. A YAML `env`
|
|
66
|
+
default can override a CLI value in some versions; a test of each side with handwritten
|
|
67
|
+
fixtures misses this. Exercise the real caller-to-flow parameter path.
|
|
68
|
+
|
|
69
|
+
## Execute through the existing owner
|
|
70
|
+
|
|
71
|
+
Use the assigned device explicitly, with the owner's endpoint and credentials. For a
|
|
72
|
+
standard supported route the command shape is:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
maestro --device "$DEVICE_ID" test --test-output-dir "$LOG_DIR/maestro" \
|
|
76
|
+
--format junit --output "$LOG_DIR/report.xml" ./maestro/flows/checkout.yaml
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Check flags and selectors with the repo-pinned CLI. A private Android ADB server may need
|
|
80
|
+
an existing adapter's explicit transport/credential configuration; `--device` alone does
|
|
81
|
+
not prove that the CLI uses that endpoint. Keep the qualified route rather than replacing
|
|
82
|
+
it with this example. If no qualified route exists or ADB works while Maestro cannot
|
|
83
|
+
authenticate, read [Android connection](android-connection.md) and inspect the pinned
|
|
84
|
+
implementation before changing settings. The runner bounds the invocation and cleanup, captures the original
|
|
85
|
+
status, and follows [evidence](evidence.md).
|
|
86
|
+
|
|
87
|
+
Reuse valid development resources between fixes. New case evidence gets a new directory;
|
|
88
|
+
this does not require a fresh emulator. Fresh-install acceptance still uses its required
|
|
89
|
+
disposable app/data state. Keep one owner through the diagnosis/fix/recheck cycle where
|
|
90
|
+
possible; a bounded attempt is not automatically the end of the assignment.
|
|
91
|
+
|
|
92
|
+
Maestro MCP inspection can help choose selectors and author a flow. The executed assertions
|
|
93
|
+
on the identified candidate, not exploration or syntax validation, establish the result.
|
|
94
|
+
|
|
95
|
+
## Sources
|
|
96
|
+
|
|
97
|
+
- [runFlow](https://docs.maestro.dev/reference/commands-available/runFlow)
|
|
98
|
+
- [launchApp](https://docs.maestro.dev/reference/commands-available/launchapp)
|
|
99
|
+
- [runScript](https://docs.maestro.dev/reference/commands-available/runScript)
|
|
100
|
+
- [HTTP requests](https://docs.maestro.dev/advanced/javascript/make-http-requests)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Replace a hybrid UI driver
|
|
2
|
+
|
|
3
|
+
Start only with task/repo authorization. Existing working journeys are not migration scope
|
|
4
|
+
merely because Maestro is installed. Preserve required CI and every original criterion.
|
|
5
|
+
|
|
6
|
+
1. **Map the real case.** Trace its local and CI commands to maintained callers, fixtures,
|
|
7
|
+
UI actions, observers and assertions. Include private/copied callers if they are actually
|
|
8
|
+
executed. Record which observations move to Maestro, stay in a non-UI helper or require
|
|
9
|
+
a named native exception. Reuse existing flows and applicable evidence.
|
|
10
|
+
2. **Prove one complete path.** Choose a representative case with meaningful UI work,
|
|
11
|
+
required backend assertions, failure capture and owned cleanup. Genuine Login
|
|
12
|
+
is required when it is part of that case's acceptance; otherwise retain the approved
|
|
13
|
+
authentication fixture. Use the current runner, not a new emulator manager. Test actual dispatch and parameter/fixture composition
|
|
14
|
+
on the host before spending a device run where those defects can be exposed there.
|
|
15
|
+
3. **Move sequencing.** Replace Python deciding each tap with a top-level Maestro journey
|
|
16
|
+
and subflows. Extract existing non-UI helpers from UI clients without duplicating financial
|
|
17
|
+
or authentication logic. Keep mid-flow assertions in their required order. Grouping is not
|
|
18
|
+
permission to move a safety check until after a financial action.
|
|
19
|
+
4. **Remove competition.** After equivalent proof exists, stop creating uiautomator2 in the
|
|
20
|
+
ordinary case's setup, parent/child, observation and failure paths. During transition,
|
|
21
|
+
handoffs retain verified ownership, expiry/currentness and cleanup checks. Keep specialist
|
|
22
|
+
drivers exclusive and explicit; retain only assertions not yet equivalently proven.
|
|
23
|
+
5. **Replace callers and retire code.** Put the working case in tracked source. Local and
|
|
24
|
+
CI use that same implementation with environment-specific configuration. Once it passes,
|
|
25
|
+
remove equivalent legacy UI helpers, private copied callers and silent driver fallbacks.
|
|
26
|
+
A public flow wired correctly does not establish that a private caller uses it.
|
|
27
|
+
6. **Verify the replacement.** Real run artifacts must show selected cases, Maestro commands,
|
|
28
|
+
unchanged domain/native assertions and cleanup on the final candidate. Keep old failures
|
|
29
|
+
and still-valid proof. A zero-Maestro run may prove business behavior, not the migration.
|
|
30
|
+
|
|
31
|
+
Finish the assigned scope, not every possible device campaign. Report unmigrated paths and
|
|
32
|
+
unrun acceptance explicitly. Script extraction, host tests and successful driver startup
|
|
33
|
+
are useful intermediate results, not whole-issue acceptance. Do not duplicate a working
|
|
34
|
+
journey permanently as reassurance, weaken an oracle, or add gates and broad framework
|
|
35
|
+
work outside the authorized migration.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
const WEEK_MS = 7 * DAY_MS;
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
function readJson(path, fallback = {}) {
|
|
13
|
+
try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return fallback; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function frontmatterValue(text, key) {
|
|
17
|
+
const frontmatter = /^---\n([\s\S]*?)\n---/.exec(text)?.[1] ?? '';
|
|
18
|
+
const match = new RegExp('^' + key + ':\\s*([^\\n]+)$', 'm').exec(frontmatter);
|
|
19
|
+
return match?.[1].trim().replace(/^['"]|['"]$/g, '') ?? null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function repositoryOf(source) {
|
|
23
|
+
const value = String(source ?? '').trim();
|
|
24
|
+
const match = /^(?:git@github\.com:|https:\/\/github\.com\/)?([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?$/.exec(value);
|
|
25
|
+
return match?.[1] ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stableVersion(value) {
|
|
29
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(String(value ?? '').trim());
|
|
30
|
+
return match ? match.slice(1).map(Number) : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function newer(left, right) {
|
|
34
|
+
for (let index = 0; index < 3; index += 1) {
|
|
35
|
+
if (left[index] !== right[index]) return left[index] > right[index];
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeCache(path, value) {
|
|
41
|
+
try {
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
writeFileSync(path, JSON.stringify(value));
|
|
44
|
+
} catch { /* A read-only cache must not block skill use. */ }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function main(now = Date.now()) {
|
|
48
|
+
if (process.env.ONEABOVEIO_NO_UPDATE_CHECK) return;
|
|
49
|
+
const bundle = readJson(join(resolve(here, '../../..'), 'package.json'));
|
|
50
|
+
if (bundle.name === '@1aboveio/skills' && bundle.nativeCodex?.schemaVersion === 1) return;
|
|
51
|
+
const skill = readFileSync(join(here, 'SKILL.md'), 'utf8');
|
|
52
|
+
const name = frontmatterValue(skill, 'name');
|
|
53
|
+
const localVersion = stableVersion(frontmatterValue(skill, 'release-version'));
|
|
54
|
+
if (!name || !localVersion) return;
|
|
55
|
+
|
|
56
|
+
const lock = readJson(join(process.env.HOME || homedir(), '.agents', '.skill-lock.json'));
|
|
57
|
+
const entry = lock.skills?.[name];
|
|
58
|
+
const repository = repositoryOf(entry?.sourceUrl || entry?.source);
|
|
59
|
+
if (!repository) return;
|
|
60
|
+
|
|
61
|
+
const cacheRoot = process.env.XDG_CACHE_HOME || join(process.env.HOME || homedir(), '.cache');
|
|
62
|
+
const cachePath = join(cacheRoot, '1aboveio-skills', 'release-check.json');
|
|
63
|
+
const all = readJson(cachePath);
|
|
64
|
+
const cache = all[repository] && typeof all[repository] === 'object' ? all[repository] : {};
|
|
65
|
+
|
|
66
|
+
if (!cache.lastCheck || now - cache.lastCheck >= DAY_MS) {
|
|
67
|
+
const result = spawnSync('gh', ['api', 'repos/' + repository + '/releases/latest', '--jq', '.tag_name'], {
|
|
68
|
+
encoding: 'utf8',
|
|
69
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
70
|
+
timeout: 1500,
|
|
71
|
+
});
|
|
72
|
+
cache.lastCheck = now;
|
|
73
|
+
if (result.status === 0 && stableVersion(result.stdout)) cache.latestTag = result.stdout.trim();
|
|
74
|
+
else delete cache.latestTag;
|
|
75
|
+
all[repository] = cache;
|
|
76
|
+
writeCache(cachePath, all);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const latestVersion = stableVersion(cache.latestTag);
|
|
80
|
+
if (!latestVersion || !newer(latestVersion, localVersion)) return;
|
|
81
|
+
if (cache.notifiedTag === cache.latestTag && cache.notifiedAt && now - cache.notifiedAt < WEEK_MS) return;
|
|
82
|
+
|
|
83
|
+
cache.notifiedTag = cache.latestTag;
|
|
84
|
+
cache.notifiedAt = now;
|
|
85
|
+
all[repository] = cache;
|
|
86
|
+
writeCache(cachePath, all);
|
|
87
|
+
const command = `npx skills@1.5.23 add '${repository}#${cache.latestTag}' --global --agent universal claude-code --skill ${name} --yes`;
|
|
88
|
+
process.stdout.write(
|
|
89
|
+
`UPDATE_AVAILABLE: A newer 1AboveIO skills release (${cache.latestTag}) is available; this skill is `
|
|
90
|
+
+ `v${localVersion.join('.')}. Ask once whether to refresh ${name}. On approval run \`${command}\`. `
|
|
91
|
+
+ 'The refreshed instructions apply next session. Either way, continue the current task and do not ask again this session.\n',
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try { main(); } catch { /* Update checks are advisory and always fail silent. */ }
|