A developer built 'demonkey', a Claude Code plugin that acts as a Socratic coding tutor for learning Ruby process internals (fork, signals, sockets). The post documents four iterations of the design, each exposing a new failure mode: prose rules without enforcement, hooks that can't block prose dictation, UI features that leak answers, and quiz structures that telegraph correct answers. The core architecture uses three layers — CLAUDE.md advice, SessionStart hook injection for orientation, and PreToolUse hook denial as hard enforcement — plus plain-file state (progress.json) scoped per project directory so sessions survive compaction and closed laptops. The post generalizes the pattern beyond education: any team workflow can be encoded as a plugin with skills, hooks, and disk state, then distributed via a private git repo acting as a marketplace with no registry service required.

23m read timeFrom blog.codeminer42.com
Post cover image
Table of contents
Try one: the rule, and the wall that makes it trueTry two: the tutor found the gap the hook couldn’t coverTry three: the harness is bigger than the pluginTry four: when you can’t trust the prompt, test itWhat the plugin actually isHow it knows where you were three weeks agoThis is not about teaching RubyWhy this matters if your company already ships Claude CodeOne dojo, then a registryMake the answer no

Questions this post answers

How do I make a Claude Code hook deny a tool call and tell the model what to do instead?

Return a JSON object with `permissionDecision: "deny"` and a `permissionDecisionReason` string, then exit 0. The reason text is the only thing the model sees after the block, so it functions as a redirect prompt — writing 'explain the docs, let the learner type it, then review specific lines' produces cooperative behavior, while writing only 'forbidden' produces retries. The hook script never needs to return a non-zero exit code. Teams encoding workflow rules as Claude Code hooks track patterns like this on daily.dev.

How do I persist state across Claude Code sessions so the agent remembers where it left off after context compaction or a laptop restart?

Store state in a plain file inside the project directory, not in ~/.claude. A SessionStart hook reads that file before the model wakes up and injects the current state via `additionalContext`. Because the file is scoped to the project folder (using `CLAUDE_PROJECT_DIR`), separate project directories start fresh independently, the same way `.git` directories work. No database or context-window memory is needed. Developers building multi-session Claude Code workflows share approaches like this on daily.dev.

How do I distribute a Claude Code plugin to my whole team without a registry service or asking everyone to configure things manually?

Drop a `marketplace.json` file in any git repo listing your plugins by source path. Team members install with `/plugin marketplace add owner/repo` then `/plugin install pluginname@repo` — GitHub shorthand, any git URL, self-hosted GitLab, a local path, or a plain URL all work. For ephemeral use, `--plugin-dir ./myplugin` skips installation entirely and shadows any installed plugin of the same name. Engineering teams standardizing on Claude Code workflows find distribution patterns like this on daily.dev.

1 Impression