mudra-skills 1.0.6 → 1.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mudra-skills
2
2
 
3
- Claude Code skills for building [Mudra Band](https://wearabledevices.co.il) apps — generate 2D flat/screen apps, 3D/XR experiences, or let the router auto-classify your prompt.
3
+ Skills for building [Mudra Band](https://wearabledevices.co.il) apps — works with Claude, Codex, and Antigravity. Generate 2D flat/screen apps, 3D/XR experiences, or let the router auto-classify your prompt.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,27 +8,61 @@ Claude Code skills for building [Mudra Band](https://wearabledevices.co.il) apps
8
8
  npx mudra-skills
9
9
  ```
10
10
 
11
- Then restart Claude Code or run `/reload-plugins`.
11
+ You'll be prompted to choose your AI assistant:
12
12
 
13
- ## What You Get
13
+ ```
14
+ Which AI assistant are you using?
15
+ (↑↓ to move, Enter to select)
14
16
 
15
- Three skills installed to `~/.claude/plugins/mudra/`:
17
+ Claude
18
+ Codex
19
+ Antigravity
20
+ ```
16
21
 
17
- | Skill | Invoke | Description |
22
+ Or skip the prompt with the `--llm` flag:
23
+
24
+ ```bash
25
+ npx mudra-skills --llm claude
26
+ npx mudra-skills --llm codex
27
+ npx mudra-skills --llm antigravity
28
+ ```
29
+
30
+ ### Where skills get installed
31
+
32
+ | AI | Install path | Invoke with |
18
33
  |---|---|---|
19
- | **mudra-master** | `/mudra:mudra-master` | Auto-router — classifies your prompt and hands off to 2D or 3D |
20
- | **mudra-preview** | `/mudra:mudra-preview` | Generates single-file HTML 2D apps |
21
- | **mudra-xr** | `/mudra:mudra-xr` | Generates single-file HTML 3D/XR apps using XR Blocks |
34
+ | **Claude** | `~/.claude/skills/` | `/mudra-master` |
35
+ | **Codex** | `~/.agents/skills/` | `$mudra-master` |
36
+ | **Antigravity** | `~/.gemini/antigravity/skills/` | `@mudra-master` |
37
+
38
+ ## What You Get
39
+
40
+ Three skills, installed as folders:
41
+
42
+ | Skill | Description |
43
+ |---|---|
44
+ | **mudra-master** | Auto-router — classifies your prompt and hands off to 2D or 3D |
45
+ | **mudra-preview** | Generates single-file HTML 2D apps |
46
+ | **mudra-xr** | Generates single-file HTML 3D/XR apps using XR Blocks |
22
47
 
23
48
  ## Usage
24
49
 
25
- Just describe what you want to build in Claude Code:
50
+ ### Claude
51
+ ```
52
+ /mudra-master build a gesture-controlled music player
53
+ ```
54
+
55
+ ### Codex
56
+ ```
57
+ $mudra-master build a gesture-controlled music player
58
+ ```
26
59
 
60
+ ### Antigravity
27
61
  ```
28
- /mudra:mudra-master build a gesture-controlled music player
62
+ @mudra-master build a gesture-controlled music player
29
63
  ```
30
64
 
31
- Or let the router figure it out describe an app idea and it will classify it as 2D or 3D automatically.
65
+ Or just describe what you wantthe router will classify it as 2D or 3D automatically.
32
66
 
33
67
  ## What Gets Generated
34
68
 
@@ -54,8 +88,8 @@ Every generated app includes:
54
88
 
55
89
  ## Requirements
56
90
 
57
- - [Claude Code](https://claude.ai/code) CLI
58
91
  - Node.js 18+
92
+ - One of: [Claude Code](https://claude.ai/code), [Codex CLI](https://github.com/openai/codex), or Antigravity
59
93
 
60
94
  ## License
61
95
 
package/bin/cli.js CHANGED
@@ -7,12 +7,24 @@ import { execSync } from 'child_process';
7
7
 
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const homeDir = process.env.HOME || process.env.USERPROFILE;
10
- const SKILLS_DIR = path.join(homeDir, '.claude', 'skills');
10
+ const CLAUDE_SKILLS_DIR = path.join(homeDir, '.claude', 'skills');
11
+ const CODEX_SKILLS_DIR = path.join(homeDir, '.agents', 'skills');
12
+ const ANTIGRAVITY_SKILLS_DIR = path.join(homeDir, '.gemini', 'antigravity', 'skills');
11
13
 
12
- const [,, command, ...args] = process.argv;
14
+ // Parse --llm flag before the command switch
15
+ let llmFlag = null;
16
+ const filteredArgv = [];
17
+ for (let i = 2; i < process.argv.length; i++) {
18
+ if (process.argv[i] === '--llm' && process.argv[i + 1]) {
19
+ llmFlag = process.argv[++i].toLowerCase();
20
+ } else {
21
+ filteredArgv.push(process.argv[i]);
22
+ }
23
+ }
24
+ const [command, ...args] = filteredArgv;
13
25
 
14
26
  switch (command ?? 'add') {
15
- case 'add': await add(args[0]); break;
27
+ case 'add': await add(args[0], llmFlag); break;
16
28
  case 'remove':
17
29
  case 'rm': remove(args[0]); break;
18
30
  case 'list':
@@ -20,7 +32,7 @@ switch (command ?? 'add') {
20
32
  default: printHelp(); break;
21
33
  }
22
34
 
23
- async function add(source) {
35
+ async function add(source, llm) {
24
36
  let skillsSource;
25
37
  let tmpDir;
26
38
 
@@ -46,14 +58,80 @@ async function add(source) {
46
58
  }
47
59
  }
48
60
 
61
+ if (!llm) {
62
+ llm = await promptLLM();
63
+ }
64
+
49
65
  try {
50
- copySkills(skillsSource);
66
+ if (llm === 'claude') {
67
+ installClaude(skillsSource);
68
+ } else if (llm === 'codex') {
69
+ installCodex(skillsSource);
70
+ } else if (llm === 'antigravity') {
71
+ installAntigravity(skillsSource);
72
+ } else {
73
+ console.error(`Unknown LLM: "${llm}". Valid options: claude, codex, antigravity`);
74
+ process.exit(1);
75
+ }
51
76
  } finally {
52
77
  if (tmpDir) fs.rmSync(tmpDir, { recursive: true, force: true });
53
78
  }
54
79
  }
55
80
 
56
- function copySkills(skillsSource) {
81
+ function promptLLM() {
82
+ return new Promise((resolve) => {
83
+ const options = [
84
+ { label: 'Claude', value: 'claude' },
85
+ { label: 'Codex', value: 'codex' },
86
+ { label: 'Antigravity', value: 'antigravity' },
87
+ ];
88
+ let selected = 0;
89
+ let firstRender = true;
90
+
91
+ const render = () => {
92
+ if (!firstRender) {
93
+ process.stdout.write(`\x1B[${options.length}A`);
94
+ }
95
+ for (let i = 0; i < options.length; i++) {
96
+ const prefix = i === selected ? '❯ ' : ' ';
97
+ process.stdout.write(`\r\x1B[2K${prefix}${options[i].label}\n`);
98
+ }
99
+ firstRender = false;
100
+ };
101
+
102
+ process.stdout.write('\nWhich AI assistant are you using?\n');
103
+ process.stdout.write('(↑↓ to move, Enter to select)\n\n');
104
+ process.stdout.write('\x1B[?25l'); // hide cursor
105
+
106
+ process.stdin.setRawMode(true);
107
+ process.stdin.resume();
108
+ process.stdin.on('data', handler);
109
+ render();
110
+
111
+ function handler(key) {
112
+ if (key[0] === 0x1b && key[1] === 0x5b) {
113
+ if (key[2] === 0x41) { selected = (selected - 1 + options.length) % options.length; render(); }
114
+ else if (key[2] === 0x42) { selected = (selected + 1) % options.length; render(); }
115
+ } else if (key[0] === 0x0d || key[0] === 0x0a) {
116
+ process.stdin.removeListener('data', handler);
117
+ process.stdin.setRawMode(false);
118
+ process.stdin.pause();
119
+ process.stdout.write('\x1B[?25h'); // show cursor
120
+ process.stdout.write('\n');
121
+ resolve(options[selected].value);
122
+ } else if (key[0] === 0x03) { // Ctrl+C
123
+ process.stdin.removeListener('data', handler);
124
+ process.stdin.setRawMode(false);
125
+ process.stdin.pause();
126
+ process.stdout.write('\x1B[?25h');
127
+ process.stdout.write('\n');
128
+ process.exit(0);
129
+ }
130
+ }
131
+ });
132
+ }
133
+
134
+ function installClaude(skillsSource) {
57
135
  if (!fs.existsSync(skillsSource)) {
58
136
  console.error(`No skills directory found at: ${skillsSource}`);
59
137
  process.exit(1);
@@ -67,7 +145,7 @@ function copySkills(skillsSource) {
67
145
  process.exit(1);
68
146
  }
69
147
 
70
- fs.mkdirSync(SKILLS_DIR, { recursive: true });
148
+ fs.mkdirSync(CLAUDE_SKILLS_DIR, { recursive: true });
71
149
 
72
150
  const installed = [];
73
151
  for (const entry of entries) {
@@ -75,7 +153,7 @@ function copySkills(skillsSource) {
75
153
  const skillFile = path.join(src, 'SKILL.md');
76
154
  if (!fs.existsSync(skillFile)) continue;
77
155
 
78
- const dest = path.join(SKILLS_DIR, entry.name);
156
+ const dest = path.join(CLAUDE_SKILLS_DIR, entry.name);
79
157
  fs.mkdirSync(dest, { recursive: true });
80
158
  execSync(`cp -r "${src}/." "${dest}"`);
81
159
  installed.push(entry.name);
@@ -87,16 +165,98 @@ function copySkills(skillsSource) {
87
165
  }
88
166
 
89
167
  console.log('');
90
- console.log(`Installed ${installed.length} skill(s) to ${SKILLS_DIR}`);
168
+ console.log(`Installed ${installed.length} skill(s) to ${CLAUDE_SKILLS_DIR}`);
91
169
  for (const name of installed) console.log(` ✓ ${name}`);
92
170
  console.log('');
93
171
  console.log('Restart Claude Code (or run /reload-skills) to activate.');
94
172
  console.log('');
95
173
  }
96
174
 
175
+ function installCodex(skillsSource) {
176
+ if (!fs.existsSync(skillsSource)) {
177
+ console.error(`No skills directory found at: ${skillsSource}`);
178
+ process.exit(1);
179
+ }
180
+
181
+ const entries = fs.readdirSync(skillsSource, { withFileTypes: true })
182
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'));
183
+
184
+ if (entries.length === 0) {
185
+ console.error('No skill folders found.');
186
+ process.exit(1);
187
+ }
188
+
189
+ fs.mkdirSync(CODEX_SKILLS_DIR, { recursive: true });
190
+
191
+ const installed = [];
192
+ for (const entry of entries) {
193
+ const src = path.join(skillsSource, entry.name);
194
+ const skillFile = path.join(src, 'SKILL.md');
195
+ if (!fs.existsSync(skillFile)) continue;
196
+
197
+ const dest = path.join(CODEX_SKILLS_DIR, entry.name);
198
+ fs.mkdirSync(dest, { recursive: true });
199
+ execSync(`cp -r "${src}/." "${dest}"`);
200
+ installed.push(entry.name);
201
+ }
202
+
203
+ if (installed.length === 0) {
204
+ console.error('No valid skills found (each skill needs a SKILL.md).');
205
+ process.exit(1);
206
+ }
207
+
208
+ console.log('');
209
+ console.log(`Installed ${installed.length} skill(s) to ${CODEX_SKILLS_DIR}`);
210
+ for (const name of installed) console.log(` ✓ ${name} → use $${name}`);
211
+ console.log('');
212
+ console.log('Codex picks up skills automatically. Invoke them with $skill-name.');
213
+ console.log('');
214
+ }
215
+
216
+ function installAntigravity(skillsSource) {
217
+ if (!fs.existsSync(skillsSource)) {
218
+ console.error(`No skills directory found at: ${skillsSource}`);
219
+ process.exit(1);
220
+ }
221
+
222
+ const entries = fs.readdirSync(skillsSource, { withFileTypes: true })
223
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'));
224
+
225
+ if (entries.length === 0) {
226
+ console.error('No skill folders found.');
227
+ process.exit(1);
228
+ }
229
+
230
+ fs.mkdirSync(ANTIGRAVITY_SKILLS_DIR, { recursive: true });
231
+
232
+ const installed = [];
233
+ for (const entry of entries) {
234
+ const src = path.join(skillsSource, entry.name);
235
+ const skillFile = path.join(src, 'SKILL.md');
236
+ if (!fs.existsSync(skillFile)) continue;
237
+
238
+ const dest = path.join(ANTIGRAVITY_SKILLS_DIR, entry.name);
239
+ fs.mkdirSync(dest, { recursive: true });
240
+ execSync(`cp -r "${src}/." "${dest}"`);
241
+ installed.push(entry.name);
242
+ }
243
+
244
+ if (installed.length === 0) {
245
+ console.error('No valid skills found (each skill needs a SKILL.md).');
246
+ process.exit(1);
247
+ }
248
+
249
+ console.log('');
250
+ console.log(`Installed ${installed.length} skill(s) to ${ANTIGRAVITY_SKILLS_DIR}`);
251
+ for (const name of installed) console.log(` ✓ ${name} → use @${name}`);
252
+ console.log('');
253
+ console.log('Antigravity picks up skills automatically. Invoke them with @skill-name or /skill-name.');
254
+ console.log('');
255
+ }
256
+
97
257
  function remove(skillName) {
98
258
  if (!skillName) { console.error('Usage: mudra-skills remove <skill-name>'); process.exit(1); }
99
- const target = path.join(SKILLS_DIR, skillName);
259
+ const target = path.join(CLAUDE_SKILLS_DIR, skillName);
100
260
  if (!fs.existsSync(target)) {
101
261
  console.error(`Skill not found: ${skillName}`);
102
262
  process.exit(1);
@@ -106,26 +266,27 @@ function remove(skillName) {
106
266
  }
107
267
 
108
268
  function list() {
109
- if (!fs.existsSync(SKILLS_DIR)) {
269
+ if (!fs.existsSync(CLAUDE_SKILLS_DIR)) {
110
270
  console.log('No skills installed yet.');
111
271
  return;
112
272
  }
113
- const skills = fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
114
- .filter(e => e.isDirectory() && !e.name.startsWith('.') && fs.existsSync(path.join(SKILLS_DIR, e.name, 'SKILL.md')));
273
+ const skills = fs.readdirSync(CLAUDE_SKILLS_DIR, { withFileTypes: true })
274
+ .filter(e => e.isDirectory() && !e.name.startsWith('.') && fs.existsSync(path.join(CLAUDE_SKILLS_DIR, e.name, 'SKILL.md')));
115
275
 
116
276
  if (skills.length === 0) {
117
277
  console.log('No skills installed yet.');
118
278
  return;
119
279
  }
120
- console.log(`Skills in ${SKILLS_DIR}:`);
280
+ console.log(`Skills in ${CLAUDE_SKILLS_DIR}:`);
121
281
  for (const s of skills) console.log(` ${s.name}`);
122
282
  }
123
283
 
124
284
  function printHelp() {
125
285
  console.log(`
126
286
  Usage:
127
- npx mudra-skills add [<github-url>] Install skills (default: bundled)
128
- npx mudra-skills remove <name> Remove a skill
129
- npx mudra-skills list List installed skills
287
+ npx mudra-skills [--llm claude|codex|antigravity] Install skills (prompts if no --llm)
288
+ npx mudra-skills add [<github-url>] [--llm <name>] Install from URL
289
+ npx mudra-skills remove <name> Remove a Claude skill
290
+ npx mudra-skills list List installed Claude skills
130
291
  `);
131
292
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mudra-skills",
3
- "version": "1.0.6",
4
- "description": "Claude Code skills for building Mudra Band apps — 2D, 3D/XR, or auto-classified",
3
+ "version": "1.2.0",
4
+ "description": "Mudra Band skills for Claude Code, Codex, and Antigravity — 2D, 3D/XR, or auto-classified",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "mudra-skills": "bin/cli.js"
@@ -24,5 +24,8 @@
24
24
  "gestures",
25
25
  "xr",
26
26
  "webxr"
27
- ]
27
+ ],
28
+ "dependencies": {
29
+ "mudra-skills": "^1.1.0"
30
+ }
28
31
  }
@@ -1,19 +0,0 @@
1
- {
2
- "name": "mudra",
3
- "description": "Claude Code skills for building Mudra Band apps — generate 2D flat/screen apps, 3D/XR experiences, or let the router auto-classify and hand off.",
4
- "version": "1.0.4",
5
- "author": {
6
- "name": "Wearable Devices",
7
- "email": "jabbour.d@wearabledevices.co.il"
8
- },
9
- "homepage": "https://github.com/wearable-devices/mudra-skills",
10
- "repository": "https://github.com/wearable-devices/mudra-skills",
11
- "license": "MIT",
12
- "keywords": [
13
- "mudra",
14
- "wearable",
15
- "gestures",
16
- "xr",
17
- "webxr"
18
- ]
19
- }
package/CLAUDE.md DELETED
@@ -1,35 +0,0 @@
1
- # Mudra Plugin
2
-
3
- This plugin provides skills for generating Mudra Band apps.
4
-
5
- Use `/mudra-master` (or just describe an app idea) to build a 2D or 3D/XR app controlled by the Mudra Band wristband.
6
-
7
- ## Skills
8
-
9
- - **mudra-master** — router: classifies prompt as 2D / 3D / ASK / DECLINE, then hands off
10
- - **mudra-preview** — generates single-file HTML 2D apps
11
- - **mudra-xr** — generates single-file HTML 3D/XR apps using XR Blocks
12
-
13
- ## Canonical Nine-Signal Table
14
-
15
- | Signal | Type | Use for |
16
- |---|---|---|
17
- | `gesture` | discrete | finger pinches (index, middle, ring, little, thumb, grab) |
18
- | `button` | discrete | hardware button press/release |
19
- | `pressure` | analog | continuous squeeze force (0–1) |
20
- | `navigation` | pointer | 2D cursor delta (x, y) — Pointer mode |
21
- | `nav_direction` | discrete | swipe direction (up/down/left/right) — Direction mode |
22
- | `imu_acc` | analog | accelerometer (x, y, z) — IMU mode |
23
- | `imu_gyro` | analog | gyroscope (x, y, z) — IMU mode |
24
- | `snc` | analog | 3-channel bio signal, batched arrays per channel |
25
- | `battery` | discrete | battery level |
26
-
27
- **Motion-mode exclusivity (non-negotiable):** each app uses exactly one of `navigation` XOR `nav_direction` XOR `imu_acc`+`imu_gyro`. The additive signals (`gesture`, `pressure`, `snc`, `battery`) combine freely with any mode.
28
-
29
- ## Protocol Rules
30
-
31
- - WebSocket at `ws://127.0.0.1:8766`
32
- - Subscribe one signal per command, key `signal` (singular): `{ "command": "subscribe", "signal": "<name>" }`
33
- - Never use `signals` (plural) or batch subscribe
34
- - Always wrap with `MudraWebSocket` class (includes mock fallback)
35
- - Every app must include: mock fallback, always-visible simulator panel, keyboard shortcuts, connection-status indicator
package/bin/install.js DELETED
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { execSync } from 'child_process';
6
-
7
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
- const pluginSrc = path.resolve(__dirname, '..');
9
- const homeDir = process.env.HOME || process.env.USERPROFILE;
10
- const pluginsDir = path.join(homeDir, '.claude', 'plugins');
11
-
12
- const pkg = JSON.parse(fs.readFileSync(path.join(pluginSrc, 'package.json'), 'utf8'));
13
- const version = pkg.version;
14
- const MARKETPLACE = 'mudra-band';
15
- const PLUGIN_NAME = 'mudra';
16
- const PLUGIN_KEY = `${PLUGIN_NAME}@${MARKETPLACE}`;
17
-
18
- // Install to top-level plugins directory (Claude Code scans here directly)
19
- const target = path.join(pluginsDir, PLUGIN_NAME);
20
- fs.mkdirSync(target, { recursive: true });
21
- execSync(`cp -r "${pluginSrc}/." "${target}"`);
22
-
23
- // Register the marketplace in known_marketplaces.json if not already there
24
- const marketplacesFile = path.join(pluginsDir, 'known_marketplaces.json');
25
- let marketplaces = {};
26
- if (fs.existsSync(marketplacesFile)) {
27
- marketplaces = JSON.parse(fs.readFileSync(marketplacesFile, 'utf8'));
28
- }
29
- if (!marketplaces[MARKETPLACE]) {
30
- marketplaces[MARKETPLACE] = {
31
- source: {
32
- source: 'git',
33
- url: 'https://github.com/jabbourWearable/mudra-skill.git',
34
- },
35
- installLocation: path.join(pluginsDir, 'marketplaces', MARKETPLACE),
36
- lastUpdated: new Date().toISOString(),
37
- };
38
- fs.writeFileSync(marketplacesFile, JSON.stringify(marketplaces, null, 2));
39
- }
40
-
41
- // Register the plugin in installed_plugins.json with the correct marketplace key
42
- const installedFile = path.join(pluginsDir, 'installed_plugins.json');
43
- let installed = { version: 2, plugins: {} };
44
- if (fs.existsSync(installedFile)) {
45
- installed = JSON.parse(fs.readFileSync(installedFile, 'utf8'));
46
- }
47
-
48
- // Remove stale npm entry from previous installs
49
- delete installed.plugins['mudra@npm'];
50
-
51
- const now = new Date().toISOString();
52
- const previousInstall = installed.plugins[PLUGIN_KEY]?.[0];
53
- installed.plugins[PLUGIN_KEY] = [
54
- {
55
- scope: 'user',
56
- installPath: target,
57
- version,
58
- installedAt: previousInstall?.installedAt ?? now,
59
- lastUpdated: now,
60
- },
61
- ];
62
-
63
- fs.writeFileSync(installedFile, JSON.stringify(installed, null, 2));
64
-
65
- console.log('');
66
- console.log('Mudra skills installed successfully!');
67
- console.log('');
68
- console.log('Next steps:');
69
- console.log(' 1. Restart Claude Code, or run /reload-plugins');
70
- console.log(' 2. Use the skills:');
71
- console.log(' /mudra:mudra-master — auto-classify and route');
72
- console.log(' /mudra:mudra-preview — generate 2D app');
73
- console.log(' /mudra:mudra-xr — generate 3D/XR app');
74
- console.log('');
75
- console.log(`Plugin location: ${target}`);