star-sdk-cli 0.1.12 → 0.1.14

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.
Files changed (3) hide show
  1. package/README.md +52 -0
  2. package/dist/cli.mjs +96 -14
  3. package/package.json +5 -3
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # star-sdk-cli
2
+
3
+ CLI for [Star SDK](https://www.npmjs.com/package/star-sdk) — register games, deploy to hosting, and install AI coding docs.
4
+
5
+ ## Usage
6
+
7
+ All commands run via `npx star-sdk` (no global install needed).
8
+
9
+ ### Commands
10
+
11
+ | Command | Description |
12
+ |---------|-------------|
13
+ | `npx star-sdk init "Game Name"` | Register a game, get a gameId and deploy key |
14
+ | `npx star-sdk deploy [path]` | Deploy your game to Star hosting |
15
+ | `npx star-sdk install [agent]` | Install SDK docs for AI coding tools |
16
+ | `npx star-sdk docs [topic]` | Print API docs (audio, canvas, leaderboard) |
17
+ | `npx star-sdk whoami` | Show current game config |
18
+
19
+ ### Workflow
20
+
21
+ ```bash
22
+ # 1. Register your game
23
+ npx star-sdk init "My Game"
24
+ # Creates .starrc with gameId and deploy key
25
+
26
+ # 2. Build your game (use the gameId from .starrc)
27
+ # ... write your game code ...
28
+
29
+ # 3. Deploy
30
+ npx star-sdk deploy
31
+ # => Live at https://buildwithstar.com/games/<id>
32
+ ```
33
+
34
+ ### AI Agent Support
35
+
36
+ Install SDK documentation directly into your AI coding tool:
37
+
38
+ ```bash
39
+ npx star-sdk install # Claude Code (default)
40
+ npx star-sdk install cursor # Cursor
41
+ npx star-sdk install codex # OpenAI Codex
42
+ npx star-sdk install windsurf # Windsurf
43
+ npx star-sdk install aider # Aider
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ Full docs at [buildwithstar.com/docs/sdk](https://buildwithstar.com/docs/sdk)
49
+
50
+ ## License
51
+
52
+ MIT
package/dist/cli.mjs CHANGED
@@ -231,6 +231,16 @@ Complete working games are in the \`examples/\` directory (also published with t
231
231
  - **reaction-time** \u2014 Test your reflexes over 5 rounds, DOM-based (no canvas)
232
232
 
233
233
  Each example is a single HTML file, no build step \u2014 imports from esm.sh.
234
+
235
+ ## Deploy
236
+
237
+ When the game is ready, deploy it with one command:
238
+
239
+ \`\`\`bash
240
+ npx star-sdk deploy
241
+ \`\`\`
242
+
243
+ This uploads the game to Star hosting and returns a live URL. Requires \`npx star-sdk init\` to have been run first (which creates the deploy key in \`.starrc\`).
234
244
  `;
235
245
  var AUDIO_DOCS = `**Installation**
236
246
 
@@ -413,24 +423,22 @@ game(({ ctx, width, height, on, loop, ui, canvas }) => {
413
423
  });
414
424
 
415
425
  // 2. Render HTML to the safe UI overlay
416
- // UI is interactive by default (scroll, buttons work)
417
- // Adding canvas.addEventListener makes UI click-through automatically
418
426
  ui.render(\`
419
427
  <div class="absolute top-4 left-4 text-white">
420
- <button id="start-btn" class="px-4 py-2 bg-blue-500 rounded pointer-events-auto">
428
+ <button id="start-btn" class="px-4 py-2 bg-blue-500 rounded">
421
429
  Click Me
422
430
  </button>
423
431
  </div>
424
432
  \`);
425
433
 
426
- // 3. Listen for button clicks
434
+ // 3. Listen for button clicks \u2014 on() auto-enables pointer-events for the target
427
435
  on('click', '#start-btn', () => {
428
436
  console.log('Button clicked!');
429
437
  });
430
438
 
431
439
  // 4. For canvas games: listen for taps on canvas
432
- // This automatically makes UI click-through (taps pass through to canvas)
433
- // Buttons with pointer-events-auto still work
440
+ // Taps pass through the UI overlay to the canvas layer
441
+ // Elements targeted by on() are automatically interactive
434
442
  canvas.addEventListener('pointerdown', (e) => {
435
443
  console.log('Canvas/screen tapped!', e);
436
444
  });
@@ -495,7 +503,7 @@ A safe manager for your HTML overlay, stacked on top of the canvas.
495
503
  - \`ui.el(selector)\`: Scoped \`querySelector\` for the UI root.
496
504
  - \`ui.all(selector)\`: Scoped \`querySelectorAll\` for the UI root.
497
505
 
498
- **Auto-detection:** When you add \`canvas.addEventListener('pointerdown', ...)\`, the SDK automatically makes UI click-through so taps reach the canvas. Buttons with \`pointer-events-auto\` still work.
506
+ **Auto-detection:** When you add \`canvas.addEventListener('pointerdown', ...)\`, the SDK automatically makes UI click-through so taps reach the canvas. Elements targeted by \`on()\` are automatically interactive \u2014 no extra CSS classes needed. Native \`<button>\` and \`<a>\` elements are also always interactive.
499
507
 
500
508
  ### Cursor Management
501
509
 
@@ -723,13 +731,13 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
723
731
  // 1. Listen for screen taps - this makes UI click-through automatically
724
732
  canvas.addEventListener('pointerdown', handleTap);
725
733
 
726
- // 2. Listen for button clicks - buttons need pointer-events-auto
734
+ // 2. Listen for button clicks \u2014 on() auto-enables pointer-events for the selector
727
735
  on('click', '#leaderboard-btn', (e) => {
728
736
  e.stopPropagation();
729
737
  leaderboard.show();
730
738
  });
731
739
 
732
- // 3. Render UI - buttons need pointer-events-auto to intercept clicks
740
+ // 3. Render UI \u2014 elements targeted by on() are automatically interactive
733
741
  let lastState = null;
734
742
  let lastScore = -1;
735
743
 
@@ -756,7 +764,7 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
756
764
  <div class="h-full flex flex-col items-center justify-center text-white">
757
765
  <div class="text-3xl mb-4">GAME OVER</div>
758
766
  <div class="text-6xl mb-4">\\\${score}</div>
759
- <button id="leaderboard-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20 pointer-events-auto">
767
+ <button id="leaderboard-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20">
760
768
  VIEW LEADERBOARD
761
769
  </button>
762
770
  <div class="text-xl animate-pulse">TAP TO RESTART</div>
@@ -1098,7 +1106,7 @@ game(({ ctx, width, height, loop, ui, on, canvas }) => {
1098
1106
  - Score submission (works for guests and logged-in users)
1099
1107
  - Leaderboard UI (modal with rankings)
1100
1108
  - Weekly/all-time timeframes
1101
- - AI-detected scoring (score/time/moves - higher or lower is better)
1109
+ - Configurable sort order (\`'asc'\` for lower-is-better, \`'desc'\` for higher-is-better)
1102
1110
 
1103
1111
  ---
1104
1112
 
@@ -1170,7 +1178,7 @@ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1170
1178
 
1171
1179
  game(({ ui, on }) => {
1172
1180
  ui.render(\`
1173
- <button id="lb-btn" class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold text-white shadow-lg shadow-blue-500/20 pointer-events-auto">
1181
+ <button id="lb-btn" class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold text-white shadow-lg shadow-blue-500/20">
1174
1182
  View Leaderboard
1175
1183
  </button>
1176
1184
  \`);
@@ -1237,7 +1245,7 @@ game(({ ctx, width, height, loop, ui, on, canvas }) => {
1237
1245
  <div class="h-full flex flex-col items-center justify-center text-white">
1238
1246
  <div class="text-3xl mb-4">GAME OVER</div>
1239
1247
  <div class="text-6xl mb-4">\\\${score}</div>
1240
- <button id="lb-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20 pointer-events-auto">
1248
+ <button id="lb-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20">
1241
1249
  VIEW LEADERBOARD
1242
1250
  </button>
1243
1251
  <div class="text-xl animate-pulse">TAP TO RESTART</div>
@@ -1289,6 +1297,27 @@ const data = await leaderboard.getScores({
1289
1297
 
1290
1298
  ---
1291
1299
 
1300
+ **Sort Order:**
1301
+
1302
+ By default, leaderboards rank higher scores first (\`'desc'\`). For games where lower is better (reaction time, speedruns, golf), set \`sort: 'asc'\`:
1303
+
1304
+ \`\`\`javascript
1305
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>', sort: 'asc' });
1306
+ \`\`\`
1307
+
1308
+ **IMPORTANT: Do NOT invert scores to fake ascending order.** Use \`sort: 'asc'\` instead.
1309
+
1310
+ \`\`\`javascript
1311
+ // BAD \u2014 do not do this
1312
+ leaderboard.submit(10000 - reactionTimeMs);
1313
+
1314
+ // GOOD \u2014 submit the real value with sort: 'asc'
1315
+ const leaderboard = createLeaderboard({ gameId, sort: 'asc' });
1316
+ leaderboard.submit(reactionTimeMs);
1317
+ \`\`\`
1318
+
1319
+ ---
1320
+
1292
1321
  **Tips:**
1293
1322
 
1294
1323
  1. **Call \`submit()\` before \`show()\`** - Ensures your score appears immediately in the leaderboard.
@@ -1327,6 +1356,52 @@ function error(message) {
1327
1356
  function info(message) {
1328
1357
  console.log(`${colors.blue}\u2139${colors.reset} ${message}`);
1329
1358
  }
1359
+ var STAR_PACKAGES = ["star-sdk", "star-canvas", "star-audio", "star-leaderboard", "star-multiplayer"];
1360
+ function resolveStarSdkVersion(deployDir) {
1361
+ const candidates = [
1362
+ path.join(deployDir, "node_modules", "star-sdk", "package.json"),
1363
+ path.join(process.cwd(), "node_modules", "star-sdk", "package.json")
1364
+ ];
1365
+ for (const p of candidates) {
1366
+ try {
1367
+ if (fs.existsSync(p)) {
1368
+ const pkg = JSON.parse(fs.readFileSync(p, "utf-8"));
1369
+ if (pkg.version) return pkg.version;
1370
+ }
1371
+ } catch {
1372
+ }
1373
+ }
1374
+ return null;
1375
+ }
1376
+ function injectImportMapIfNeeded(html, deployDir) {
1377
+ if (/<script\s[^>]*type\s*=\s*["']importmap["'][^>]*>/i.test(html)) {
1378
+ return html;
1379
+ }
1380
+ const bareImportPattern = /\bfrom\s+['"](?:star-sdk|star-canvas|star-audio|star-leaderboard|star-multiplayer)['"]/;
1381
+ if (!bareImportPattern.test(html)) {
1382
+ return html;
1383
+ }
1384
+ const version = resolveStarSdkVersion(deployDir);
1385
+ const suffix = version ? `@${version}` : "";
1386
+ const imports = {};
1387
+ for (const pkg of STAR_PACKAGES) {
1388
+ imports[pkg] = `https://esm.sh/${pkg}${suffix}`;
1389
+ }
1390
+ const importmapTag = `<script type="importmap">
1391
+ ${JSON.stringify({ imports }, null, 2)}
1392
+ </script>
1393
+ `;
1394
+ const moduleScriptMatch = html.match(/<script\s[^>]*type\s*=\s*["']module["'][^>]*>/i);
1395
+ if (moduleScriptMatch && moduleScriptMatch.index !== void 0) {
1396
+ return html.slice(0, moduleScriptMatch.index) + importmapTag + html.slice(moduleScriptMatch.index);
1397
+ }
1398
+ const headMatch = html.match(/<head[^>]*>/i);
1399
+ if (headMatch && headMatch.index !== void 0) {
1400
+ const insertPos = headMatch.index + headMatch[0].length;
1401
+ return html.slice(0, insertPos) + "\n" + importmapTag + html.slice(insertPos);
1402
+ }
1403
+ return html;
1404
+ }
1330
1405
  function showHelp() {
1331
1406
  log(`
1332
1407
  ${colors.bright}Star SDK CLI${colors.reset} v${VERSION}
@@ -1524,6 +1599,12 @@ async function deployCommand(dirPath) {
1524
1599
  }
1525
1600
  log(`Deploying ${colors.bright}${config.name}${colors.reset} from ${colors.dim}${deployDir}${colors.reset}`);
1526
1601
  try {
1602
+ const originalHtml = fs.readFileSync(indexPath, "utf-8");
1603
+ const processedHtml = injectImportMapIfNeeded(originalHtml, deployDir);
1604
+ const htmlModified = processedHtml !== originalHtml;
1605
+ if (htmlModified) {
1606
+ info("Injected importmap for bare imports (star-sdk \u2192 esm.sh)");
1607
+ }
1527
1608
  const archiver = (await import("archiver")).default;
1528
1609
  const zipBuffer = await new Promise((resolve2, reject) => {
1529
1610
  const chunks = [];
@@ -1533,9 +1614,10 @@ async function deployCommand(dirPath) {
1533
1614
  archive.on("error", reject);
1534
1615
  archive.glob("**/*", {
1535
1616
  cwd: deployDir,
1536
- ignore: ["node_modules/**", ".starrc", ".git/**", ".DS_Store"],
1617
+ ignore: ["node_modules/**", ".starrc", ".git/**", ".DS_Store", "index.html"],
1537
1618
  dot: false
1538
1619
  });
1620
+ archive.append(processedHtml, { name: "index.html" });
1539
1621
  archive.finalize();
1540
1622
  });
1541
1623
  log(` ${colors.dim}Uploading ${(zipBuffer.length / 1024).toFixed(1)} KB...${colors.reset}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "star-sdk-cli",
3
- "version": "0.1.12",
4
- "description": "CLI tool for Star SDK - register games and manage leaderboards",
3
+ "version": "0.1.14",
4
+ "description": "CLI for Star SDK register games, install AI docs, and deploy to Star hosting",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "star-sdk": "./dist/cli.mjs"
@@ -33,7 +33,9 @@
33
33
  "sdk",
34
34
  "cli",
35
35
  "games",
36
- "leaderboard"
36
+ "leaderboard",
37
+ "deploy",
38
+ "hosting"
37
39
  ],
38
40
  "dependencies": {
39
41
  "archiver": "^7.0.0"