There is a quiet shift happening in how serious teams use AI for software work. The novelty of “the model that writes code” has worn off. What used to feel impressive, a function generated from a prompt, a regex explained, a stack trace decoded, is now just baseline. The interesting question is no longer can the model write code. It is how do you wire the model into a real workflow without turning your team into full-time prompt operators.
That question is what brought us to test and use different solutions, one of them being Claude Code, and eventually to building our own layer on top of it. After several months, we converged on a setup we now call SHINE Code, an opinionated configuration that turns the Anthropic CLI from a smart assistant into something closer to an in-house operator. It is open-source, free, and we use it on every internal and client engagement.
This article is in two parts. The first part is a technical walkthrough of how Claude Code actually works under the hood: the runtime, the configuration files, the hook lifecycle, sub-agents, MCP servers, and the way context flows through a session. If you have only ever opened Claude Code and typed a question, this is the part that will change how you use it. The second part is the story of SHINE itself, what we built, why we built it, and the design choices that came out of running an agency through it.
Before to introduce SHINE code, we need to go under the hood of Anthropic’s Claude Code at the runtime level. We must strip away the “AI magic” to look at its architecture, its agentic loops, and the local configuration files that dictate its behavior.
👉 Source code (GitHub repository)
📖 Architecture documentation | How it works walkthrough | Customization guide
Part 1: Claude Code, actually explained
What Claude Code is, and what it is not
It helps to start with what Claude Code is not. It is not a chat website with a code editor bolted on. It is not a VS Code extension that lives in a sidebar. It is a terminal-first runtime that you launch with the claude command inside a project directory. Once it starts, it behaves more like a small operating system for an LLM than like a chat interface.
The reason this distinction matters is that almost everything interesting about Claude Code, and everything that makes building something like SHINE possible, lives in the runtime layer. The chat experience is just the surface.
When you run claude in a directory, three things happen in quick succession:
- The CLI reads its global configuration in
~/.claude/. - It loads any project-level instructions it finds (typically a
CLAUDE.mdat the project root). - It opens a session, with a defined model, a defined set of tools, a defined set of hooks, and a defined memory layout.
Every prompt you type after that is processed inside this configured session. The model is not just “Claude with a system prompt”, it is “Claude with a system prompt, plus a set of tools it can invoke, plus a set of scripts that fire at specific lifecycle events, plus a structured memory it can read and write”.
The ~/.claude directory, where the runtime lives
Most people who use Claude Code never open ~/.claude/. That is a missed opportunity, because that directory is where the entire behaviour of the tool is configured. After a fresh install, it contains a handful of files. After a serious setup, it can contain hundreds.
The two files that matter most on day one are:
settings.json, the user-editable configuration. This is where you pin the model, set environment variables, register hooks, declare which plugins are enabled, and connect MCP servers. Think of it as the equivalent of a shell rc file, but for the CLI runtime.CLAUDE.md, an instruction file that gets loaded into the system prompt at session start. This is the most important file in the entire stack. Anything you write here is in front of the model on every turn.
Around these two files, Claude Code expects (or supports) a number of conventional directories:
~/.claude/
├── CLAUDE.md # global system instructions
├── settings.json # model, env, hooks, plugins, MCP
├── agents/ # sub-agent definitions
├── skills/ # slash-command skills
├── hooks/ # lifecycle scripts
├── memory/ # typed persistent memory
├── sessions/ # ephemeral session state
└── projects/ # per-project transcripts (managed by the CLI)
You do not need any of these directories on day one. Claude Code works fine with just settings.json and a system prompt. But each one of them is an extension point, and the design of these extension points is what makes the tool genuinely powerful.
CLAUDE.md, the system prompt you actually control
Most chat-based LLM tools hide the system prompt. With Claude Code, CLAUDE.md is the system prompt extension, and you own it.
There are two layers. A global ~/.claude/CLAUDE.md is loaded for every session, on every project. A project-level ./CLAUDE.md at the root of a repo is loaded on top, and applies only to that project. The two are concatenated.
This sounds simple. In practice it is the most underused feature of the tool. People treat CLAUDE.md as a place to leave a few notes (“we use TypeScript, please follow our naming convention”). What it can be, if you commit to it, is a deterministic router. You can write rules of the form if the prompt contains X, do Y, and the model will follow them with surprising reliability, because they sit at the very top of the context window on every turn.
We will come back to this idea, because the entire SHINE system is built around it.
Tools, the actual unit of action
Inside a session, the model does not “do things” directly. It calls tools. Out of the box, Claude Code exposes a set of built-in tools: Read, Write, Edit, MultiEdit, Bash, Glob, Grep, Task, plus a few others. Each one is a typed function the model can invoke, and each call goes through a permission layer you control in settings.json.
This is the part most people miss when they compare Claude Code to a chat interface. When the model wants to read a file, it does not hallucinate the contents, it calls Read and reasons over what came back. When it wants to refactor across a folder, it calls Glob to enumerate, then Edit to mutate. When it needs to run something, it calls Bash. The model is reasoning over real tool output, not vibes.
Two consequences follow. First, the loop is genuinely agentic, in the sense we described in our earlier piece on agentic AI architectures: the model decides which tool to call, observes the result, and decides what to do next. Second, every tool call is a place where you can intervene, which is where hooks come in.
Hooks, the lifecycle events nobody tells you about
Hooks are arbitrary scripts that Claude Code runs at specific moments in a session. They are declared in settings.json, mapped to a lifecycle event, and optionally filtered by tool name. There are seven event types worth knowing:
| Event | When it fires | What you can do with it |
|---|---|---|
SessionStart |
When claude opens a session |
Load context, sync configuration, print a banner |
UserPromptSubmit |
After you press Enter, before the model sees the prompt | Inject additional context, detect intent, route silently |
PreToolUse |
Before a tool call | Block dangerous calls, scan for secrets, warn |
PostToolUse |
After a tool call | Log, monitor context size, react to results |
PreCompact |
Before the CLI compacts the conversation | Snapshot state so you can recover after compaction |
Stop |
At the end of a turn | Log metadata for later learning |
SessionEnd |
When the session terminates | Summarise, persist, hand off |
Hooks can do three things: they can inject (write to stdout in a JSON format the CLI understands), they can warn (write to stderr and exit zero), or they can block (exit non-zero on PreToolUse, which aborts the tool call and tells the model why). They cannot rewrite tool inputs in flight, which is a deliberate design choice that keeps the system easy to reason about.
This is where Claude Code stops being a chat tool and starts being a programmable runtime. A PreToolUse hook on Write|Edit that scans for secret-shaped patterns and aborts the call if it finds one is, effectively, a guardrail you control. A UserPromptSubmit hook that detects a client name in the prompt and pre-loads that client’s memory file is, effectively, a router for context.
Sub-agents, fresh context on demand
The Task tool is special. When the model calls it, it spawns a sub-agent, a separate context window with its own tool budget, its own system prompt, and its own focus. The sub-agent runs to completion, then returns a structured result to the main thread.
The reason this matters is twofold. First, context windows are finite, and long sessions degrade as the window fills. Delegating a sub-investigation to an agent keeps the main thread clean. Second, different tasks need different system prompts. A debugging agent and a documentation agent should not behave the same way, and sub-agents let you encode that.
Agents live in ~/.claude/agents/ as Markdown files with frontmatter that defines their name, description, allowed tools, and behaviour. The main thread does not call them by name in the chat: it calls the Task tool, and a router (typically driven by your CLAUDE.md rules) decides which agent gets the job.
MCP servers, the way you extend the tool surface
The built-in tools cover filesystem, shell and a handful of utilities. Everything else, web search, GitHub, databases, browser automation, vector memory, observability, comes through MCP servers.
The Model Context Protocol is an open standard introduced by Anthropic for letting LLM clients talk to external tools through a uniform interface. An MCP server is a small process (local or remote) that exposes a set of tools and resources. You register it in settings.json (or via claude mcp add), and from that moment on, the model can call its tools the same way it calls Read or Bash.
In practice, this is how Claude Code becomes useful for non-trivial work. A few examples of MCP servers we use daily:
- Serena for symbol-aware code navigation, which is dramatically better than
Grepon large codebases. - Context7 for live framework documentation, which removes a whole class of “outdated training data” hallucinations.
- Playwright for browser automation, which is the only honest way to verify a UI flow.
- SearXNG for self-hosted web search, which avoids every paid API for a large fraction of research tasks.
The MCP layer is also where the open-source ecosystem moves fastest right now. New servers appear weekly. The constraint is not “what can the model do”, it is “which servers do I trust enough to wire in”.
Memory, the part that has to be designed
Out of the box, Claude Code has no persistent memory beyond the per-project transcript. If you want context to survive across sessions, you have to design it.
The simplest pattern is a memory/ directory under ~/.claude/ containing Markdown files, with frontmatter describing what each file is. You can then reference them in CLAUDE.md (“when client X is mentioned, load memory/client-x.md“) and rely on hooks to do the loading silently. This is a small amount of plumbing, but it is the difference between an assistant that re-introduces itself every morning and one that already knows your stack, your clients, and your preferences.
We will come back to this in the SHINE section, because the way you organise memory is one of the few decisions that compounds over time.
Part 2: introducing SHINE Code
We started using Claude Code internally on a few projects, the way most teams do: open a terminal, type a prompt, see what happens. It was good. It was also frustrating, in a specific and recurring way.
Every session began with the same context-rebuilding ritual. We would re-explain the client. We would re-explain the tone. We would remind the model which MCP server to use for search, which one for charts, which one for security scans. We would forget to mention a constraint and only notice when a draft email was sent in the wrong language or a proposal landed without the right discount structure.
The model was capable. The orchestration was on us, every single turn. For a one-off side project that is fine. For an agency running multiple clients in parallel, it is exhausting and error-prone.
We did not want a wrapper around Claude Code. The CLI itself is excellent, and any wrapper we wrote would be obsolete the next time Anthropic shipped a feature. What we wanted was a configuration layer that did three things:
- Removed the manual tool-selection burden by encoding our routing rules once and for all.
- Persisted institutional knowledge in a way the model could load on its own, without us re-typing it.
- Set sensible guardrails for an agency context: never send an email without review, never call a paid API without asking, never store a secret in a memory file.
That configuration layer became SHINE.
What SHINE actually is
SHINE is not a fork of Claude Code. It is not a separate binary. It is a curated ~/.claude/ directory: a global CLAUDE.md with 29 decision rules, a typed memory index, 149 skills exposed as slash commands, 45 sub-agents, 11 lifecycle hooks, an opinionated set of plugins and MCP servers, and an installer that backs up your existing setup before changing anything.
You install it with one command, you can preview every change with --dry-run, and you can roll back with ./uninstall.sh. The whole thing is open-source under CC0, has its own dedicated site and wiki, lives on GitHub, and is calibrated for the kind of work an agency does day-to-day: proposals, client emails, audits, lead enrichment, compliance checks, debugging, documentation.
The name is an acronym for the methodology baked into the agent roster: Strategize, Handle, Implement, Navigate, Evaluate. It replaces the generic plan/code/ship loop with a richer cycle that includes context gathering at the front and verification at the back, which matches how consulting work actually unfolds.
The 29 decision rules, the actual brain
The single most important file in SHINE is the global CLAUDE.md. It contains, among other things, a numbered list of 29 decision rules that pattern-match the incoming prompt and dictate which tool chain to use. A few representative examples:
- Rule 1, “plan / strategy / roadmap” plus technical scope, delegates to the
shine-planneragent. - Rule 3, “why doesn’t…” / “error” / “broken”, delegates to the
shine-debuggeragent, which returns a structured five-section report before any fix is attempted. - Rule 16, any factual claim about a company, person, number or URL, must be grounded in a live retrieval. No fabrication. The output carries an inline source watermark.
- Rule 17, a known client slug plus a communication verb, loads
memory/client-<slug>.mdandmemory/style-email-*.md, then runs thedraft-emailskill. Output is a draft, never auto-sent. - Rule 19, “proposal” or “preventivo”, loads the client memory and runs the
proposalskill with our standard MoSCoW structure, our man-day pricing, and our usual discount option flagged. - Rule 21, the tiered fallback rule: any tool selection with free and paid alternatives goes through Tier 1 (free, used silently), then Tier 2 (freemium, asks first), then Tier 3 (paid, requires explicit approval). No surprise bills.
- Rules 22 to 29 map specific MCP capability clusters: research, data analysis, charting, vulnerability scanning, sandboxed code execution, infrastructure operations.
These rules are literal. You can read the file. When a prompt comes in, the model pattern-matches it against the rules and follows whichever one fires. If two rules match, the more specific one wins. If none match, the model falls back to its default behaviour. The point is not to be clever, it is to be predictable.
Memory, typed and shared
Every memory file in SHINE has frontmatter that declares its type:
---
type: client # preference | client | project | style | external
name: CONTOSO Italia
last_updated: 2026-04-14
tags: [tech-seo, salesforce, flat-budget]
---
Each type has a defined load strategy. preference files are injected on every SessionStart and are always available. client files are loaded only when the client slug appears in the prompt, via a UserPromptSubmit hook that detects the slug and injects the file as additional context for the next turn. style files travel with their matching client. project and external files are lazy.
This sounds bureaucratic until you realise the alternative is re-explaining everything in every session. With typed memory, the model walks into every conversation already knowing the relevant facts, and ignores everything else. Sessions become shorter, drift becomes rarer, and onboarding a new collaborator means handing them the memory directory rather than a forty-page Notion doc.
Skills and agents, two different things
SHINE separates skills from agents, and the distinction matters.
A skill is a slash command. You type /proposal CONTOSO tech-SEO and the CLI loads skills/proposal/SKILL.md as a system instruction and runs it, single-shot, user-triggered. Skills are explicit. The 149 skills shipped with SHINE cover everything from /draft-email and /lead-enrich to /shine-execute-phase and /shine-audit-milestone.
An agent is a sub-process. The main thread delegates to it via the Task tool when the work needs fresh context or a different tool budget, typically because a decision rule fired. Agents have their own context window, their own allowed tools, and they return a structured report. The rule of thumb we settled on is: if the user asks for it by name, it is a skill; if the main thread needs help staying focused, it is an agent.
Hooks, where guardrails live
SHINE ships with eleven hooks. A few of them illustrate the philosophy:
shine-prompt-guard.js, aPreToolUsehook onWrite|Edit, scans the file path and content for secret-shaped patterns (Stripe live keys, GitHub tokens, AWS access keys, Slack tokens, PEM blocks). On a hit, it exits non-zero, the tool call is aborted, and the model is told why. It is a single small script, and it has caught real secrets multiple times.shine-client-detect.js, aUserPromptSubmithook, scans the prompt for client slugs derived from the memory directory. On a hit, it emits a JSON directive that injects the client memory into the very next turn. No “which client did you mean?” round-trip.shine-tone-calibrator.js, alsoUserPromptSubmit, watches for tone-correction signals across five axes (formality, length, warmth, assertiveness, jargon) in English and Italian. When it detects one, it appends a delta to the matching style file. The next time you draft anything for that client, the model has accumulated your past corrections.shine-context-monitor.js, aPostToolUsehook, tracks transcript size and prints a soft warning at 800 KB and a hard warning at 1.6 MB. Long sessions are where things go wrong; this gives you a chance to compact deliberately.
None of these hooks rewrite tool inputs. They block, warn, or inject. That constraint keeps the system reasonable to debug.
Tiered fallback, why it exists
Rule 21 is worth its own section. The Claude Code MCP ecosystem is full of tools that come in free, freemium and paid flavours. Search is the obvious example: SearXNG is free and self-hosted, Brave Search has a freemium API, Perplexity is paid. If the model picks freely, you can rack up a bill or trigger a quota in ways you did not intend.
Rule 21 enforces a default order. Tier 1, free and local, is used silently when available. Tier 2, freemium, requires the model to ask before consuming credits. Tier 3, paid, requires explicit approval and a short justification. The rule applies across all 20 MCP capability categories that SHINE maps, from search to charts to vulnerability scanning.
The result is that a fresh installation is usable end-to-end without any paid API key. You add the freemium and paid tiers later, deliberately, when the use case justifies it.
What it looks like in practice
To make this concrete, here is what an actual day with SHINE looks like.
I open a terminal in a project directory and type claude. The session starts. SHINE’s SessionStart hooks symlink the project’s memory to the global one, check for a framework update, and refresh the auto-generated plugin block in CLAUDE.md. The statusline at the bottom of the terminal shows the model, the project, the active client, and the context size.
I type “draft a reply to Jamie about ACME link building”. Before the model sees the prompt, the UserPromptSubmit hooks fire. The client-detect hook spots ACME in the prompt, finds memory/client-acme.md, and emits an injection directive. The tone-calibrator hook checks for correction signals and finds none. The model, on its very first reasoning step, has the client file in context.
Rule 17 fires. The Gmail MCP fetches the last thread. The /draft-email skill produces a subject line in our standard ACME | <topic> format, a warm Italian opening, a bullet body, the right CC list. The output is a draft. Nothing is sent. I review it, edit two lines, and copy it into Gmail.
Total elapsed time: under a minute. No tool selection. No re-explaining the client. No surprise emails. No surprise costs.
The same pattern applies to a debug request, a proposal, a lead enrichment, a GDPR check. The decision rules handle the orchestration. The memory provides the context. The skills do the work. The hooks enforce the guardrails. SHINE is the configuration that makes Claude Code behave like an in-house operator instead of a brilliant intern.
Where SHINE ends and your judgement begins
A few honest caveats. SHINE is opinionated about the agency context we built it in. The decision rules assume you do consulting work in English, French, Italian or Spanish. The proposal skill assumes a man-day-based pricing model. The email skill assumes you want a draft, not an autosend. If your context is different, the configuration is meant to be forked and adapted, which is exactly why the whole thing is CC0.
SHINE is not magic. The model still makes mistakes. The rules still need maintenance. The memory still needs curation. Tiered fallback prevents surprise bills, but it does not prevent surprise outputs, the verifier and auditor agents exist precisely because output review is non-negotiable. The point of a configuration layer is not to remove judgement from the loop, it is to remove the parts of the loop where judgement was being wasted on plumbing.
And SHINE is not the only way to do this. Anyone running Claude Code seriously is going to converge on some flavour of routing rules, typed memory, lifecycle hooks and MCP discipline. The interesting question is not whether you adopt SHINE specifically; it is whether you treat your ~/.claude/ directory as a system you design, or as a folder the CLI happens to write to.
SHINE Code Video explainer.
Try it, fork it, take what you need
If you use Claude Code daily and want to skip the months of trial and error we spent converging on this configuration, the fastest path is to install SHINE and read its CLAUDE.md. Even if you eventually rewrite half of it, the structure is a good starting point.
👉 Source code (GitHub repository)
📖 Architecture documentation | How it works walkthrough | Customization guide
If you would rather see how the same orchestration mindset shows up in a different form factor, our Prismo toolkit packages the same Claude-Code-as-engine pattern into a portable USB toolkit for on-site audits. And if you are earlier in the journey and still trying to decide what to delegate to an AI in the first place, our piece on agentic AI architectures is the right place to start.

