Documentation
¶
Overview ¶
Package github is the typed adapter between yottacode and GitHub.
Shipped surface (v0.5.0):
- Typed client (TypedClient) wrapping go-github/v66, with HTTPClient injection for tests.
- Auth resolver chain: $GITHUB_TOKEN → `gh auth token` → ~/.yottacode/github.json.
- PR surface: CreatePR, ReadPR, ReadPRDiff, ListPRChecks, ListFailedWorkflowJobLogTails, RerunFailedPRChecks, UpdatePR.
- Issue surface: ReadIssue, ListOpenIssues.
- PR comment surface: AddPRComment.
Callers depend on the Interface, not on TypedClient directly, so the cloud bot (SaaS Phase 2) can swap in a JWT-installation-token implementation behind the same surface without changing call sites.
Index ¶
- Variables
- func DetectRepo(ctx context.Context, cwd string) (owner, repo string, err error)
- func IsGhAvailable(ctx context.Context) bool
- func ParseRemoteURL(remote string) (owner, repo string, err error)
- func RemoveTokenFile() (path string, existed bool, err error)
- func SaveVerifiedToken(ctx context.Context, token string) (path string, login string, err error)
- func WriteTokenFile(token, user string) (string, error)
- type AddPRCommentRequest
- type AddPRCommentResult
- type CacheStats
- type CachingClient
- func (c *CachingClient) AddPRComment(ctx context.Context, req AddPRCommentRequest) (AddPRCommentResult, error)
- func (c *CachingClient) CreateIssue(ctx context.Context, req CreateIssueRequest) (CreateIssueResult, error)
- func (c *CachingClient) CreatePR(ctx context.Context, req CreatePRRequest) (CreatePRResult, error)
- func (c *CachingClient) ListFailedWorkflowJobLogTails(ctx context.Context, req FailedWorkflowLogsRequest) (FailedWorkflowLogsResult, error)
- func (c *CachingClient) ListOpenIssues(ctx context.Context, req ListIssuesRequest) ([]IssueSummary, error)
- func (c *CachingClient) ListPRChecks(ctx context.Context, req ReadPRRequest) ([]CheckRun, error)
- func (c *CachingClient) RateLimit() RateLimitSnapshot
- func (c *CachingClient) ReadIssue(ctx context.Context, req ReadIssueRequest) (IssueDetails, error)
- func (c *CachingClient) ReadPR(ctx context.Context, req ReadPRRequest) (PRDetails, error)
- func (c *CachingClient) ReadPRDiff(ctx context.Context, req ReadPRRequest) (string, error)
- func (c *CachingClient) RerunFailedPRChecks(ctx context.Context, req ReadPRRequest) (RerunFailedPRChecksResult, error)
- func (c *CachingClient) Reset()
- func (c *CachingClient) Stats() CacheStats
- func (c *CachingClient) UpdatePR(ctx context.Context, req UpdatePRRequest) (UpdatePRResult, error)
- type CheckRun
- type CreateIssueRequest
- type CreateIssueResult
- type CreatePRRequest
- type CreatePRResult
- type FailedWorkflowJobLog
- type FailedWorkflowLogsRequest
- type FailedWorkflowLogsResult
- type Interface
- type IssueComment
- type IssueDetails
- type IssueSummary
- type ListIssuesRequest
- type PRDetails
- type RateLimitSnapshot
- type ReadIssueRequest
- type ReadPRRequest
- type RerunFailedPRChecksResult
- type TokenFile
- type TokenResolver
- type TypedClient
- func (c *TypedClient) AddPRComment(ctx context.Context, req AddPRCommentRequest) (AddPRCommentResult, error)
- func (c *TypedClient) AuthedUserLogin(ctx context.Context) (string, error)
- func (c *TypedClient) CreateIssue(ctx context.Context, req CreateIssueRequest) (CreateIssueResult, error)
- func (c *TypedClient) CreatePR(ctx context.Context, req CreatePRRequest) (CreatePRResult, error)
- func (c *TypedClient) ListFailedWorkflowJobLogTails(ctx context.Context, req FailedWorkflowLogsRequest) (FailedWorkflowLogsResult, error)
- func (c *TypedClient) ListOpenIssues(ctx context.Context, req ListIssuesRequest) ([]IssueSummary, error)
- func (c *TypedClient) ListPRChecks(ctx context.Context, req ReadPRRequest) ([]CheckRun, error)
- func (c *TypedClient) RateLimit() RateLimitSnapshot
- func (c *TypedClient) ReadIssue(ctx context.Context, req ReadIssueRequest) (IssueDetails, error)
- func (c *TypedClient) ReadPR(ctx context.Context, req ReadPRRequest) (PRDetails, error)
- func (c *TypedClient) ReadPRDiff(ctx context.Context, req ReadPRRequest) (string, error)
- func (c *TypedClient) RerunFailedPRChecks(ctx context.Context, req ReadPRRequest) (RerunFailedPRChecksResult, error)
- func (c *TypedClient) UpdatePR(ctx context.Context, req UpdatePRRequest) (UpdatePRResult, error)
- type UpdatePRRequest
- type UpdatePRResult
- type VerifyResult
Constants ¶
This section is empty.
Variables ¶
ErrGitHubUnavailable signals that the local environment cannot satisfy a GitHub call — either the `gh` binary isn't installed, or it is installed but unauthenticated. Callers branch on this so the procedural /create-pr can fall through to a draft-only preview instead of failing the turn opaquely.
var ErrGitHubUnreachable = errors.New("github API unreachable")
ErrGitHubUnreachable signals that the network couldn't reach api.github.com — DNS failure, refused connection, TLS error, timeout. Distinct from ErrGitHubUnavailable (auth) and ErrPRNotFound (logical) so callers can branch on the right recovery — auth failures want `gh auth login`, unreachable wants "check your network".
Surface bubbled up from net.OpError / *url.Error / net DNSError shapes returned by go-github. Tested in cache_test.go and typed_test.go.
var ErrIssueNotFound = errors.New("issue not found")
ErrIssueNotFound signals that the requested issue doesn't exist. Sibling of ErrPRNotFound — same semantic, different resource. Callers branch on this so /git-implement-issue can surface a clean "no issue found" and STOP instead of treating the missing issue as an opaque API failure.
var ErrNoToken = errors.New("no GitHub auth token configured (set $GITHUB_TOKEN, run `gh auth login`, or run `yottacode setup github`)")
ErrNoToken signals that the auth chain found no usable token. Distinct from ErrGitHubUnavailable: gh might be installed and authed but not on PATH (env var path), or vice versa.
var ErrPRNotFound = errors.New("pull request not found")
ErrPRNotFound signals that the requested PR doesn't exist (no open PR for the supplied branch, or the explicit number resolves to a missing PR). Callers branch on this to surface a clean "no PR found" instead of treating the missing PR as an opaque gh exit-non-zero.
Functions ¶
func DetectRepo ¶
DetectRepo reads the cwd's `origin` remote URL and parses it into (owner, repo). Returns an error when no remote is configured, the remote isn't a GitHub URL, or the URL shape doesn't match the expected forms.
Specifically targets `origin` (not other remotes) because that's the convention every gh / git workflow assumes. Users with multi-remote setups can pass owner/repo explicitly via the request types, which short-circuits this lookup.
func IsGhAvailable ¶
IsGhAvailable reports whether the auth chain can resolve a usable token from any tier. The historical name is preserved for caller compatibility (the pr_context tool exposes a "GhAvailable" flag in its snapshot), but with the typed client in place the check is provider-agnostic: any of the three auth tiers ($GITHUB_TOKEN, gh, file) satisfying counts.
Cheap: doesn't make a GitHub API call. Returns false when every tier misses — same semantics as the old gh-only check but no longer dependent on gh specifically being installed.
func ParseRemoteURL ¶
ParseRemoteURL is the pure-function core of DetectRepo. Tries each known URL shape; returns the first match. Exposed for testability — callers shouldn't need to invoke it directly.
func RemoveTokenFile ¶
RemoveTokenFile deletes the on-disk token. Returns the path removed and a bool indicating whether the file existed pre-call (so callers can render "Already empty" vs "Removed" distinctly). Missing-file is NOT an error — idempotent by design.
func SaveVerifiedToken ¶
SaveVerifiedToken is the setup wizard's atomic "verify-then-persist" entry point. Doesn't write anything on verify failure — keeping the on-disk state consistent across failed setup attempts. Returns the on-disk path on success so the caller can surface it ("Saved to <path>").
func WriteTokenFile ¶
WriteTokenFile persists a token to the canonical location (~/.yottacode/github.json). Atomic via temp-file + rename so a crash mid-write never leaves a half-written file. Mode 0600 on the file, 0700 on the parent dir — the same posture other yottacode secret stores use (auth/openai-auth.json, etc.).
Caller passes the verified token + the GitHub login the token authenticated as (for the User field). Created is stamped here so callers don't have to remember.
Types ¶
type AddPRCommentRequest ¶
AddPRCommentRequest is the typed payload for Interface.AddPRComment. Owner / Repo follow the same optional-inference semantics. Ref accepts a PR number or branch name, mirroring ReadPRRequest. Body is the Markdown source — required, non-empty.
type AddPRCommentResult ¶
AddPRCommentResult is the typed envelope AddPRComment returns on success. URL is the comment's permalink — callers surface it so the user can jump to the posted comment on GitHub. ID is the comment's numeric ID, useful for future edit/delete flows (out of scope for v0.5.0 but exposed now to avoid a breaking change later).
type CacheStats ¶
Stats reports per-method cache occupancy. Useful for debugging and for the eventual doctor probe — we want to see whether the cache is actually hot during a session.
type CachingClient ¶
type CachingClient struct {
Inner Interface
// contains filtered or unexported fields
}
CachingClient is a memoizing wrapper around any Interface. Reads are cached for the lifetime of the wrapper (which the runtime holds for the session). Writes pass through and invalidate matching read entries so the next read sees fresh state.
Cache shape is deliberately simple: per-method maps keyed by request fingerprints. No TTL — the runtime tears the wrapper down at session end, which is the only invalidation event the roadmap commits to.
Thread-safe via a single mutex. Sessions can have parallel tool calls in flight (the agent runs read tools concurrently when they're parallel-safe), so locking matters. The mutex is held only across map mutation, not across the Inner call — a cache miss releases the lock, makes the call, then re-acquires to store. Two concurrent misses on the same key make two calls; that's the cost of not holding the lock across the network. The double-fetch is rare in practice (the agent dispatches reads once per turn) and avoiding it would require per-key singleflight, which is more machinery than the current signal warrants.
func NewCachingClient ¶
func NewCachingClient(inner Interface) *CachingClient
NewCachingClient wraps the supplied Interface with read caches. Returns a *CachingClient (not Interface) so wiring sites can reach the Stats / Reset methods for debugging without a type assertion. The struct satisfies Interface.
func (*CachingClient) AddPRComment ¶
func (c *CachingClient) AddPRComment(ctx context.Context, req AddPRCommentRequest) (AddPRCommentResult, error)
AddPRComment is a write — passes through. Doesn't invalidate any cached read: comments aren't carried in PRDetails (only the PR body is), so the next ReadPR is still accurate. If a future PRDetails grows a Comments field, this method must learn to evict matching ReadPR entries; the test suite pins that expectation so the regression is caught.
func (*CachingClient) CreateIssue ¶
func (c *CachingClient) CreateIssue(ctx context.Context, req CreateIssueRequest) (CreateIssueResult, error)
CreateIssue is a write — passes through, then drops the cached issue lists: the new issue belongs in any matching filter's results, so every cached list is now stale. Per-issue reads stay cached — an issue that existed before the create is unchanged by it.
func (*CachingClient) CreatePR ¶
func (c *CachingClient) CreatePR(ctx context.Context, req CreatePRRequest) (CreatePRResult, error)
CreatePR is a write — passes through and invalidates nothing. A newly opened PR didn't exist before so there's no stale entry to evict. The PR's URL / number become known after this call, but the next ReadPR will populate the cache fresh.
func (*CachingClient) ListFailedWorkflowJobLogTails ¶ added in v0.4.0
func (c *CachingClient) ListFailedWorkflowJobLogTails(ctx context.Context, req FailedWorkflowLogsRequest) (FailedWorkflowLogsResult, error)
ListFailedWorkflowJobLogTails is intentionally not cached. Logs and failed-job state are fetched only after CI reports a failure, and a rerun can update that state while the head SHA stays stable.
func (*CachingClient) ListOpenIssues ¶
func (c *CachingClient) ListOpenIssues(ctx context.Context, req ListIssuesRequest) ([]IssueSummary, error)
ListOpenIssues is cached by the full filter fingerprint — labels (order-independent), assignee, milestone, owner/repo. Two model calls with the same filter share one API request.
func (*CachingClient) ListPRChecks ¶
func (c *CachingClient) ListPRChecks(ctx context.Context, req ReadPRRequest) ([]CheckRun, error)
ListPRChecks is intentionally NOT cached. Check runs transition from queued/in-progress to completed without a PR metadata or head-SHA change, and users often ask repeatedly whether CI has passed. Serving a session- cached pending result would make yottacode lie until restart, so every call goes to the inner client for a live CI snapshot.
func (*CachingClient) RateLimit ¶
func (c *CachingClient) RateLimit() RateLimitSnapshot
RateLimit passes through to the inner. Cache layer has no independent rate state — the inner is what makes the API calls.
func (*CachingClient) ReadIssue ¶
func (c *CachingClient) ReadIssue(ctx context.Context, req ReadIssueRequest) (IssueDetails, error)
ReadIssue is cached by (owner, repo, number, maxComments). Comments are part of the cached value, so the maxComments element of the key matters (different caps produce different result shapes).
func (*CachingClient) ReadPR ¶
func (c *CachingClient) ReadPR(ctx context.Context, req ReadPRRequest) (PRDetails, error)
ReadPR is cached by (owner, repo, ref). Cache miss falls through to Inner; success populates the entry. Errors (including the typed sentinels) are NOT cached — re-fetching after a transient failure is the right behavior, and caching ErrPRNotFound would make subsequent retries impossible without a Reset.
func (*CachingClient) ReadPRDiff ¶
func (c *CachingClient) ReadPRDiff(ctx context.Context, req ReadPRRequest) (string, error)
ReadPRDiff is cached by the same key shape as ReadPR. Diffs don't change without a new commit, and the cache lives only for the session — so a long diff fetched once carries through any follow-up reads in the same turn.
func (*CachingClient) RerunFailedPRChecks ¶ added in v0.4.0
func (c *CachingClient) RerunFailedPRChecks(ctx context.Context, req ReadPRRequest) (RerunFailedPRChecksResult, error)
RerunFailedPRChecks is a write — passes through without caching. Check snapshots and log tails are uncached, so no explicit invalidation is needed.
func (*CachingClient) Reset ¶
func (c *CachingClient) Reset()
Reset drops every cached entry. Not used during normal session lifecycle (the cache lives for the session); exposed for tests and for a future "force fresh fetch" debug command.
func (*CachingClient) Stats ¶
func (c *CachingClient) Stats() CacheStats
func (*CachingClient) UpdatePR ¶
func (c *CachingClient) UpdatePR(ctx context.Context, req UpdatePRRequest) (UpdatePRResult, error)
UpdatePR rewrites title/body — the next ReadPR must see fresh data, so we evict the matching ReadPR entry before passing through. Diff is unaffected (the head SHA hasn't moved), so that cache stays. Checks are not cached because CI state changes while a head SHA is stable.
type CheckRun ¶
type CheckRun struct {
Name string
State string
Conclusion string
StartedAt time.Time
CompletedAt time.Time
}
CheckRun is one row from ListPRChecks. Name is the check's label (e.g. "build", "test", "lint"). State is the lifecycle state ("QUEUED" / "IN_PROGRESS" / "COMPLETED"). Conclusion is the outcome once State is COMPLETED ("SUCCESS" / "FAILURE" / "CANCELLED" / "NEUTRAL" / "SKIPPED" / "TIMED_OUT" / "ACTION_REQUIRED"); empty before completion.
Times are zero-valued when the check hasn't started or completed yet — callers must IsZero-check before formatting.
type CreateIssueRequest ¶
type CreateIssueRequest struct {
Owner string // repo owner (optional; inferred from cwd when empty)
Repo string // repo name (optional; inferred from cwd when empty)
Title string // issue title (required)
Body string // issue body / description (optional)
Labels []string // labels to apply (optional)
Assignees []string // assignees to assign (optional)
}
CreateIssueRequest is the typed payload for Interface.CreateIssue.
Owner and Repo are optional: when both are empty, the underlying implementation infers them from the working directory's git remote (the gh CLI's default behavior). Setting them explicitly is what the future cloud bot will need (it can't rely on cwd) and the local CLI can always use it for cross-repo cases.
type CreateIssueResult ¶
CreateIssueResult is the typed envelope CreateIssue returns on success. Number is the GitHub issue number. URL is the canonical https://github.com/... issue URL.
type CreatePRRequest ¶
type CreatePRRequest struct {
Owner string // repo owner (optional; inferred from cwd when empty)
Repo string // repo name (optional; inferred from cwd when empty)
Base string // base branch the PR merges into (required)
Head string // head branch / SHA the PR ships (optional; "" = current branch)
Title string // PR title (required)
Body string // PR body / description (required)
Draft bool // open as draft (default: open as ready-for-review)
}
CreatePRRequest is the typed payload for Interface.CreatePR.
Owner and Repo are optional: when both are empty, the underlying implementation infers them from the working directory's git remote (the gh CLI's default behavior). Setting them explicitly is what the future cloud bot will need (it can't rely on cwd) and the local CLI can always use it for cross-repo cases.
type CreatePRResult ¶
CreatePRResult is the typed envelope CreatePR returns on success. Number is the GitHub PR number (best-effort: shellout impl returns 0 when it can't parse one; typed v0.5.0 impl will always populate it). URL is the canonical https://github.com/... PR URL.
type FailedWorkflowJobLog ¶ added in v0.4.0
type FailedWorkflowJobLog struct {
WorkflowRunID int64
WorkflowName string
WorkflowURL string
JobID int64
JobName string
JobURL string
Conclusion string
LogTail []string
LogError string
}
FailedWorkflowJobLog is one failed GitHub Actions job plus the capped tail of its log. WorkflowName and JobName are separate so a renderer can group or display either without parsing check labels.
type FailedWorkflowLogsRequest ¶ added in v0.4.0
type FailedWorkflowLogsRequest struct {
Owner string // repo owner (optional; inferred from cwd when empty)
Repo string // repo name (optional; inferred from cwd when empty)
HeadSHA string // commit SHA whose workflow runs should be inspected (required)
TailLines int // max lines per failed job; <=0 uses implementation default
MaxRuns int // max workflow runs to inspect; <=0 uses implementation default
}
FailedWorkflowLogsRequest selects the failed GitHub Actions logs to fetch for a single PR head SHA. Owner / Repo follow the same optional inference semantics as the other GitHub request types.
type FailedWorkflowLogsResult ¶ added in v0.4.0
type FailedWorkflowLogsResult struct {
HeadSHA string
Jobs []FailedWorkflowJobLog
}
FailedWorkflowLogsResult is the typed envelope for failed job log tails. Missing logs are reported per job via LogError rather than as a hard failure so callers still see which checks failed.
type Interface ¶
type Interface interface {
// CreatePR opens a pull request. Returns ErrGitHubUnavailable when
// the local environment can't make the call (no gh, no auth)
// so callers can fall back gracefully rather than reporting a
// generic execution failure.
CreatePR(ctx context.Context, req CreatePRRequest) (CreatePRResult, error)
// ReadPR fetches typed metadata about a single pull request.
// Ref accepts either a PR number ("17") or a branch name; gh
// itself accepts both, and the v0.5.0 typed client will mirror
// that ergonomics. Returns ErrPRNotFound when nothing matches.
ReadPR(ctx context.Context, req ReadPRRequest) (PRDetails, error)
// ReadPRDiff fetches the unified diff for a pull request as a
// single string. Capped by the caller (tool wrapper trims for
// model consumption); the Interface itself returns the full diff.
ReadPRDiff(ctx context.Context, req ReadPRRequest) (string, error)
// ListPRChecks returns the typed status of every check run on a
// PR. Empty slice is a valid result (PR with no CI). The
// `/git-review-pr` flow surfaces failing checks at the top of
// the review, which is why typed access matters here.
ListPRChecks(ctx context.Context, req ReadPRRequest) ([]CheckRun, error)
// ListFailedWorkflowJobLogTails returns capped log tails for failed
// GitHub Actions jobs on a specific head SHA. This is the typed
// read-only counterpart to `gh run view --log-failed | tail`, used
// only after checks report a failure.
ListFailedWorkflowJobLogTails(ctx context.Context, req FailedWorkflowLogsRequest) (FailedWorkflowLogsResult, error)
// RerunFailedPRChecks re-runs failed GitHub Actions jobs for every
// failed workflow run attached to a PR's current head SHA. It uses
// GitHub's failed-jobs endpoint rather than rerunning successful jobs.
RerunFailedPRChecks(ctx context.Context, req ReadPRRequest) (RerunFailedPRChecksResult, error)
// UpdatePR rewrites an existing PR's title and body. Used by
// /git-update-pr after follow-up commits make the original
// description stale. Other PR-level edits (labels, base,
// reviewers, draft toggle) are intentionally out of scope —
// the v0.5.0 spec defers them until a concrete workflow asks.
// Returns ErrPRNotFound when nothing matches the ref;
// ErrGitHubUnavailable when the local environment can't make the
// call.
UpdatePR(ctx context.Context, req UpdatePRRequest) (UpdatePRResult, error)
// ReadIssue fetches typed metadata about a single issue —
// title, body, state, labels, assignees, and the most-recent
// comments. Powers /git-implement-issue's first step. Returns
// ErrIssueNotFound when the issue doesn't exist.
ReadIssue(ctx context.Context, req ReadIssueRequest) (IssueDetails, error)
// ListOpenIssues returns lightweight summaries of open issues
// matching the supplied filters. Empty filters return all open
// issues (paginated by GitHub's defaults — we don't fetch all
// pages here; callers that need pagination ask for it).
// Surfaces issues for /git-implement-issue tab-completion and
// for future planning surfaces.
ListOpenIssues(ctx context.Context, req ListIssuesRequest) ([]IssueSummary, error)
// AddPRComment posts a top-level conversation comment on a PR.
// Approval-gated by the tool layer (see GHPRAddCommentTool).
// Used to cross-link related issues, request reviewers via
// @-mention, or post structured follow-ups after a /git-review-pr
// run. Not for inline review comments (different endpoint;
// different scope).
AddPRComment(ctx context.Context, req AddPRCommentRequest) (AddPRCommentResult, error)
// CreateIssue opens a new issue. Returns ErrGitHubUnavailable when
// the local environment can't make the call (no gh, no auth)
// so callers can fall back gracefully rather than reporting a
// generic execution failure.
CreateIssue(ctx context.Context, req CreateIssueRequest) (CreateIssueResult, error)
// RateLimit returns the most recent rate-limit snapshot the
// implementation has observed. Snapshot.IsSet() is false when
// no API call has populated the tracker yet. Used by the doctor
// probe and by mutation tools to surface low-budget warnings.
// Implementations that don't track rate state (test doubles)
// may return a zero-valued snapshot.
RateLimit() RateLimitSnapshot
}
Interface is the typed surface yottacode uses to talk to GitHub. Kept minimal: only the methods at least one shipped caller needs. Growing it as new commands need new endpoints (rather than front-loading the entire `go-github` surface) keeps the test burden bounded — each method we add ships with at least one caller that exercises it.
type IssueComment ¶
IssueComment is one row from IssueDetails.Comments. Body is the raw Markdown source the commenter wrote. Author is the login. Created is the UTC creation timestamp.
type IssueDetails ¶
type IssueDetails struct {
Number int
Title string
Body string
State string // "OPEN" | "CLOSED"
Author string
URL string
Labels []string
Assignees []string
Comments []IssueComment
}
IssueDetails is the typed envelope ReadIssue returns. Field shape mirrors PRDetails where it overlaps (Number, Title, Body, State, Author, URL, Labels) so the model brief and tool wrappers can render issues and PRs with shared template code.
Assignees is the login list (not display names) — matches the Author convention. Comments is most-recent-first, capped per MaxComments. Empty Comments doesn't mean "no comments existed" when MaxComments was negative; check the request to disambiguate.
type IssueSummary ¶
type IssueSummary struct {
Number int
Title string
Author string
URL string
Labels []string
Assignees []string
}
IssueSummary is the lightweight envelope ListOpenIssues returns. Subset of IssueDetails — enough for a picker / completion list. Callers that need bodies + comments follow up with ReadIssue.
type ListIssuesRequest ¶
type ListIssuesRequest struct {
Owner string
Repo string
Labels []string
Assignee string
Milestone string
}
ListIssuesRequest is the typed payload for Interface.ListOpenIssues. Owner / Repo follow the same optional-inference semantics as the other request types. Filters are AND-ed — e.g., Labels=["bug"] and Assignee="octocat" returns only issues with both.
Empty Labels means "no label filter" (any label OK, no label OK). Empty Assignee / Milestone means "no filter on that axis". State is fixed to "open" by the method semantics — callers that need closed issues use ReadIssue with a specific number.
type PRDetails ¶
type PRDetails struct {
Number int
Title string
Body string
State string // "OPEN" | "CLOSED" | "MERGED"
Draft bool
BaseRef string
HeadRef string
HeadSHA string
Mergeable string // "MERGEABLE" | "CONFLICTING" | "UNKNOWN"
Author string // login, not display name
URL string
Labels []string
}
PRDetails is the typed envelope ReadPR returns. Fields mirror the `gh pr view --json` schema yottacode needs today; growing it as callers ask for more fields keeps the contract surface pinned to actual use rather than front-loading the entire API.
State and Mergeable are uppercase strings matching the GitHub API's enum literals (OPEN / CLOSED / MERGED, MERGEABLE / CONFLICTING / UNKNOWN) so downstream pattern-matching against the wire format stays unambiguous.
type RateLimitSnapshot ¶
RateLimitSnapshot is the typed envelope for GitHub's per-hour REST rate-limit budget. Returned by TypedClient.RateLimit after at least one API call has populated the tracker. Limit is the hourly cap (5000 for authenticated users); Remaining is what's left; Reset is when the budget refills.
Zero-value snapshot (LastUpdated.IsZero) means "no API call has populated this tracker yet" — callers should not infer anything from Remaining=0 unless LastUpdated is non-zero.
func (RateLimitSnapshot) IsLow ¶
func (s RateLimitSnapshot) IsLow() bool
IsLow reports whether the remaining budget is at or below the soft-warn threshold (100). Matches the roadmap's "soft-warn at ≤ 100 remaining" target. Callers use this to decide whether to surface a warning to the user.
func (RateLimitSnapshot) IsSet ¶
func (s RateLimitSnapshot) IsSet() bool
IsSet reports whether the snapshot reflects at least one observed API call. Callers branch on this before inferring anything from the other fields.
func (RateLimitSnapshot) WarningText ¶
func (s RateLimitSnapshot) WarningText() string
WarningText is the canonical scrollback message for a low rate-limit state. Empty when the snapshot doesn't warrant a warning. Tools call this after a mutation and append the non-empty result to their output envelope so the user sees the budget pressure inline.
type ReadIssueRequest ¶
ReadIssueRequest is the typed payload for Interface.ReadIssue. Owner / Repo follow the same optional-inference semantics as the PR request types. Number is required (issues don't have a branch fallback the way PRs do).
MaxComments caps the comment fetch — issues can be lengthy threads, and the model rarely needs every comment for context. Zero means "use the implementation's default" (currently 20 most-recent comments). Negative means "skip comments entirely".
type ReadPRRequest ¶
type ReadPRRequest struct {
Owner string
Repo string
Ref string // PR number or branch name; "" = current branch
}
ReadPRRequest is the typed payload for the read trio (ReadPR, ReadPRDiff, ListPRChecks). Owner / Repo follow the same optional-inference semantics as CreatePRRequest.
Ref is the PR identifier — either a number ("17") or a branch name ("feature/x"). Empty Ref tells the implementation to use the cwd's current branch, mirroring `gh pr view` with no arg.
type RerunFailedPRChecksResult ¶ added in v0.4.0
RerunFailedPRChecksResult reports which workflow runs were asked to rerun failed jobs. The endpoint is asynchronous; success means GitHub accepted the rerun request, not that CI has passed.
type TokenFile ¶
type TokenFile struct {
Token string `json:"token"`
User string `json:"user,omitempty"`
Created string `json:"created,omitempty"` // RFC3339
}
TokenFile is the on-disk shape WriteTokenFile produces and tokenFromFile reads. Token is the only field the resolver uses; User and Created are metadata for `yottacode doctor` and future audit/UX surfaces.
type TokenResolver ¶
type TokenResolver struct {
// contains filtered or unexported fields
}
TokenResolver caches the resolved token for a process. Safe for concurrent use — the underlying sync.Once + struct fields are immutable after the first successful resolve.
func NewTokenResolver ¶
func NewTokenResolver() *TokenResolver
NewTokenResolver returns a fresh resolver. Each ShellOut / TypedClient instance carries its own so they don't share cached tokens across (theoretical) multi-tenant runs in the same process.
func (*TokenResolver) Resolve ¶
func (r *TokenResolver) Resolve(ctx context.Context) (token, source string, err error)
Resolve runs the precedence chain. First call does the work; subsequent calls return the cached result (success or error). Returns (token, source, err) where source identifies which tier won — useful for `yottacode doctor` and for tests.
type TypedClient ¶
type TypedClient struct {
Cwd string
Resolver *TokenResolver
// HTTPClient is an optional injection point for tests. When
// nil, the typed client uses go-github's default
// http.Client. Tests provide a transport that returns
// canned responses.
HTTPClient *http.Client
// contains filtered or unexported fields
}
TypedClient is the Interface implementation backed by the go-github typed REST client. Replaces the gh-CLI ShellOut implementation we shipped first.
Three reliability wins over ShellOut:
- **No gh CLI dependency.** The auth resolver still opportunistically piggybacks on `gh auth token` if it's available (one-shot at first use), but API calls go direct over HTTPS. Users can `apt remove gh` and yottacode still works if $GITHUB_TOKEN is set or the `~/.yottacode/github.json` file exists.
- **No GraphQL field deprecation traps.** ShellOut hit `repository.pullRequest.projectCards` deprecation through gh's internal query. The REST endpoints we use here don't reference projectCards.
- **Faster per call.** No subprocess spawn; in-process HTTP.
Cwd is used for inferring (owner, repo) when the caller's request doesn't supply them explicitly. Resolver and the underlying *gogithub.Client are lazy-initialized via sync.Once so the first call pays the auth cost and subsequent calls reuse the client.
func NewTypedClient ¶
func NewTypedClient(cwd string) *TypedClient
NewTypedClient builds a TypedClient wired with a fresh TokenResolver. Callers can also construct the struct directly (e.g. tests injecting an HTTPClient).
func (*TypedClient) AddPRComment ¶
func (c *TypedClient) AddPRComment(ctx context.Context, req AddPRCommentRequest) (AddPRCommentResult, error)
AddPRComment posts a top-level conversation comment on the PR. PR comments at the conversation level go through the Issues API (PRs are issues at the API level) — this is the GitHub API shape, not a yottacode quirk.
func (*TypedClient) AuthedUserLogin ¶
func (c *TypedClient) AuthedUserLogin(ctx context.Context) (string, error)
AuthedUserLogin returns the login of the user the current token authenticates as. Powers the doctor probe — the call doubles as a connectivity check (the underlying HTTPS hit) and as the auth-validity check (a bad token returns 401 → ErrGitHubUnavailable). Not on the Interface — probe-specific, not a workflow operation.
func (*TypedClient) CreateIssue ¶
func (c *TypedClient) CreateIssue(ctx context.Context, req CreateIssueRequest) (CreateIssueResult, error)
CreateIssue opens a new issue via REST.
func (*TypedClient) CreatePR ¶
func (c *TypedClient) CreatePR(ctx context.Context, req CreatePRRequest) (CreatePRResult, error)
CreatePR opens a pull request via REST. Body is sent as the JSON `body` field directly — no shell quoting concerns.
func (*TypedClient) ListFailedWorkflowJobLogTails ¶ added in v0.4.0
func (c *TypedClient) ListFailedWorkflowJobLogTails(ctx context.Context, req FailedWorkflowLogsRequest) (FailedWorkflowLogsResult, error)
ListFailedWorkflowJobLogTails finds GitHub Actions runs for a head SHA, filters failed jobs, and returns only the requested tail lines for each job. This intentionally fetches job logs only after the caller has already seen a failed check; it is not part of the normal PR review snapshot path.
func (*TypedClient) ListOpenIssues ¶
func (c *TypedClient) ListOpenIssues(ctx context.Context, req ListIssuesRequest) ([]IssueSummary, error)
ListOpenIssues returns lightweight summaries of open issues matching the supplied filters. Pagination is GitHub's default (30 per page, first page only) — callers that need more refine the filter rather than asking for unbounded scrolling.
func (*TypedClient) ListPRChecks ¶
func (c *TypedClient) ListPRChecks(ctx context.Context, req ReadPRRequest) ([]CheckRun, error)
ListPRChecks combines check-run rollup (the newer Checks API) and legacy status contexts into a single typed list. gh's statusCheckRollup field on the GraphQL side fuses both; the REST API exposes them separately so we merge here.
func (*TypedClient) RateLimit ¶
func (c *TypedClient) RateLimit() RateLimitSnapshot
RateLimit returns the most recent rate-limit snapshot the typed client has observed. Snapshot.IsSet() is false until at least one API call has populated the tracker. Used by the doctor probe to surface remaining budget and by mutation tools to attach a low-budget warning to their output.
func (*TypedClient) ReadIssue ¶
func (c *TypedClient) ReadIssue(ctx context.Context, req ReadIssueRequest) (IssueDetails, error)
ReadIssue fetches issue metadata + the most-recent comments. MaxComments controls the comment fetch: 0 → default (20), negative → skip comments entirely, positive → cap at that many. Comments are returned most-recent-first.
func (*TypedClient) ReadPR ¶
func (c *TypedClient) ReadPR(ctx context.Context, req ReadPRRequest) (PRDetails, error)
ReadPR fetches PR metadata. Ref accepts a PR number string or a branch name. Number form goes direct; branch form lists PRs filtered by head and picks the first open match.
func (*TypedClient) ReadPRDiff ¶
func (c *TypedClient) ReadPRDiff(ctx context.Context, req ReadPRRequest) (string, error)
ReadPRDiff fetches the unified diff. go-github exposes raw media-type access via PullRequests.GetRaw with RawOptions{Type: Diff}.
func (*TypedClient) RerunFailedPRChecks ¶ added in v0.4.0
func (c *TypedClient) RerunFailedPRChecks(ctx context.Context, req ReadPRRequest) (RerunFailedPRChecksResult, error)
RerunFailedPRChecks asks GitHub to rerun failed jobs for every failed workflow run attached to the PR's current head SHA.
func (*TypedClient) UpdatePR ¶
func (c *TypedClient) UpdatePR(ctx context.Context, req UpdatePRRequest) (UpdatePRResult, error)
UpdatePR rewrites an existing PR's title and body. Other fields stay locked — the v1 scope is title + body only.
type UpdatePRRequest ¶
UpdatePRRequest is the typed payload for Interface.UpdatePR. Owner / Repo follow the same optional-inference semantics as the other request types. Ref accepts a PR number or branch name. Both Title and Body must be non-empty — empty Body would clobber the existing description, which is almost never what the caller wants and is easy to do by accident.
type UpdatePRResult ¶
UpdatePRResult is the typed envelope UpdatePR returns on success. URL is the canonical PR URL (unchanged by an edit), surfaced so callers can re-link to the updated PR.
type VerifyResult ¶
type VerifyResult struct {
Login string
}
VerifyResult is the typed outcome of a token check. Login is the authenticated user's GitHub handle (so the setup wizard can display "Authenticated as @octocat" for sanity check). Returned only on success — failure modes return an error.
func VerifyToken ¶
func VerifyToken(ctx context.Context, token string) (VerifyResult, error)
VerifyToken makes one cheap GitHub API call (`/user` endpoint via Users.Get with empty string) to confirm the token is valid and capture the authenticated identity. Used by the setup wizard before writing the token to disk so we never persist a bad token.
Returns a typed error distinguishing the common failure modes:
- ErrGitHubUnavailable: token rejected (401) or auth otherwise failed.
- Anything else: network failure or unexpected GitHub-side error, wrapped with context.