Thanks for contributing.
- Improve Roblox Studio support (properties, capabilities, new operations)
- Add support for another coding agent
- Improve the MCP interface
- Improve platform-specific native integration
- Improve interaction and testing reliability
- Fix compatibility with Roblox Studio updates
For anything large, open an issue first so scope and direction can be agreed before implementation.
packages/protocol/ Shared operation, value, and error definitions
packages/server/ Local server: Studio bridge, permissions, CLI, MCP
packages/app/ Electron harness and agent adapters
plugin/ Roblox Studio plugin (Luau)
assets/ Icons and shared art
- Node.js 20.11+ and pnpm, for the harness
- Luumen (
luu) and Rokit, for the plugin - Roblox Studio, for anything that touches the plugin
The two halves are independent: you can work on the plugin without the Node toolchain, and on the harness without Studio.
pnpm install
pnpm build
pnpm testpnpm dev # the app, with reload
pnpm serve # the local server on its own, no windowpnpm dev runs on its own dev channel: an amber icon, a window titled
Luu Code (dev), its own taskbar identity, and its own chats, settings, and
plugin file (LuuCodeDev.rbxm). Nothing it writes touches an installed copy.
The local server is shared across channels — Studio finds it on a fixed port, so
a dev build and an installed build cannot run at the same time. The second to
start reports the port as taken. Set LUU_CODE_PORT and LUU_CODE_HOME to run
both.
LUU_CODE_CHANNEL overrides the channel, so LUU_CODE_CHANNEL=nightly pnpm dev
shows the nightly identity from source.
The plugin is a separate build; see below. A source checkout has no Studio panel
until you have run luu build at least once.
Both app icons — the blue release one and the purple nightly one — are rendered
from assets/icon.svg and assets/icon-nightly.svg:
pnpm assets:iconsOutput is committed; the build copies the icons in, it does not generate them.
There are three masters, one per channel: blue icon.svg for release, purple
icon-nightly.svg, and amber icon-dev.svg for a build run from source. All
three ship in every build; which one appears is decided at runtime.
The plugin is a Luumen project with its own pinned toolchain and tasks in plugin/.config.luau.
cd plugin
luu install # Rojo, StyLua, Selene, luau-lsp, Lune
luu dev # rebuild into the Studio plugins folder on every save
luu run check # stylua, selene, luau-lspluu build and luu dev install as LuuCodeDev.rbxm, the same name the
app's dev channel uses. An installed app's LuuCode.rbxm is left alone. luu run bundle writes a plain LuuCode.rbxm artifact.
While luu dev is running the watch owns the plugin: the app will not install
over it, and the switch in Settings → Updates is off. Restart Studio to pick up
a build.
The tag decides the version. CI stamps every manifest and the plugin's
PLUGIN_VERSION from the tag, so the app, the Studio plugin, and the MCP server
always ship as one version. Nothing in the repo needs bumping first.
git tag v0.2.0 && git push origin v0.2.0That runs .github/workflows/release.yml: the plugin is built once on Linux,
the app is packaged on Windows, and everything lands in a draft GitHub release
that the last job publishes. Nightlies run from nightly.yml at 05:00 UTC, skip
when nothing has changed, and publish as prereleases on their own update
channel.
Windows is the only platform that ships today; Studio does not run on Linux.
Adding macOS back means restoring macos-latest in the matrices of ci.yml,
release.yml, and nightly.yml together.
Packaging locally:
pnpm --filter @luumen/code-app run package # installers into packages/app/release/Set LUU_CODE_CHANNEL=nightly to build the nightly identity instead — its own
application id, product name, purple icon, and update feed, so it installs
beside a release build rather than over it.
Builds are unsigned. The signing hooks are in electron-builder.cjs and the
workflows; CSC_LINK/CSC_KEY_PASSWORD and the Apple notarization secrets turn
them on. Until then macOS cannot install its own updates, and the app says so.
Operations are defined once and flow outward from there.
- Add the entry to
COMMANDSinpackages/protocol/src/commands.ts, with its params schema, permission group, capability, and summary. - Add its result type to
CommandResultsin the same file. - Implement the handler in
plugin/src/Commands/and register it inCommands/init.luau, or handle it server-side indispatcher.tsif it does not belong in Studio. - Add a title and, where it helps, a detail line in
packages/server/src/core/activity.ts, so the user sees Roblox language rather than an op id. - Add an MCP tool in
packages/server/src/mcp/tools.ts. Write the description for an agent that has never seen this project. - Add a test.
A new permission group is three more places: PERMISSION_GROUPS and DEFAULT_PERMISSIONS in packages/protocol/src/capabilities.ts, and the label and one-line detail the user reads, in AccessChip.tsx and SettingsView.tsx. Existing installs pick up the default, so nothing needs migrating.
Implement AgentAdapter in packages/app/src/main/agents/ and add it to discovery.ts. An adapter's whole job is to start the CLI the user already has, point it at the Luu Code MCP server, and translate its output into AgentEvent. It must not hold credentials, proxy a model, or make decisions on the agent's behalf.
- Keep changes focused. Avoid unrelated refactors in the same pull request.
- Fail clearly. Every Roblox-facing failure should carry a code an agent can act on and, where possible, a hint naming what to try instead.
- Do not guess at a target. Ambiguity is an error, not a coin flip.
- Do not report a partial success as a success.
- Probe capabilities rather than assuming them. Studio differs across versions, platforms, and run states.
- Prefer a structured Studio API over desktop automation whenever both would work.
- Keep the surface small. The product is the Roblox Studio connection, not a general local coding environment.
pnpm check # build, typecheck, test, and the plugin checksOr individually:
pnpm test
pnpm typecheck
cd plugin && luu run checkThe server suite runs a simulated Studio plugin through the real transport: the handshake, the sync loop, command round trips, and the failure paths. If you change transport or routing behaviour, extend it.
Describe intent:
plugin: read script source through ScriptEditorServiceserver: route commands by realm instead of recencyapp: name a thread from its first message
Include a clear summary, why the change is needed, and what you ran to check it. If your change touches Studio behaviour, say which Studio version you tested against.