Skip to content

Commit 533a4f7

Browse files
Jeffrey Chenclaude
andcommitted
Republish claude_code_nano after the style/branding sweep
Refresh the course site (de-branded lecture pages + landing, rebuilt starter.zip) and drop "CS61A-style" from the projects-buffer anchor description ("build your own Claude Code, a guided project"). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012nqHzGMvCaDtp7MYU1dGqw
1 parent f4ea6b2 commit 533a4f7

10 files changed

Lines changed: 69 additions & 92 deletions

claude_code_nano/01_the_shape_of_an_agent.html

Lines changed: 10 additions & 13 deletions
Large diffs are not rendered by default.

claude_code_nano/02_read_only_tools.html

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@
1010
<nav class="topbar"><div class="topbar-inner"><span class="brand"><a href="index.html">Claude Code Nano</a></span><span class="crumbs"><a href="index.html">Home</a> <span class="dot">&middot;</span> <a href="01_the_shape_of_an_agent.html">&larr; Prev</a> <span class="dot">&middot;</span> <a href="03_mutating_tools.html">Next &rarr;</a></span></div></nav>
1111
<main class="handout">
1212
<h1 id="tools-i-eyes-read_file-list_files">Tools I: Eyes (read_file, list_files)</h1>
13-
<h2 id="a-cs61a-style-lecture-for-builders-of-turn-based-simulators-reading-their-first-tool-contract">A CS61A-Style Lecture for Builders of Turn-Based Simulators, Reading Their First Tool Contract</h2>
1413
<hr>
1514
<h2 id="lecture-overview">Lecture Overview</h2>
16-
<p>Lecture 01 was a spec-reading exercise — you traced a conversation, answered questions, and fixed one warm-up bug that had nothing to do with agents. Today you write the first real function of the course: <code>runTool</code>, the piece of <code>index.ts</code> that turns a model's <em>request</em> into something that actually happens on your machine.</p>
17-
<p>Recall the two-part definition from lecture 01: an agent is a model that can ask, plus a harness that runs and reports back. <code>TOOLS</code> — the schema array you read last time — is the "ask" half, already fully written, GIVEN, not yours to touch. <code>runTool</code> is the "run" half, and right now it's a single line: <code>throw new Error(&quot;YOUR CODE HERE&quot;)</code>. Every test in <code>tests/02_read_only_tools.test.ts</code> calls <code>runTool</code> directly — no model, no loop, no network — so today is entirely about that one function.</p>
18-
<p>You'll implement <code>runTool</code> as a <strong>dispatch table</strong>: a <code>switch</code> on the tool's <code>name</code> that routes to the right implementation, with a <code>default</code> case that refuses anything it doesn't recognize. This lecture builds two of its five branches — <code>read_file</code> and <code>list_files</code>, the tools that only look, never change anything. <code>write_file</code>, <code>edit_file</code>, and <code>bash</code> — the branches that touch disk and process — are lecture 03's job, added into this <em>same</em> switch statement.</p>
19-
<div class="table-wrap"><table><thead><tr><th>Tool</th><th>Half already given (<code>TOOLS</code>)</th><th>Half you write (<code>runTool</code>)</th></tr></thead><tbody><tr><td><code>read_file</code></td><td>done (lecture 01)</td><td><strong>today</strong></td></tr><tr><td><code>list_files</code></td><td>done (lecture 01)</td><td><strong>today</strong></td></tr><tr><td><code>write_file</code></td><td>done (lecture 01)</td><td>lecture 03</td></tr><tr><td><code>edit_file</code></td><td>done (lecture 01)</td><td>lecture 03</td></tr><tr><td><code>bash</code></td><td>done (lecture 01)</td><td>lecture 03</td></tr></tbody></table></div>
20-
<p><strong>How this lecture works:</strong> two numbered problems, 2.1 and 2.2, each with a spec, a worked example, constraints, and 2–3 unlock questions whose answers live only in the Appendix. Attempt each problem — write real code in <code>index.ts</code> — before checking its snippet. This lecture is worth <strong>2 of the course's 10 points</strong>: <code>./ok 02</code> green plus correct unlock answers.</p>
15+
<p>This lecture writes the first real function of the course: <code>runTool</code>, the piece of <code>index.ts</code> that turns a model's tool-use request into something that actually happens on your machine. <code>TOOLS</code> — the schema array read in lecture 01 — is the "ask" half, already fully written; <code>runTool</code> is the "run" half, and right now it's a single line: <code>throw new Error(&quot;YOUR CODE HERE&quot;)</code>. Every test in <code>tests/02_read_only_tools.test.ts</code> calls <code>runTool</code> directly — no model, no loop, no network — so today is entirely about that one function.</p>
16+
<p><code>runTool</code> is a <code>switch</code> on the tool's <code>name</code> that routes to the right implementation, with a <code>default</code> case that refuses anything it doesn't recognize. This lecture writes two of its five branches — <code>read_file</code> and <code>list_files</code>, the tools that only look, never change anything. <code>write_file</code>, <code>edit_file</code>, and <code>bash</code> — the branches that touch disk and process — go into this <em>same</em> <code>switch</code> in lecture 03.</p>
17+
<p><strong>How this lecture works:</strong> two numbered problems, 2.1 and 2.2, each with a spec, a worked example, constraints, and 2–3 unlock questions whose answers live only in the Appendix. Write real code in <code>index.ts</code> before checking each snippet. This lecture is worth <strong>2 of the course's 10 points</strong>: <code>./ok 02</code> green plus correct unlock answers.</p>
2118
<hr>
2219
<h2 id="1-what-is-a-tool-really-two-halves">1. What Is a Tool, Really? Two Halves</h2>
2320
<h3 id="concept">Concept</h3>
@@ -26,8 +23,8 @@ <h3 id="concept">Concept</h3>
2623
// plain function that RUNS it. The model never executes anything — it only
2724
// asks; runTool() below is where things actually happen on this machine.
2825
//
29-
// DSA framing: a tool is an interface. The schema is the type signature the
30-
// model programs against; runTool is the implementation behind it.</code></pre>
26+
// A tool is a contract: the schema is the signature the model programs
27+
// against; runTool is the implementation behind it.</code></pre>
3128
<p>This is the same split you've used for years without naming it "agent architecture": an <strong>interface</strong> (a name, a signature, a contract — no body) versus an <strong>implementation</strong> (the body that actually does the work). <code>TOOLS[0]</code> — the <code>read_file</code> entry — is the interface: it tells the model "there's a function called <code>read_file</code>, it takes one required string field <code>path</code>, and here's a sentence describing what it does." Nothing in that entry touches a disk. The disk-touching happens forty lines lower, inside <code>runTool</code>, in code the model never sees and never runs itself.</p>
3229
<h3 id="why-it-matters">Why It Matters</h3>
3330
<p>This split is why you can trust an LLM near your filesystem at all without also trusting it to <em>directly execute arbitrary code</em>. The model's output is always just text in a particular shape — a <code>tool_use</code> block asking for <code>read_file</code> with some <code>path</code>. That request is completely inert (lecture 01, §1's checkpoint) until your <code>runTool</code> — code you wrote, code you can read start to finish — decides what "running <code>read_file</code>" actually means. If <code>runTool</code>'s <code>read_file</code> case silently also deleted the file first, the model would never know; it only sees whatever string <code>runTool</code> hands back. The schema is a promise about <em>shape</em>; <code>runTool</code> is the only place that promise gets <em>kept</em>.</p>
@@ -50,7 +47,7 @@ <h3 id="checkpoint">Checkpoint</h3>
5047
<hr>
5148
<h2 id="2-reading-the-contract-the-tools-array">2. Reading the Contract: The TOOLS Array</h2>
5249
<h3 id="concept-1">Concept</h3>
53-
<p>Before writing <code>runTool</code>, read <code>TOOLS</code> closely — all of it, not just the two entries you'll implement today. It's the model-facing <strong>interface</strong> for all five tools, and it's already complete; your job in this section is comprehension, not code. Think of it the way you'd read a <code>.d.ts</code> file or an abstract base class before implementing its subclasses: every field in there is a constraint your implementation must satisfy, and every field it <em>omits</em> is freedom your implementation has.</p>
50+
<p>Before writing <code>runTool</code>, read <code>TOOLS</code> closely — all of it, not just the two entries you'll implement today. It's the model-facing <strong>interface</strong> for all five tools, and it's already complete; your job in this section is comprehension, not code. Every field in there is a constraint your implementation must satisfy, and every field it <em>omits</em> is freedom your implementation has.</p>
5451
<p>Two fields matter most for today's work:</p>
5552
<ul><li><strong><code>required</code></strong><code>read_file</code>'s schema has <code>required: [&quot;path&quot;]</code>; <code>list_files</code>'s has <code>required: []</code>. That's not decoration. It's the contract's way of saying "the model must always supply a path to read a file, but may omit it when listing, and here's what happens if it does" (see <code>list_files</code>'s description: <code>&quot;defaults to &#x27;.&#x27;&quot;</code>).</li><li><strong><code>description</code></strong> — the <em>only</em> channel the model has for understanding what a tool does, since it never sees <code>runTool</code>'s source. Every English sentence in there is doing real work: "one level," "Directories end with '/'," "Call again on a subdirectory to go deeper" are all promises your <code>runTool</code> implementation is about to make true.</li></ul>
5653
<h3 id="why-it-matters-1">Why It Matters</h3>
@@ -73,7 +70,7 @@ <h3 id="concept-2">Concept</h3>
7370
default:
7471
// ...
7572
}</code></pre>
76-
<p>Each <code>case</code> is a branch; <code>default</code> is the fallback when the key matches nothing — the same role a hash map's "key not found" branch plays, or a state machine's "invalid transition" handler. <code>runTool</code>'s <code>default</code> case is not optional flavor: since <code>name</code> arrives as a plain <code>string</code> (not a restricted union type — <code>input: any</code> means TypeScript isn't checking any of this at compile time), <em>any</em> string could show up, including typos, or tool names from some future version of <code>TOOLS</code> you haven't written a case for yet. <code>default</code> is where you refuse those, loudly.</p>
73+
<p>Each <code>case</code> is a branch; <code>default</code> is the fallback when <code>name</code> matches no case at all. <code>runTool</code>'s <code>default</code> case is not optional flavor: since <code>name</code> arrives as a plain <code>string</code> (not a restricted union type — <code>input: any</code> means TypeScript isn't checking any of this at compile time), <em>any</em> string could show up, including typos, or tool names from some future version of <code>TOOLS</code> you haven't written a case for yet. <code>default</code> is where you refuse those, loudly.</p>
7774
<p><strong>TS-ism:</strong> each <code>case</code> here ends in a <code>return</code>, so there's no fallthrough to worry about (the classic C/Java <code>switch</code> footgun where control "falls through" to the next case unless you <code>break</code>). If a <code>case</code> doesn't <code>return</code>, control does fall through to the next one — but every branch you write today returns immediately, so you can set that concern aside for now.</p>
7875
<h3 id="why-it-matters-2">Why It Matters</h3>
7976
<p>Five tools, one function, one <code>name</code> string deciding which of five completely different pieces of code runs. Get the dispatch wrong — misspell a case label, forget <code>default</code>, let a case fall through — and the failure mode is silent and specific: the model asks for <code>list_files</code>, and either nothing happens, the wrong tool runs, or an unrelated exception surfaces with no clue that dispatch was the problem. A dispatch table's whole value proposition is that adding a sixth tool later means adding one <code>case</code>, touching nothing else — which is exactly how lecture 03 will extend this same function without rewriting it.</p>
@@ -139,7 +136,7 @@ <h3 id="checkpoint-4">Checkpoint</h3>
139136
<hr>
140137
<h2 id="key-takeaways">Key Takeaways</h2>
141138
<ol><li><strong>A tool is a schema plus a function, and only one of those two you're allowed to touch today.</strong> <code>TOOLS</code> is a fixed, model-facing contract; <code>runTool</code> is the implementation, and nothing checks that the two agree except careful reading on your part.</li></ol>
142-
<ol><li><strong><code>switch (name) { ... default: ... }</code> is a dispatch table</strong>the DSA structure behind "route to one of N implementations by a key," with <code>default</code> as the mandatory safety net for any tool name that isn't (yet, or ever) a real case.</li></ol>
139+
<ol><li><strong><code>switch (name) { ... default: ... }</code> is a dispatch table</strong>one entry point that routes to N implementations by a key, with <code>default</code> as the mandatory safety net for any tool name that isn't (yet, or ever) a real case.</li></ol>
143140
<ol><li><strong>Node's <code>fs.readFileSync</code>/<code>fs.readdirSync</code> are synchronous and block until disk I/O completes</strong> — simple and correct for this single-user CLI; the <code>async</code>/<code>await</code> alternative and <em>why</em> it matters is lecture 04's job.</li></ol>
144141
<ol><li><strong><code>{ withFileTypes: true }</code> turns filenames into <code>Dirent</code>s</strong>, the only way to know whether an entry is a file or a directory without a second <code>fs</code> call per entry.</li></ol>
145142
<ol><li><strong><code>list_files</code> never recurses — the model does, one <code>tool_use</code> call at a time.</strong> "One level" in the schema and "no loop in <code>runTool</code>" are the same design decision seen from two sides.</li></ol>

0 commit comments

Comments
 (0)