// Five rule sources lifted from netcracker/interview. // Toggling a rule injects its body into the ruled prompt. const RULES = [ { id: 'agents', name: 'AGENTS.md', kind: 'Repo-wide instruction', path: 'AGENTS.md', en: 'Read AGENTS.md before touching this repo. Stack: pnpm + turbo monorepo, Go API, Next.js apps. Gates run from the monorepo root: pnpm lint, typecheck, test.', pt: 'Leia AGENTS.md antes de tocar neste repo. Stack: pnpm + turbo monorepo, API em Go, apps Next.js. Gates rodam da raiz: pnpm lint, typecheck, test.' }, { id: 'skill', name: 'gate-discipline skill', kind: 'Skill body', path: '.agents/skills/gate-discipline/SKILL.md', en: 'Skill `gate-discipline`: every gate runs separately with `$?`. No `| tail`. `TURBO_FORCE=true` if a pass looks too cheap. Generated code is regenerated, never hand-edited.', pt: 'Skill `gate-discipline`: cada gate roda separado com `$?`. Nada de `| tail`. `TURBO_FORCE=true` se o passar for bom demais. Código gerado se regenera, nunca se edita.' }, { id: 'husky', name: 'Husky pre-commit', kind: 'Git hook (commit time)', path: '.husky/pre-commit', en: 'Pre-commit runs `pnpm exec lint-staged`, then `node scripts/check-ui-contract.mjs` (UI ratchet), then commitlint. Counts in the baseline may only go DOWN.', pt: 'Pre-commit roda `pnpm exec lint-staged`, depois `node scripts/check-ui-contract.mjs` (ratchet de UI), depois commitlint. Contadores do baseline só podem DIMINUIR.' }, { id: 'enforcer', name: 'check-ui-contract.mjs', kind: 'Custom enforcer (CLI)', path: 'scripts/check-ui-contract.mjs', en: 'Enforcer scans for raw `).join(''); ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`; } function renderPrompts() { const lang = state.lang; promptNaive.textContent = NAIVE[lang]; const active = RULES.filter((r) => state.enabled.has(r.id)); const blocks = active.map((r) => `# ${r.name} (${r.path})\n${r[lang]}`).join('\n\n'); const head = `Task: ${TASK[lang].goal}\nFile: ${TASK[lang].ctx}`; const tail = lang === 'en' ? '\n\nRun each gate separately and print $? before claiming done.' : '\n\nRode cada gate separado e imprima $? antes de dizer que terminou.'; promptRuled.textContent = blocks ? `${head}\n\n${blocks}${tail}` : head + tail; } function bind() { ruleList.addEventListener('click', (e) => { const btn = e.target.closest('.toggle'); if (!btn) return; const id = btn.dataset.rule; if (state.enabled.has(id)) state.enabled.delete(id); else state.enabled.add(id); btn.setAttribute('aria-pressed', String(state.enabled.has(id))); ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`; renderPrompts(); }); langButtons.forEach((btn) => btn.addEventListener('click', () => { state.lang = btn.dataset.lang; langButtons.forEach((b) => b.setAttribute('aria-pressed', String(b === btn))); renderPrompts(); })); copyBtn.addEventListener('click', async () => { try { await navigator.clipboard.writeText(promptRuled.textContent); copyBtn.dataset.copied = 'true'; copyBtn.textContent = 'Copied'; setTimeout(() => { copyBtn.dataset.copied = 'false'; copyBtn.textContent = 'Copy ruled prompt'; }, 1400); } catch { copyBtn.textContent = 'Copy failed — select and copy manually'; } }); } renderRules(); renderPrompts(); bind();