This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Smalruby 3 Editor monorepo - a Ruby-based visual programming environment forked from MIT's Scratch 3.0. The monorepo contains all packages needed to build and run the Smalruby editor.
This project uses npm workspaces with the following packages:
packages/scratch-gui: React-based web interface with Ruby mode, custom extensions, and Google Drive integrationpackages/scratch-vm: Virtual machine that executes projects and manages blockspackages/scratch-render: WebGL-based rendering engine for sprites and backdropspackages/scratch-svg-renderer: SVG processing for vector imagespackages/task-herder: Asynchronous task queue with throttling and concurrency control
The infra/ directory contains AWS CDK infrastructure projects (independent from npm workspaces):
infra/smalruby-mesh-v2: AWS CDK project for the Mesh v2 networking service (AppSync + DynamoDB)
- Development builds: Always use
build:devfor development builds, never the defaultbuildcommand. - Test runner: Tests in
scratch-guiusejest(nottap). Do not confuse test runners or their assertion APIs (e.g., useexpect().toEqual()nott.deepEqual()). Onlyscratch-vmusestap.
CRITICAL: All npm commands MUST be run inside Docker containers. Never run npm commands directly on the host.
- Service name:
app - Port: 8601
- Working directory:
/app(inside container)
# Start development server
docker compose up app
# Run commands in container
docker compose run --rm app bash -c "COMMAND"
# Stop services
docker compose stop appInstall dependencies for all packages:
docker compose run --rm app npm installBuild all packages:
# Production build
docker compose run --rm app npm run build
# Development build (faster, with source maps)
docker compose run --rm app npm run build:devStart the GUI development server (http://localhost:8601):
docker compose up appOr run in background:
docker compose up -d app
docker compose logs -f appRun all tests (lint + unit + integration):
docker compose run --rm app npm testRun unit tests only:
docker compose run --rm app npm run test:unitRun integration tests only:
docker compose run --rm app npm run test:integrationIMPORTANT: test:unit and test:integration commands do NOT accept file arguments. To run individual test files, use npm exec (or npx):
For unit tests:
# scratch-vm unit tests (uses tap)
docker compose run --rm app bash -c "cd packages/scratch-vm && npm exec tap test/unit/specific-file.js"
# scratch-gui unit tests (uses jest)
docker compose run --rm app bash -c "cd packages/scratch-gui && npm exec jest test/unit/specific-file.test.js"For integration tests (uses jest):
docker compose run --rm app bash -c "cd packages/scratch-gui && npm exec jest test/integration/specific-file.test.js"docker compose run --rm app npm run lintIMPORTANT: Lint must pass with zero errors AND zero warnings. The scratch-gui package uses --max-warnings 0 to enforce this. Fix all warnings (including JSDoc issues) before committing.
Remove build artifacts from all packages:
docker compose run --rm app npm run cleanSmalruby provides a Ruby code editor (Monaco Editor) in scratch-gui. Ruby code is parsed using @ruby/prism (a WebAssembly-based Ruby parser) and converted to/from Scratch blocks within the browser.
- Parser:
@ruby/prism— parses Ruby source into an AST (WebAssembly, runs in browser and Node.js) - Ruby → Blocks:
src/lib/ruby-to-blocks-converter/— converts prism AST nodes into Scratch blocks - Blocks → Ruby:
src/lib/ruby-generator/— generates Ruby source from Scratch block data - Integration:
src/containers/ruby-tab/— Monaco Editor integration and tab switching logic
Smalruby supports loading and saving projects to Google Drive. Setup requires:
- Google Cloud Platform project with Drive API, Picker API, and Generative Language API enabled
- OAuth 2.0 client credentials
- Environment variables:
GOOGLE_CLIENT_ID,GOOGLE_API_KEY
See packages/scratch-gui/docs/google-api-setup.md for detailed setup instructions.
Custom Smalruby extensions are located in packages/scratch-vm/src/extensions/:
koshien/: Smalruby Koshien competition supportmicrobitMore/: Enhanced micro:bit supportscratch3_mesh/: Mesh networking (deprecated)scratch3_mesh_v2/: Mesh networking v2
Each package has its own development workflow. See package-specific rules in .claude/rules/:
.claude/rules/scratch-gui/- GUI development, Ruby mode, testing.claude/rules/scratch-vm/- VM development, extensions, playground.claude/rules/scratch-render/- Rendering engine development.claude/rules/scratch-svg-renderer/- SVG processing.claude/rules/task-herder/- Task queue utility
Set environment variables in .env file at project root:
# Google Drive Integration
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_API_KEY=your-api-key
# Mesh Networking (optional)
MESH_GRAPHQL_ENDPOINT=https://your-endpoint
MESH_API_KEY=your-key
MESH_AWS_REGION=ap-northeast-1
# Development
NODE_ENV=development
DEBUG=*After changing environment variables, restart the service:
docker compose restart app- When fixing a bug pattern (e.g.,
set_xxxmethods, log level changes), always check for ALL instances of the pattern across the codebase before proposing a fix. Do not fix only the reported instance. - When designing a fix, consider assignment/usage contexts (e.g.,
flag = true) not just standalone expressions.
- For UI/browser bugs, prefer reproducing with Playwright first before deep code exploration. Don't spend extensive time reading code when the bug is visually reproducible.
- When debugging block conversion issues, start from the user-facing symptom and trace inward, rather than guessing at coordinates or layout causes.
Always use URL parameters to streamline Playwright MCP testing:
http://localhost:8601?no_beforeunload=1&tab=ruby&ruby_version=2
| Parameter | Values | Description |
|---|---|---|
no_beforeunload |
1, true |
Always use this. Disables beforeunload dialog that blocks navigation. |
tab |
code, costumes, sounds, ruby |
Activate a specific tab on startup. |
ruby_version |
1, 2 |
Set Ruby version, overriding localStorage. |
When verifying behavior in the browser using Playwright MCP, use the window.smalruby debug global object exposed by packages/scratch-gui/src/containers/ruby-tab.jsx:
// Available after visiting the Ruby tab at least once
window.smalruby.vm // Scratch VM instance
window.smalruby.sprite // Current editing target (RenderedTarget)
window.smalruby.blocks // Current target's blocks
window.smalruby.comments // Current target's comments
window.smalruby.stage // Stage target
window.smalruby.runtime // VM runtime
// Monaco editor instance
monaco.editor.getEditors()[0] // Get the first Monaco editorTo access the RubyGenerator (webpack bundled module):
// Obtain webpack require via webpackChunkGUI
let req;
window.webpackChunkGUI.push([['__probe__'], {}, r => { req = r; }]);
// Find RubyGenerator
for (const id in req.c) {
const m = req.c[id]?.exports;
if (m?.default?.targetsToCode) { /* found RubyGenerator = m.default */ break; }
}Follow TDD (Test-Driven Development) approach:
- RED: Write failing tests first to validate test correctness
- GREEN: Implement code to make tests pass
- REFACTOR: Improve code while keeping tests green (only when needed)
- UI behavior and features that involve browser interaction should use integration tests, not unit tests.
- Local testing: Run only the directly affected test files and lint before committing and pushing. Full test suites (unit + integration) are run by CI.
- Run lint:
docker compose run --rm app npm run lint - Run specific tests:
docker compose run --rm app bash -c "cd packages/scratch-gui && npm exec jest test/unit/lib/your-test.test.js"
- Run lint:
- CI testing: The full test suite runs automatically on push. Do not wait for full local test runs before committing — trust CI for comprehensive regression detection.
packages/: All workspace packages (npm workspaces)infra/: AWS CDK infrastructure projects (independent projects, not workspaces)infra/smalruby-mesh-v2/: Mesh v2 networking service (AppSync + DynamoDB)
scripts/: Monorepo-level build scripts.github/workflows/: CI/CD configuration.claude/rules/: Package-specific development rules
Japanese locale files are located at:
packages/scratch-gui/src/locales/ja.js— Main Japanese locale (kanji/hiragana)packages/scratch-gui/src/locales/ja-Hira.js— Hiragana-only (phonetic) locale
Always check these paths first when adding or modifying user-facing strings.
Packages depend on each other through workspace references:
scratch-guidepends onscratch-vm,scratch-render,scratch-svg-rendererscratch-vmdepends onscratch-render,scratch-svg-renderer- All packages are built together in dependency order
When modifying packages that affect others, test compatibility after changes.
This project is a fork of the Scratch Editor maintained by the Scratch Foundation.
- Upstream remote:
https://github.com/scratchfoundation/scratch-editor.git - Upstream branch:
develop(NOT main or master) - Local development branch:
develop
CRITICAL: The upstream repository contains a GitHub Pages branch with extremely large changes. Always fetch only the specific branch you need to avoid downloading unnecessary data:
# Fetch only the develop branch from upstream
git fetch -p upstream develop
# DO NOT use bare `git fetch upstream` - it will fetch all branches including gh-pagesUse the /upstream:merge slash command for interactive, semi-automated merge workflow:
/upstream:mergeThis command provides:
- Step-by-step guidance for merge execution
- Automatic conflict detection for known areas (gui.ts, extension-manager.js)
- Resolution guidance with code markers
- Automated testing (lint, build, unit, integration)
- Progress tracking and documentation generation
- PR creation with comprehensive summary
Manual merge is NOT recommended - use the slash command to ensure consistent process and complete documentation.
See .claude/skills/upstream-merge/SKILL.md for detailed workflow documentation.