Problem: forge shorthand parsing hardcoded `%l%l` (exactly 2 lowercase
letters), preventing custom prefixes like `github:`. Completions also
hardcoded `gh:`, `gl:`, `cb:` patterns.
Solution: iterate `_by_shorthand` keys dynamically in `_parse_shorthand`
instead of matching a fixed pattern. Build completion patterns from
`forge.backends()`. Add `shorthand` field to `ForgeInstanceConfig` so
users can override prefixes via config, applied in `_ensure_instances()`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem: adding a new forge required touching 5 lookup tables
(`FORGE_HOSTS`, `FORGE_CLI`, `FORGE_AUTH_CMD`, `SHORTHAND_PREFIX`,
`_warned_forges`) and every branching site in `_api_args`,
`fetch_metadata`, and `parse_ref`.
Solution: introduce a `ForgeBackend` class with `parse_url`,
`api_args`, and `parse_state` methods, plus a `register()` /
`backends()` registry. New forges (Gitea, Forgejo) are a single
`register()` call via the `gitea_backend()` convenience constructor.
Problem: Forge metadata fetching required manual token management —
config fields, CLI token extraction, and curl with auth headers. Each
forge had a different auth path, and Codeberg had no CLI support at all.
Solution: Delete `get_token()` and `_api_url()`, replace with
`_api_args()` that builds `gh api`, `glab api`, or `tea api` arg
arrays. The CLIs handle auth internally. Add `warn_missing_cli` config
(default true) that warns once per forge per session on failure. Add
forge CLI checks to `:checkhealth`. Remove `token` from config/docs.
Problem: existing tests asserted that `parse.body()` stripped forge
tokens from the description and populated `meta.forge_ref`. The
`conceal_patterns` test referenced a removed function.
Solution: update `parse.body` integration tests to assert tokens stay
in the description. Add `find_refs()` tests covering single/multiple
refs, URLs, byte offsets, and empty cases. Remove `conceal_patterns`
test. Update diff tests to assert description includes the token.
Problem: no test coverage for forge link shorthand parsing, URL
parsing, label formatting, or API URL generation.
Solution: add `spec/forge_spec.lua` covering `_parse_shorthand`,
`parse_ref` for all three forges, full URL parsing including nested
GitLab groups, `format_label`, and `_api_url`.