Claude Code harness setup: 3 skills, 30 minutes

Before this session: zero project-scoped permissions, recurring approval prompts on git and npm commands, no session-start visibility into the open-work backlog, and five allow rules in user-global settings that belonged to a completely different project. One session with three skills fixed most of it — and each skill surfaced something that wasn’t obvious going in. The skills: /fewer-permission-prompts scanned 15 transcripts and found one strict-survivor pattern from ~50 candidates, /update-config relocated the five misplaced allow rules and added a SessionStart hook that surfaces the open-work checklist as a systemMessage in the UI, and /schedule shipped two routines and rejected a third because remote agents can’t see gitignored directories. Total elapsed: 30 minutes. The post documents the receipts and three non-obvious edges that don’t live in any skill’s documentation.

/fewer-permission-prompts: 15 transcripts, ~50 patterns, 1 survivor

The skill scans session transcript JSONLs in ~/.claude/projects/<project>/, extracts Bash command frequencies, and filters them against Claude Code’s built-in auto-allow list. Commands that appear 3+ times and aren’t already covered are candidates for your project .claude/settings.json.

15 transcripts scanned. ~50 distinct command patterns extracted. 1 strict survivor.

That survivor was Bash(npm run build) — 14 hits, not auto-allowed, safe to add. I added Bash(npm run dev) and Bash(npm run preview) proactively even though neither had hit the threshold — both are documented npm scripts, no side effects, and local dev work hadn’t started yet but would.

The gap between expectation and reality: Claude Code’s built-in auto-allow list covers more than most people assume. git status, git diff, git log, cat, most read-only filesystem operations — all auto-allowed already. The commands I was actually being prompted on (gh api, git push, node scripts/*) either carry mutation risk or are flagged by the skill as arbitrary-code-execution candidates. Bash(gh api *) would authorize all HTTP methods, not just GETs. No good narrow pattern fits.

The honest deliverable from this skill on an active project: you’re probably already covered on what matters, and you’ll add 1–3 lines.

The edge case the skill missed: the skill says to write to .claude/settings.json (committed to the repo). I wanted .claude/settings.local.json (gitignored — no reason to commit three npm commands). But settings.local.json wasn’t in .gitignore yet. The skill doesn’t check for this; I had to add the entry manually before writing the file.

/update-config: 5 misplaced rules, 1 missing hook

Two problems going in:

  1. User-global ~/.claude/settings.json had 5 allow rules for a different project — gcloud deploy commands from an unrelated bot project that had been set globally instead of in that project’s local settings. They applied to every project on the machine.

  2. The open-work list in CLAUDE.local.md wasn’t visible at session start. Eight active items; none surfaced automatically when opening a session.

The skill’s core instruction — READ before WRITE, MERGE not REPLACE — earned its keep immediately. The existing ~/.claude/settings.json had 10 deny rules and 2 hooks. Writing blind would have lost all of them.

Fix 1 — stray rules: appended the 5 allow rules to the bot project’s .claude/settings.local.json (now 60 rules, all gcloud-related and scoped correctly). User-level allow: 5 → 0.

Fix 2 — session-start hook: created .claude/hooks/session-start-checklist.mjs — 18 lines. Reads CLAUDE.local.md, regex-extracts the ## Open work section, emits {"systemMessage": ...} JSON. Registered as a SessionStart hook in .claude/settings.json.

The systemMessage vs additionalContext distinction: both can carry content from a SessionStart hook. systemMessage shows it to the human in the UI — the checklist appears as a visible message when you open a session. additionalContext injects it into the model’s context silently. For a checklist you want to read yourself, systemMessage is right. The skill’s documentation mentions both; which to use isn’t obvious until you’ve seen them in practice.

One regex edge case during pipe-testing: the initial \n## pattern matched ambiguously at end-of-file. Tightened to (?=\n## |\n*$). Pipe-test to confirm valid JSON before registering — the skill’s instructions include this step and it caught a real bug.

/schedule: 2 routines created, 1 declined

Three routines requested:

  1. +7 days: check custom domain status and Search Console setup readiness
  2. +14 days: audit whether Haines and obra wiki entries still have TODO Receipts
  3. Weekly recurring: run /skill-evolve against receipts-drafts/

Two created, one declined with explanation.

Routine #1 (run once, +7 days): web-only checks on whether agentcookbooks.com has resolved and Search Console is ready to verify. No repo access needed.

Routine #2 (run once, +14 days): repo read — scan wiki entries for TODO markers in Receipts sections.

Routine #3 declined. The remote agent runs against the GitHub checkout of the repo. receipts-drafts/ is in .gitignore. The entire purpose of /skill-evolve on this project is to read that directory — real session notes captured locally as skills run. The remote agent would clone the repo, find no receipts-drafts/ directory, and have nothing to do.

Nowhere in the /schedule skill documentation does it say this. The constraint is only discoverable by cross-referencing .gitignore.

Remote agents = GitHub repo state. Not your local working tree, not gitignored directories, not uncommitted files. Any routine that depends on local-only data needs to be redesigned before it can be scheduled — either commit the data or restructure the workflow so the remote agent can source what it needs from somewhere public.

Both create calls batched in one turn to avoid a second cache-miss round-trip.

What each skill actually delivered

SkillOutputThe non-obvious thing
/fewer-permission-prompts1 survivor from ~50 patternsClaude Code’s auto-allow list already covers most read-only commands
/update-config5 rules relocated, 1 hook addedsystemMessage shows to human; additionalContext injects silently
/schedule2 routines shipped, 1 rejectedRemote agents can’t see gitignored directories

All three took about 30 minutes. The harness is cleaner in each case. The useful output in each case was the thing the skill found that wasn’t obvious going in — not the mechanical output, but the edge case that the documentation doesn’t mention.

Subscribe to RSS for the rest.