mudra-skills 1.0.6 → 1.1.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
@@ -4,21 +4,30 @@ Claude Code skills for building [Mudra Band](https://wearabledevices.co.il) apps
4
4
 
5
5
  ## Install
6
6
 
7
+ ### Recommended
8
+
7
9
  ```bash
8
10
  npx mudra-skills
9
11
  ```
10
12
 
11
- Then restart Claude Code or run `/reload-plugins`.
13
+ Installs directly to `~/.claude/skills/` — no global install needed. Then restart Claude Code.
14
+
15
+ ### Alternative: global install
16
+
17
+ ```bash
18
+ npm install -g mudra-skills
19
+ mudra-skills add
20
+ ```
12
21
 
13
22
  ## What You Get
14
23
 
15
- Three skills installed to `~/.claude/plugins/mudra/`:
24
+ Three skills installed to `~/.claude/skills/`:
16
25
 
17
26
  | Skill | Invoke | Description |
18
27
  |---|---|---|
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 |
28
+ | **mudra-master** | `/mudra-master` | Auto-router — classifies your prompt and hands off to 2D or 3D |
29
+ | **mudra-preview** | `/mudra-preview` | Generates single-file HTML 2D apps |
30
+ | **mudra-xr** | `/mudra-xr` | Generates single-file HTML 3D/XR apps using XR Blocks |
22
31
 
23
32
  ## Usage
24
33
 
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.1.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"
@@ -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}`);