本文へ移動
cccskills
無料GitHub で公開

moi-workspace

The moi workspace — the web UI the user chats from, extended with agent-authored applets (widgets, views) plus theme & config. Read this FIRST when a message carries a hidden moi-context envelope or the user uses moi vocab such as workspace, applet, widget, view, scratchpad, dashboard, or a `moi` command, or asks to build, edit, customize, or theme the workspace UI or its layout.

インストール方法を見る

含まれるファイル(6)

  • SKILL.md24.4 KB
  • references/COLLAB.md10.3 KB
  • references/DESIGN.md13.8 KB
  • references/INTENTS.md5.8 KB
  • references/SCRATCHPAD.md3.7 KB
  • references/UI-COMPONENTS.md3.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

moi workspace

Glossary

"Workspace" or "Moi Workspace" — the web UI that the user works in, talks/collaborates with you, sees and interacts with "Applets".

"Project" - the primary working folder you (as an agent) work in. Managed by your harness; moi does not have a clear definition, but assumes this is the root folder in which it stores its state files.

"Chat" — a conversation between the user and an agent in the workspace.

"Applets" are standalone full-stack components that you write and maintain. They extend the Workspace UI.

"Applet Type" (one of)

  • "Widgets" (live on the Overview page)
  • "Views" are custom full-size pages that user can switch between.

"Moi CLI" — the globally installed moi command that you use to build applets, customize, and send events to "Workspace".

Overview

You are working inside a moi workspace. It is a web UI that the user communicates with you through. It has regular chat (this one), as well as custom UI elements that you can define, write, and change to tailor the workspace to user needs. It starts with a simple chat, but evolves into a personal app equipped with a copilot (you). Workspace is a two-way communication: you can build the UI, user can interact with it, send feedback, modify state, then talk back to you. It's a shared UI that you and user work together in.

Workspace features/pages:

  • "Overview" - the workspace home, where small reusable full-stack Widgets surface quick info, status, and actions.
  • "Scratchpad" - a shared low-fi canvas for prototyping, working on ideas together, visualising concepts. Read references/SCRATCHPAD.md before building on or modifying it.
  • "Views" - provide a dedicated tab for sustained work, such as exploring data, filtering a table, or completing a multi-step task.

User can switch between these, but can access the chat (this conversation and other chats) from any place in the app through the docked sidebar or floating popup.

Working in the workspace

Boundaries

  • Never read or modify files outside the .moi directory, unless the user explicitly asks. If you do need it -> ask for permission.
  • Do not start, stop, or inspect the Workspace web server — it is managed externally.
  • Do not run Git commands while building or verifying applets unless the user asks for Git work.

Files and folders

Source of truth - .moi folder in the root of "Project" folder. Contains source code of all Applets, bundled code, settings, etc. Can be committed to version control. Folder is partially initialised when Workspace starts, you have full ownership of it.

You do have access to the files in the root of Project — you can reference and load them from the "Applets" and elsewhere.

Folder structure:

my-agent-folder/
  .moi/
    widgets/                  <- source code of Widget React components
      total-users.tsx
      rps-chart.tsx
      _utils.tsx              <- `_`-prefixed files are shared code, not applets (optional)
      server-metrics.server.ts <- Server-side async functions the widget can call (optional)
      ...
    views/
      users.tsx
      crm.tsx
      users-api.server.ts     <- Server-side async functions the view can call (optional)
      ...
    package.json              <- Applet dependencies that you manage
    .workspace.json           <- Auto-generated. Do NOT read, edit, or `cat` this file. Use Moi CLI instead.
    .scratchpad.json          <- Scratchpad canvas snapshot. Internal — inspect only via `moi scratch read`, never open it.
    .scratchpad/              <- Scratchpad image files. Internal — pull pixels via `moi scratch read-image`, never open it.

Every dot-prefixed file or folder inside .moi/ (.build/, .cache/, .workspace.json, .scratchpad*, .gitignore, …) is a moi internal: auto-generated and liable to change format without notice. Avoid them as much as possible — do not read, edit, or delete them, and never point tooling at them; go through the moi CLI instead. Version control needs no special handling: the scaffolded .moi/.gitignore already excludes the machine-local entries (.build/, .cache/, node_modules/), while .workspace.json and .scratchpad.json are workspace state that ships with the repo — commit them as-is, just never hand-edit them. Your surface is the non-dot files: widgets/, views/, package.json, and code you place under .moi/ yourself.

Using the moi CLI

Treat moi as an external command — you cannot inspect or modify its sources. Use only the documented subcommands (moi bundle, moi bundle --force, etc.). Call moi --help for documentation. Run all moi commands from the project root — the folder that contains .moi/, never from inside .moi/ itself. You don't pass paths; moi resolves the workspace from where it's run.

Use the task-specific sections below for workflow guidance. The CLI will grow over time, so run moi --help to discover commands and moi <command> --help before using an unfamiliar command or option.

  • Develop applets: moi check, moi bundle, and moi refresh.
  • Call actions: moi call-server-fn.
  • Debug applets: moi debug logs (see Debugging).
  • Navigate the workspace: moi tabs and moi navigate <address> (see Workspace navigation).
  • Customize the workspace: moi theme and moi config (see Appearance and settings).
  • Use workspace env: moi env and moi env exec (see Environment and secrets).
  • Maintain workspace guidance: moi skill (see Keeping this skill current).

Workspace navigation

Use moi:/ addresses in chat, CLI commands, and applets. They refer to the current workspace, independently of its domain and deployment path.

Supported destinations: moi:/overview, moi:/scratchpad, and moi:/views/<id>. Run moi tabs to discover views and their addresses. Widgets are not navigation destinations.

Put view params in the query string. Values are strings; read the target view's Params type for supported keys and use URLSearchParams to encode dynamic values.

Link in chat:

[Open order](moi:/views/orders?order=o-1024)

Navigate from the CLI:

moi navigate 'moi:/views/orders?order=o-1024'

The CLI moves the most recently focused browser showing this workspace and waits for its URL acknowledgement. A timeout may mean navigation happened; inspect the browser before retrying.

For applet navigate(url) and resolveUrl(url) usage, see Applet intents.

After building or editing an applet, follow Verification and handoff.

Appearance and settings

  • "Config": set name, icon, change other settings. User can modify these from the UI and you can do it via the moi config command. Call moi config --help for further docs.
  • "Theme": customize workspace fonts, colors, visual appearance. User can modify these from the UI and you can do it via the moi theme command. Call moi theme --help for further docs.

moi theme shows the current font, color, radius, and agent appearance plus the available keys. Set one or several dimensions in a single command:

moi theme --font=<key> --color=<key> --radius=<key> --agent=<key>

Omit dimensions you do not want to change. Inspect with moi theme first instead of guessing keys; use moi theme --help if its options change.

Environment and secrets

Each workspace has an effective env: keys from the project's .env / .env.local (when inheritance is enabled in settings) plus custom secrets the user manages in the workspace env settings. moi injects this env into:

  • applet server functions — read it as process.env inside .server.ts
  • any command run via moi env exec -- <cmd>
  • your own shell (Bash tool) — but only in some harnesses (e.g. Claude Code). Don't assume it: verify the key is visible first, or just use moi env exec, which works everywhere.

Rules:

  • Check before you assume. When a task needs a key or token — an API pull, a widget calling a service — run moi env first. It lists key names with their source (.env / custom) and flags declared requiredEnv keys that are missing. Values are never shown.
  • Key present → say which key you'll use and where it's from ("using NOTION_TOKEN from .env") and proceed. To run a script or one-off command with the workspace env, use moi env exec -- bun script.ts — it also picks up values changed after your session started.
  • Key missing → never invent or hardcode a value, and don't edit .env yourself. Tell the user the exact key name to add in the workspace env settings. Still build and wire the applet: declare the key in config.requiredEnv and handle its absence, so it works the moment the user sets it. If the user pastes a value in chat, store it with moi env set KEY=value (moi env unset KEY removes it).
  • Never print secret values — not in chat, not in logs. Refer to keys by name only.

Use moi env exec -- <command> [args...] for an arbitrary script or tool that needs the effective workspace env. The -- separator is required, and the env is resolved again on every invocation, so this also sees values added or changed during the chat.

moi env exec -- bun script.ts
moi env exec -- bun test integration.test.ts

Use moi call-server-fn instead when exercising an exported applet server function through its real runtime path.

process.env is readable only inside .server.ts (the .tsx runs in the browser) — keep API keys there. Either source may be absent, so always handle a missing key. List expected keys in config.requiredEnv — advisory only (it surfaces a hint in the UI and moi env; it's never enforced).

// forecast.server.ts
export async function getForecast(city: string) {
  const key = process.env.WEATHER_API_KEY // always current — env changes respawn the worker
  if (!key) return { error: 'Add WEATHER_API_KEY to your env' }
  const res = await fetch(`https://api.example.com/forecast?city=${encodeURIComponent(city)}`, {
    headers: { Authorization: `Bearer ${key}` }
  })
  if (!res.ok) return { error: `Weather API error ${res.status}` }
  return { data: await res.json() }
}

Developing applets

Every applet — a Widget or a View — is a default-exported React component in .moi/<type>/<name>.tsx, optionally paired with a <name>.server.ts. moi bundle compiles each into a live module and reloads it in the browser. Run it after source edits. Read references/DESIGN.md first. Write normal React + Tailwind — below is only what's moi-specific.

Build environment

  • Bun is the required dependency of moi, so it must be installed
  • For package management always use bun
  • package.json is scaffolded during init. You are free to install/remove/do whatever with packages.
  • if packages aren't installed, it's your responsibility to call bun install
  • react and react-dom are stubs — they're provided by moi at runtime via the browser importmap. They're listed only so editors pick up the correct types.
  • moi bundle runs Bun's bundler, so standard Bun imports, loaders, and tricks apply (JSON, text, etc.) — see the Bun docs. The moi-specific imports are covered below.

Anatomy and imports

// .moi/widgets/hello.tsx
import { useEffect, useState } from 'react'
import { getGreeting } from './hello.server' // optional server fn — see below

// Optional config — fields are per type (see Widgets / Views below). requiredEnv is shared.
export const config = { requiredEnv: ['API_KEY'] }

export default function Hello() {
  const [msg, setMsg] = useState('')
  useEffect(() => {
    getGreeting().then(setMsg) // call server fns like any async function
  }, [])
  return <div className="h-full w-full p-4">{msg}</div>
}

Imports resolve relatively (same folder, or elsewhere under .moi/ — e.g. ../lib/format) or from .moi/package.json deps — no @/ aliases. Files starting with _ (e.g. _utils.tsx) in widgets/ and views/ are never applet entry points — put code shared between applets there. moi bundle tracks these local imports: editing a shared module rebuilds every applet using it. Applets never import from each other, not even types.

Widgets

Live cards on the Overview grid — many visible at once. config sets the grid footprint:

export const config = {
  colSpan: 2, // columns the card spans — 1–4
  rowSpan: 1, // rows the card spans — 1–4
  requiredEnv: ['API_KEY'] // optional env-key hints (advisory; see Environment and secrets)
} as const

Render content only: a plain h-full w-full region with no card chrome (rounded-*, shadow-*, or outer border) — the dashboard owns the shell, spacing, and elevation. It does not own the fill, so the widget must set its own opaque background. Changing colSpan/rowSpan needs moi bundle --force --only widgets/<id>. See references/DESIGN.md.

Views

Views are full-screen apps, one per tab.

export const config = {
  title: 'Customer overview', // sentence-case nav label — defaults to the file name
  icon: 'user', // icon id; use the selected icon for a pending view
  requiredEnv: ['CRM_API_KEY'] // optional env-key hints (advisory; see Environment and secrets)
} as const

A view owns its whole page — its own h-full w-full layout, scrolling (overflow-auto), padding, and chrome. Build it to read like an app screen. See references/DESIGN.md.

Keep shareable or reload-safe state in URL query params, read it from params, and update it with navigate(). Keep temporary state, such as drafts and hover, in React. See Applet intents.

Creating views from chat

When the user asks for a new view, use the current session ID and pin state from the hidden context. If this chat is pinned, build in this turn:

moi views create --requirements "<complete requirements>"

Otherwise, start the build in a separate chat by forking the current session:

moi views create --from-session <session-id> --requirements "<complete requirements>"

The command returns a permanent viewId and opens its view tab before returning.

  • mode: "handoff": another chat continues the build. Finish this turn with a link such as [Open the view](moi:/views/<viewId>).
  • mode: "in-place": no chat was created or attached. Continue the build in this turn using the returned id and build instructions.
  • Navigation fails: include the same [Open the view](moi:/views/<viewId>) link in your reply.

Pending view builds

Read the applet design guidance before editing. When the direction is clear, choose a sentence-case title and relevant icon. Preserve proper names and acronyms. Set this provisional metadata before the first bundle:

moi views set <view-id> --title "<title>" --icon <icon-id>

Choose from the available view icons in the hidden context when that list is present. Then write .moi/views/<view-id>.tsx and use the same title and icon in its config. Run moi check --only views/<view-id> before moi bundle --only views/<view-id>. A successful bundle replaces the pending screen in the same tab. Keep the user's current tab open and finish with a moi:/views/<view-id> link.

Styling and UI components

  • Use Tailwind for static styling. Do not add custom CSS, @apply, or static style={{}} values.
  • Use style={{}} only for computed data such as chart geometry, progress, per-item delays, or per-frame transforms.
  • Use icons from @tabler/icons-react. Do not add raw SVG icons or another icon pack. When the project provides .agents/rules/icons.md, follow its size and stroke policy.
  • Applets cannot import the host project's cn. If .moi/ui/utils.ts exists, import cn from ../ui/utils; otherwise use a local cx() when classes are conditional. Never build class names with template-literal ternaries.
function cx(...classes: (string | false | undefined | null)[]) {
  return classes.filter(Boolean).join(' ')
}

Use bundled components for standard controls. Read the shared usage rules before composing them.

  • moi ui-components lists available components and recipes with installed state.
  • moi ui-components docs <name…> prints bundled usage docs. Read them before using an unfamiliar component or when its API may have changed.
  • moi ui-components add <name…> --install copies source into .moi/ui/ and installs npm dependencies. Pass all needed names in one call.
  • Import relatively, e.g. import { Button } from '../ui/button', then run moi bundle after editing applets.

Reuse familiar installed components without rereading their catalog entries, docs, or source. Inspect source only for local customizations, doc conflicts, or concrete build issues.

Server functions

In <name>.server.ts, export named async functions (only — no const, sync, or class) and call them from the component like ordinary async functions; arguments and return values are auto-serialized (Date, Map, Set, … work). They run on the Bun server with process.env and full filesystem access, at cwd = <workspace root> (the parent of .moi/, where you operate) — so workspace files are plain relative paths:

// hello.server.ts — read files, call APIs, query DBs…
export async function getGreeting(): Promise<string> {
  return (await Bun.file('./notes.md').text()).split('\n')[0]
}

The component fetches on mount; after you change underlying data a server fn reads, run moi refresh to re-pull it without a rebuild.

It's plain Bun — every Bun API is available with no setup: bun:sqlite, Bun.redis, Bun.s3, Bun.file, fetch, …

Files and assets

  • Bundled asset — import logo from './logo.png' resolves to a URL at build time (images & fonts: png jpg gif svg webp avif ico woff woff2 ttf otf). For small art shipped beside the .tsx.

  • Workspace file — stream a file from the workspace via resolveUrl from the moi package:

    import { resolveUrl } from 'moi'
    ;<video src={resolveUrl('moi:/files/clips/intro.mp4')} controls />
    

    Paths are relative to the workspace root. Files stream with HTTP range support for media seeking. Only media/asset extensions are served; dotfiles, source, and JSON are rejected. File URLs can be resolved at module load.

    For a dynamic path, such as one returned by a server function, encode each segment:

    const fileAddress = `moi:/files/${clip.file.split('/').map(encodeURIComponent).join('/')}`
    ;<video src={resolveUrl(fileAddress)} controls />
    

Applet intents

Use intents to make applets feel like part of the wider workspace: navigate between tabs, open another view with a specific item selected, add context to chat, or send a message on the user's behalf when they click a button. For example, a chart can open a detailed view, or an order card can ask the agent to investigate a delay. Read Applet intents for the available functions and when to use them.

Verification and handoff

Use moi check --only views/<id> or moi check --only widgets/<id> for the applet you changed. Use the kind alone only when the work spans several applets. The command owns the supported applet TypeScript setup and can gain more checks later. Do not create .moi/tsconfig.json, invoke tsc directly, or retry with ad hoc compiler flags and missing type packages.

For a frontend rebuild, use this stopping point:

  1. Run moi check --only <kind>/<id> once after source edits, then moi bundle --only <kind>/<id>.
  2. In the browser, exercise the changed interaction, such as reveal and rating, and check one narrow layout.
  3. Inspect moi debug logs --only <kind>/<id> --json for runtime errors.

Stop when these checks pass. Expand verification only when a check fails or the changed behavior needs another focused check. Do not search for repo tests by default. Run an existing applet test or moi call-server-fn only when the change touches the behavior it covers. If native-app inspection is unavailable, keep verification in the browser instead of retrying the unsupported tool.

After the final successful checks, hand off the result according to the task:

  • After building or editing a widget, run moi navigate 'moi:/overview'.
  • After editing an existing view, run moi navigate 'moi:/views/<view-id>'.
  • After a pending view build, keep the current tab open and link to moi:/views/<view-id> in the final reply.

Keep the final reply brief and user-facing. Do not include file or storage links, file paths, or bundle, test, and runtime-log summaries.

Debugging

moi bundle only proves an applet compiles — it can still fail to load in the browser, crash on render, or throw in its server functions. For frontend rebuilds, check runtime logs as required above. For other work, use these channels when smoke-testing new behavior or investigating a problem:

  • moi call-server-fn widgets/hello/getGreeting / moi call-server-fn views/crm/searchUsers '["ann", 10]' — run one .server.ts function directly (args are one JSON array). Each invocation runs in a fresh, isolated one-shot process with the same env, module loading, and timeout as the browser's calls, so a pass means the real path works — handy for trying a function without touching the UI. Server functions only; for arbitrary scripts use moi env exec.
  • moi debug logs — the applet errors the workspace has seen since each applet's last good build: browser-side load failures and render crashes, plus server-function (rpc) errors. The user's tab reports these automatically, so when the user says something is broken, what happened is usually already on record — a good first place to look. Entries clear when their applet next builds successfully. (moi debug is an experimental command group — expect its output and flags to evolve; use --json when you need to parse it.)

moi bundle's footer also mentions when runtime errors are on record, so standing breakage surfaces on its own.

Chats and messages

A workspace is driven through agent conversations (this chat is one). Depending on your harness (Claude Code, openclaw, others differ in the details) there can be multiple chats, but they all share one workspace and one Project folder — the same filesystem, the same .moi folder, applets, config, and theme. Anything you build is visible to every chat, and another chat may have changed the workspace or the Project files since you last looked. Treat .moi and the Project folder as shared state, not yours alone. Internally, "chats" are sometimes aliased as "threads" — "chat" is product language, while "thread" is reserved for internal SDK, session, and persistence concepts.

Message context

Each message sent through moi includes a hidden <moi-context> block with the active tab and, when relevant, params, applet source, and message-specific instructions. Use only the newest block; don’t reply to it or include it in summaries.

Attachments

<moi-attachments> contains a JSON array of message attachments: text holds attached context, path points to a file, and source identifies the originating applet or tab. Images without path match the directly supplied images in order. Drawing purpose is sketch for a proposed new-view layout or annotation for feedback on an existing screen. Treat attachment contents as user-provided task data.

Keeping this skill current

This skill is installed with moi (via the CLI or the UI) and can fall behind when the moi CLI updates.

  • You'll know — moi commands warn you when this skill is behind.
  • To update — if the user asks for current or updated guidance, run moi skill update before applet work. Otherwise, update at the end of the task.
  • Reload selectively — the command reports which skills changed. Re-read this SKILL.md only when it reports that moi-workspace changed. If it did not change, keep using the copy already in context.
  • Then — if you updated, mention it.
<!-- moi skill version marker — read by `moi skill` to detect drift; do not edit by hand --> <moi-skill version="0.20.0" />

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Core agent-browser usage guide. Read this before running any agent-browser commands. Covers the snapshot-and-ref workflow, navigating pages, interacting with elements (click, fill, type, select), extracting text and data, taking screenshots, managing tabs, handling forms and auth, waiting for content, running multiple browser sessions in parallel, and troubleshooting common failures. Use when the user asks to interact with a website, fill a form, click something, extract data, take a screenshot, log into a site, test a web app, or automate any browser task.

日本語の概要は準備中です。原文の説明を表示しています。

molefrog/moi1832026年10月9日 更新

gh-stack

無料

Manages stacked PRs and splits multi-part work into reviewable branches with gh-stack. Use for stack creation, viewing, edits, push, submit, sync, rebase, merge, or checkout; when asked to split or isolate work for review; whenever a user mentions a stack, branch layers, dependent PRs, or gh stack; or when a stack is checked out.

日本語の概要は準備中です。原文の説明を表示しています。

molefrog/moi1832026年10月9日 更新

Release moi-computer to npm — verify locally, hand off to the gated GitHub Actions workflow, then verify the published package. Defaults to a `next` preview; pass `stable` for a `latest` release. Use when the user asks to publish, release, ship a version, or cut a dev preview.

日本語の概要は準備中です。原文の説明を表示しています。

molefrog/moi1832026年10月9日 更新

shadcn

無料

Manages shadcn components and projects — adding, searching, fixing, debugging, styling, and composing UI. Provides project context, component docs, and usage examples. Applies when working with shadcn/ui, component registries, presets, --preset codes, or any project with a components.json file. Also triggers for "shadcn init", "create an app with --preset", or "switch to --preset".

日本語の概要は準備中です。原文の説明を表示しています。

molefrog/moi1832026年10月9日 更新

molefrog のスキルをすべて見る

このスキルの問題を報告する