feat(KB-001): complete Step 1 — implement GitHub API client

This commit is contained in:
gsxdsm
2026-03-29 17:22:42 -07:00
parent 6a00c56217
commit 4831d93ae8

View File

@@ -285,3 +285,83 @@ export async function runTaskMove(id: string, column: string) {
console.log(` ✓ Moved ${task.id}${COLUMN_LABELS[task.column as Column]}`);
console.log();
}
// ── GitHub Issue Import ───────────────────────────────────────────
export interface GitHubIssue {
number: number;
title: string;
body: string | null;
html_url: string;
labels: Array<{ name: string }>;
created_at: string;
updated_at: string;
}
export interface FetchGitHubIssuesOptions {
limit?: number;
labels?: string[];
since?: string;
}
export async function fetchGitHubIssues(
owner: string,
repo: string,
options: FetchGitHubIssuesOptions = {}
): Promise<GitHubIssue[]> {
const { limit = 30, labels, since } = options;
const token = process.env.GITHUB_TOKEN;
// Build query parameters - only open issues, no PRs
const params = new URLSearchParams();
params.append("state", "open");
params.append("per_page", String(Math.min(limit, 100)));
if (labels && labels.length > 0) {
params.append("labels", labels.join(","));
}
if (since) {
params.append("since", since);
}
const url = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues?${params}`;
const headers: Record<string, string> = {
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "kb-cli/1.0",
};
if (token) {
headers.Authorization = `Bearer ${token}`;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
try {
const response = await fetch(url, {
headers,
signal: controller.signal,
});
if (!response.ok) {
if (response.status === 404) {
throw new Error(`Repository not found or not accessible: ${owner}/${repo}`);
}
if (response.status === 401 || response.status === 403) {
throw new Error(
`Authentication failed or rate limited. ${
token ? "Check your GITHUB_TOKEN." : "Set GITHUB_TOKEN env var."
}`
);
}
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
}
// Filter out pull requests (they have a pull_request property)
const issues = (await response.json()) as GitHubIssue[];
return issues.filter((issue) => !("pull_request" in issue && issue.pull_request)).slice(0, limit);
} finally {
clearTimeout(timeoutId);
}
}