Files
claude-playground/CLAUDE.md
T

5.2 KiB
Raw Blame History

claude-playground

A tiny zero-dependency Node.js todo CLI, used as a sandbox for trying Claude Code features.

Commands

  • npm install — once per clone (no dependencies): installs the git hooks that block commits and pushes to main. If it fails in the sandbox, ask the human to run it.
  • npm test — run the test suite (built-in node:test)
  • npm start -- add "something" / npm start -- ls — run the CLI (data in todos.json, override with TODO_FILE)

Conventions

  • ES modules, Node >= 20, no third-party dependencies.
  • src/todo.js holds pure functions; keep I/O in load/save and src/cli.js.
  • Every behavior change gets a test in test/.
  • Commit messages: short imperative subject line.

Workflow (required)

Never commit, merge, or push to main. Only a human (Christian) merges into main, in Gitea. The git hooks installed by npm install and Gitea branch protection enforce this (see "Protecting main" in the README). Never get around them: no --no-verify, no changing core.hooksPath, no editing .git/.

  1. Branch. Run git fetch origin on its own, then git switch --no-track -c <type>/<short-name> origin/main (e.g. fix/duplicate-ids).
  2. Commit, test, push. npm test must pass. Check that git branch --show-current isn't main, then run git push -u origin HEAD.
  3. Open the PR, once per branch (see "Gitea API").
  4. Review with /code-review <branch> medium.
  5. Triage. The review lists unverified candidates, and it will always find some. Check each one against the code. A finding blocks only if it is a correctness bug in this PR's change that you have confirmed, ideally with a test that fails. Everything else is a note: cleanups, judgement calls, hardening ideas and unconfirmed findings.
  6. Comment on the PR:
    • First line: Needs fixes if anything blocks, otherwise Approved.
    • Second line: _Automated self-review by Claude; not an independent review._
    • Then the blocking findings, then the notes, then the findings you rejected, each with the reason.
  7. At most one fix round. If the PR needs fixes, fix only the blocking findings, each with a test. Push, review once more, and post the verdict from steps 5–6. Then stop, whatever that second review says. Findings about the fixes themselves go to the human as notes; don't fix them.
  8. Hand off. Never merge, even if the PR is approved. Tell the human it's ready, with the verdict and any notes.

Don't widen scope beyond the PR's purpose to satisfy a review. If a fix would add significant new code (a new mechanism, not a correction), list it as a note for the human instead.

Bring in the latest main only when the PR conflicts or the human asks:

  1. Run git fetch origin.
  2. Run git merge --no-ff --no-commit origin/main.
  3. If it says "Already up to date", stop here. Otherwise resolve any conflicts and run npm test.
  4. Run git commit --no-edit.

Run each as its own command, and never rebase or force-push.

Gitea API

No gh/tea: use curl with $GITEA_TOKEN.

One-time human setup in the git-ignored .claude/settings.local.json: set env.GITEA_TOKEN, and add code.westerkam.org to sandbox.network.allowedDomains. Then restart the session (claude --continue); settings load only at start. If the token is unset, stop and ask the human.

Rules for every API call:

  • Make each call one self-contained command, because shell state doesn't carry over between commands.
  • Put titles and bodies in files in $TMPDIR (never in the repo) and pass them with --rawfile, never inline in double quotes, where backticks and $(…) would run.
  • The token goes in a private header file, so it doesn't show up in the process list.
  • pipefail, curl -f and the jq checks make every failure exit non-zero.
# Open a PR for the current branch (once). Write $TMPDIR/pr-title.txt and $TMPDIR/pr-body.md first.
set -o pipefail; API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
(umask 077; printf 'Authorization: token %s\n' "$GITEA_TOKEN" > "$TMPDIR/gitea-auth")
jq -n --arg head "$(git branch --show-current)" --rawfile title "$TMPDIR/pr-title.txt" --rawfile body "$TMPDIR/pr-body.md" \
  '{head:$head, base:"main", title:($title|rtrimstr("\n")), body:$body}' \
  | curl -fsS -X POST -H @"$TMPDIR/gitea-auth" -H "Content-Type: application/json" -d @- "$API/pulls" \
  | jq -er 'if .number then "#\(.number) \(.html_url)" else error("no PR number in response") end'

# Comment on PR <number>. Write $TMPDIR/comment.md first.
set -o pipefail; API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
(umask 077; printf 'Authorization: token %s\n' "$GITEA_TOKEN" > "$TMPDIR/gitea-auth")
jq -n --rawfile body "$TMPDIR/comment.md" '{body:$body}' \
  | curl -fsS -X POST -H @"$TMPDIR/gitea-auth" -H "Content-Type: application/json" -d @- "$API/issues/<number>/comments" \
  | jq -er .html_url

Commands listed under sandbox.excludedCommands in .claude/settings.json (git fetch, git commit, git push and others) run outside the sandbox only when they are run standalone: no cd … && before them and nothing chained after. They need that to reach the remote and to sign commits.

Remote

Gitea at code.westerkam.org — origin = git@code.westerkam.org:christian/claude-playground.git.