agentsclimarketplace

Scratch project file

Skill yokobond/scratch-skills/skills/scratch-project-file

Downloads (saves) and uploads (loads) Scratch project .sb3 files. Use this skill when you need to save a Scratch project to a local file or load a previously saved .sb3 file into the editor.From its SKILL.md

Install
npx -y skills add yokobond/scratch-skills --skill scratch-project-file

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.3 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it

Scratch Project File Skill

This skill handles saving Scratch projects as .sb3 files to the local filesystem and loading .sb3 files back into the Scratch editor.

Prerequisites

This skill drives the browser via the playwright-cli skill. Ensure that skill is installed and a browser session has been opened (e.g. playwright-cli open --headed https://scratch.mit.edu/projects/editor/).

Visibility: Always use --headed when opening the browser so that the Scratch editor is visible to the user during project creation.

When to Use

  • When the user asks to save/download/export a Scratch project
  • When the user asks to load/upload/import a .sb3 file into the editor
  • When you need to persist a project before making destructive changes
  • When transferring a project between sessions

Downloading (Saving) a Project

Use playwright-cli run-code to trigger vm.saveProjectSb3(), create a download link, and capture the downloaded file via Playwright's download event.

Step 1: Ensure VM is Connected

The VM must be available as window.vm. If not, find it first (see scratch-project-edit skill).

Step 2: Download the .sb3 File

playwright-cli run-code "$(cat <<'EOF'
async (page) => {
  // Set up download handler BEFORE triggering download
  const downloadPromise = page.waitForEvent('download', { timeout: 10000 });

  // Generate .sb3 blob and trigger browser download
  await page.evaluate(async (filename) => {
    const blob = await window.vm.saveProjectSb3();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }, 'my-project.sb3');

  // Capture the download and save to local path
  const download = await downloadPromise;
  await download.saveAs('/absolute/path/to/my-project.sb3');
  return 'Saved to /absolute/path/to/my-project.sb3';
}
EOF
)"

Key points:

  • page.waitForEvent('download') MUST be called BEFORE triggering the download (before page.evaluate). Otherwise the event is missed.
  • The filename passed to a.download is just a hint for the browser; the actual save path is determined by download.saveAs().
  • Always use an absolute path for download.saveAs().

Complete Download Example

playwright-cli run-code "$(cat <<'EOF'
async (page) => {
  const downloadPromise = page.waitForEvent('download', { timeout: 10000 });

  await page.evaluate(async () => {
    const vm = window.vm;
    if (!vm) throw new Error('VM not connected. Run VM finder first.');
    const blob = await vm.saveProjectSb3();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'project.sb3';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  });

  const download = await downloadPromise;
  const savePath = '/Users/username/projects/project.sb3';
  await download.saveAs(savePath);
  return 'Project saved to: ' + savePath;
}
EOF
)"

Uploading (Loading) a Project

To load a .sb3 file into the Scratch editor, bypass the UI file chooser and load the project programmatically via the VM.

Note: This method does NOT update certain UI states (like the project title).

Method 1: Fetch via HTTP (Recommended)

If the project file is accessible via an HTTP URL (local or remote), use fetch() inside page.evaluate() to load the .sb3 directly in the browser context. This is the most reliable method because it avoids Node.js/browser context boundary issues entirely.

playwright-cli run-code "$(cat <<'EOF'
async (page) => {
  await page.evaluate(async (url) => {
    if (!window.vm) throw new Error('VM not connected. Run VM finder first.');
    const resp = await fetch(url);
    if (!resp.ok) throw new Error('Fetch failed: ' + resp.status);
    const buffer = await resp.arrayBuffer();
    await window.vm.loadProject(buffer);
  }, 'https://example.com/path/to/project.sb3');

  await page.waitForTimeout(3000);
  return 'Project loaded via fetch';
}
EOF
)"

Key points:

  • The URL must be reachable from the browser. For local files, start a local HTTP server (e.g., live-server with --host=0.0.0.0 --port=5500) and use its URL (e.g., https://0.0.0.0:5500/<workspace-relative-path>).
  • vm.loadProject() accepts an ArrayBuffer directly — no base64 encoding needed.
  • Wait at least 2–3 seconds after loading for assets (costumes, sounds) to initialize.
  • If the HTTPS certificate is self-signed, you may need to visit the server URL directly first to accept the certificate in the browser.

Method 2: Read via Node.js require('fs') (Fallback)

If no HTTP server is available, read the file in the Node.js context of playwright-cli run-code and pass it as base64 to the browser.

WARNING: require('fs') may fail with ReferenceError: require is not defined depending on the playwright-cli version or environment. If this happens, use Method 1 instead.

playwright-cli run-code "$(cat <<'EOF'
async (page) => {
  const fs = require('fs');
  const fileBuffer = fs.readFileSync('/absolute/path/to/project.sb3');
  const base64 = fileBuffer.toString('base64');

  await page.evaluate(async (b64) => {
    const binary = atob(b64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    await window.vm.loadProject(bytes.buffer);
  }, base64);

  await page.waitForTimeout(2000);
  return 'Project loaded via fs';
}
EOF
)"

IMPORTANT: require('fs') is only available in the outer Node.js layer of playwright-cli run-code, NOT inside page.evaluate() (which runs in the browser). The file must be read in the outer layer and passed to page.evaluate as a JSON-serializable argument (base64 string).

Handling File Chooser Dialogs

Avoiding Stale Dialogs

  • For downloads, always use the downloadPromise pattern (set up listener before triggering). This avoids stray file chooser dialogs in the first place.
  • If stale file chooser modals are blocking other commands (you'll see [File chooser]: can be handled by upload in the modal state), clear them by running playwright-cli upload "" repeatedly until all are dismissed.
  • As a last resort, close the browser tab and re-open with playwright-cli goto.

Tips & Troubleshooting

Upload Fails with "require is not defined"

  • require('fs') may not be available in all playwright-cli environments.
  • Use Method 1 (fetch) instead. Start a local HTTP server (e.g. live-server) to serve the .sb3 file, then use fetch() inside page.evaluate().
  • require('fs') is NEVER available inside page.evaluate() (browser context).

File Chooser Dialog Not Appearing

  • The "Load from your computer" menu item internally clicks a hidden <input type="file">. If the menu is not found, verify the editor is fully loaded.
  • Use playwright-cli snapshot to check the current page state.

Project Not Fully Loading After Upload

  • Wait at least 2-3 seconds after loading for assets (costumes, sounds) to initialize.

  • After loading, re-find the VM reference as loadProject may invalidate previous references:

    playwright-cli run-code "$(cat <<'EOF'
    async page => await page.evaluate(() => {
      const el = document.querySelector('canvas');
      const key = Object.keys(el).find(k =>
        k.startsWith('__reactFiber') || k.startsWith('__reactInternalInstance')
      );
      let fiber = el[key];
      while (fiber) {
        if (fiber.memoizedProps?.vm) { window.vm = fiber.memoizedProps.vm; break; }
        fiber = fiber.return;
      }
      return window.vm ? 'VM reconnected' : 'VM not found';
    })
    EOF
    )"
    

Verifying Project Contents After Load

playwright-cli run-code "$(cat <<'EOF'
async page => await page.evaluate(() => {
  const vm = window.vm;
  const sprites = vm.runtime.targets.map(t => t.sprite.name);
  const stage = vm.runtime.targets.find(t => t.isStage);
  const backdrops = stage.sprite.costumes.map(c => c.name);
  return { sprites, backdrops };
})
EOF
)"

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,790. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.