Files
command-center/agent/managed-skills/gitea-pr-api-cycle/SKILL.md
T

4.7 KiB
Raw Blame History

name, description
name description
gitea-pr-api-cycle Open/update/merge PRs and issues on the Dankclaw Gitea API (192.168.0.143:3000) with the token-safe sequence, the form-encoded merge field, the empty-token-returns-404 trap, and post-merge live-checkout fast-forward.

Gitea PR/issue cycle — Dankclaw API

Friction-earned 2026-10-18 (PRs #15–#20, issue #18) and re-earned 2026-10-19 on dank/dankvoice PRs #4–#6: the merge payload claim below was wrong, and an empty token masqueraded as a missing repo.

Where

  • API base: http://192.168.0.143:3000/api/v1/repos/dank/<repo>/{pulls,issues} — repo varies (command-center, dankvoice, serverAppConfigs); numbering is per-repo, so a footer citing #36 means nothing without the repo.
  • Token: never inline, never in argv (-H "Authorization: token …" lands in /proc/<pid>/cmdline, world-readable). Extract into a 600-perm curl config and hand it to curl with -K:
    tok=$(sed -nE 's#^https?://[^:/@]+:([^@]+)@192\.168\.0\.143.*#\1#p' ~/.config/secrets/gitea | head -1)
    [ "${#tok}" -ge 40 ] || { echo "token extraction failed"; exit 1; }
    cfg=$(mktemp); chmod 600 "$cfg"; printf 'header = "Authorization: token %s"\n' "$tok" > "$cfg"
    curl -sS -K "$cfg" … ; rm -f "$cfg"
    
  • Empty token answers 404 {"message":"not found"}, NOT 401 — Gitea hides the private repo. A 404 on a path you have used before means auth, not a typo'd repo. The trap that produces the empty token: awk -F'/' … '{print $NF}' on the store line (scheme://user:token@host/) — the host is in $3, the match never fires, you get a silent empty variable. Always assert the length before POSTing.
  • Multi-line bodies: build JSON in python (json.dumps({"title":…, "body":open(f).read(),…})) or jq -n --arg b "$(cat b.txt)". NEVER pass --- as a bare jq argument (parsed as a flag). Escaped quotes inside python3 -c '…' break on \" — write the script to a file or use a heredoc.
  • Always capture status: -o r.json -w '%{http_code}'; a bare -o hides failures.

Create

POST /pulls with {title, body, base:"main", head:<branch>} — JSON is accepted here (unlike merge).

  • 409 pull request already exists for these targets [issue_id: N, …] — a PR for that head/base exists already. Common cause: the push message printed /pulls/new/<branch> and someone (Frank, or an xdg-open you ran) clicked Create. GET /pulls?state=all, find it, then PATCH /pulls/N with your title+body instead of re-posting.
  • GET /pulls?state=all on this Gitea omits head/base per entry — read /pulls/N for those, or key off number/user.
  • Issues: same shape, POST /issues {title, body}; PATCH /issues/N fixes a body retroactively (works for PRs too, since a PR is an issue).

Merge (the 422 trap) — corrected

POST /pulls/N/merge needs form-encoded fields; the merge form field name is lowercase do:

curl -sS -X POST -K "$cfg" \
  --data-urlencode 'do=merge' --data-urlencode 'merge_style=no-ff' \
  http://192.168.0.143:3000/api/v1/repos/dank/<repo>/pulls/N/merge

JSON {"Do":"merge"} (what this skill used to claim) and header variants all return 422 {"message":"[Do]: Required"}. merge_style ∈ no-ff|merge|rebase|squash. Success returns 200 with an empty body; verify by landing a checkout rather than re-reading the PR. Merge only with Frank's explicit per-PR OK.

Post-merge landing

  • Command-center: the checkout IS the deployment — git -C ~/.omp fetch -q origin && git -C ~/.omp merge --ff-only origin/main.
  • Package repos (~/Projects/<pkg>): same --ff-only, then reinstall (omp plugin upgrade <name>) and cmp the installed dist/index.js against the repo dist/index.js to prove the live artifact is the merged one.
  • A fast-forward that adds or removes a tracked node_modules changes it on disk. Landing a PR that deleted the tracked symlink wiped the dev checkout's installed deps, and bun run typecheck then died with tsc: command not found (exit 127); bun install restored them. Check ls -ld node_modules after landing anything that touched it.
  • Prune: git worktree remove --force <path> (force: untracked deps link), git branch -D, git push origin --delete <branch> (allowed: local Gitea).

Every issue/PR body ends with ---, then cwd: <session cwd> · session: <session id>. Read the real values, never invent an id: the session id is the suffix of the transcript dir name (ls -t ~/.omp/agent/sessions/ → …_01a155bb-…), and cwd is that session's own working directory. local://<name>.md artifacts live under that same session dir in local/ — resolve with read local://<name>.md (its header prints the absolute path) instead of guessing.