Files
holocron/plugins/gitea/.apm/skills/gitea-files/references/writing.md
Defame1297 d5954d3d99 refactor(gitea-files): retrofit to the ADR-0020 context contract
Description 787 -> 263 chars, body 922 -> 302 words, Gotchas 9 entries/69% of
body -> 3/24.8%. Clears both size FAILs and both Gotchas suggestions.

Deleted the second trigger register outright -- ~300 chars re-quoting the same
six verbs as user phrasings, which ADR-0020 names this skill for specifically.

Split the body on the read/write boundary: one invocation cannot both read and
write, so a dispatch table is mandatory. Six operations collapse into two flow
files rather than six -- create/update/delete share one tool pair and one
SHA-first lifecycle whose preamble would otherwise be triplicated, and the three
read tools share ref selection plus a 'neither listing is a SHA source'
comparison that only exists between them.

references/examples.md removed; all eight of its content blocks and all nine
named parameters carry into references/writing.md, verified against git HEAD.
Two deltas are corrections: the repo tree is now ruled out as a SHA source, and
reusing a SHA captured earlier in the conversation is now forbidden.

Four Gotchas relocated to the flow file that needs them; two promoted to gates
(SHA-as-concurrency-token opens writing.md; owner/repo became ## Inputs).

Refs #99
2026-08-30 12:09:52 +00:00

77 lines
3.3 KiB
Markdown

---
source_keys:
- gitea-mcp-repo
- gitea-mcp-slim-go
---
# Creating, updating and deleting files
`sha` is the optimistic-concurrency token for every write, and it comes from
`get_file_contents`'s **top-level** `sha` field — never from `content.sha`, a directory listing, or
a tree entry. Do not guess or reuse a stale value: a mismatched SHA is rejected exactly like a
missing one.
`content` is base64-encoded, and the branch the commit lands on is `branch_name`, not `ref`.
## Create a new file
Call `create_or_update_file(owner, repo, path, content, message, branch_name)` with `sha` omitted
entirely. An omitted `sha` always means *create*, so the call returns HTTP 409 if the path already
exists.
To branch off as part of the same write, pass `new_branch_name`: the branch is created and the
commit lands on it in one call, replacing a separate branch-creation step.
## Update an existing file
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `create_or_update_file(owner, repo, path, content, message, branch_name, sha: <that value>)`.
## Delete a file
Same SHA-first pattern, with no create-style fallback — `delete_file` without `sha` returns
HTTP 422.
1. `get_file_contents(owner, repo, ref: <branch>, path)` → read the top-level `sha`.
2. `delete_file(owner, repo, path, message, branch_name, sha: <that value>)`.
## Worked sequence — new file on a new branch, then a PR
```
1. create_or_update_file
owner, repo
path: "docs/example.md"
content: "<base64-encoded content>"
message: "docs: add example"
branch_name: "main"
new_branch_name: "feat/add-example" ← branches off before the commit lands
(sha omitted — this is a new file)
2. Hand off to gitea-prs to open a PR from "feat/add-example" into "main".
```
Step 1 needs no preceding read: a brand-new path has no SHA. Fetch the current file first only
when the write replaces an existing one.
## Triaging a failed write
| Symptom | Cause | Action |
|---|---|---|
| HTTP 409 | `sha` omitted on a path that already exists | Fetch the current SHA, retry as an update |
| HTTP 422 | `sha` missing or stale | Re-fetch the SHA immediately before the write |
| 403 or 422 with no SHA explanation | Branch protection requires signed commits | Stop and report |
| HTTP 413 | Reverse-proxy body limit in front of Gitea | Report; retrying cannot fix it |
| HTTP 404 | Wrong path, or a token without `write:repository` | Verify the path, then the token's scopes |
**Signed commits.** These writes create commits server-side from a bare API token with no 2FA or
PGP context. If the target branch's protection rule requires signed commits, Gitea rejects the
write as a generic 403 or 422 that never names signing, and reads against that same branch keep
succeeding right up until the write. When a write fails without a clean 409 or 404 explanation,
check the branch's protection rule before assuming the SHA is wrong and retrying.
**Payload size.** base64 inflates `content` roughly 33% over the raw file size, and a 413 is
usually a reverse-proxy body-size limit in front of the Gitea instance rather than a Gitea-side
rejection. No amount of retrying, or changing the SHA, path, or branch, will fix it — the proxy's
config has to be raised, which is outside this skill's control. Surface that distinction instead
of retrying the same call.