An agent reads CLAUDE.md once, at the start, and has usually stopped using it by the moment a rule applies. So it writes session notes into CLAUDE.md, where they become orders. It hands a worker a note template and gets the template back, copied into every prompt. It ships a skill that nothing ever loads, and calls a screen fixed without looking at the screen. Each of these happened here, more than once. A longer CLAUDE.md does not help: the rule is there, just not in front of the agent when it acts.
This plugin puts the rule there. A hook watches for the moments those mistakes happen and gives the agent a short block (300 to 630 characters) right then: an edit to CLAUDE.md, a brief handed to a worker, a new skill, a hook, a test or an eval. Each block names one skill. The skill holds the depth and loads only when its moment comes.
| skill | what it holds | named at |
|---|---|---|
| /agentic-engineering:context-engineering | what an agent's window holds, when, and in what words: any text an agent reads | an edit to CLAUDE.md, a skill, an agent or a prompt; a brief to a worker |
| /agentic-engineering:harness-engineering | everything around an agent except the model, in six parts: instructions (what reaches it before it acts: channels, files, hooks), checks (denies, lints, tests, baseline lints), tools (CLI commands), verification (how it tests its own result), state (what is kept across sessions and compactions) and maintenance (measuring pieces and removing the ones that no longer help); what a harness is on its front page, the published sources and their evidence (references/field.md), a hook template (assets/hook.py) |
every start; wiring a hook or a setting |
| /agentic-engineering:skill-development | whether it should be a skill at all, then designing, delivering, proving and retiring one | a new SKILL.md, agent or command; a plugin listing |
| /agentic-engineering:agent-development | an agent file: which kind (subagent, fork, teammate, the session itself with --agent), its frontmatter, its body, memory, MCP servers, and how Claude Code runs it (background, nesting, limits, resuming) |
no block yet: one comes with its eval case (plugins/EVALS.md) |
| /agentic-engineering:evals | which eval or test answers which question: unit, contract, claude -p, tmux, sandbox rehearsal, eval cases |
writing a test or an eval; starting Claude from a script or tmux |
| /agentic-engineering:sandboxing | trying a plugin where it can't reach the real setup: the level (container, macOS VM), what data goes in and how (snapshot, read-only), reading back what it changed | no block yet: one comes with its eval case (plugins/EVALS.md) |
| /agentic-engineering:fewer-faster-tests | a project's test suite under an agent, on any stack: how long checking one change takes, which tests can fail for a reason and which cannot, what is missing as well as what is extra, whether the agent can run the app and see the result, whether a change runs only its affected tests; options with their trade-offs, the person chooses | no block yet: one comes with its eval case (plugins/EVALS.md) |
Skills link each other inside this plugin, where they always ship together; a skill from outside the plugin or workspace may not be installed, so none is named. When two skills need one fact, it lives in one of them and the other links it.
agentic-engineering:agentic-engineer (agents/agentic-engineer.md) is a subagent to hand agentic-engineering
work to; @agent-agentic-engineering:agentic-engineer calls it by name. It starts with context-engineering and
skill-development loaded whole (skills: in its frontmatter) and can load the other two through the Skill tool.
The skills are its instructions; its body holds one step, the check before it reports agent text done: the
checklist in context-engineering § Before a text is done, then a review by a subagent that has not seen the
conversation, with a fixed brief (tasks walked through the links, each Claude Code claim against the docs, facts
stated twice, steps the reader cannot run, metaphors). The blocks still reach it, because the plugin's
hooks fire inside subagents too.
Three commands in hooks/new.tsx. /new-plugin makes your own plugin: one folder, tracked with git, for the
skills and agents you want in every project. /new-agent and /new-skill make a new agent or skill file, by
default in that plugin.
/new-plugin my-tools (or /new-plugin --name my-tools --folder ~/code/my-tools, or a form)
├─ writes ~/my-tools-claude/: .claude-plugin/plugin.json, .claude-plugin/marketplace.json (a marketplace of
│ this one plugin, source "./"), skills/, agents/, README.md (templates/plugin-README.md), .gitignore,
│ AGENTS.md (templates/plugin-AGENTS.md: where things go, and /plugin-authoring and
│ /agentic-engineering:harness-engineering for advanced customization), CLAUDE.md (@AGENTS.md: Claude Code
│ reads CLAUDE.md, not AGENTS.md)
├─ runs git init · claude plugin marketplace add ~/my-tools-claude · claude plugin install my-tools@my-tools --scope user
├─ keeps {name, folder} in this plugin's store, then runs /reload-plugins
└─ you see created your plugin my-tools at ~/my-tools-claude: /new-skill and /new-agent write there now
- Live: Claude Code reads a plugin from a directory marketplace's folder, not from a copy (
claude plugin updatesays so on 2.1.294): an edit there loads at/reload-pluginsor the next session. - Refused: a folder with other files in it, a name another plugin or marketplace has, a bad name. A failing
claude pluginstep stops it with that command's last line, and no plugin is kept. - A folder that already holds the plugin (a clone on a new machine, a run that stopped half-way): it is added and
installed, and only a missing
marketplace.jsonis written. Nothing else in the folder is written over. - Names: Claude Code puts the plugin's name before each skill and agent in it (
/my-tools:release-notes).
/new-agent and /new-skill make a new agent or skill file, three ways:
/new-agent --name code-reviewer --description "Reviews diffs for bugs"
├─ writes .claude/agents/code-reviewer.md from agent-development's _template.md:
│ name and description set, every other field commented out, the body a placeholder
├─ you see agentic-engineering: created .claude/agents/code-reviewer.md
└─ your next prompt carries one line of context for the model, which you don't see, saying what was created
/new-agent a reviewer for pull requests
└─ runs /agentic-engineering:agent-development a reviewer for pull requests for you:
Claude starts at once, the whole skill loaded (/new-skill: …:skill-development)
/new-agent
└─ a pane opens, the cursor in Name
✻ New agent
Writes .claude/agents/<name>.md from the agent template.
Name: code-reviewer
Description: Reviews diffs for bugs, after each change
[ Create file ] [ Design with Claude ]
Tab next field · Enter create · Esc cancel
Create file (or Enter in a field) writes it, as the flags do; Design with Claude runs the skill
command with the description and "(name: …)", or "a new agent" when both are empty
- Flags:
--nameand--descriptioneach take the words up to the next flag; quotes keep words together;--name=code-reviewerworks too.--descriptionalone is refused: a name is needed. - Name: lowercase letters, digits and hyphens;
Code ReviewerandCode_Reviewerbecomecode-reviewer. A bad name is refused (in the pane, shown there, the pane staying open). An empty description becomes a TODO line. - The template is the skill's own
_template.md, read from the plugin at each run. Its links point inside the skill, so in the copy each becomes the skill's command and the file's path in it.tests/test_new_templates.pychecks a template still has what the command replaces: one single-linename:anddescription:, and links that resolve. - Never overwrites: an existing file is left untouched, and the command says so.
- Where: with a plugin of yours, in it (the same path without
.claude/:skills/<name>/SKILL.md,agents/<name>.md), then/reload-pluginsruns so it loads now; the form's Where row, or--where project, writes under the session's working directory instead. Without one, under the working directory: a.claude/agents/that did not exist when the session started is not watched, and the new agent loads in the next session. A plugin whose folder no longer holds itsplugin.jsonis ignored. Design with Claude and free text add where the file goes to the words:(put it in my plugin my-tools: <folder>/agents/<name>.md). - Headless (
claude -p, the SDK): the flags and free text work the same; with nothing after the command, the usage. - Runs the moment you type it, even while the agent is mid-turn.
Where the pane sits is Claude Code's choice, not the plugin's (the pane's placement): in the fullscreen
renderer from 110 columns it is dock, a side panel beside the transcript on a grey background Claude Code draws,
and the form draws its own border; in the classic renderer (/tui default, or CLAUDE_CODE_NO_FLICKER=0) it is
inline, above the prompt in Claude Code's own frame. Colors are theme keys (claude, subtle, error), so the
form follows the user's theme.
The skill loads because its slash command runs, not because Claude chooses it: the module calls
$.command.run after the command has answered ($.clock.after), since Claude Code refuses command.run from
inside a command.run hook.
A Claude Mod is a plugin's TypeScript hooks module, run inside Claude Code: "modules" in hooks/hooks.json,
beside the Python hooks. Its API is in Claude Code's built-in skill, /plugin-authoring. Its hooks (on('command.run', …)) call Claude Code through $. A skill could not do
this: a skill's text always goes to the model, and a hook that blocks a skill's expansion shows
"UserPromptExpansion operation blocked by hook" and drops its additionalContext.
- The module loads only with
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1; without it the commands are missing and the rest of the plugin works as before./new-agentmissing from the/menu means it did not load:claude plugin validate plugins/agentic-engineeringlists the module and the hooks the loader sees. - A skill or command of yours named
new-agent,new-skillornew-pluginwins: Claude Code refuses the plugin's command at session start. - Tested on Claude Code 2.1.292;
/new-pluginon 2.1.294. The mods API is early access.
From the repo root, in order:
claude plugin validate plugins/agentic-engineering
npx -y -p typescript@5 tsc -p plugins/agentic-engineering/tsconfig.json
cd plugins/agentic-engineering && CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude plugin test .
tscreads the API fromplugins/agentic-engineering/.claude/types/(git-ignored):/plugin-typeswrites it, or copy~/.claude/plugins/marketplaces/claude-code-plugins/mods/types/claude-code.d.tsthere.claude plugin testreads its own arguments: nothing may come beforeplugin test. With the kit's cc-shim in front ofclaude, which adds a flag, prefixCC_SYSPROMPT_FILE=/dev/null.tests/new.test.tsruns on cut-down templates (a mod's test has no disk);uv run pytest -qchecks the real ones.
mdmap moved into the vault plugin on 2026-09-30: ak vault map <dir> (ak mdmap still answers,
hidden), source plugins/vault/src/vault/map.py. It reads a [[name]] through the vault's own link
engine, so there is no resolver copy here any more.
Each block is one plain-text file in prompts/, printed whole by a hook. It arrives once per agent
(the main session and each worker), and again after a compaction.
| block | arrives |
|---|---|
session.txt |
at every start (new, resume, clear, compact, fork) and in each worker (hooks/session.py) |
instruction-files.txt |
before a Write or Edit of CLAUDE.md, AGENTS.md, CLAUDE.local.md, .claude/rules/* |
agent-text.txt |
before an edit of an existing SKILL.md, a skill's references/, agents/*.md, commands/*.md, a plugin's prompts/* |
skills.txt |
before writing a new SKILL.md, agent or command; an edit of .claude-plugin/*.json |
hooks.txt |
before an edit of hooks.json, a hook script, .claude/settings*.json |
evals.txt |
before an edit in a plugin's tests/ or evals/; a claude -p, claude plugin eval, env … claude -p or tmux command |
brief.txt |
before an Agent, Task or Workflow call (not a Workflow resume) |
Every block except session.txt comes from hooks/on_edit.py on PreToolUse. Bash reaches it through
one handler per if rule in hooks/hooks.json (claude *, env *claude *, tmux *), so every
other command starts no process. The script re-reads the command, since the rule is best effort.
Who gets blocks. A person's session and every worker it starts. A program's session (claude -p, the SDK, CI) gets the start line and nothing else: no block on an edit. An eval opts
in with EVAL_AK_CAPTURE=1 (AK_CAPTURE=1 anywhere else). claude -p --bare skips every hook.
After the edit. When a CLAUDE.md, AGENTS.md or CLAUDE.local.md has been written, a check
tells the model what it found: over 200 lines, an AGENTS.md Claude does not read, a link that does
not resolve. The person sees <path> edited (+n −m) (hooks/after_edit.py).
Not seen. An edit made through Bash (sed -i, a heredoc). A script that launches claude. A tool
description.
claude --plugin-dir plugins/agentic-engineering
Run it as a person's session, not -p. Then:
- Ask it to add a line to a CLAUDE.md. The instruction-files block arrives once. Ask a subagent to do the same, and it gets the block too.
- Ask it to run
claude -pon something. The evals block arrives before the run.
Headless, the way the evals run it. Unset CLAUDECODE when you start it from inside Claude;
--setting-sources local keeps your other plugins out of the run:
EVAL_AK_CAPTURE=1 claude -p "add a line to CLAUDE.md" --plugin-dir plugins/agentic-engineering \
--allowedTools Edit --setting-sources local
Each block leaves a record in the transcript of the agent that got it:
~/.claude/projects/<project>/<session>.jsonl, and a worker's under <session>/subagents/. Grep it
for the block's tag, for example [agentic-engineering: evals].
- A block given as context is an attachment of type
hook_additional_context. - A skill's real loads are Skill tool calls in the transcripts, not its line in the listing.
In the kit workspace, ak plugin log agentic-engineering shows the same for one session, and
ak plugin inspect agentic-engineering counts how often each hook fired and what it cost.
evals/<case>/ holds prompt.md (the request and its settings), scaffold.sh (the repo the agent
works in) and graders/ (one check per file; the score is the share that pass). Every case is born
from a session that went wrong and keeps its story: the session, the person's words, what it guards
(plugins/EVALS.md). It runs red on the release before the change that makes it pass.
claude plugin eval plugins/agentic-engineering --scaffold --allow-tools Write Edit \
--model sonnet --runs 3 --trust-plugin --no-publish
- Each case sets
EVAL_AK_CAPTURE=1. An eval runs asclaude -p, a program's session, which gets no block without it, andclaude plugin evalpasses onlyEVAL_*variables to a case. - Eval runs set
CLAUDE_CODE_DISABLE_CLAUDE_MDS=1, so each case copies its scaffold's CLAUDE.md intoappend_system_prompt. Keep the two in sync. claude-md-is-a-promptalso carries memory's start text, rendered from this checkout byevals/memory_line.py --write. Its scaffold fails a run whose copy is stale.- Bash is left out of
--allow-tools: on a machine where~/.docker/binholds symlinks, the harness refuses any run that grants it. A case that needs Bash (claude -p, tmux) runs throughevals/run.pyinstead.
uv run pytest -q runs the hooks the way Claude Code runs them.