Skip to main content
ClaudeWave
Skill82.2k repo starsupdated 2d ago

compose-atoms

Decompose a heavy domain feature into mountable capability atoms. Use when a Viewer/Page/index.tsx owns fetch, filters, mutations, modals, and host integrations together; a visual split still leaves store calls and actions on the page; a portal, embed, share, mobile, or micro-app needs a subset of the same domain; or a new capability is landing as another `readOnly`/`mode`/`variant` flag. Triggers on `compose-atoms`, sink state, 状态下沉, 重业务拆分, 拆成原子, 原子组件, 组装, god component, fat viewer, module graph, slot composition, host seam.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/lobehub/lobehub /tmp/compose-atoms && cp -r /tmp/compose-atoms/.agents/skills/compose-atoms ~/.claude/skills/compose-atoms
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Compose Atoms

A heavy domain is a **kit**, not a viewer with modes. Each host is an import list. An atom owns the data, actions, and dependencies of one capability. The assembler only chooses what to mount.

This is a **module-graph** split. Hiding UI does not drop a module. A host that never imports a file is the only host that does not ship it.

Do not use this skill to slice one component into smaller files. That is `react`.

## When

The folder is a product surface (Conversation, Acceptance, Document, Verify, Task, …) and at least one of these is true:

- One entry owns read + write + workflow + host integration.
- A second host already exists or is the next change (page, portal, share, mobile, popup, micro-app).
- The next feature is another boolean / `mode` / `variant` on the fat tree.
- Reusing a header, list, or card would import trays, stores, or chat.

If the file is large but has **one** capability and **one** host, stop. Use `react`.

## Grain

An atom is the **smallest unit a host is allowed not to mount**.

Ask, for every chunk: _would any host ship the rest and skip this?_ If yes, it is an atom. If no, it stays inside its parent.

That grain is coarser than a visual section and finer than the whole page.

| Too coarse                         | Right grain                         | Too fine                                      |
| ---------------------------------- | ----------------------------------- | --------------------------------------------- |
| One `Viewer` with `readOnly`       | Identity, goal, list, decision, …   | Every badge, row, and icon as a public export |
| `Chat` as a single import          | List, composer, intervention, …     | Every message sub-row the page then rewires   |
| `Document` as editor + every panel | Canvas, header, comments as omitted | Internal toolbar buttons                      |

A **workflow** (focus review, thread, publish) is one atom to hosts that skip the whole mode. Inside it, keep splitting only if a host would mount a piece of that workflow alone.

## Sink State

The split is worthless if the assembler still holds the domain state. **Imports follow the hook.** A `useStore` / `useX` / `handleAccept` left on the page keeps that module (and everything it imports) on every host that mounts the page.

Sink everything the atom needs into the atom:

| Sinks into the atom                        | Stays on the assembler                        |
| ------------------------------------------ | --------------------------------------------- |
| Resource fetch                             | The record id / scope identity                |
| Derived view model (counts, labels, scope) | Which atoms are mounted (the import list)     |
| Transient UI (filter, collapse, pending)   | Host layout state (focused vs overview, rail) |
| Mutation handlers and their stores/modals  | Host seams (provider value the runtime owns)  |

```tsx
// wrong — visual split, state still on the page
const Page = () => {
  const { data, mutate } = useX(id);
  const [filter, setFilter] = useState('all');
  const accept = () => void mutate(...);
  return (
    <>
      <Identity data={data} />
      <List data={data} filter={filter} onFilter={setFilter} />
      <DecisionBar onAccept={accept} />
    </>
  );
};

// right — each atom reads and acts; page only assembles
const Page = () => (
  <Scope id={id}>
    <Identity />
    <List />
    <Decision />
  </Scope>
);
```

Do not lift "to fetch once" or "so siblings share data". Two atoms calling the same SWR hook share the cache by key. Sibling writes go through that cache (or a store slice the write atom imports), not through page `useState`.

Lift only when the state **is** the assembler's job: which workflow is on screen, whether a rail is open. If a value is used to render or mutate one capability, it belongs in that capability.

## Kit

| Piece            | Owns                                              | Must not own                                       |
| ---------------- | ------------------------------------------------- | -------------------------------------------------- |
| Domain primitive | Types, predicates, formatters                     | React, stores, services                            |
| Data access      | One resource, one file                            | Sibling list / infinite / document hooks           |
| Read atom        | Present + local UI state + its own fetch          | Mutations, trays, owner services, host stores      |
| Write atom       | One mutation and its UI                           | Page layout, unrelated writes                      |
| Workflow atom    | A mode assembled from other atoms                 | Becoming the only entry other hosts can import     |
| Domain slot      | `ReactNode` hole for another **same-domain** atom | A callback whose implementation lives in this file |
| Host seam        | Optional host-provided UI, **null by default**    | A fallback that imports the host graph             |
| Assembler        | Layout + the import list                          | Domain actions; domain fetch except local chrome   |

A visual block that both reads and writes is a read atom + a write atom, joined by a domain slot.

Two seam types — do not mix them:

- **Domain slot** — same feature, optional capability (`editSlot`, `toolbar`). The host imports the write atom and passes it in.
- **Host seam** — only some runtimes can resolve it (chat store, topic drawer, Electron). Context default is `null`. The micro-app / share page never imports the provider value. See `split-micro-app` for the runtime cut.

## Ownership

State and imports live in the atom that needs them.

- Read atoms call the isolated resource hook. Filter / collapse / expand stay in the atom.
- Write atoms call the same hook, then mutate. They import their own modals and services. They return `null` when the record is not writable.
- Workflow atoms compose other atoms. They still do not become the ligh
add-provider-docSkill

Add documentation for a new AI provider — usage docs, env vars, Docker config, image resources.

add-setting-envSkill

Add server-side environment variables that control default values for user settings.

agent-runtime-hooksSkill

Agent runtime lifecycle hooks. Use for before/after tool or step hooks, tool mocks, human intervention, sub-agent calls, context compression, evals, callAgent, or lifecycle events.

agent-signalSkill

Build or extend LobeHub Agent Signal pipelines. Use for signal sources, signal/action types, policies, middleware, workflow handoff, dedupe, scope behavior, or observability.

agent-tracingSkill

Agent tracing CLI for execution snapshots. Use for agent-tracing, traces, snapshots, LLM call inspection, context engine data, agent step analysis, execution debugging, or pulling remote/production traces ("拉线上 tracing") by operation id. Also the first stop for debugging agent tool calls — wrong or missing tool_calls, unexpected tool arguments or results, which tools were available at a step, or why a tool ran where it did.

builtin-toolSkill

Build LobeHub builtin tool packages. Use when adding agent-callable tools, manifests, executors, runtimes, inspectors, renders, placeholders, streaming, interventions, portals, or tool registries.

chat-sdkSkill

Build multi-platform chat bots with the chat SDK. Use for Slack, Teams, Google Chat, Discord, GitHub, Linear bots, webhooks, mentions, slash commands, cards, modals, or streaming responses.

cli-backend-testingSkill

>