main
1/**
2 * Repository action handlers for GitHub extension
3 */
4
5import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
6import type { GhDetails } from "../types";
7import { parseRepo, parseReleaseList, getErrorMessage, formatRelativeDate, execGh } from "../utils";
8
9/**
10 * View repository info
11 */
12export async function handleRepoView(
13 pi: ExtensionAPI,
14 params: any,
15 signal: AbortSignal | undefined,
16 onUpdate: any,
17 ctx: ExtensionContext,
18): Promise<any> {
19 onUpdate?.({ content: [{ type: "text", text: "Fetching repo info..." }] });
20
21 const result = await execGh(pi, ctx,
22 [
23 "repo",
24 "view",
25 "--json",
26 "nameWithOwner,description,defaultBranchRef,visibility,url,stargazerCount,forkCount,isArchived",
27 ],
28 { signal, timeout: 20000 },
29 );
30
31 if (result.code !== 0) {
32 return {
33 content: [{ type: "text", text: getErrorMessage(result.stderr, "View repo") }],
34 details: { action: "repo-view", error: result.stderr } as GhDetails,
35 isError: true,
36 };
37 }
38
39 const repo = parseRepo(result.stdout);
40 if (!repo) {
41 return {
42 content: [{ type: "text", text: "Could not parse repository information" }],
43 details: { action: "repo-view", error: "parse_error" } as GhDetails,
44 isError: true,
45 };
46 }
47
48 let output = `# ${repo.nameWithOwner}\n\n`;
49 if (repo.description) output += `${repo.description}\n\n`;
50 output += `Visibility: ${repo.visibility}\n`;
51 output += `Default branch: ${repo.defaultBranch}\n`;
52 output += `Stars: ${repo.stargazerCount} | Forks: ${repo.forkCount}\n`;
53 output += `URL: ${repo.url}\n`;
54 if (repo.isArchived) output += `⚠️ This repository is archived\n`;
55
56 return {
57 content: [{ type: "text", text: output }],
58 details: { action: "repo-view", output } as GhDetails,
59 };
60}
61
62/**
63 * List releases
64 */
65export async function handleReleaseList(
66 pi: ExtensionAPI,
67 params: any,
68 signal: AbortSignal | undefined,
69 onUpdate: any,
70 ctx: ExtensionContext,
71): Promise<any> {
72 const args = [
73 "release",
74 "list",
75 "--json",
76 "tagName,name,isDraft,isPrerelease,publishedAt",
77 ];
78
79 if (params.limit) args.push("--limit", String(params.limit));
80 else args.push("--limit", "10");
81
82 onUpdate?.({ content: [{ type: "text", text: "Fetching releases..." }] });
83
84 const result = await execGh(pi, ctx, args, { signal, timeout: 20000 });
85
86 if (result.code !== 0) {
87 return {
88 content: [{ type: "text", text: getErrorMessage(result.stderr, "List releases") }],
89 details: { action: "release-list", error: result.stderr } as GhDetails,
90 isError: true,
91 };
92 }
93
94 const releases = parseReleaseList(result.stdout);
95
96 if (releases.length === 0) {
97 return {
98 content: [{ type: "text", text: "No releases found." }],
99 details: { action: "release-list", output: "No releases found" } as GhDetails,
100 };
101 }
102
103 let output = `Releases (${releases.length}):\n\n`;
104 for (const release of releases) {
105 const flags: string[] = [];
106 if (release.isDraft) flags.push("draft");
107 if (release.isPrerelease) flags.push("pre-release");
108 const flagStr = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
109 const date = formatRelativeDate(release.publishedAt);
110
111 output += `${release.tagName}`;
112 if (release.name && release.name !== release.tagName) output += ` - ${release.name}`;
113 output += `${flagStr}`;
114 if (date) output += ` (${date})`;
115 output += "\n";
116 }
117
118 return {
119 content: [{ type: "text", text: output }],
120 details: { action: "release-list", output } as GhDetails,
121 };
122}