Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Context files for coding agents

After this lesson: I can write a short context file that tells a coding agent how to work in a repository.

60 min lesson7 commandsAI coding agent lab: use opencode to explain and fix code, and check its work

What a context file for an agent is

A context file is a short file in a repository that a coding agent reads before every task. It says how to run the tests, which conventions the team keeps and what must not be touched.

Without it, the agent learns your project again on every task and guesses the rest. A good context file is short, true and safe, and a script can check all three.

By the end of this lesson

Note

I can write a short context file that tells a coding agent how to work in a repository.

Key words

WordWhat it meansExample
Context filestanding notes an agent reads before every taskAGENTS.md
Conventiona rule the team always follows
Commanda line you type to run somethingnpm run test
Protected patha file or folder the agent must not change
Line limitthe most lines the file may have

The cells students fill in are left blank in this preview.

A context file says what the code cannot

The code shows what the program does. It does not show how the team works. A context file holds four kinds of line: the commands to test and run, the conventions, the protected paths and where things live.

opencode, the agent in this lab, reads a file named AGENTS.md at the top of the project. Other agents read files with other names.

Worked example: lines to keep or cut

LineKeep or cutWhy
Run the tests with npm run testKeepA command the agent needs every time
Prices are whole pence, never decimalsKeepA convention the code does not say
Do not touch: data/prices.jsonKeepA protected path
The shop began in 2019CutHistory the agent does not need
total adds up the basketCutThe code already says it
API_KEY=abc123CutA secret never goes in the repository

The cells students fill in are left blank in this preview.

Short, true and safe

The agent reads every line on every task, so each line costs time and space. Set a line limit, such as 40 lines, and keep to it.

A line that is no longer true is worse than no line. A command that does not exist sends the agent the wrong way. A path that has moved protects nothing.

A common mistake

Warning

Many people think a longer context file gives better work. A rule on line 300 is easy to miss, for an agent and for a person. Keep the file short, and check that every command and path in it still exists.

Write the model helper, part 1

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// The token counts of a reply, or null when the reply carries none.' 'function count(data, name) {' '  return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the model helper, part 2

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"status \" + res.status);' '    const data = await res.json();' '    const text = data.choices[0].message.content;' '    return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' '  } catch (error) {' '    console.log(\"Recorded sample: no model answered (\" + error.message + \").\");' '    return { text: sample, inTokens: null, outTokens: null, recorded: true };' '  }' '}' 'module.exports = { ask };' >> model.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the sample repository and checker, part 1

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { ask } = require(\"./model\");' 'const NL = String.fromCharCode(10);' 'fs.mkdirSync(\"ctx-demo/data\", { recursive: true });' 'fs.mkdirSync(\"ctx-demo/src\", { recursive: true });' 'const pkg = { name: \"shop\", scripts: { test: \"node --test\", lint: \"node lint.js\" } };' 'fs.writeFileSync(\"ctx-demo/package.json\", JSON.stringify(pkg, null, 2));' 'fs.writeFileSync(\"ctx-demo/data/prices.json\", JSON.stringify({ apple: 35, juice: 85 }));' 'fs.writeFileSync(\"ctx-demo/src/shop.js\", \"module.exports = {};\" + NL);' > context.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the sample repository and checker, part 2

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const draft = [\"# Notes for coding agents\", \"Run the tests with npm run test\",' '  \"Check the style with npm run lint\", \"Build the site with npm run build\",' '  \"Prices are whole pence, never decimals.\", \"Do not touch: data/prices.json\",' '  \"Do not touch: secrets/keys.txt\", \"API_KEY=abc123 for the test server\"];' 'fs.writeFileSync(\"ctx-demo/AGENTS.md\", draft.join(NL) + NL);' >> context.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the sample repository and checker, part 3

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const LIMIT = 40;' 'const lines = fs.readFileSync(\"ctx-demo/AGENTS.md\", \"utf8\").trim().split(NL);' 'const scripts = JSON.parse(fs.readFileSync(\"ctx-demo/package.json\", \"utf8\")).scripts;' 'const found = [];' 'if (lines.length > LIMIT) found.push(\"too long: \" + lines.length + \" lines\");' 'lines.forEach(function (line, at) {' '  const words = line.split(\" \");' '  const i = words.indexOf(\"run\");' '  const where = \"line \" + (at + 1) + \": \";' '  if (i > 0 && words[i - 1] === \"npm\" && scripts[words[i + 1]] === undefined) found.push(where + \"no script \" + words[i + 1]);' '  const path = line.slice(14);' '  if (line.indexOf(\"Do not touch: \") === 0 && fs.existsSync(\"ctx-demo/\" + path) === false) found.push(where + \"no such path \" + path);' '  if (line.indexOf(\"KEY=\") >= 0 || line.toLowerCase().indexOf(\"password\") >= 0) found.push(where + \"looks like a secret\");' '});' >> context.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Write the sample repository and checker, part 4

Command
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  console.log(\"AGENTS.md: \" + lines.length + \" lines, limit \" + LIMIT);' '  found.forEach(function (one) { console.log(one); });' '  const q = \"In one short sentence, why should a context file for a coding agent stay short?\";' '  const sample = \"The agent reads every line on every task, so a long file buries the rules that matter.\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 60);' '  console.log(\"model said: \" + reply.text);' '}' 'main();' >> context.js"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run the context checker

Command
docker exec -w /home/node/project agent node context.js

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

What the Run prints

LineWhat it shows
AGENTS.md: 8 lines, limit 40The draft is short enough
line 4: no script buildpackage.json has no build script
line 7: no such path secrets/keys.txtThe protected path does not exist
line 8: looks like a secretAn invented key, which must come out
model said: ...A sentence from the lab. A Recorded sample line if no model answers

The cells students fill in are left blank in this preview.

About this lab and its source: AI coding agent lab: use opencode to explain and fix code, and check its work

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.