diff --git a/src/kilocode/agent-manager/gitea-client.ts b/src/kilocode/agent-manager/gitea-client.ts index a94aa10..a35ccc8 100644 --- a/src/kilocode/agent-manager/gitea-client.ts +++ b/src/kilocode/agent-manager/gitea-client.ts @@ -1,5 +1,5 @@ // kilocode_change - integrated module -// Gitea API client for logging agent performance +// Gitea API 1.21+ client for logging agent performance const GITEA_API_URL = process.env.GITEA_API_URL || "https://git.softuniq.eu/api/v1" const GITEA_TOKEN = process.env.GITEA_TOKEN || "" @@ -11,28 +11,51 @@ export interface GiteaConfig { repo: string } +// Label structure per Gitea API 1.21+ +export interface Label { + id: number + name: string + color: string + description?: string + exclusive?: boolean + is_archived?: boolean + url?: string +} + export interface IssueComment { + id?: number body: string created_at?: string updated_at?: string + user?: { login: string; id: number } } export interface Issue { + id?: number number: number title: string body: string state: "open" | "closed" - labels: Array<{ name: string; color: string }> - assignees: Array<{ login: string }> + labels: Label[] + assignees?: Array<{ id: number; login: string; full_name?: string }> comments: number created_at: string updated_at: string + html_url?: string } export interface CreateIssueOptions { title: string body: string - labels?: string[] + labels?: number[] | string[] + assignees?: string[] +} + +export interface UpdateIssueOptions { + title?: string + body?: string + state?: "open" | "closed" + labels?: number[] assignees?: string[] } @@ -40,6 +63,11 @@ export interface CreateCommentOptions { body: string } +// IssueLabelsOption supports both IDs and names +export interface IssueLabelsOption { + labels: number[] | string[] +} + export class GiteaClient { private baseUrl: string private token: string @@ -85,9 +113,16 @@ export class GiteaClient { throw new Error(`Gitea API error: ${response.status} - ${error}`) } + // Handle 204 No Content + if (response.status === 204) { + return {} as T + } + return response.json() } + // ==================== Issues ==================== + async getIssue(issueNumber: number): Promise { return this.request( "GET", @@ -103,11 +138,76 @@ export class GiteaClient { ) } - async addLabel(issueNumber: number, labelId: number): Promise { + async updateIssue(issueNumber: number, options: UpdateIssueOptions): Promise { + return this.request( + "PATCH", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}`, + options + ) + } + + async closeIssue(issueNumber: number): Promise { + return this.updateIssue(issueNumber, { state: "closed" }) + } + + async reopenIssue(issueNumber: number): Promise { + return this.updateIssue(issueNumber, { state: "open" }) + } + + // ==================== Issue Comments ==================== + + async getComments(issueNumber: number): Promise { + return this.request( + "GET", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments` + ) + } + + async createComment(issueNumber: number, options: CreateCommentOptions): Promise { + return this.request( + "POST", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`, + options + ) + } + + async updateComment(commentId: number, options: CreateCommentOptions): Promise { + return this.request( + "PATCH", + `/repos/${this.owner}/${this.repo}/issues/comments/${commentId}`, + options + ) + } + + async deleteComment(commentId: number): Promise { await this.request( + "DELETE", + `/repos/${this.owner}/${this.repo}/issues/comments/${commentId}` + ) + } + + // ==================== Issue Labels ==================== + + async getIssueLabels(issueNumber: number): Promise { + return this.request( + "GET", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels` + ) + } + + async addLabels(issueNumber: number, labels: number[] | string[]): Promise { + return this.request( "POST", `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels`, - { labels: [labelId] } + { labels } + ) + } + + async replaceLabels(issueNumber: number, labels: number[] | string[]): Promise { + return this.request( + "PUT", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels`, + { labels } ) } @@ -118,76 +218,87 @@ export class GiteaClient { ) } - async createComment(issueNumber: number, options: CreateCommentOptions): Promise<{ id: number; body: string }> { - return this.request( - "POST", - `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments`, - options - ) - } - - async getComments(issueNumber: number): Promise { - return this.request( - "GET", - `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/comments` - ) - } - - async updateComment(issueNumber: number, commentId: number, body: string): Promise { + async clearLabels(issueNumber: number): Promise { await this.request( - "PATCH", - `/repos/${this.owner}/${this.repo}/issues/comments/${commentId}`, - { body } + "DELETE", + `/repos/${this.owner}/${this.repo}/issues/${issueNumber}/labels` ) } - async closeIssue(issueNumber: number): Promise { + // ==================== Repository Labels ==================== + + async getRepoLabels(): Promise { return this.request( - "PATCH", - `/repos/${this.owner}/${this.repo}/issues/${issueNumber}`, - { state: "closed" } - ) - } - - async reopenIssue(issueNumber: number): Promise { - return this.request( - "PATCH", - `/repos/${this.owner}/${this.repo}/issues/${issueNumber}`, - { state: "open" } - ) - } - - async getStatusLabels(issueNumber: number): Promise { - const issue = await this.getIssue(issueNumber) - return issue.labels - .filter(l => l.name.startsWith("status:")) - .map(l => l.name) - } - - async setStatus(issueNumber: number, status: string): Promise { - const statusLabel = `status: ${status}` - - const allLabels = await this.request>( "GET", `/repos/${this.owner}/${this.repo}/labels` ) + } + + async getLabel(labelId: number): Promise