| 1 | /** Pure presentation of admitted Core GitHub outcomes. No network, process, store or policy API. */ |
| 2 | import type { Json } from '../../protocol.ts' |
| 3 | interface Result { ok: true; result: { content: string; success: boolean; metadata: Json } } |
| 4 | type Row = Record<string, unknown> |
| 5 | const REPOSITORY = 'Hmbown/CodeWhale' |
| 6 | function row(value: unknown): Row { if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('invalid GitHub snapshot'); return value as Row } |
| 7 | function text(value: unknown): string { if (typeof value !== 'string') throw new Error('invalid GitHub text'); return value } |
| 8 | function strings(value: unknown): string[] { if (!Array.isArray(value)) throw new Error('invalid GitHub list'); return value.map(text) } |
| 9 | // Rust char indices count Unicode scalars before discarding control characters. |
| 10 | export function githubSummary(value: string, limit: number): string { |
| 11 | let out = '', index = 0 |
| 12 | for (const char of value) { |
| 13 | if (index++ >= Math.max(0, limit - 3)) return `${out}...` |
| 14 | const code = char.codePointAt(0)! |
| 15 | if ((code <= 31 || (code >= 127 && code <= 159)) && char !== '\n' && char !== '\t') continue |
| 16 | out += char |
| 17 | } |
| 18 | return out |
| 19 | } |
| 20 | export function renderIssueReview(value: unknown): string { |
| 21 | const report = row(value), fields = row(report.fields), id = text(report.id) |
| 22 | let out = `# ${text(fields.title)}\n\nDraft: ${id}\nStatus: ready for review\nPublication: unavailable\nDuplicate search: not performed\nDestination: ${REPOSITORY}\n\nReview the contents before sharing. Redaction does not guarantee privacy.\n` |
| 23 | if (report.revises !== null) out += `Revises: ${text(report.revises)}\n` |
| 24 | for (const [heading, key] of [['Expected behavior','expected'],['Actual behavior','actual'],['Impact','impact']]) out += `\n## ${heading}\n\n${text(fields[key])}\n` |
| 25 | for (const [heading, key] of [['Steps to reproduce (agent reported)','steps'],['Observed by the agent','observed'],['Inferences (not verified)','inferred']]) { |
| 26 | const items = strings(fields[key]); out += `\n## ${heading}\n\n` |
| 27 | out += items.length ? items.map(item => `- ${item}\n`).join('') : 'None recorded.\n' |
| 28 | } |
| 29 | out += `\n## Runtime context\n\n- Codewhale: ${text(report.version)}\n- Platform: ${text(report.platform)}\n- Active model: ${text(report.model)}\n` |
| 30 | for (const [label,key] of [['Provider','reported_provider'],['Tool','reported_tool'],['Terminal','reported_terminal']]) out += `- ${label} (agent reported): ${fields[key] === null ? 'unknown' : text(fields[key])}\n` |
| 31 | if (!Array.isArray(fields.related_issues) || fields.related_issues.some(value => !Number.isInteger(value) || Number(value) < 1 || Number(value) > 4294967295)) throw new Error('invalid related issue') |
| 32 | if (fields.related_issues.length) out += '\n## Related issues (agent supplied; not verified or searched)\n\n' + fields.related_issues.map(number => `- [#${number}](https://github.com/${REPOSITORY}/issues/${number})\n`).join('') |
| 33 | const redactions = strings(report.redactions) |
| 34 | if (redactions.length) out += `\nRedacted categories: ${redactions.join(', ')}\n` |
| 35 | return out + `\nReview: \`/feedback review ${id}\`\nRevise: \`/feedback edit ${id} <change>\`\n` |
| 36 | } |
| 37 | export function transformGithubSnapshot(value: unknown): Result { |
| 38 | const snapshot = row(value), action = text(snapshot.action) |
| 39 | if (action === 'report_draft' || action === 'report_read' || action === 'report_review') { |
| 40 | const report = row(snapshot.report), id = text(report.id) |
| 41 | const review = renderIssueReview(report) |
| 42 | const content = action === 'report_review' ? review : JSON.stringify({ report_id:id, revises:report.revises, state:'ready_for_review', publication:'unavailable', duplicate_search:'not_performed', review, artifact:`artifacts/issue-reports/${id}.json` }) |
| 43 | return { ok:true, result:{success:true,content,metadata:null} } |
| 44 | } |
| 45 | const number = text(snapshot.number) |
| 46 | if (!/^[0-9]{1,20}$/.test(number)) throw new Error('invalid captured issue number') |
| 47 | if (action === 'issue_context' || action === 'pr_context') { |
| 48 | const raw = structuredClone(row(snapshot.raw)), kind = action === 'issue_context' ? 'issue' : 'pr' |
| 49 | if (snapshot.large_body === true) { |
| 50 | const body = text(raw.body) |
| 51 | raw.body_summary = githubSummary(body,900); raw.body_artifact = snapshot.body_artifact as Json; raw.body = githubSummary(body,1200) |
| 52 | } |
| 53 | if (action === 'pr_context' && snapshot.diff !== null) { raw.diff_summary=githubSummary(text(snapshot.diff),900); raw.diff_artifact=snapshot.diff_artifact as Json } |
| 54 | const subject = kind === 'issue' ? 'Issue' : 'PR' |
| 55 | return { ok:true, result:{success:true,metadata:null,content:JSON.stringify({summary:`${subject} #${number}: ${typeof raw.title === 'string' ? raw.title : ''}`,[kind]:raw})} } |
| 56 | } |
| 57 | const target = text(snapshot.target) |
| 58 | if (!['issue','pr'].includes(target)) throw new Error('invalid captured GitHub target') |
| 59 | const subject = target === 'issue' ? 'issue' : 'PR' |
| 60 | let content: string |
| 61 | if (snapshot.dirty === true) content = `Refusing to close ${subject}: worktree is dirty and allow_dirty was false.` |
| 62 | else if (action === 'comment') content = snapshot.dry_run === true ? `Dry run: would comment on ${target} #${number}.` : `Commented on ${target} #${number}.` |
| 63 | else if (action === 'close_issue' || action === 'close_pr') content = snapshot.dry_run === true ? `Dry run: would close ${subject} #${number}.` : `Closed ${subject} #${number}.` |
| 64 | else throw new Error('unknown captured GitHub operation') |
| 65 | return {ok:true,result:{success:snapshot.dirty !== true,content,metadata:null}} |
| 66 | } |
| 67 |