- Titles and bodies go through --rawfile files, never inline shell quotes - pipefail + curl -f + jq -e so a failed call can't look successful - Fix rounds merge latest main and skip re-opening the PR - Check the branch isn't main before pushing; never bypass the hooks - Review at a pinned level; only correctness findings checked against the code block approval Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.3 KiB
claude-playground
A tiny zero-dependency Node.js todo CLI, used as a sandbox for trying Claude Code features.
Commands
npm test— run the test suite (built-innode:test, no install needed)npm start -- add "something"/npm start -- ls— run the CLI (data intodos.json, override withTODO_FILE)
Conventions
- ES modules, Node >= 20, no third-party dependencies.
src/todo.jsholds pure functions; keep I/O inload/saveandsrc/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.
This is enforced by the git hooks that npm install installs and by Gitea branch protection (see "Protecting main" in the README). Never get around them: no --no-verify, no changing core.hooksPath, no editing .git/.
- Branch. Run
git fetch originon its own, thengit switch --no-track -c <type>/<short-name> origin/main(e.g.fix/duplicate-ids,docs/readme).--no-trackstops the branch from trackingmain. - Commit and test. Commit on the branch;
npm testmust pass before every push. - Push. Check that
git branch --show-currentisn'tmain, then rungit push -u origin HEAD. - Open the PR, once per branch (see "Gitea API" below).
- Review with
/code-review <branch> medium. Name both the branch and the level, so every round diffs againstmainat the same depth. - Comment on the PR with the verdict.
- First line: Needs fixes if any correctness finding still holds after you check it against the code; otherwise Approved. Cleanup and judgement-call findings are listed but don't block.
- Second line:
_Automated self-review by Claude; not an independent review._ - Then the findings, including any you checked and rejected, and why.
- Fix rounds. If it needs fixes, first bring in the latest
main: rungit fetch origin, thengit merge --no-ff --no-commit origin/main, thengit commit --no-edit, each as its own command. Don't rebase, because force pushes are denied. Fix the correctness findings, then repeat steps 2, 3, 5 and 6 (not step 4: the PR already exists). Do at most 2 fix rounds, then post what's left and hand off. - Stop. Never merge, even if approved; tell the human it's ready.
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. Settings load at session start, so restart the session after adding them (claude --continue). If the token is unset, stop and ask the human; don't work around it.
Rules for every API call:
- Make each call one self-contained command, because shell state doesn't carry over between commands.
- Write titles and bodies to files in
$TMPDIR(never in the repo) and pass them with--rawfile. Never put them inline in double quotes, where backticks and$(…)would run. pipefail,curl -fandjq -emake 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
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 "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/pulls" \
| jq -er '"#\(.number) \(.html_url)"'
# Comment on PR <number>. Write $TMPDIR/comment.md first.
set -o pipefail; API=https://code.westerkam.org/api/v1/repos/christian/claude-playground
jq -n --rawfile body "$TMPDIR/comment.md" '{body:$body}' \
| curl -fsS -X POST -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d @- "$API/issues/<number>/comments" \
| jq -er .html_url
Run git fetch, git commit and git push as standalone commands, with no cd … && before them and nothing chained after. Only then do they run outside the sandbox, which they need to reach the remote and to sign commits.
Remote
Gitea at code.westerkam.org — origin = git@code.westerkam.org:christian/claude-playground.git.