(pr: ParamProposal)
| 295 | |
| 296 | /** Render a proposal as a compact, reviewable block. PURE. */ |
| 297 | export function formatParamProposal(pr: ParamProposal): string { |
| 298 | if (!pr.ok) return `No mechanical parameterization: ${pr.reason}`; |
| 299 | const lines = [ |
| 300 | `Proposed shared helper \`${pr.helperName}(${pr.params.map(p => p.name).join(', ')})\` — the bodies differ ONLY in these ${pr.params.length} spot(s):`, |
| 301 | ]; |
| 302 | for (const c of pr.calls) lines.push(` ${c.member}(…) → ${pr.helperName}(${c.args.join(', ')})`); |
| 303 | if (pr.dropped?.length) lines.push(` (not covered — different structure: ${pr.dropped.join(', ')})`); |
| 304 | lines.push('', '```', pr.sketch.split('\n').slice(0, 16).join('\n'), '```'); |
| 305 | return lines.join('\n'); |
| 306 | } |
| 307 | |
| 308 | /** A concise, agent-readable report of extraction opportunities. PURE. */ |
| 309 | export function formatHelperClusters(clusters: HelperCluster[]): string { |
no test coverage detected