Clawness / openclaw
CLAUDE.md5 starsChanged 4 months agoMITView on GitHub
Orientation for agents/devs working on openclaw/. The consumer-facing overview (what it is, layout, how to build/test) is in README.md; this file is the why. It loads automatically when Claude Code works in this subtree, so it costs nothing on the root CLAUDE.md. - OpenClaw is NOT a build dependency. The host provides openclaw/* modules at runtime; we import them against the minimal ambient types in src/openclaw.d.ts, never a package. Its engine floor (Node ≥22.22.3) is stricter than what we build/test…
- Installs packages
In this repository17 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- addSkill
- audit-rulesSkill
- bootstrapSkill
- claude-mdSkill
- eval-setSkill
- messagingSkill
- openclaw-auditSkill
- perfSkill
- refreshSkill
- reviewSkill
- security-auditSkill
- statusSkill
- testSkill
- user-docsSkill
- vuln-huntSkill
- vuln-reportSkill
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 59
nothing changes Claude Code / shared-engine behaviour: All logic is OpenClaw-only — src/{compaction,install,memory}.ts (host-agnostic, …
≈ “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 — working on the OpenClaw adapter
Orientation for agents/devs working on openclaw/. The consumer-facing overview
(what it is, layout, how to build/test) is in README.md; this file is the
why. It loads automatically when Claude Code works in this subtree, so it costs nothing
on the root CLAUDE.md.
Core design rules (don't undo without reading these)
- OpenClaw is NOT a build dependency. The host provides
openclaw/*modules at runtime; we import them against the minimal ambient types insrc/openclaw.d.ts, never a package. Its engine floor (Node ≥22.22.3) is stricter than what we build/test against, and pulling it in would couple our build to an 87 MB host. Keep the ambient stub as the only type surface, and keep it accurate (see the enqueue lesson below). src/index.tsis the ONLY file that touches the OpenClaw API, and it is deliberately thin: every handler wraps its work in try/catch and fails toward doing nothing. All real logic lives inbridge.ts/translate.ts/notes.ts, which are host-agnostic and unit-tested against the real Python. Correcting a host field name touches onlyindex.tsor the keyword tables intranslate.ts, never the tested core.- We shell out to the exact
../hooks/scripts — no reimplementation. One engine, one rules corpus, one test-gated Python contract shared with the Claude Code path.
Native commands — read-only only, and that is a hard SDK limit
src/commands.ts defines host-agnostic command specs; index.ts maps each onto
api.registerCommand. Three ship, all backed by the bundled clawness CLI via
runPythonCli: /clawness-status (stats), /clawness-query <prompt> (query,
prompt forwarded as ONE positional so spaces survive), /clawness-audit-rules [flags]
(audit-rules, raw args split into flag tokens). Every command is clawness- prefixed
because plugin commands share one GLOBAL namespace with no auto-prefix and many plain
words (status, …) are reserved by core; acceptsArgs:true is mandatory or the matcher
drops args. formatCliOutput surfaces stderr on non-zero exit so a broken invocation is
visible, not silent.
add/refreshare NOT ported, and cannot be — this is verified, don't retry it. They need the model to do multi-step work with its own file tools (research docs, grep the codebase, propose, get approval, write YAML). An external plugin command cannot inject an instruction the agent runs:handlePluginCommandhands the handler a copy ofcommandBody(a string) and no handle tocommand/sessionCtx, socontinueAgent:truecontinues the agent on the user's ORIGINAL body and the handler'sreply.textis dropped on the continue branch (traced throughcommands-handlers.runtimeget-replyin openclaw 2026.6.34).runtimeContext.llm.completeis optional and side-generates without file tools/approval/doc-lookup — wrong forrefreshby design.agentPromptGuidanceis static system-prompt text on every turn — the always-on cost Clawness avoids. Soadd/refreshstay Claude-Code skills; the full write-up and the runtime evidence are in COMMANDS-PLAN.md Phase 3.
- OpenClaw skills are a separate channel from plugin commands (
openclaw skills install), so the ambition of "give OpenClaw users add/refresh" isn't dead — it just can't ride the plugin. That port is unscoped; don't assume its file format or git-install support without verifying against the SDK. - The command reply path is still owed a live pass.
openclaw plugins inspectconfirms registration, butopenclaw agent --localroutes/clawness-*to the LLM (same one-shot-CLI gap as SessionStart notes), so the actual reply must be confirmed on a real interactive host.
OpenClaw-native extensions beyond the shared hooks (1.13.0)
Three capabilities with no Claude Code equivalent, built under one hard constraint:
nothing changes Claude Code / shared-engine behaviour. All logic is OpenClaw-only —
src/{compaction,install,memory}.ts (host-agnostic, unit-tested) wired by index.ts, plus
pyhooks/*.py that reuse clawness.{trust,memory} read-only. The pyhooks self-locate
the repo root (parents[2]) and run through the same bridge as the shared hooks; they never
run on the Claude Code path. Each fails toward doing nothing. Contracts were read from the
real installed SDK (openclaw 2026.7.1-2); the findings are in EXTENSIONS-PLAN.md.
after_compactionre-orientation (src/compaction.ts). The native home for the context-watch + handoff. The compaction hooks are observe-only (=> void), so a hook cannot author a rich handoff (no model in the loop) — don't try to make it. What it CAN do: rules + ranked memory self-heal on the nextbefore_prompt_build, so we re-inject only the SessionStart-only orientation a compaction drops — a notice + the handoff and stack notes (theREORIENTATION_NOTE_HOOKSsubset innotes.ts). Do NOT re-run the full session-note set here: the once-per-project nags (changelog/claude_md/trust/git/ memory_init) key their ledgers on the session id, which a compaction can ROTATE, so re-running them re-asks answered questions. The idempotency key embeds a per-compaction marker (previousSessionId), so retries de-dupe but the next compaction re-fires.registerCompactionProvideris deliberately unused — it replaces the summarizer, not our goal.before_installtrust vetting (src/install.ts+pyhooks/install_scan.py). The event carries the artifact's on-disksourcePath; the result{findings, block, blockReason}lets us surface findings AND block. We reusescan_injection_tellsline-by-line for real line numbers. Findings are ADVISORY and always surface; blocking is OPT-IN (CLAW_INSTALL_BLOCK=1), then arms only on a CRITICAL tell (agent-hijack / exfil-host / metadata / decode-execute). Why opt-in and not the reverse (learned the hard way, live): the line-by-line regex scan false-positives on any artifact that documents these patterns. Scanning Clawness's OWN repo produced 208 findings / 37 "critical" — fromtrust.py's regexes and the security rules themselves — so a block-by-default would have blocked our ownplugins update. Same lesson asaudit-skillsbeing deliberately report-only.CLAW_NO_INSTALL_SCANturns it off entirely. This is the access-guard framing: prefer surfacing, widen block conservatively — here, so conservatively that the user must ask for it.- Memory corpus supplement (
src/memory.ts+pyhooks/memory_corpus.py). ADDITIVE, not a replacement: the ranked block still injects every turn viabefore_prompt_build; this makes the same lessons discoverable through OpenClaw's native memory search.registerMemoryPromptSection/…Supplement's builder is sync, returnsstring[], and gets no prompt — a dead end (can't call our async ranker, can't rank the turn). The fit isregisterMemoryCorpusSupplement({search, get}), which is async + query-bearing + "additive (non-exclusive)". Known limit:search/getcarry no cwd, so the supplement resolves the project from the most-recent session cwd (lastCwdinindex.ts) — correct for single-workspace; a multi-workspace live pass is owed. - Context engine (
registerContextEngine) — evaluated and DECLINED. It is an exclusive whole-transcript store (bootstrap/ingest/assemble/…). Using it means reimplementing OpenClaw's context storage and DISPLACING the host default, to do whatbefore_prompt_buildalready does by appending. More risk, no user-visible gain, and it collides with the no-core-regression constraint. Don't revisit without a new reason.
How we verify against the real SDK, without depending on it
Two offline layers plus one owed live pass:
npm run plugin:check(@openclaw/plugin-inspector, pinned devDep, ~870 KB) — statically validates the manifest,register()shapes, and hook wiring against the real SDK. CI-gated (exits non-zero on a hard breakage). Currently PASS / 0 breakages, with one non-fatal proof-gap it can't settle statically:before_tool_callblock/approval runtime semantics.- Type-check
src/against the real.d.ts, out-of-tree. Theplugin-test-apimock the docs describe is excluded from the published package (!dist/plugin-sdk/ plugin-test-api.*in itsfiles), so that route does not exist. But the package ships the real types. The verification is a throwaway:npm i openclawin a scratch dir +tsc --noEmitofsrc/against the real types, with our ambientopenclaw.d.tsremoved. Disposable step, not a dependency — and it earned its keep (see below). It pins to the LATESTopenclawon npm, so it cannot catch that an OLDER host lacks the API entirely.definePluginEntryand the wholeplugin-sdk/plugin-entrysubpath do not exist before2026.3.24-beta.2(verified: absent everywhere in a pinnednpm i openclaw@2026.3.11— the first live install target, which is why it was discovered but failed to load). That is a version floor, not an import-path bug: the import stays the focused subpath the docs mandate (rootopenclaw/plugin-sdkis the deprecated barrel, scheduled for removal), and the floor is declared asopenclaw.compat.pluginApi/install.minHostVersion>=2026.3.24-beta.2in BOTH package.json files so a too-old host is rejected at install instead of failing to load. - Live smoke test — DONE (OpenClaw 2026.7.1, git install, real agent turns). Results
under What's confirmed vs open below. Re-run against any host
>=2026.3.24-beta.2; anything older cannot load this API.
What's confirmed vs open
Confirmed offline (inspector + the type-check pass):
- The four hook names resolve:
before_prompt_build,session_start,before_tool_call,after_tool_call; plusdefinePluginEntry/register()shapes, the manifest, and theplugin-sdk/plugin-entryimport subpath — on the latest SDK only (the offline check pins latest; the API floor is>=2026.3.24-beta.2, declared in the manifests). - Our returns are valid against the real hook result types:
before_prompt_build{appendContext};before_tool_call{block, blockReason}/{requireApproval}/{params}.
The type-check caught one real bug. The session_start note path was calling
enqueueNextTurnInjection({idempotencyKey, appendContext}), but the real
PluginNextTurnInjection requires {sessionKey, text} and has no appendContext — so
notes were being dropped by the host. appendContext is correct for the
before_prompt_build result, a different shape; the two were conflated. Fixed via
buildNextTurnInjection in translate.ts (tested), and src/openclaw.d.ts now carries
the verified NextTurnInjection shape so the in-tree build enforces it. Lesson: the
ambient stub is only as good as its last verification — re-run the type-check pass after
changing anything we send to the host.
Confirmed live (OpenClaw 2026.7.1, git install, real agent turns):
- Rules reach the model.
before_prompt_buildfires and the host injects our{appendContext}into the model prompt — the mandatory block appears verbatim. The prompt arrives underprompt,resolvePromptText's first candidate, so no reorder was needed. - Access guard is honoured.
before_tool_callfires on real tool calls; a returned{block}stops the tool and surfacesblockReasonto the model, and{requireApproval}round-trips.after_tool_callsettles the ask-ledger (a repeated out-of-project write goes ask→allow). - Tool names/params. Real events: write=
{toolName:"write",params:{path,content}}, read={toolName:"read",params:{path}}; thetranslate.tskeyword/field tables map both correctly. The shell tool's name went unobserved — the model refuses the deny-tier baits before running a shell command, which is whyblockwas proved via a forced-block A/B on a benign write (file created un-forced, absent when blocked), not a real malicious command.
Still open — one path, plus the engine floor:
- SessionStart notes don't surface through the one-shot
openclaw agentCLI. Thesession_start→enqueueNextTurnInjectioncall is real and awaited but returnsundefined, and the note text never appears across turns. Root-caused as a CLI-harness limit, not our bug — next-turn injection is presumably delivered on an interactive channel, which the CLI can't exercise. Verify once on a real channel. It fails silent (a dropped note costs nothing), and the load-bearing no-Python warning also ridesbefore_prompt_build, so it lands regardless.placementstays unconfirmed for the same reason (we omit it and take the host default). - Node engine. OpenClaw requires Node ≥22.22.3; this package builds/tests on ≥22.19, but the host runtime floor is OpenClaw's.
Distribution — git install (don't undo the root-level files)
Users install with openclaw plugins install git:github.com/fullymiddleaged/Clawness.
That shape drove four decisions that look odd in isolation; each is load-bearing.
- There is a
package.jsonand anopenclaw.plugin.jsonat the REPO ROOT, not just here. OpenClaw's git installer clones the repo and reads the plugin manifest at the clone root — the docs describe no subdirectory/monorepo support — so a manifest that lives only inopenclaw/is never found. The rootpackage.json'sopenclaw.extensionspoints at./openclaw/dist/src/index.js; the rootopenclaw.plugin.jsonis a byte copy of this dir's.tests/test_openclaw_manifest.pyfails CI if the two copies drift or the entry path changes. Don't "clean up" the rootpackage.jsonas stray Node cruft in a Python repo — deleting it makes the plugin uninstallable. The subdirpackage.jsonstays too: it's the dev/build/test package (tsc, node --test, the inspector devDep). openclaw/dist/src/*.jsis COMMITTED. Git installs load prebuilt JS and do not run a build/prepare step (the docs: "TypeScript source entries are only for source checkouts and local development paths"). So the entry must be in the clone. The root.gitignorestill ignoresdist/(that's the Python sdist/wheel output) but carves out!openclaw/dist/, andopenclaw/.gitignoreusesdist/*+!dist/src/to expose onlydist/src(runtime), neverdist/test. A.gitattributespins those files toeol=lfso a Windows checkout doesn't rewrite them. The CIopenclawjob rebuilds andgit diff --exit-code dist/srcto catch a stale commit — rebuild and commitdist/srcwhenever you touchsrc/, or CI fails.- No engine vendoring — and that is the point of git-install over npm. The clone brings
hooks/,rules/,clawness/with it, sobridge.ts's../hooks/...resolves against the real engine: one source of truth, no second copy to sync. An npm package of justopenclaw/could not carry the sibling engine (files outside the package dir), which is why that option was rejected. The cost is the committeddist/srcabove. - Open — the packaging fact only a live host settles: does the installer keep the
whole clone as the plugin dir (so
../hooksexists at runtime), or prune it to the declared entry? If it prunes, every hook spawn fails silently — same signature as a Python-less box — and the fix is to vendor the engine after all. This is the FIRST thing the live pass checks; seeLIVE-TEST.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
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.

