An introduction to the two agentic coding assistants this course uses. Less slide, more building: you'll open one project in Claude Code or Cline and extend it four ways — agent, skill, tool, hook — then watch a grounded filing agent run end to end.
code/practicals/04-llm-agents/ — copy-ready skeletons live in
templates/, the live agents in .claude/.
/ask a grounded, graded filing questionBoth are the Perceive → Reason → Act loop from the lecture, with your repository as the environment. The model proposes actions (read a file, run a command); the tool executes them and feeds the result back.
.claude/;
a native hook engine fires scripts on lifecycle events. Install:
npm i -g @anthropic-ai/claude-code, then run claude in the folder.
.clinerules /
AGENTS.md, runs workflows, and connects tools over MCP. Install from the
VS Code marketplace, open the folder, point it at a model provider.
AGENTS.md runners) the same way.
There is no hidden configuration. An agent, a skill, and a hook are each a tracked file a reviewer can read in a pull request. In a regulated workflow, the entire capability surface is inspectable from a git log.
code/practicals/04-llm-agents/ ├── CLAUDE.md / AGENTS.md # project instructions (both tools read these) ├── .clinerules # Cline's project rules ├── .claude/ │ ├── settings.json # permissions + HOOKS wiring │ ├── agents/ retriever.md analyst.md grader.md │ ├── skills/ ask/SKILL.md # the /ask command │ └── hooks/ log-interaction.sh timed-commit.sh ├── templates/ # copy-ready skeletons (today's exercises) └── tools/ retrieve.py grade.py # deterministic Python the agent calls
The same four extension points, mapped to concrete file locations in each tool. Today you'll add one of each.
| Capability | Claude Code | Cline |
|---|---|---|
| Agent — a persona | .claude/agents/<name>.md | .clinerules / AGENTS.md, or a custom mode |
Skill — a /command | .claude/skills/<name>/SKILL.md | .clinerules/workflows/<name>.md |
| Tool — a new action | MCP in .mcp.json, or a CLI via Bash | MCP in cline_mcp_settings.json |
| Hook — an event callback | .claude/settings.json + .claude/hooks/ | none native → git / MCP wrapper |
Before you reach for a custom tool, remember both assistants already act on your repo through a shared standard toolset. A custom tool is only for what the environment can't already do.
Read — open a fileEdit / Write — change or create filesGrep — regex search across the repoGlob — find files by name patternBash — run any shell command (incl. your Python tools)tools/ (retrieve.py,
grade.py), called via Bash. Deterministic code does the
arithmetic; the model only chooses inputs and reads outputs — never recalls a number.
An agent is a persona with a single responsibility, allowed tools, and one artifact it must
produce. Start from templates/claude/agent.md (or
templates/cline/agent.md).
--- name: risk-extractor description: Extract the risk factors from a filing chunk as a bullet list. Use when asked "what risks…". tools: Read, Grep --- You are a risk-factor extractor. Read the retrieved chunks in reports/_context.json and list each distinct risk as one bullet, citing the chunk id. Invent nothing; if no risks appear in the context, say "No risk factors in the retrieved text."
.claude/agents/risk-extractor.md. Claude auto-discovers it and can
delegate to it based on the description.
.clinerules (or
.clinerules/risk-extractor.md) — Cline reads it as a project rule.
/commandA skill names an ordered procedure you can invoke by typing /name.
In Claude Code it's a SKILL.md; in Cline it's a workflow file — same idea.
# .claude/skills/risk-brief/SKILL.md → invoke with /risk-brief "<company>" --- name: risk-brief description: Retrieve, extract, and grade a one-paragraph risk brief. Usage /risk-brief "<company>" --- 1. python -m tools.retrieve "<company> risk factors" -k 4 > reports/_context.json 2. Delegate to the risk-extractor agent to list the risks with citations. 3. python -m tools.grade ... ; if faithfulness < 0.7, revise. 4. Save to reports/<company>-risks.md.
.clinerules/workflows/risk-brief.md and run
/risk-brief. Skeleton: templates/cline/workflow.md.
A hook is a shell script wired to a lifecycle event in .claude/settings.json.
Both scripts already exist in .claude/hooks/; you just wire and observe them.
logs/llm-interactions.log — the audit trail, made real.
// .claude/settings.json
"hooks": {
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": ".claude/hooks/log-interaction.sh" }]}],
"PostToolUse": [{ "hooks": [{ "type": "command", "command": ".claude/hooks/log-interaction.sh" }]}],
"Stop": [{ "hooks": [{ "type": "command", "command": ".claude/hooks/timed-commit.sh" }]}]
}
tail -f logs/llm-interactions.log — one line per turn.chore: timed safety commit appears in git log.while true; do bash .claude/hooks/timed-commit.sh; sleep 300; donetemplates/cline/git-hooks-README.md.
The bundled agent retrieves the most relevant chunks, writes a cited answer, grades it for faithfulness and relevance, and revises if it drifted off the source — entirely offline.
/ask "What is NovaCorp's customer concentration risk?" # under the hood, the skill runs: python -m tools.retrieve "customer concentration risk" -k 4 > reports/_context.json # analyst agent drafts a cited answer from _context.json python -m tools.grade --question "..." --answer "..." --context reports/_context.json # if faithfulness < 0.7 → revise or answer "Not answerable from the available filings"
retriever → analyst →
grader, each a one-job agent in .claude/agents/.
-k to 1 and watch relevance fall./risk-brief skill from Exercise 2 and run it on NovaCorp.logs/llm-interactions.log.python -m pytest -q.
.claude/
and Cline's .clinerules express the same four capabilities.Read,
Grep, Bash & friends ship in; MCP is for
what the environment can't already do.Right now the agent calls tools/grade.py via Bash.
Expose it as a first-class MCP tool so both Claude Code and Cline can call it by name with a typed schema.
grade(question, answer, context) in a small MCP serverquestion, answer, context_path{faithfulness, relevance} as structured output.mcp.jsoncline_mcp_settings.json/ask and confirm the grader is now a typed tool call, not a shell commandBash call.