scalable/ai/claude agent sdk/lesson 07 cph / /
lesson 07 / 08 · 12 min · updated ·

system prompts and skills

tailoring the agent: system prompt overrides, project-level skills, and how the sdk loads context.

tl;dr. the system prompt has three modes — keep claude code's default and append, replace it entirely with a string, or build your own from scratch. settingSources controls which on-disk settings layer in. skills are markdown files under .claude/skills/ that the agent loads on demand — the right place for "how we do x in this repo" knowledge.

three system prompt modes

by default the sdk uses an empty system prompt — bare api, no claude-code conventions, no tool documentation beyond what tools declare themselves. that's rarely what you want. there are two upgrades.

preset + append — keep defaults, add your rules
import { query } from "@anthropic-ai/claude-agent-sdk";

// the most common pattern: keep claude code's default system prompt,
// append your own additions. nothing of the agent's wiring is lost.
const run = query({
  prompt: "...",
  options: {
    systemPrompt: {
      type: "preset",
      preset: "claude_code",
      append: `
You are running inside scalable.dk's deploy bot.
- Always run \`npm run typecheck\` after editing typescript files.
- Never modify files outside src/ or scripts/.
- When you push, push to the deploy branch, never main.
`,
    },
  },
});
full override — only when the agent's job isn't software work
// total override — useful when the agent's job has nothing to do with code.
// you lose the claude code defaults (file io conventions, tool descriptions, etc),
// so prefer "append" unless you really mean it.
const run = query({
  prompt: "draft an outage post-mortem from the slack threads in /tmp/threads/",
  options: {
    systemPrompt:
      "You are an SRE writing post-mortems. Output should be a markdown doc following our template at /tmp/template.md. No commentary, no preamble.",
  },
});

settingSources — which on-disk settings count

claude code reads layered settings: user (~/.claude/), project (./.claude/), and local (./.claude/settings.local.json). the sdk doesn't load any of these by default — your query() is a clean surface unless you opt in. that opt-in is settingSources.

opt into project settings only — host machine settings stay out
// settingSources controls which on-disk settings the sdk inherits.
// default is "none" — nothing from the host machine bleeds in.
const run = query({
  prompt: "...",
  options: {
    // load .claude/settings.json from this project, but ignore user / global
    settingSources: ["project"],
    // alternatives: ["user"], ["project", "user", "local"], or omit entirely
  },
});

enable "project" when you want the agent to inherit repo-level conventions. enable "user" only if the agent is running on the developer's own machine. for a deployed agent the safe default is to enable "project" only — host-machine settings should never leak into a server-side run.

skills

skills are markdown files under .claude/skills/<name>/SKILL.md that document a piece of know-how — "how we write migrations," "the company's brand voice," "the steps to roll a new release." the agent loads them by description-match: when a user prompt or in-flight context implies a skill is relevant, it gets pulled into the agent's working context. if it isn't relevant, the skill stays off the prompt and you don't pay for the tokens.

a project skill file the agent loads on demand
// .claude/skills/postgres-migrations/SKILL.md
// -----
// ---
// name: postgres-migrations
// description: write and run knex postgres migrations following our conventions
// ---
//
// when writing a migration:
// - file name format: `<unix-timestamp>_<snake_case_description>.ts`
// - always provide both up() and down()
// - never use `alterTable` for column drops — use a new migration with a guard
// - run `npm run db:migrate` after writing
// - if migration fails, run `npm run db:rollback` and edit; do not edit a migration that's already been run on staging
//
// helper code lives in scripts/migrations.ts.

// in code, just enable skills — the agent picks them up by description match.
const run = query({
  prompt: "add a 'last_login' timestamp column to users",
  options: {
    settingSources: ["project"],
    // The agent reads .claude/skills/*/SKILL.md and treats them as available
    // know-how. It pulls relevant skills into context only when they apply.
  },
});
skills beat ever-growing system prompts. the temptation when an agent gets a thing wrong is to add another line to the system prompt. that scales until the prompt is 4 KB and the agent ignores most of it. skills are the alternative: domain knowledge lives in the repo as discoverable markdown, the model fetches what it needs, and the system prompt stays focused.

structuring a SKILL.md

what to notice

scalable labs·cvr 30091604·github·linkedin·hello@scalable.dk