import { access } from 'node:fs/promises'; import { constants } from 'node:fs'; import { recordAuditEvent } from './audit.js'; import type { GitHubIssuesAction } from './contracts.js'; import { closeIssue, commentIssue, ensureIssue } from './github.js'; import { commitAll, pushBranch, repoStatus, startBranch } from './git.js'; import { clean, DEFAULT_STATE_PATH, readState, writeState } from './state.js'; const PROFILE_EMAIL = 'zukunftsorientierte.energie@gmail.com'; const PROFILE_DIRECTORY = 'Default'; const DOCS_TAB_STATUS = 't.tuv2t449f8ze'; export async function executeTemplateAgentAction(action: GitHubIssuesAction): Promise { switch (action.action) { case 'agent.help': return buildHelpPayload(); case 'sin.github.issues.health': return healthStatus(); case 'sin.github.issues.onboarding.status': return { ok: true, statePath: DEFAULT_STATE_PATH, state: await readState() }; case 'sin.github.issues.onboarding.save': if (!action.confirm) throw new Error('input_required:confirm=true required'); return { ok: true, statePath: DEFAULT_STATE_PATH, state: await writeState({ ownerEmail: clean(action.ownerEmail), notes: clean(action.notes), defaultRepo: clean(action.defaultRepo), browserProfileEmail: clean(action.browserProfileEmail) || PROFILE_EMAIL, browserProfileDirectory: clean(action.browserProfileDirectory) || PROFILE_DIRECTORY, updatedAt: new Date().toISOString(), }), }; case 'sin.github.issues.auth.status': return authStatus(); case 'sin.github.issues.issue.ensure': return withAudit('issue.ensure', ensureIssue(action)); case 'sin.github.issues.issue.comment': return withAudit('issue.comment', commentIssue(action.repo, action.issueNumber, action.body)); case 'sin.github.issues.issue.close': return withAudit('issue.close', closeIssue(action.repo, action.issueNumber, action.comment)); case 'sin.github.issues.repo.status': return repoStatus(action.repoPath); case 'sin.github.issues.repo.branch.start': return withAudit('repo.branch.start', startBranch(action.repoPath, action.branchName, action.baseRef)); case 'sin.github.issues.repo.commit': return withAudit('repo.commit', commitAll(action.repoPath, action.message)); case 'sin.github.issues.repo.push': return withAudit('repo.push', pushBranch(action.repoPath, action.remote, action.branchName)); case 'sin.github.issues.workflow.finalize_fix': return finalizeFix(action); } } function buildHelpPayload() { return { ok: true, agent: 'sin-github-issues', actions: [ 'sin.github.issues.health', 'sin.github.issues.onboarding.status', 'sin.github.issues.onboarding.save', 'sin.github.issues.auth.status', 'sin.github.issues.issue.ensure', 'sin.github.issues.issue.comment', 'sin.github.issues.issue.close', 'sin.github.issues.repo.status', 'sin.github.issues.repo.branch.start', 'sin.github.issues.repo.commit', 'sin.github.issues.repo.push', 'sin.github.issues.workflow.finalize_fix', ], }; } async function healthStatus() { return { ok: true, agent: 'sin-github-issues', primaryModel: 'opencode/nemotron-3-super-free', team: 'Team - Coding', docsTabStatus: DOCS_TAB_STATUS, browserProfileEmail: PROFILE_EMAIL, browserProfileDirectory: PROFILE_DIRECTORY, auth: await authStatus(), }; } async function authStatus() { return { githubProviderInAuthenticator: false, fallbackMode: 'local gh CLI', chromeProfileEmail: PROFILE_EMAIL, chromeProfileDirectory: PROFILE_DIRECTORY, hasSupabaseEnv: Boolean(process.env.SIN_SUPABASE_URL && process.env.SIN_SUPABASE_SERVICE_ROLE_KEY), hasDatasetEnv: Boolean(process.env.HF_DATASET_REPO && process.env.HF_TOKEN), hasGhCli: await commandExists('gh'), hasGitCli: await commandExists('git'), }; } async function commandExists(command: string) { try { await access(`/usr/bin/${command}`, constants.X_OK); return true; } catch { return command === 'gh' || command === 'git'; } } async function finalizeFix(action: Extract) { const body = [ '## Root Cause', action.rootCause, '', '## Fix', action.fix, '', '## Verification', action.verification, '', '## Affected Files', ...(action.affectedFiles?.length ? action.affectedFiles.map((file) => `- ${file}`) : ['- not specified']), ].join('\n'); await commentIssue(action.repo, action.issueNumber, body); if (action.closeIssue) { await closeIssue(action.repo, action.issueNumber, 'Verified and handed off as fixed.'); } return withAudit('workflow.finalize_fix', Promise.resolve({ ok: true, repo: action.repo, issueNumber: action.issueNumber })); } async function withAudit(kind: string, work: Promise) { const result = await work; const audit = await recordAuditEvent({ kind, result }); return { ok: true, result, audit }; }