GitHub Issues linking
Source: packages/core/src/git/github.ts, packages/cli/src/commands/link.ts
· Spec: §12, §14 (M4)
Sessions carry links.githubIssue (schema §3.3) so Beetl can complement an
issue tracker rather than replace it (plan Q3). beetl link is the write path.
Issue identity (M-05)
Section titled “Issue identity (M-05)”links.githubIssue stores the full {owner, repo, number, url} identity. A
URL keeps its own owner/repo — cross-repo links are preserved verbatim, never
rebound to the current remote — and a bare number resolves against the
origin GitHub slug or is rejected (INVALID_INPUT) when there is none.
Legacy stores may hold a bare number: it stays parseable, but its provenance
is unknowable, so it reads as legacy-unknown (never silently the current
repo), renders as #n (legacy-unknown) with no URL, and beetl doctor warns
(LEGACY_ISSUE_LINK) until the session is re-linked with the full issue URL.
Commands
Section titled “Commands”beetl link <ref> <issue>— record an existing issue. Accepts123,#123, or a fullhttps://github.com/<owner>/<repo>/issues/<n>URL.beetl link <ref> --create— file a GitHub issue from the session via theghCLI (gh issue create --title … --body-file -) and link the returned number. The body mirrors theshow --mdshape (report / reproduction / diagnosis / fix sections that are present) with a beetl-session footer. Requires an installed, authenticatedgh; failures map toGH_FAILURE(exit 1).beetl link <ref> --clear— remove the issue link (githubIssue: null).beetl link <ref> --related <refs...>— cross-link other sessions intolinks.related(deduped, validated to exist, self-links rejected).
<issue>, --create, and --clear are mutually exclusive; a bare link with
no operation is INVALID_INPUT. Links land through applyUpdates, so the
session logs a normal update event and terminal sessions stay read-only.
Output
Section titled “Output”--json returns the standard { session, ordinal } envelope plus issueUrl
when the URL is known — always for --create and for every full-identity
link (the URL comes from the stored identity, never reconstructed from the
current remote); absent only for legacy legacy-unknown links. show/
show --md render GitHub issue (owner/repo#n) and Related rows.
All gh/git calls go through an injectable GithubExec
(packages/core/src/git/github.test.ts uses fakes; the CLI test stubs a gh
script on PATH, skipped on Windows). Core stays terminal-free; the CLI never
calls gh unless --create is passed.