import type { GitHubCommentResult, PRComment } from '../../shared/github/comment-types' import type { IssueSourcePreference } from '../../shared/repo-types' import type { LocalGitExecOptions, OwnerRepo } from './gh-utils' import { resolveGitHubRepoExecution, resolveIssueGitHubApiRepositorySource } from './github-api-repository' import { acquire, classifyGhError, ghExecFileAsync, release } from './gh-utils' import { codeHostFailure, classifiedFailure } from '../../shared/code-host-failure-copy' // An explicit target keeps replies bound to the conversation; otherwise use the selected source. export async function addIssueComment( repoPath: string, issueNumber: number, body: string, connectionId?: string | null, ownerRepoOverride?: OwnerRepo | null, localGitOptions: LocalGitExecOptions = {}, preference?: IssueSourcePreference ): Promise { const { ownerRepo, ghOptions } = await resolveGitHubRepoExecution( repoPath, ownerRepoOverride ?? (async () => ( await resolveIssueGitHubApiRepositorySource( repoPath, preference, connectionId, localGitOptions ) ).source), connectionId, localGitOptions ) if (!ownerRepo) { return codeHostFailure('github.repoUnresolved') } await acquire() try { const { stdout } = await ghExecFileAsync( [ 'api', '-X', 'POST', `repos/${ownerRepo.owner}/${ownerRepo.repo}/issues/${issueNumber}/comments`, '--input', '-' ], { ...ghOptions, stdin: JSON.stringify({ body }) } ) const data = JSON.parse(stdout) as { id?: number node_id?: string | null user: { login: string; avatar_url: string; type?: string } | null body?: string created_at?: string html_url?: string } if (typeof data.id !== 'number' || !Number.isSafeInteger(data.id) || data.id < 1) { return codeHostFailure('github.unexpectedResponse') } const comment: PRComment = { id: data.id, reactionSubjectId: data.node_id?.trim() || undefined, author: data.user?.login ?? 'You', authorAvatarUrl: data.user?.avatar_url ?? '', body: data.body ?? body, createdAt: data.created_at ?? new Date().toISOString(), url: data.html_url ?? '', isBot: data.user?.type === 'Bot' } return { ok: true, comment } } catch (err) { const stderr = err instanceof Error ? err.message : String(err) return classifiedFailure(classifyGhError(stderr)) } finally { release() } }