//#region src/types/config.d.ts interface GhfsUserConfig { /** * The repository to sync. * * Will try to detect the repository from the current working directory or the `package.json` file. */ repo?: string; /** * The directory to store the synced issues and pull requests. * * @default '.ghfs' */ directory?: string; /** * The authentication configuration. */ auth?: { /** * The GitHub personal access token to use for authentication. * * When not provided, will try to get the token from `gh auth token` or the environment variables `GH_TOKEN` or `GITHUB_TOKEN`. */ token?: string; }; /** * Additional bot logins to ignore when computing the "last updated" * sort order for issues and pull requests. Logins ending with `[bot]` * (e.g. `dependabot[bot]`) are always detected automatically; use this * list for non-suffix bots like `coderabbitai`. Case-insensitive. * * @default [] */ bots?: string[]; sync?: { /** * Whether to sync issues. * * @default true */ issues?: boolean; /** * Whether to sync pull requests. * * @default true */ pulls?: boolean; /** * When to sync closed issues and pull requests. * * - `true`: sync all closed issues and pull requests. * - `false`: don't sync any closed issues and pull requests. And delete any existing closed issues and pull requests from the local filesystem. * * @default false */ closed?: boolean; /** * When to download the pull request patch files. * * - `'open'`: only download open pull request patch files. * - `'all'`: download all pull request patch files. * - `false`: don't download any pull request patch files. * * @default 'open' */ patches?: 'open' | 'all' | false; }; } type GhfsResolvedConfig = Required & { cwd: string; auth: Required; sync: Required; }; //#endregion //#region src/types/issue.d.ts type IssueKind = 'issue' | 'pull'; type IssueState = 'open' | 'closed'; //#endregion //#region src/utils/reactions.d.ts /** * GitHub's wire format for reaction content (REST + GraphQL accept these * strings, sometimes uppercased in GraphQL). Order matches REACTION_KEYS. */ declare const REACTION_CONTENTS: readonly ["+1", "-1", "laugh", "hooray", "confused", "heart", "rocket", "eyes"]; type ReactionContent = typeof REACTION_CONTENTS[number]; //#endregion //#region src/types/provider.d.ts interface ProviderReactions { totalCount: number; plusOne: number; minusOne: number; laugh: number; hooray: number; confused: number; heart: number; rocket: number; eyes: number; } type IssueStateReason = 'completed' | 'not_planned' | 'reopened'; interface ProviderItem { number: number; kind: IssueKind; url?: string; state: IssueState; stateReason?: IssueStateReason | null; updatedAt: string; createdAt: string; closedAt: string | null; title: string; body: string | null; author: string | null; authorAvatarUrl?: string; labels: string[]; assignees: string[]; milestone: string | null; reactions?: ProviderReactions; } interface ProviderComment { id: number; body: string | null; createdAt: string; updatedAt: string; author: string | null; authorAvatarUrl?: string; reactions?: ProviderReactions; } type ProviderReviewDecision = 'approved' | 'changes_requested' | 'review_required'; interface ProviderPullMetadata { isDraft: boolean; merged: boolean; mergedAt: string | null; baseRef: string; headRef: string; requestedReviewers: string[]; /** * Whether GitHub computed the PR to be mergeable. `null`/omitted when GitHub * hasn't finished computing yet — the UI should treat as unknown. */ mergeable?: boolean | null; /** * Raw GitHub `mergeable_state` string: typically one of * `clean | dirty | blocked | behind | unstable | draft | unknown`. */ mergeableState?: string; /** * Aggregate review state. Prefers GitHub's own `reviewDecision` (set when * branch protection requires reviews); falls back to a derivation from the * latest non-dismissed review per reviewer. `null` when there is no useful * signal (no reviews submitted and no reviewers requested). */ reviewDecision?: ProviderReviewDecision | null; } interface ProviderReviewComment { id: number; body: string | null; author: string | null; authorAvatarUrl?: string; createdAt: string; updatedAt: string; /** File path the comment is anchored to. */ path: string; /** Line in the file (right side for additions); null if the line is no longer present. */ line: number | null; /** Start line for multi-line comments. */ startLine?: number | null; side?: 'LEFT' | 'RIGHT'; /** Snippet of the diff for context — already includes the leading `@@` hunk header. */ diffHunk: string; commitId?: string; /** REST id of the parent review (groups comments into a single review). */ pullRequestReviewId: number | null; /** REST id of the comment this is a reply to, when threaded. */ inReplyToId: number | null; reactions?: ProviderReactions; } type MergeMethod = 'squash' | 'merge' | 'rebase'; interface MergeOptions { method?: MergeMethod; commitTitle?: string; commitMessage?: string; } interface ProviderCommit { sha: string; message: string; authorLogin: string | null; authorName: string | null; authorDate: string; committerLogin: string | null; committerDate: string; url?: string; } /** Cross-reference target: the issue/PR that mentioned this item. */ interface ProviderTimelineSource { number: number; kind: 'issue' | 'pull'; title?: string; url?: string; repo?: string; } type ProviderReviewState = 'approved' | 'changes_requested' | 'commented' | 'dismissed' | 'pending'; interface ProviderTimelineEventBase { id: string; createdAt: string; actor: string | null; actorAvatarUrl?: string; } type ProviderTimelineEvent = (ProviderTimelineEventBase & { kind: 'committed'; sha: string; commitMessage: string; body?: string | null; commitUrl?: string; }) | (ProviderTimelineEventBase & { kind: 'closed'; stateReason?: string | null; sha?: string; commitUrl?: string; }) | (ProviderTimelineEventBase & { kind: 'reopened'; }) | (ProviderTimelineEventBase & { kind: 'merged'; sha?: string; commitUrl?: string; }) | (ProviderTimelineEventBase & { kind: 'labeled' | 'unlabeled'; label: { name: string; color: string; }; }) | (ProviderTimelineEventBase & { kind: 'assigned' | 'unassigned'; assignee: string; }) | (ProviderTimelineEventBase & { kind: 'review_requested' | 'review_request_removed'; requestedReviewer: string; isTeam?: boolean; }) | (ProviderTimelineEventBase & { kind: 'reviewed'; review: { /** REST id — used to attach inline review comments to their parent review. */id?: number; state: ProviderReviewState; body: string | null; submittedAt: string; /** GraphQL node ID — required to react to a review body. */ nodeId?: string; reactions?: ProviderReactions; }; body?: string | null; }) | (ProviderTimelineEventBase & { kind: 'review_dismissed'; dismissedReview: { state: string; reviewId: number; dismissalMessage: string | null; }; reviewedBy?: string; }) | (ProviderTimelineEventBase & { kind: 'commented'; commentId?: number; body?: string | null; }) | (ProviderTimelineEventBase & { kind: 'renamed'; rename: { from: string; to: string; }; }) | (ProviderTimelineEventBase & { kind: 'referenced' | 'cross-referenced' | 'connected' | 'disconnected' | 'marked_as_duplicate' | 'unmarked_as_duplicate'; source?: ProviderTimelineSource; }) | (ProviderTimelineEventBase & { kind: 'milestoned' | 'demilestoned'; milestone?: string; }) | (ProviderTimelineEventBase & { kind: 'transferred'; fromRepo?: string; }) | (ProviderTimelineEventBase & { kind: 'base_ref_changed'; oldRef?: string; newRef?: string; }) | (ProviderTimelineEventBase & { kind: 'head_ref_force_pushed'; sha?: string; commitUrl?: string; }) | (ProviderTimelineEventBase & { kind: 'head_ref_deleted' | 'head_ref_restored'; }) | (ProviderTimelineEventBase & { kind: 'locked'; lockReason?: string; }) | (ProviderTimelineEventBase & { kind: 'unlocked' | 'ready_for_review' | 'convert_to_draft' | 'pinned' | 'unpinned'; }) | (ProviderTimelineEventBase & { kind: 'mentioned' | 'subscribed' | 'unsubscribed'; }) | (ProviderTimelineEventBase & { kind: 'auto_merge_enabled' | 'auto_merge_disabled' | 'auto_squash_enabled' | 'auto_squash_disabled' | 'auto_rebase_enabled' | 'auto_rebase_disabled'; commitTitle?: string; commitMessage?: string; }) | (ProviderTimelineEventBase & { kind: 'unknown'; rawKind?: string; }); interface ProviderRepository { name: string; full_name: string; description: string | null; private: boolean; archived: boolean; default_branch: string; html_url: string; fork: boolean; open_issues_count: number; has_issues: boolean; has_projects: boolean; has_wiki: boolean; created_at: string; updated_at: string; pushed_at: string | null; owner: { login: string; }; /** Whether the repo allows merge commits (`Create a merge commit`). */ allow_merge_commit?: boolean; /** Whether the repo allows squash-merging (`Squash and merge`). */ allow_squash_merge?: boolean; /** Whether the repo allows rebase-merging (`Rebase and merge`). */ allow_rebase_merge?: boolean; /** * Whether the default branch is gated by a merge queue. When true, the UI * shows a "Merge when ready" button that enqueues the PR via GraphQL. * `null`/omitted when the setting hasn't been fetched yet. */ merge_queue_enabled?: boolean | null; } interface ProviderLabel { name: string; color: string; description: string | null; default: boolean; } interface ProviderAuthenticatedUser { login: string; name: string | null; avatarUrl: string; } interface ProviderMilestone { number: number; title: string; state: 'open' | 'closed'; description: string | null; due_on: string | null; open_issues: number; closed_issues: number; created_at: string; updated_at: string; closed_at: string | null; } interface ProviderItemSnapshot { number: number; kind: IssueKind; updatedAt: string | null; } interface ProviderUpdateCounts { issues: number; pulls: number; } type ProviderLockReason = 'resolved' | 'off-topic' | 'too heated' | 'too-heated' | 'spam'; /** * Where a reaction is applied. `item` = issue/PR body (uses `op.number`). * `comment` = issue/PR conversation comment. `review` = a PR review body * (review reactions go through GraphQL and need the review's node ID). */ type ReactionTarget = { kind: 'item'; } | { kind: 'comment'; commentId: number; } | { kind: 'review'; reviewId: string; }; interface PaginateItemsOptions { state: IssueState | 'all'; since?: string; } interface RepositoryProvider { paginateItems: (options: PaginateItemsOptions) => AsyncIterable; fetchItems: (options: PaginateItemsOptions) => Promise; eachItem: (options: PaginateItemsOptions) => AsyncIterable; fetchItemsByNumbers: (numbers: number[]) => Promise; fetchComments: (number: number) => Promise; fetchPullMetadata: (number: number) => Promise; fetchPullPatch: (number: number) => Promise; fetchPullCommits: (number: number) => Promise; fetchReviewComments: (number: number) => Promise; fetchTimeline: (number: number) => Promise; fetchItemSnapshot: (number: number) => Promise; fetchRepository: () => Promise; fetchRepositoryLabels: () => Promise; fetchRepositoryMilestones: () => Promise; fetchAuthenticatedUser: () => Promise; countUpdatedSince: (since: string) => Promise; getRequestCount: () => number; actionClose: (number: number) => Promise; actionReopen: (number: number) => Promise; actionSetTitle: (number: number, title: string) => Promise; actionSetBody: (number: number, body: string) => Promise; actionAddComment: (number: number, body: string) => Promise; actionAddLabels: (number: number, labels: string[]) => Promise; actionRemoveLabels: (number: number, labels: string[]) => Promise; actionSetLabels: (number: number, labels: string[]) => Promise; actionAddAssignees: (number: number, assignees: string[]) => Promise; actionRemoveAssignees: (number: number, assignees: string[]) => Promise; actionSetAssignees: (number: number, assignees: string[]) => Promise; actionSetMilestone: (number: number, milestone: string | number) => Promise; actionClearMilestone: (number: number) => Promise; actionLock: (number: number, reason?: ProviderLockReason) => Promise; actionUnlock: (number: number) => Promise; actionRequestReviewers: (number: number, reviewers: string[]) => Promise; actionRemoveReviewers: (number: number, reviewers: string[]) => Promise; actionMarkReadyForReview: (number: number) => Promise; actionConvertToDraft: (number: number) => Promise; actionApprove: (number: number, body?: string) => Promise; actionRequestChanges: (number: number, body: string) => Promise; actionReviewComment: (number: number, body: string) => Promise; actionMerge: (number: number, options: MergeOptions) => Promise; actionEnqueueMerge: (number: number) => Promise; actionAddReaction: (number: number, reaction: ReactionContent, target: ReactionTarget) => Promise; actionRemoveReaction: (number: number, reaction: ReactionContent, target: ReactionTarget) => Promise; fetchViewerReactions: (number: number, target: ReactionTarget) => Promise; } //#endregion //#region src/providers/factory.d.ts interface CreateRepositoryProviderOptions { token: string; repo: string; } declare function createRepositoryProvider(options: CreateRepositoryProviderOptions): RepositoryProvider; //#endregion //#region src/index.d.ts declare function defineConfig(config: GhfsUserConfig): GhfsUserConfig; //#endregion export { type GhfsResolvedConfig, type GhfsUserConfig, type IssueKind, type IssueState, type PaginateItemsOptions, type ProviderComment, type ProviderItem, type ProviderItemSnapshot, type ProviderLockReason, type ProviderPullMetadata, type RepositoryProvider, createRepositoryProvider, defineConfig };