thismoon / mods
CLAUDE.md2 starsChanged 10 days agoBSD-3-ClauseView on GitHub
Claude Code mods: in-process plugin modules that draw bands, panes, and toasts and observe events, beside the skills, hooks, and services in this repo. A mod may also carry a pi face, loaded by the pi coding agent from the same directory. The decision and its limits are in docs/adr/0021-mods-additive-claude-ui-layer.md and docs/adr/0023-mods-pi-face.md; read both before adding one. One complete plugin per directory under mods/<name>/: A mod isn't a component: no Makefile, no release-please entry, no name/vX.Y.Z tag. plugin.json carries the…
In this repository13 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- webkit/CLAUDE.mdCLAUDE.md
- llms.txtllms.txt
- askSkill
- golang-proSkill
- golang-styleSkill
- handoffSkill
- harvestSkill
- humanizerSkill
- loomSkill
- presentSkill
- upgrade-noteSkill
- worklogSkill
The file
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 25
Every hook that does I/O or sits on a guard-shaped event carries .catch, which logs with $.ui.log(text, { to: 'debug' }) and returns …
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md - mods
Claude Code mods: in-process plugin modules that draw bands, panes, and toasts and observe events, beside the skills, hooks, and services in this repo. A mod may also carry a pi face, loaded by the pi coding agent from the same directory. The decision and its limits are in docs/adr/0021-mods-additive-claude-ui-layer.md and docs/adr/0023-mods-pi-face.md; read both before adding one.
Layout
One complete plugin per directory under mods/<name>/:
.claude-plugin/plugin.json name, version, description, author, optional types
hooks/hooks.json { "modules": ["./register.tsx"] }, one module
hooks/register.tsx export const register: Register = (on, options) => { ... }
lib/*.ts pure helpers, no `$`, no pi API, imported relatively by both faces
pi/index.ts optional pi face: export default (pi: ExtensionAPI) => { ... }
types/index.d.ts the PluginState contract when the mod keeps `$.state`
test/*.test.mjs node --test over lib/ and, when present, pi/ (a fake `pi` object)
README.md under 60 lines: what it shows, signal sources, dev loop, what it never doesA mod isn't a component: no Makefile, no release-please entry, no name/vX.Y.Z tag. plugin.json carries the version, and a merge to main is the deploy, the same as a recipe.
Rules
- Additive only. No skill, hook, or service may require a mod. A mod reads what they already produce (events, tool results, answer text) and never changes their contract. belt remains the fail-closed guard layer and the only enforcement a rule may rely on; a mod hook fails open on a throw or a timeout. A mod may return a deny in two cases only: the user's own Cancel in a hold dialog, or a convenience guard. Such a guard fails open, is off by a config key, and duplicates no belt guard.
- Every hook that does I/O or sits on a guard-shaped event carries
.catch, which logs with$.ui.log(text, { to: 'debug' })and returnsnext(e). The engine gives a hook 10 s; past it the hook is skipped and the chain goes on without it. - Localhost probes have a 400 ms budget (
Promise.raceagainst$.clock.sleep) and stay silent when the service is down: a debug line at most, never a toast, never a blocked turn. Work that outlives a dispatch starts from$.clock.afteror$.clock.everyinsession.start. - Never hook an event the built-in guard skips for the user tier:
classic.*,prompt.compose,prompt.context,prompt.section,prompt.attachment,skill.prompt,session.compact,session.append,settings.read,attribution.text.cc-plugin-sec-default@builtinis seated under managed settings or a Team or Enterprise login, and then it forwards these past every user-tier mod and the hook never runs. The rule holds on every machine, seated or not. Usetool.callinstead (before or afternext; a settings-hook deny comes back fromnext(e)as an errored result),turn.startorturn.complete, thecontextaprompt.submithook attaches, or a prompt rewrite onagent.spawn. CI fails on a hit undermods/*/hooks/(.github/scripts/mods-skipped-events.mjs). - Observe, then pass on. A
tool.callobserver callsnext(e), reads the result, and returns it unchanged. Aui.renderhook onAbovePromptincludesawait next(e)in its tree so a later mod's band survives. claude plugin validatereads the module statically and refuses what it cannot follow. Keep event names string literals and spell$.noun.methodin full (never destructure$). Pass$only to functions declared at the top of the module, import relatively, and register one hook per event and matcher. Write$.statekeys as literals matching the contract.- State lives in
$.state, declared under the mod's name intypes/index.d.tsand named inplugin.jsonas"types". Module variables reset on every hot reload;$.statesurvives a reload and a compaction./clearputs every value back to its default (the mods test docs, https://code.claude.com/docs/en/plugins/mods/test, say a test starts the way/clearleaves state). - Public repo. No employer names, internal hosts, or ticket prefixes other than
MAD-. The belt write guard denies such writes; remove the name rather than working around it. - Two harnesses, one lib. Claude Code loads
hooks/register.tsxthrough the marketplace; pi loadspi/index.tsthrough the package manifestmods/package.json(pi.extensions: ./*/pi/index.ts), declared in pi'ssettings.jsonby the consuming dotfiles. Each face owns its harness's API and importslib/only; neither imports the other, andlib/imports neither API. pi has no hook chain, so a pi face observes split events (tool_call,tool_result) or the extension bus (pi.events, e.g.belt:denyfrom the permission gate in the belt pi package,tools/belt/pi) instead ofnext(e). A pi face follows the same additive rules as the Claude face: no deny except the person's Cancel, fail open,hasUIguard before any dialog or status. A mod may have only a Claude face. Codex never loads a mod; anything it needs goes in a skill.
Naming
Directory, plugin.json name, and the plugin field of every $.state reference are the same kebab-case string. Debug lines start with <name>:.
Dev loop
claude --plugin-dir mods/<name> # from a checkout; a save hot-reloads the module
claude --debug --plugin-dir mods/<name> # the debug log names every skipped hook and refused tree
claude plugin validate mods/<name> # before every commit
node --test mods/<name>/test/*.test.mjs
pi -p --no-session -e mods "hi" # loads every pi face from the worktree for one run; errors print to stderrOn load the engine writes .claude-plugin/types/ and a tsconfig.json beside the mod, both ignored by git. claude-code/index.d.ts there is the API for the build you run and wins over any doc; npx -p typescript tsc -p mods/<name> type-checks against it. claude plugin test mods/<name> runs *.test.ts files against the engine's test kit when a mod needs event-level tests.
Marketplace
.claude-plugin/marketplace.json at the repo root, named thismoon, lists every mod with a relative source: "./mods/<name>". mods/package.json is the pi side: a local pi package whose pi.extensions glob finds every pi/index.ts, so a new pi face needs no manifest edit either. The four planned mods are listed from the start (three as stubs), so building one of them never touches the file. A fifth mod adds its own entry; CI fails when a mods/* directory is missing from the list. Registering the marketplace on a machine (claude plugin marketplace add ~/.config/ralph/sources/thismoon) and enabling mods through enabledPlugins is machine-private wiring and lives in the consuming repo (ADR-0006), never here.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

