Repository navigation
Plugin Layout
This page tells how the repository arranges its files in 1.0.0.
Read it before you add a file, a slice, or a plugin.
It covers the source in src/, the generated plugins in plugins/, and the folders for tools, tests, the wiki, and the spec.
It does not cover how the parts work at run time.
That is on Architecture.
The decisions behind the layout are on Decisions-Layout.
| Path | What it holds | Tracked |
|---|---|---|
src/ |
The source of each plugin, the kernel, the texts, and the wiki sources. | Yes |
plugins/ |
The plugins that users install. The generator writes them. | Yes, generated |
tools/ |
The scripts of the repository, and the generator in tools/generate/. |
Yes |
tests/ |
The tests, one folder for each plugin, and kernel/, support/, and tools/. |
Yes |
wiki/ |
The pages of the wiki. | Yes |
.claude-plugin/ |
The marketplace, marketplace.json. Hand-written. |
Yes |
.claude/rules/ |
The rules that Claude Code loads for matching paths. | Yes |
.dotclaude/spec/ |
The spec of each change, such as changes/1.0.0/. |
Yes |
schemas/ |
The settings schemas, written by just schema. |
Yes, generated |
docs/ |
A link to the wiki only. | Yes |
external/ |
Local sources, such as the extracts of the Claude Code binary. | No, git ignores it |
Each file under plugins/ names its source in a header, except a JSON file.
A file in plugins/ that has no source fails just generate --check.
tools/generate/stray.ts makes this check, and a file that git ignores is not checked.
The repository root is the marketplace root.
.claude-plugin/marketplace.json lists six plugins in this order: dotclaude, dotclaude-codegraph, dotclaude-browser, dotclaude-docs, dotclaude-sessions, and dotclaude-lab.
| Plugin | Source | Tests | Page |
|---|---|---|---|
dotclaude |
src/plugins/dotclaude/ |
tests/dotclaude/ |
Architecture |
dotclaude-codegraph |
src/plugins/dotclaude-codegraph/ |
tests/codegraph/ |
Code-Graph |
dotclaude-browser |
src/plugins/dotclaude-browser/ |
tests/browser/ |
Browser |
dotclaude-docs |
src/plugins/dotclaude-docs/ |
tests/docs/ |
Library-Docs |
dotclaude-sessions |
src/plugins/dotclaude-sessions/ |
tests/sessions/ |
Sessions |
dotclaude-lab |
src/plugins/dotclaude-lab/ |
tests/lab/ |
Lab |
The test folder has no dotclaude- prefix.
Each add-on plugin imports nothing from dotclaude.
It imports nothing from another add-on either.
src/kernel/ shared domain code and ports, with no I/O
src/kernel/node/ adapters of the Node and Bun runtimes
src/fragments/ parts that more than one text repeats, no output of their own
src/wiki/ the sources of the generated wiki pages
src/plugins/<plugin>/plugin.ts the manifest data: name, description, keywords
src/plugins/<plugin>/slices/<s>/ one capability of the plugin (see below)
src/plugins/<plugin>/hooks/hooks.json classic hooks of an add-on, with its texts
src/plugins/<plugin>/evals/ eval cases of the plugin, when it has some
The core plugin has 15 slices.
Each add-on has one to four slices.
The list of slices is in Architecture.
You do not edit a list of slices, because the generator finds each slice.ts.
The slice handoff shows the usual parts.
src/plugins/dotclaude/slices/handoff/
slice.ts the manifest: the name, and each part that the slice adds
limits.ts the bounds of the slice
domain/ pure code: data and functions, no I/O
adapters/mod.ts mod handlers, when the slice has any
adapters/cli.ts a CLI, when the slice has one
skills/handoff/SKILL.md a skill, one folder for each skill
lab/handoff.test.ts a hook lab test
Other slices add these parts when they need them.
| Part | Use | Output in plugins/<plugin>/
|
|---|---|---|
slice.ts |
Always. It exports slice, and slice.name equals the folder name. |
Lines in plugin.json, hooks.json, and register.ts
|
domain/ |
Pure logic, with no import of an adapter. | slices/<s>/domain/ |
ports.ts |
The interfaces that the domain needs, in the slices that have them. | slices/<s>/ports.ts |
adapters/mod.ts |
The functions that a mod handler runs. |
hooks/register.ts, and slices/<s>/adapters/mod.ts
|
adapters/cli.ts |
A CLI. It exports main. |
bin/<bin>, a launcher, and slices/<s>/adapters/cli.ts
|
skills/<name>/SKILL.md |
A skill. | skills/<name>/SKILL.md |
agents/<name>.md |
An agent, in the slice agents. |
agents/<name>.md |
styles/<name>.md |
An output style, in the slice styles. |
output-styles/<name>.md |
texts.ts/ |
A folder of .md or .json texts. The generator gives a module. |
slices/<s>/texts.ts |
lab/*.test.ts |
Hook lab tests. |
tests/*.test.ts, which claude plugin test runs |
evals/<case>/ |
Eval cases. | evals/<case>/ |
limits.ts |
The bounds of the slice. | slices/<s>/limits.ts |
The domain of a slice imports no adapter.
It imports no src/kernel/node/ file either.
Biome enforces this rule with noRestrictedImports overrides for the folders under slices/.
The skills and agents fields of slice.ts declare what the slice ships.
The generator finds the texts by their folder.
So a file in the folder ships even if the field is missing.
- Choose the plugin. A capability that costs tokens when unused, or that needs an outside tool, goes in an add-on plugin (Add-Ons). A new plugin needs the next step too.
- Create
src/plugins/<plugin>/slices/<s>/slice.ts. Exportslicewithnameset to the folder name. Add each part that the slice needs, such asmod,cli, oruserConfig. The contract is insrc/kernel/slice.ts. - Add the code.
Put the logic in
domain/, and the entry points inadapters/. Export each mod handler fromadapters/mod.ts, and name it inrun. The generator finds the export name by its value. - Add the texts.
Put each skill in
skills/<name>/SKILL.md, each agent inagents/<name>.md, and each output style instyles/<name>.md. - Add the bounds to
limits.tsof the slice, and the shared bounds tosrc/kernel/budget.ts. Do not put a bound in two files. - Add the tests.
Put the repository tests in
tests/<folder>/<s>/, and the hook lab tests inlab/. Name each hook eval inevalsof its mod handler, so the generator writesevals/hooks.json. - For a new plugin, add
src/plugins/<plugin>/plugin.tsthat exportsplugin. Then add the plugin to.claude-plugin/marketplace.jsonby hand. - Run
just generate, which writes the files underplugins/. Commit the source and the generated files together.just checkthen runsjust generate --check, and it fails on a difference. - Update the slice table in Architecture by hand.
The generator fails with a message on each of these errors:
- A
slice.namethat differs from the folder. - Two mod handlers with the same event, filter, and phase.
- A CLI without
adapters/cli.ts.
The fixture of the generator tests is in tests/tools/generate/fixture/.
The import rules are in tests/support/import-rules.ts.
tests/dotclaude/imports.test.ts runs them over plugins/.
- A plugin imports nothing from another plugin,
tools/, ortests/. - A plugin imports only relative paths and
node:orbun:modules. The one allowed package is a type import ofclaude-code. - A slice imports only
src/kernel/and its own folder. - Each add-on has its own copy of the kernel files that it imports.
tools/generate/closure.tswrites these copies.
The rules for each folder are in .claude/rules/, such as slice-imports.md and plugin-imports.md.
-
plugins/<plugin>/hooks/register.tsis generated, and it is the only hooks module. It has one literalon()call for each mod handler. - A file in the import graph of the hooks module has no
node:import, nobun:import, and no use ofBun. The rule is in.claude/rules/hooks-module.md.tests/support/node-imports.test.tschecks it. - A CLI adapter runs on Bun.
So it may use
node:,bun:, andBun. The rule is in.claude/rules/cli-adapters.md.
| Code | Runtime |
|---|---|
Runtime code in plugins/
|
.ts files with erasable syntax, on Bun 1.4.3 or later. |
Tests and tools/
|
Bun 1.4.3 or later, with bun test. The version is in .bun-version. |
| Python | None now. No .py file is tracked. |
A plugin ships no .js, .mjs, or .tsx file.
A .d.ts file is allowed only when a manifest names it.
tests/support/runtimes.test.ts fails on any other script type.
Each bound has one owner. Tests pin the copies of a bound in code and config, not in prose. The values are on Bounds.
| Tier | File | Examples |
|---|---|---|
| Shared runtime | src/kernel/budget.ts |
CONTEXT_WINDOW (300,000), BASH_OUTPUT_MAX_CHARS (4,000), POLICY_TIMEOUT_MS (5,000) |
| Slice | src/plugins/<plugin>/slices/<s>/limits.ts |
JEV_TIMEOUT_MS (45,000) in the slice decide
|
| Repository | tools/limits.ts |
FILE_MAX_LINES (300), TEST_FILE_MAX_LINES (500), CLAUDE_MD_MAX_BYTES (6 KB), START_TOKENS (9,960) |
- A slice with no bound has no
limits.ts. Some slices have none, such aspromptin the core plugin, anddocsindotclaude-docs. Slicelimits.tsfiles exist for slices such asguard,handoff, andstatusline. - The first request has a range in
tools/limits.ts, fromFIRST_REQUEST_LOW_BYTEStoFIRST_REQUEST_HIGH_BYTES.just measurerunstools/measure.ts. That script passesSTART_TOKENSto its check, and it exits with that status. - A
userConfigfield inslice.tsis for a choice that the user makes. The generator copies it to.claude-plugin/plugin.json. Each option adds one dialog at install. So an option needs a real user choice.
- A normal file has 300 lines or fewer.
A test file has 500 lines or fewer.
A test file has a name that ends in
.test.ts. -
tools/validate.tschecks both bounds injust validate.tests/support/file-size.test.tspins the rule at its edges. - The bound applies to Markdown too. So a long page is split into linked pages.
- Two kinds of file are out of the bound.
A file under
schemas/is out. A file with the header ofjust generateis out too.
- Start
- How dotclaude Works
- Add-On Plugins
- How Claude Code Works
- Reference
- Rationale
- Evidence
- Development
- Archive