@ssheleg/xr-dev 0.3.0
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/CHANGELOG.md +143 -0
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/SECURITY.md +39 -0
- package/bin/xr-dev.js +186 -0
- package/package.json +56 -0
- package/plugins/xr-dev/.claude-plugin/plugin.json +37 -0
- package/plugins/xr-dev/skills/quest-lifecycle/SKILL.md +98 -0
- package/plugins/xr-dev/skills/quest-lifecycle/references/engine-paths.md +38 -0
- package/plugins/xr-dev/skills/quest-lifecycle/references/immersive-design.md +45 -0
- package/plugins/xr-dev/skills/quest-lifecycle/references/stage-map.md +60 -0
- package/plugins/xr-dev/skills/quest-native/SKILL.md +191 -0
- package/plugins/xr-dev/skills/quest-native/references/doc-map.md +90 -0
- package/plugins/xr-dev/skills/quest-native/references/frame-loop.md +92 -0
- package/plugins/xr-dev/skills/quest-native/references/manifest-and-gradle.md +111 -0
- package/plugins/xr-dev/skills/quest-native/references/mixed-reality.md +46 -0
- package/plugins/xr-dev/skills/quest-native/references/project-playbook.md +56 -0
- package/plugins/xr-dev/skills/quest-perf/SKILL.md +128 -0
- package/plugins/xr-dev/skills/quest-perf/references/capture-playbook.md +102 -0
- package/plugins/xr-dev/skills/quest-perf/references/mobile-rendering.md +56 -0
- package/plugins/xr-dev/skills/quest-perf/references/rendering-playbook.md +51 -0
- package/plugins/xr-dev/skills/quest-spatial/SKILL.md +170 -0
- package/plugins/xr-dev/skills/quest-spatial/references/budgets-and-traps.md +73 -0
- package/plugins/xr-dev/skills/quest-spatial/references/build-and-audit.md +36 -0
- package/plugins/xr-dev/skills/quest-spatial/references/docs-map.md +91 -0
- package/plugins/xr-dev/skills/quest-spatial/references/hybrid-activities.md +31 -0
- package/plugins/xr-dev/skills/quest-spatial/references/samples-map.md +60 -0
- package/plugins/xr-dev/skills/quest-store/SKILL.md +145 -0
- package/plugins/xr-dev/skills/quest-store/references/launch-and-growth.md +96 -0
- package/plugins/xr-dev/skills/quest-store/references/production-readiness.md +56 -0
- package/plugins/xr-dev/skills/quest-store/references/store-asset-production.md +48 -0
- package/plugins/xr-dev/skills/quest-store/references/vrc-checklist.md +193 -0
- package/plugins/xr-dev/skills/quest-tooling/SKILL.md +158 -0
- package/plugins/xr-dev/skills/quest-tooling/references/research-navigation.md +47 -0
- package/plugins/xr-dev/skills/quest-tooling/references/source-research.md +58 -0
- package/plugins/xr-dev/skills/quest-tooling/references/tool-matrix.md +54 -0
- package/plugins/xr-dev/skills/quest-webxr/SKILL.md +141 -0
- package/plugins/xr-dev/skills/quest-webxr/references/pwa-packaging.md +85 -0
- package/plugins/xr-dev/skills/quest-webxr/references/runtime-delivery.md +40 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## 0.3.0 — 2026-09-21
|
|
8
|
+
|
|
9
|
+
- Add quest-lifecycle: a portable product-stage map with proactive next actions, engine selection, immersive-design acceptance and evidence gates.
|
|
10
|
+
- Extend all six existing owners with platform/research/rendering coverage, cooperative hybrid activities, source-conflict handling, commercial readiness, capture and Store asset production, and post-launch analytics.
|
|
11
|
+
- Correct stale descriptions, hard Spatial limits, CPU/GPU timing claims, release manifest guidance and duplicate MCP setup defaults. Keep current SDK/device/Dashboard checks explicit.
|
|
12
|
+
- Add lifecycle evaluation cases and record isolated baseline/candidate planning probes separately from routing, build and headset verification.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## [0.2.1] — 2026-09-20
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- `quest-spatial`'s description trimmed to 882 characters. It shipped at 955 and
|
|
20
|
+
moved the family's reserve ratchet — the 60 characters below the 970 cap exist
|
|
21
|
+
for the "NOT for" clause the next neighbouring skill will force, and this pack
|
|
22
|
+
now sits beside Meta's `hz-spatial-sdk`, which is exactly such a neighbour.
|
|
23
|
+
- The card the umbrella generates for this member, regenerated from the new
|
|
24
|
+
manifest.
|
|
25
|
+
|
|
26
|
+
## [0.2.0] — 2026-09-20
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **`quest-spatial`** — the Kotlin lane. Meta Spatial SDK is how an Android team
|
|
31
|
+
reaches Horizon OS, and the pack had no answer for it: which lane it is right
|
|
32
|
+
for, the toolchain it demands (Horizon OS v69+, AGP 8.11.1 under the Gradle
|
|
33
|
+
9.4.1 wrapper, Kotlin 2.1.0, JDK 17, Spatial Editor for all but two samples),
|
|
34
|
+
and the budgets that decide a design rather than tune it — 2,000 entity
|
|
35
|
+
operations per tick, 500 physics objects, ~1,000 scene-graph entities, and a
|
|
36
|
+
panel table whose sharpest rows are three video panels and two activity-based
|
|
37
|
+
ones.
|
|
38
|
+
- Its three references: `samples-map.md` (15 official samples mapped to the
|
|
39
|
+
question each answers, plus the `hzdb` MCP server a clone brings with it),
|
|
40
|
+
`docs-map.md` (197 pages, by prefix and by topic), `budgets-and-traps.md`
|
|
41
|
+
(the tables, and the five issues Meta lists against itself — including debug
|
|
42
|
+
builds being slow enough to invalidate a measurement, and `finish()` on an
|
|
43
|
+
immersive activity crashing inside `libMetaSpatialSDK.so`).
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- The pack now routes six lanes, not five; Meta's `hz-spatial-sdk` still owns
|
|
48
|
+
the implementation of a Spatial SDK app, and `quest-spatial` says when to be
|
|
49
|
+
there at all and where to read.
|
|
50
|
+
|
|
51
|
+
## [0.1.5] — 2026-09-20
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- `test/social_preview.py`, the last CI step this repository inherited a job for
|
|
56
|
+
and never inherited the script for. Every step of both jobs has now been run
|
|
57
|
+
locally against this tree before the push, which is how the previous two
|
|
58
|
+
failures should have been caught.
|
|
59
|
+
|
|
60
|
+
## [0.1.4] — 2026-09-20
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- CI was red on the first push and the repository did not know it: the online
|
|
65
|
+
schema check imports `SCHEMA_FOR` from the validator, which exported only an
|
|
66
|
+
internal `SCHEMAS` map, and the audit job ran a `test/evals_validate.py` that
|
|
67
|
+
had never been copied in. Both closed — the pinned addresses now live in one
|
|
68
|
+
map that both halves read, and the eval data validates with its own planted
|
|
69
|
+
defect.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- `test/evals/README.md` and `RESULTS.md`. Results says, in as many words, that
|
|
74
|
+
no model run has happened yet: an unrun eval reported as a pass is worse than
|
|
75
|
+
no eval.
|
|
76
|
+
- The trigger split now carries **both classes in each half** — a train split of
|
|
77
|
+
positives only cannot show over-firing, which is the failure mode that matters
|
|
78
|
+
beside Meta's 29 neighbouring skills.
|
|
79
|
+
|
|
80
|
+
## [0.1.3] — 2026-09-20
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
|
|
84
|
+
- `docs/assets/social-preview.png` — the card the family umbrella generates for
|
|
85
|
+
this member from `skills.json`, byte-compared in its suite so the pixels and
|
|
86
|
+
the manifest cannot drift apart.
|
|
87
|
+
|
|
88
|
+
## [0.1.2] — 2026-09-20
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
|
|
92
|
+
- `docs/AGENT_SYNC.md`, generated from the live coordination config, and a
|
|
93
|
+
repository `CLAUDE.md` that links it and states the invariants each gate
|
|
94
|
+
checks. A guarded-file list no agent instruction points at is a list nobody
|
|
95
|
+
reads.
|
|
96
|
+
|
|
97
|
+
## [0.1.1] — 2026-09-20
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
|
|
101
|
+
- `quest-tooling`'s description trimmed back inside the 60-character reserve the
|
|
102
|
+
family keeps below the 970 cap — the room a near-miss neighbour's "NOT for"
|
|
103
|
+
clause will need.
|
|
104
|
+
- README says where its verification commands run: a clone, not the published
|
|
105
|
+
package, which ships the skills without `test/`.
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- `.claude/agent-sync.json`, so a second agent in this repository claims the
|
|
110
|
+
guarded files instead of overwriting them.
|
|
111
|
+
|
|
112
|
+
## [0.1.0] — 2026-09-20
|
|
113
|
+
|
|
114
|
+
First release. Five skills, seven references.
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- `quest-native` — the native OpenXR lane: development versus release manifest,
|
|
119
|
+
SDK levels including the 1 March 2026 target-34 rule, the Khronos loader and
|
|
120
|
+
its two initialisation extensions, the frame loop and the focus rules review
|
|
121
|
+
enforces, capability checks over device models.
|
|
122
|
+
- `quest-perf` — per-refresh-rate frame budgets, the `GPU%` reading that falls
|
|
123
|
+
exactly when an app is GPU-bound and the 50% threshold behind it, the four
|
|
124
|
+
recovery levers in order, and a capture playbook for Perfetto, simpleperf,
|
|
125
|
+
RenderDoc and OVR Metrics through `metavr`.
|
|
126
|
+
- `quest-tooling` — the metavr CLI surface, its MCP server and how one server
|
|
127
|
+
ends up registered twice, `metavr init`'s all-agents default measured at 4129
|
|
128
|
+
files inside a repository, the managed tool matrix, and the hygiene rules.
|
|
129
|
+
- `quest-store` — the four release channels and what review actually checks,
|
|
130
|
+
the VRC list generated from Meta's page (80 requirements, 14 groups), the
|
|
131
|
+
release manifest delta, uploading with `metavr store dist`.
|
|
132
|
+
- `quest-webxr` — requesting an immersive session on load, IWSDK versus three.js
|
|
133
|
+
versus A-Frame, Bubblewrap packaging with the Digital Asset Links step, and
|
|
134
|
+
the performance honesty a browser build needs.
|
|
135
|
+
|
|
136
|
+
[0.2.1]: https://github.com/ssheleg/xr-dev/releases/tag/v0.2.1
|
|
137
|
+
[0.2.0]: https://github.com/ssheleg/xr-dev/releases/tag/v0.2.0
|
|
138
|
+
[0.1.5]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.5
|
|
139
|
+
[0.1.4]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.4
|
|
140
|
+
[0.1.3]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.3
|
|
141
|
+
[0.1.2]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.2
|
|
142
|
+
[0.1.1]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.1
|
|
143
|
+
[0.1.0]: https://github.com/ssheleg/xr-dev/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ssheleg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# xr-dev — Meta Quest product lifecycle, seven skills
|
|
2
|
+
|
|
3
|
+
Agent skills for taking Quest games and apps from platform choice to release and
|
|
4
|
+
operation. Each stage names its owner, evidence and next action, so the agent can
|
|
5
|
+
surface account, design, performance and launch work before it becomes a blocker.
|
|
6
|
+
|
|
7
|
+
Part of the [ssheleg harness](https://skills.sshlg.me/harness/). This pack works
|
|
8
|
+
independently and composes with the family delivery, UX and design skills.
|
|
9
|
+
|
|
10
|
+
## The seven skills
|
|
11
|
+
|
|
12
|
+
| Skill | Answers |
|
|
13
|
+
|---|---|
|
|
14
|
+
| [quest-lifecycle](plugins/xr-dev/skills/quest-lifecycle/SKILL.md) | What stage is this product at, what is missing, and what should happen next? |
|
|
15
|
+
| [quest-native](plugins/xr-dev/skills/quest-native/SKILL.md) | How is the C/C++ OpenXR project built and audited, including MR capability and lifecycle boundaries? |
|
|
16
|
+
| [quest-spatial](plugins/xr-dev/skills/quest-spatial/SKILL.md) | How do Kotlin Spatial SDK, ECS, panels and hybrid activities fit this app? |
|
|
17
|
+
| [quest-perf](plugins/xr-dev/skills/quest-perf/SKILL.md) | What limits frames, memory or sustained performance, and which rendering experiment tests it? |
|
|
18
|
+
| [quest-tooling](plugins/xr-dev/skills/quest-tooling/SKILL.md) | Which tools and sources are available, current and appropriate for this host? |
|
|
19
|
+
| [quest-store](plugins/xr-dev/skills/quest-store/SKILL.md) | What remains for testing, submission, monetization, Store assets, launch and operation? |
|
|
20
|
+
| [quest-webxr](plugins/xr-dev/skills/quest-webxr/SKILL.md) | How does the browser/PWA experience handle sessions, optional features and delivery? |
|
|
21
|
+
|
|
22
|
+
References live inside their owning skill and load on demand. The skill bodies
|
|
23
|
+
link every shipped reference with a use condition. For the complete change and
|
|
24
|
+
verification record, see [lifecycle release evidence](docs/evidence/verification/2026-09-21-lifecycle/README.md).
|
|
25
|
+
|
|
26
|
+
## Start with the product stage
|
|
27
|
+
|
|
28
|
+
For a new or inherited product, invoke `/quest-lifecycle` from a skills directory
|
|
29
|
+
or `/xr-dev:quest-lifecycle` from the plugin. It inspects the actual engine and app
|
|
30
|
+
category, preserves existing project records, and proposes the earliest unresolved
|
|
31
|
+
step. For a single bug, capture or upload, use that specialist directly.
|
|
32
|
+
|
|
33
|
+
The workflow covers platform selection, immersive UX, a device-tested vertical
|
|
34
|
+
slice, content/performance budgets, commercial prerequisites, real gameplay
|
|
35
|
+
capture, Store review and post-launch checks. It distinguishes 2D, immersive,
|
|
36
|
+
hybrid, WebXR, packaged PWA and PC VR requirements.
|
|
37
|
+
|
|
38
|
+
[Meta's own companion skills](https://github.com/meta-quest/agentic-tools) supply
|
|
39
|
+
engine and tool workflows when installed. The lifecycle engine map also points
|
|
40
|
+
to Godot's official XR/export/vendor-plugin documentation. This pack supplies
|
|
41
|
+
fallback procedures; it does not install an engine, promise every device feature,
|
|
42
|
+
or treat a generated image as gameplay evidence.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
Claude Code plugin:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
/plugin marketplace add ssheleg/xr-dev
|
|
50
|
+
/plugin install xr-dev@xr-dev
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Portable Agent Skills:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx skills add ssheleg/xr-dev
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The standalone npm installer also works when the same Claude plugin is absent:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx @ssheleg/xr-dev --help
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use one channel per agent. For a composing family installation, update through
|
|
66
|
+
`npx sshlg-skills update`. Restart the agent after updates so skills reload.
|
|
67
|
+
|
|
68
|
+
## Tools and source freshness
|
|
69
|
+
|
|
70
|
+
Inspect existing CLI/MCP/plugin registration before setup. `quest-tooling` maps
|
|
71
|
+
Meta VR CLI, device/debug/profiling tools and Markdown documentation indexes,
|
|
72
|
+
with the operator's gateway policy taking precedence over generic setup examples.
|
|
73
|
+
No MCP uses CLI; no CLI uses official docs; absent engine/device/account access
|
|
74
|
+
leaves dependent checks explicitly unverified. An HTTP 200 may be an unavailable
|
|
75
|
+
page, and a source example is not a universal SDK or Store requirement.
|
|
76
|
+
|
|
77
|
+
## Verifying a change
|
|
78
|
+
|
|
79
|
+
<!-- commands-run-in: a clone -->
|
|
80
|
+
The repository carries tests; the npm payload carries the skills and references.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm test
|
|
84
|
+
npm run test:negatives
|
|
85
|
+
node test/installer_test.js
|
|
86
|
+
python3 test/evals_validate.py
|
|
87
|
+
claude plugin validate . --strict
|
|
88
|
+
claude plugin validate plugins/xr-dev --strict
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
[Test evaluation documentation](test/evals/README.md) separates scenario definitions,
|
|
92
|
+
isolated model planning probes, runtime routing and device/product evidence.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT for this pack. Primary documentation is linked and dated; third-party tools,
|
|
97
|
+
assets and models retain their own licenses. Recheck volatile requirements before
|
|
98
|
+
implementation and submission.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Reporting
|
|
4
|
+
|
|
5
|
+
Report anything sensitive privately to **security@sshlg.me**, not in a public
|
|
6
|
+
issue. Include the file, what happens, and what you expected.
|
|
7
|
+
|
|
8
|
+
## What executes when you install this
|
|
9
|
+
|
|
10
|
+
| Path | Runs when | What it touches |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `bin/xr-dev.js` | `npx @ssheleg/xr-dev` | copies the skill directories into `~/.claude/skills/<name>`; refuses (exit 3) when the plugin is already installed; writes nothing else |
|
|
13
|
+
| `install.sh` | run by hand | the same copy, in POSIX shell |
|
|
14
|
+
| `test/*.py`, `test/*.js` | only in CI or by hand | read the repository; `check_schemas.py` makes outbound HTTPS requests to schemastore.org |
|
|
15
|
+
|
|
16
|
+
The skills themselves ship **no hooks, no MCP server and no background
|
|
17
|
+
process**. Nothing here runs at session start.
|
|
18
|
+
|
|
19
|
+
## What the skills tell an agent to run
|
|
20
|
+
|
|
21
|
+
The bodies name `metavr`, `adb`, `keytool`, `bubblewrap`, `npm` and `git`
|
|
22
|
+
commands. Two classes deserve attention before an agent runs them unattended:
|
|
23
|
+
|
|
24
|
+
- **Device-mutating**: `metavr app install|uninstall`, `metavr files rm`,
|
|
25
|
+
`metavr device configure-testing`, `metavr shell`. They act on a connected
|
|
26
|
+
headset.
|
|
27
|
+
- **Outward-facing**: `metavr store dist upload` and `copy-build` put a build in
|
|
28
|
+
front of testers or the Store. Uploads default to a **draft**; `--publish` is
|
|
29
|
+
the flag that makes it visible. Treat both as actions needing a human's say-so.
|
|
30
|
+
|
|
31
|
+
No skill asks an agent to bypass a consent prompt, and no skill fetches its own
|
|
32
|
+
instructions from a URL at runtime.
|
|
33
|
+
|
|
34
|
+
## Credentials
|
|
35
|
+
|
|
36
|
+
Nothing in this repository stores or reads a credential. `metavr auth login`
|
|
37
|
+
keeps its token in the OS keychain; `METAVR_TOKEN` exists for CI. Signing
|
|
38
|
+
keystores belong outside the repository, and every skill that mentions one says
|
|
39
|
+
so.
|
package/bin/xr-dev.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* xr-dev installer CLI.
|
|
4
|
+
*
|
|
5
|
+
* Installs every xr-dev skill into ~/.claude/skills/<name>
|
|
6
|
+
* (same layout as install.sh). Idempotent: an existing install is skipped unless
|
|
7
|
+
* --force. Zero dependencies.
|
|
8
|
+
*
|
|
9
|
+
* For other agents (Cursor, Codex, 70+) use: npx skills add ssheleg/xr-dev
|
|
10
|
+
*/
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
18
|
+
const REPO = 'ssheleg/xr-dev';
|
|
19
|
+
const PLUGIN = 'xr-dev';
|
|
20
|
+
|
|
21
|
+
// Exit codes are the contract: 0 installed or skipped, 1 corrupted package,
|
|
22
|
+
// 2 usage error, 3 refused — the plugin channel owns this agent (--force overrides).
|
|
23
|
+
const EXIT_PLUGIN_PRESENT = 3;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The plugin spec (`<name>@<marketplace>`) installed for `name` in this home,
|
|
27
|
+
* or null.
|
|
28
|
+
*
|
|
29
|
+
* `installed_plugins.json` is the record of what is actually installed. The
|
|
30
|
+
* `plugins/marketplaces/<name>` directory — the only signal older family
|
|
31
|
+
* installers read — under-reports: a marketplace added from a local `directory`
|
|
32
|
+
* source has no dir there at all, and plugin names differ from marketplace
|
|
33
|
+
* names, so a check keyed on it stays green while the shadow lands. Absence
|
|
34
|
+
* and corruption both read as "no plugin": the fresh HOME is the common case,
|
|
35
|
+
* and an installer that crashes on a parse error refuses the machines that
|
|
36
|
+
* need it most.
|
|
37
|
+
*/
|
|
38
|
+
function installedPluginSpec(home, name) {
|
|
39
|
+
try {
|
|
40
|
+
const raw = fs.readFileSync(
|
|
41
|
+
path.join(home, '.claude', 'plugins', 'installed_plugins.json'), 'utf8');
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
const plugins =
|
|
44
|
+
parsed && typeof parsed === 'object' &&
|
|
45
|
+
parsed.plugins && typeof parsed.plugins === 'object'
|
|
46
|
+
? parsed.plugins
|
|
47
|
+
: parsed;
|
|
48
|
+
if (!plugins || typeof plugins !== 'object') return null;
|
|
49
|
+
for (const spec of Object.keys(plugins)) {
|
|
50
|
+
if (spec === name) return `${name}@${name}`;
|
|
51
|
+
if (spec.startsWith(name + '@')) return spec;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// missing or corrupt = no plugin — fail open on absence, never crash
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function usage() {
|
|
60
|
+
console.log(`xr-dev installer
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
npx @ssheleg/xr-dev [--force] install all xr-dev skills
|
|
64
|
+
into ~/.claude (skip existing unless --force)
|
|
65
|
+
npx @ssheleg/xr-dev --help
|
|
66
|
+
|
|
67
|
+
Exit codes:
|
|
68
|
+
0 installed or skipped 2 usage error
|
|
69
|
+
1 corrupted package 3 refused: the xr-dev PLUGIN is installed in
|
|
70
|
+
this home — plain copies would shadow it (pass
|
|
71
|
+
--force to write them anyway)
|
|
72
|
+
|
|
73
|
+
Other install paths:
|
|
74
|
+
Claude Code plugin: /plugin marketplace add ${REPO}
|
|
75
|
+
/plugin install xr-dev@xr-dev
|
|
76
|
+
Any agent (70+): npx skills add ${REPO}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function copyDir(src, dest) {
|
|
80
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
81
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
82
|
+
const s = path.join(src, entry.name);
|
|
83
|
+
const d = path.join(dest, entry.name);
|
|
84
|
+
if (entry.isDirectory()) copyDir(s, d);
|
|
85
|
+
else fs.copyFileSync(s, d);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function installOne(label, src, dest, isDir, force) {
|
|
90
|
+
if (fs.existsSync(dest) && !force) {
|
|
91
|
+
console.log(`skip: ${label} already installed at ${dest} (rerun with --force to overwrite)`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
95
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
96
|
+
if (isDir) copyDir(src, dest);
|
|
97
|
+
else fs.copyFileSync(src, dest);
|
|
98
|
+
console.log(`Installed ${label} -> ${dest}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function main(argv) {
|
|
102
|
+
const args = argv.slice(2);
|
|
103
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
104
|
+
usage();
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
const force = args.includes('--force');
|
|
108
|
+
const unknown = args.filter((a) => a !== '--force');
|
|
109
|
+
if (unknown.length) {
|
|
110
|
+
console.error(`unknown argument(s): ${unknown.join(' ')}`);
|
|
111
|
+
usage();
|
|
112
|
+
return 2;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const skillsRoot = path.join(ROOT, 'plugins/xr-dev/skills');
|
|
116
|
+
if (!fs.existsSync(skillsRoot)) {
|
|
117
|
+
console.error(`error: skill sources missing at ${skillsRoot} — corrupted package?`);
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const names = fs
|
|
122
|
+
.readdirSync(skillsRoot, { withFileTypes: true })
|
|
123
|
+
.filter((e) => e.isDirectory())
|
|
124
|
+
.map((e) => e.name)
|
|
125
|
+
.sort();
|
|
126
|
+
if (!names.length) {
|
|
127
|
+
console.error(`error: no skills found under ${skillsRoot} — corrupted package?`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const home = os.homedir();
|
|
132
|
+
|
|
133
|
+
// One channel per agent. A plain ~/.claude/skills/<name> beside an installed
|
|
134
|
+
// plugin is two listings of the same skill, and the stale copy wins — the
|
|
135
|
+
// exact shadow the family canon forbids. Refuse rather than create it, and
|
|
136
|
+
// refuse LOUDLY: reproduced live 2026-08-29 ON THIS PACKAGE — a bare
|
|
137
|
+
// `npx @ssheleg/xr-dev` shipped all three skills as plain copies into
|
|
138
|
+
// the operator's ~/.claude/skills/ while the xr-dev plugin was
|
|
139
|
+
// enabled, because until v0.1.9 nothing here looked. A refusal that exits 0
|
|
140
|
+
// reads as success to every script above it, so this one exits 3.
|
|
141
|
+
const spec = installedPluginSpec(home, PLUGIN);
|
|
142
|
+
const marketplace = path.join(home, '.claude', 'plugins', 'marketplaces', PLUGIN);
|
|
143
|
+
const viaMarketplaceDir = !spec && fs.existsSync(marketplace);
|
|
144
|
+
if ((spec || viaMarketplaceDir) && !force) {
|
|
145
|
+
const found = spec
|
|
146
|
+
? `installed as the Claude Code plugin ${spec}\n` +
|
|
147
|
+
' (declared in ~/.claude/plugins/installed_plugins.json)'
|
|
148
|
+
: `registered as a Claude Code marketplace\n (${marketplace})`;
|
|
149
|
+
console.error(
|
|
150
|
+
`refused: xr-dev is already ${found}.\n` +
|
|
151
|
+
" Plain copies in ~/.claude/skills/ would shadow the plugin's skills\n" +
|
|
152
|
+
' and serve this frozen version forever. Update the plugin channel\n' +
|
|
153
|
+
' instead:\n' +
|
|
154
|
+
' claude plugin marketplace update xr-dev\n' +
|
|
155
|
+
` claude plugin update ${spec || 'xr-dev@xr-dev'}\n` +
|
|
156
|
+
' Family launcher (updates every member, prunes shadow copies):\n' +
|
|
157
|
+
' npx --yes sshlg-skills@latest update\n' +
|
|
158
|
+
' Pass --force to write the plain copies anyway — a deliberate choice\n' +
|
|
159
|
+
' to run two channels, where the stale one wins.'
|
|
160
|
+
);
|
|
161
|
+
return EXIT_PLUGIN_PRESENT;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const name of names) {
|
|
165
|
+
installOne(
|
|
166
|
+
`${name} skill`,
|
|
167
|
+
path.join(skillsRoot, name),
|
|
168
|
+
path.join(home, '.claude', 'skills', name),
|
|
169
|
+
true,
|
|
170
|
+
force
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// The last line says how the next version arrives — "Installed" is not a
|
|
175
|
+
// complete sentence. Auto-update is off on purpose: this member composes
|
|
176
|
+
// with its family, and per-marketplace autoUpdate moves each member on its
|
|
177
|
+
// own clock, into combinations nobody tested together.
|
|
178
|
+
console.log(
|
|
179
|
+
'\nUpdates: rerun `npx @ssheleg/xr-dev@latest --force`, or refresh the\n' +
|
|
180
|
+
'whole family with `npx --yes sshlg-skills@latest update` (every channel,\n' +
|
|
181
|
+
'and it prunes plain copies that would shadow a plugin).'
|
|
182
|
+
);
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
process.exit(main(process.argv));
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ssheleg/xr-dev",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Seven skills for the Meta Quest product lifecycle: platform and engine discovery, immersive design, native OpenXR, Kotlin Spatial and hybrid apps, WebXR/PWA, rendering experiments, tool and source discovery, Store submission, monetization, launch assets and post-launch operation. Agents receive stage-specific next actions, evidence gates and fallbacks when tools or devices are absent. Engine specialists remain separate; live requirements are verified against current Meta documentation.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"xr-dev": "bin/xr-dev.js"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"plugins",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"SECURITY.md"
|
|
18
|
+
],
|
|
19
|
+
"repository": "github:ssheleg/xr-dev",
|
|
20
|
+
"homepage": "https://skills.sshlg.me/skills/xr-dev/",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": {
|
|
23
|
+
"name": "ssheleg",
|
|
24
|
+
"url": "https://x.com/sshlg93"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=16"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"agent-skills",
|
|
31
|
+
"android-ndk",
|
|
32
|
+
"ar",
|
|
33
|
+
"bubblewrap",
|
|
34
|
+
"claude-code",
|
|
35
|
+
"cursor",
|
|
36
|
+
"horizon-os",
|
|
37
|
+
"kotlin",
|
|
38
|
+
"meta-quest",
|
|
39
|
+
"metavr",
|
|
40
|
+
"mruk",
|
|
41
|
+
"openxr",
|
|
42
|
+
"perfetto",
|
|
43
|
+
"pwa",
|
|
44
|
+
"quest",
|
|
45
|
+
"renderdoc",
|
|
46
|
+
"spatial-sdk",
|
|
47
|
+
"vr",
|
|
48
|
+
"vulkan",
|
|
49
|
+
"webxr",
|
|
50
|
+
"xr"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "python3 test/validate.py",
|
|
54
|
+
"test:negatives": "python3 test/validate.py --self-test"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "xr-dev",
|
|
4
|
+
"displayName": "XR Dev",
|
|
5
|
+
"description": "Seven skills for the Meta Quest product lifecycle: platform and engine discovery, immersive design, native OpenXR, Kotlin Spatial and hybrid apps, WebXR/PWA, rendering experiments, tool and source discovery, Store submission, monetization, launch assets and post-launch operation. Agents receive stage-specific next actions, evidence gates and fallbacks when tools or devices are absent. Engine specialists remain separate; live requirements are verified against current Meta documentation.",
|
|
6
|
+
"version": "0.3.0",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "ssheleg",
|
|
9
|
+
"url": "https://x.com/sshlg93"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://skills.sshlg.me/skills/xr-dev/",
|
|
12
|
+
"repository": "https://github.com/ssheleg/xr-dev",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"agent-skills",
|
|
16
|
+
"android-ndk",
|
|
17
|
+
"ar",
|
|
18
|
+
"bubblewrap",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"cursor",
|
|
21
|
+
"horizon-os",
|
|
22
|
+
"kotlin",
|
|
23
|
+
"meta-quest",
|
|
24
|
+
"metavr",
|
|
25
|
+
"mruk",
|
|
26
|
+
"openxr",
|
|
27
|
+
"perfetto",
|
|
28
|
+
"pwa",
|
|
29
|
+
"quest",
|
|
30
|
+
"renderdoc",
|
|
31
|
+
"spatial-sdk",
|
|
32
|
+
"vr",
|
|
33
|
+
"vulkan",
|
|
34
|
+
"webxr",
|
|
35
|
+
"xr"
|
|
36
|
+
]
|
|
37
|
+
}
|