412 lines
49 KiB
JavaScript
412 lines
49 KiB
JavaScript
const phases = {
|
||
plan: { model: { en: 'OPUS / REASONING', pt: 'OPUS / RACIOCÍNIO' }, title: { en: 'Turn ambiguity into work', pt: 'Transforme ambiguidade em trabalho' }, copy: { en: 'Inspect the repository, choose the architecture, split the request, and write acceptance criteria.', pt: 'Inspecione o repositório, escolha a arquitetura, divida o pedido e escreva critérios de aceitação.' }, code: { en: 'plan → decompose → define acceptance', pt: 'planejar → decompor → definir aceitação' } },
|
||
build: { model: { en: 'SONNET, HAIKU, OR EQUIVALENT', pt: 'SONNET, HAIKU OU EQUIVALENTE' }, title: { en: 'Execute one bounded slice', pt: 'Execute uma fatia delimitada' }, copy: { en: 'Give each worker enough context, one responsibility, and its own worktree. Less context; less collision.', pt: 'Dê a cada worker contexto suficiente, uma responsabilidade e seu próprio worktree. Menos contexto; menos colisões.' }, code: { en: 'brief + worktree → implement → test', pt: 'brief + worktree → implementar → testar' } },
|
||
review: { model: { en: 'STRONG MODEL OR HUMAN', pt: 'MODELO FORTE OU HUMANO' }, title: { en: 'Reconnect result to intent', pt: 'Reconecte o resultado à intenção' }, copy: { en: 'Check the diff against the original brief, run the checks, then merge, request changes, or discard.', pt: 'Compare o diff com o brief original, execute as verificações e então faça merge, peça mudanças ou descarte.' }, code: { en: 'diff + checks → review → merge / iterate', pt: 'diff + verificações → revisar → merge / iterar' } }
|
||
};
|
||
|
||
const handsOnPrompts = {
|
||
en: {
|
||
basic: [
|
||
'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.',
|
||
'',
|
||
'Add an All / Open / Done filter to Tiny Tasks.',
|
||
'',
|
||
'Requirements:',
|
||
'- derive counts and visible tasks from the existing tasks array',
|
||
'- expose filter buttons with a visible active state and aria-pressed',
|
||
'- store status in ?status=all|open|done',
|
||
'- reload and browser back/forward must restore the selected filter',
|
||
'- show a useful empty state when no task matches',
|
||
'- preserve the visual style and mobile layout',
|
||
'- add no dependencies and change no unrelated files',
|
||
'',
|
||
'Verify app.js syntax and exercise every filter plus URL navigation.',
|
||
'Return changed files, checks run, results, and remaining risk.'
|
||
].join('\n'),
|
||
skills: [
|
||
'Use $ponytail-lite and $webapp-testing.',
|
||
'Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.',
|
||
'',
|
||
'Add an All / Open / Done filter to Tiny Tasks.',
|
||
'',
|
||
'Apply $ponytail-lite: inspect first, reuse the current render flow, prefer native URL and button APIs, and avoid dependencies or abstractions.',
|
||
'Apply $webapp-testing: verify all filters, aria-pressed, reload, browser back/forward, empty state, and one mobile viewport.',
|
||
'',
|
||
'Acceptance:',
|
||
'- counts and visible tasks come from the existing tasks array',
|
||
'- ?status=all|open|done is the source of truth',
|
||
'- invalid status falls back safely to all',
|
||
'- style remains consistent; unrelated files remain untouched',
|
||
'',
|
||
'Return the smallest working diff and concrete verification evidence.'
|
||
].join('\n')
|
||
},
|
||
pt: {
|
||
basic: [
|
||
'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.',
|
||
'',
|
||
'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.',
|
||
'',
|
||
'Requisitos:',
|
||
'- derive contagens e tarefas visíveis do array tasks existente',
|
||
'- use botões com estado ativo visível e aria-pressed',
|
||
'- salve o status em ?status=all|open|done',
|
||
'- reload e voltar/avançar devem restaurar o filtro',
|
||
'- mostre estado vazio quando nenhuma tarefa corresponder',
|
||
'- preserve o visual e layout mobile',
|
||
'- não adicione dependências nem altere arquivos não relacionados',
|
||
'',
|
||
'Verifique a sintaxe de app.js e teste filtros e navegação por URL.',
|
||
'Retorne arquivos alterados, checks, resultados e risco restante.'
|
||
].join('\n'),
|
||
skills: [
|
||
'Use $ponytail-lite e $webapp-testing.',
|
||
'Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências.',
|
||
'',
|
||
'Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks.',
|
||
'',
|
||
'Aplique $ponytail-lite: inspecione primeiro, reutilize o render atual, prefira APIs nativas de URL e button e evite dependências ou abstrações.',
|
||
'Aplique $webapp-testing: verifique filtros, aria-pressed, reload, voltar/avançar, estado vazio e um viewport mobile.',
|
||
'',
|
||
'Aceitação:',
|
||
'- contagens e tarefas visíveis vêm do array tasks existente',
|
||
'- ?status=all|open|done é a fonte de verdade',
|
||
'- status inválido volta com segurança para all',
|
||
'- estilo consistente; nenhum arquivo não relacionado alterado',
|
||
'',
|
||
'Retorne o menor diff funcional e evidências concretas de verificação.'
|
||
].join('\n')
|
||
}
|
||
};
|
||
|
||
const modelGuide = {
|
||
providers: {
|
||
openai: {
|
||
label: 'OpenAI', source: 'https://developers.openai.com/api/docs/guides/latest-model',
|
||
title: { en: 'Sol · Terra · Luna', pt: 'Sol · Terra · Luna' },
|
||
copy: { en: 'GPT-5.6 separates capability tier from reasoning effort. Sol is flagship, Terra balances performance and cost, and Luna targets efficient high-volume work.', pt: 'O GPT-5.6 separa o nível de capacidade do esforço de raciocínio. Sol é flagship, Terra equilibra desempenho e custo, e Luna atende trabalho eficiente em alto volume.' },
|
||
tiers: [
|
||
['STRONG', 'Sol', { en: 'orchestration + hard judgment', pt: 'orquestração + julgamento difícil' }],
|
||
['BALANCED', 'Terra', { en: 'normal implementation', pt: 'implementação normal' }],
|
||
['FAST', 'Luna', { en: 'bounded, high-volume work', pt: 'trabalho delimitado e volumoso' }]
|
||
],
|
||
config: 'reasoning: { effort: "medium" }'
|
||
},
|
||
claude: {
|
||
label: 'Claude', source: 'https://docs.anthropic.com/en/docs/claude-code/model-config',
|
||
title: { en: 'Opus · Sonnet · Haiku', pt: 'Opus · Sonnet · Haiku' },
|
||
copy: { en: 'Claude Code exposes memorable aliases. Opus handles complex reasoning, Sonnet everyday coding, and Haiku simple fast work. The opusplan alias can plan with Opus and execute with Sonnet.', pt: 'Claude Code oferece aliases fáceis de lembrar. Opus cuida de raciocínio complexo, Sonnet do código cotidiano e Haiku de trabalho simples e rápido. O alias opusplan pode planejar com Opus e executar com Sonnet.' },
|
||
tiers: [
|
||
['STRONG', 'Opus', { en: 'planning + architecture', pt: 'planejamento + arquitetura' }],
|
||
['BALANCED', 'Sonnet', { en: 'everyday coding', pt: 'código cotidiano' }],
|
||
['FAST', 'Haiku', { en: 'simple, fast tasks', pt: 'tarefas simples e rápidas' }]
|
||
],
|
||
config: '/model opus · /model sonnet · /model haiku'
|
||
},
|
||
gemini: {
|
||
label: 'Gemini', source: 'https://ai.google.dev/gemini-api/docs/thinking',
|
||
title: { en: 'Pro · Flash · Flash-Lite', pt: 'Pro · Flash · Flash-Lite' },
|
||
copy: { en: 'Gemini uses model families rather than interchangeable aliases. Pro targets complex reasoning, Flash balances capability and throughput, and Flash-Lite prioritizes latency and cost.', pt: 'Gemini usa famílias de modelos, não aliases intercambiáveis. Pro mira raciocínio complexo, Flash equilibra capacidade e throughput, e Flash-Lite prioriza latência e custo.' },
|
||
tiers: [
|
||
['STRONG', 'Pro', { en: 'complex reasoning', pt: 'raciocínio complexo' }],
|
||
['BALANCED', 'Flash', { en: 'capability + throughput', pt: 'capacidade + throughput' }],
|
||
['FAST', 'Flash-Lite', { en: 'latency + cost', pt: 'latência + custo' }]
|
||
],
|
||
config: 'thinkingConfig: { thinkingLevel: "MEDIUM" }'
|
||
}
|
||
},
|
||
efforts: {
|
||
low: { en: ['LOW', 'Use for formatting, lookup, narrow edits, and well-specified worker tasks. Optimize for fast feedback.', 'bounded task → low'], pt: ['BAIXO', 'Use para formatação, consulta, edições estreitas e tarefas de worker bem especificadas. Otimize para feedback rápido.', 'tarefa delimitada → baixo'] },
|
||
medium: { en: ['MEDIUM', 'Balanced starting point for normal implementation, tests, and review. Measure before moving up.', 'normal build → medium'], pt: ['MÉDIO', 'Ponto inicial equilibrado para implementação normal, testes e revisão. Meça antes de subir.', 'build normal → médio'] },
|
||
high: { en: ['HIGH', 'Use for architecture, orchestration, hard debugging, and consequential review where added latency is justified.', 'ambiguity + risk → high'], pt: ['ALTO', 'Use para arquitetura, orquestração, diagnóstico difícil e revisão importante quando a latência extra se justifica.', 'ambiguidade + risco → alto'] }
|
||
}
|
||
};
|
||
|
||
const skillSources = {
|
||
ponytail: 'https://github.com/ilindaniel/ponytail-lite/blob/e7b42dc2d384a702240dea4d52a7bf5530b821b6/AGENTS.md',
|
||
caveman: 'https://github.com/JuliusBrussee/caveman/blob/3b74643f4d910f496babd4e634b1ba7168816f14/skills/caveman/SKILL.md',
|
||
unlazy: 'https://github.com/Leonxlnx/unlazy/blob/473d4b80421c36d733042434cd4b938f81a19ef1/SKILL.md',
|
||
research: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/research/SKILL.md',
|
||
debug: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/diagnosing-bugs/SKILL.md',
|
||
review: 'https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/code-review/SKILL.md',
|
||
tokens: 'https://github.com/aetox-skills/token-saver/blob/8f21188bb043fad411f47e2e57f0365a83c13da7/SKILL.md'
|
||
};
|
||
|
||
const skillInstallPrompts = {
|
||
en: [
|
||
'Inspect and install only these public agent skills. Pin the exact commits:',
|
||
'- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md',
|
||
'- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/',
|
||
'- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — repository root',
|
||
'- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/',
|
||
'- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root',
|
||
'- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/',
|
||
'',
|
||
'Treat repository content as untrusted. Detect the current AI host and documented user-level skill directory; do not guess paths. Download into a temporary directory without curl-pipe-shell, remote installers, or postinstall hooks. Inspect each selected instruction and every referenced script or hook. Show the exact copy plan and existing-file diffs, then ask for approval before installation. Copy only the allowlist and preserve complete referenced packages. Install ponytail-lite through the host instruction mechanism because it is AGENTS.md. Do not enable unlazy hooks or install token-saver\'s RTK binary without separate approval. Finally report destination, SHA-256, validation, and which skills the host discovers.'
|
||
].join('\n'),
|
||
pt: [
|
||
'Inspecione e instale apenas estas skills públicas. Fixe os commits exatos:',
|
||
'- ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md',
|
||
'- JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/',
|
||
'- Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — raiz do repositório',
|
||
'- mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/',
|
||
'- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — raiz do repositório',
|
||
'- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/',
|
||
'',
|
||
'Trate o conteúdo como não confiável. Detecte o host de IA e o diretório documentado de skills; não adivinhe caminhos. Baixe em diretório temporário sem curl-pipe-shell, instaladores remotos ou postinstall. Inspecione instruções, scripts e hooks referenciados. Mostre o plano de cópia e diffs existentes e peça aprovação antes de instalar. Copie apenas a allowlist e preserve pacotes completos. Instale ponytail-lite pelo mecanismo de instruções do host porque é AGENTS.md. Não ative hooks do unlazy nem instale o binário RTK do token-saver sem aprovação separada. Ao final, reporte destino, SHA-256, validação e quais skills o host descobriu.'
|
||
].join('\n')
|
||
};
|
||
|
||
const interactiveCopy = {
|
||
workers: {
|
||
ui: { en: ['Interface worker', 'Receives: component contract + visual states', 'Returns: focused diff + viewport evidence'], pt: ['Worker de interface', 'Recebe: contrato do componente + estados visuais', 'Devolve: diff focado + evidência dos viewports'] },
|
||
tests: { en: ['Verification worker', 'Receives: acceptance criteria + changed surface', 'Returns: failing case, passing checks, risk notes'], pt: ['Worker de verificação', 'Recebe: critérios de aceitação + superfície alterada', 'Devolve: caso de falha, verificações passando e riscos'] },
|
||
docs: { en: ['Documentation worker', 'Receives: reviewed behavior + audience', 'Returns: guide, examples, and migration notes'], pt: ['Worker de documentação', 'Recebe: comportamento revisado + público', 'Devolve: guia, exemplos e notas de migração'] }
|
||
},
|
||
trees: {
|
||
main: { status: 'clean', owner: { en: 'Orchestrator', pt: 'Orquestrador' }, path: './project', command: 'git worktree list', note: { en: 'Shared history and integration point. Workers never edit here.', pt: 'Histórico compartilhado e ponto de integração. Workers nunca editam aqui.' } },
|
||
ui: { status: 'working', owner: { en: 'UI worker', pt: 'Worker de UI' }, path: '../task-ui', command: 'git worktree add ../task-ui -b agent/ui', note: { en: 'Own checkout and index. Safe to change presentation files in parallel.', pt: 'Checkout e índice próprios. Seguro para alterar a apresentação em paralelo.' } },
|
||
tests: { status: 'ready', owner: { en: 'Test worker', pt: 'Worker de testes' }, path: '../task-tests', command: 'git diff main...agent/tests', note: { en: 'Checks are green. Review the diff before merging into main.', pt: 'Verificações passaram. Revise o diff antes do merge em main.' } },
|
||
docs: { status: 'review', owner: { en: 'Docs worker', pt: 'Worker de docs' }, path: '../task-docs', command: 'git merge --no-ff agent/docs', note: { en: 'Review requested. Merge, request changes, or discard without touching another checkout.', pt: 'Revisão solicitada. Faça merge, peça mudanças ou descarte sem tocar em outro checkout.' } }
|
||
},
|
||
routes: {
|
||
plan: { score: 92, label: { en: 'High ambiguity', pt: 'Alta ambiguidade' }, why: { en: 'Architecture and decomposition have a wide error surface. Spend reasoning here.', pt: 'Arquitetura e decomposição têm grande superfície de erro. Invista raciocínio aqui.' } },
|
||
build: { score: 38, label: { en: 'Bounded execution', pt: 'Execução delimitada' }, why: { en: 'The brief already removed ambiguity. Optimize for speed and tight feedback.', pt: 'O brief já removeu a ambiguidade. Otimize para velocidade e feedback curto.' } },
|
||
explore: { score: 22, label: { en: 'Read-only discovery', pt: 'Descoberta somente leitura' }, why: { en: 'Search, map, and report. A lightweight model can return facts without editing.', pt: 'Busque, mapeie e reporte. Um modelo leve devolve fatos sem editar.' } },
|
||
review: { score: 74, label: { en: 'Independent judgment', pt: 'Julgamento independente' }, why: { en: 'Reconnect the diff to intent with fresh context and adversarial attention.', pt: 'Reconecte o diff à intenção com contexto novo e atenção crítica.' } }
|
||
},
|
||
skillFiles: {
|
||
skill: { icon: '◇', title: 'SKILL.md', en: 'Trigger, procedure, constraints, and the exact evidence the agent must return.', pt: 'Gatilho, procedimento, restrições e a evidência exata que o agente deve devolver.' },
|
||
references: { icon: '≡', title: 'references/', en: 'Stable facts loaded only when the procedure needs them. Keep the main instruction lean.', pt: 'Fatos estáveis carregados apenas quando o procedimento precisa. Mantenha a instrução principal enxuta.' },
|
||
scripts: { icon: '›_', title: 'scripts/', en: 'Deterministic checks and repeated operations. Prefer executable proof over prose.', pt: 'Verificações determinísticas e operações repetidas. Prefira prova executável a prosa.' },
|
||
assets: { icon: '▧', title: 'assets/', en: 'Templates and examples the agent can copy without reinventing the expected shape.', pt: 'Templates e exemplos que o agente pode copiar sem reinventar o formato esperado.' }
|
||
},
|
||
skillWorkflow: {
|
||
observe: { number: '01', title: { en: 'Start from repeated friction', pt: 'Comece pelo atrito repetido' }, question: { en: 'Which non-obvious decision keeps being rediscovered?', pt: 'Qual decisão não óbvia continua sendo redescoberta?' }, action: { en: 'Collect two or three realistic requests. Separate durable judgment from one project’s temporary details.', pt: 'Colete dois ou três pedidos realistas. Separe julgamento durável dos detalhes temporários de um projeto.' }, output: { en: 'A narrow capability and concrete examples.', pt: 'Uma capacidade estreita e exemplos concretos.' }, proof: { en: 'Without the skill, agents repeatedly make the same avoidable mistake.', pt: 'Sem a skill, agentes repetem o mesmo erro evitável.' } },
|
||
trigger: { number: '02', title: { en: 'Make discovery precise', pt: 'Torne a descoberta precisa' }, question: { en: 'When should this load—and when should it stay out?', pt: 'Quando isto deve carregar — e quando deve ficar de fora?' }, action: { en: 'Choose a short action-oriented name. Write a discriminating description that names the task and meaningful boundary.', pt: 'Escolha um nome curto orientado à ação. Escreva uma descrição discriminante que nomeie a tarefa e seu limite.' }, output: { en: 'YAML name + description in SKILL.md.', pt: 'Nome + descrição YAML em SKILL.md.' }, proof: { en: 'Relevant prompts select it; nearby unrelated prompts do not.', pt: 'Prompts relevantes selecionam; prompts próximos mas não relacionados, não.' } },
|
||
scaffold: { number: '03', title: { en: 'Choose only useful anatomy', pt: 'Escolha apenas a anatomia útil' }, question: { en: 'What must be instructions, executable, consulted, or copied?', pt: 'O que deve ser instrução, executável, consultado ou copiado?' }, action: { en: 'Keep shared guidance in SKILL.md. Add scripts for repeated deterministic work, references for conditional facts, and assets for generated output.', pt: 'Mantenha orientação comum em SKILL.md. Adicione scripts para trabalho determinístico, referências para fatos condicionais e assets para saída.' }, output: { en: 'Smallest folder structure that supports the workflow.', pt: 'A menor estrutura de pastas que sustenta o fluxo.' }, proof: { en: 'Every file has a real caller; no placeholder directories.', pt: 'Cada arquivo tem um consumidor real; nenhuma pasta placeholder.' } },
|
||
write: { number: '04', title: { en: 'Write what changes decisions', pt: 'Escreva o que muda decisões' }, question: { en: 'What would a capable agent still get wrong?', pt: 'O que um agente capaz ainda erraria?' }, action: { en: 'State outcome, non-obvious constraints, routing, and stopping conditions. Remove generic advice, duplicate facts, and speculative rules.', pt: 'Declare resultado, restrições não óbvias, roteamento e condições de parada. Remova conselhos genéricos, fatos duplicados e regras especulativas.' }, output: { en: 'Lean SKILL.md with progressive links.', pt: 'SKILL.md enxuto com links progressivos.' }, proof: { en: 'Another agent can act correctly without loading irrelevant detail.', pt: 'Outro agente consegue agir corretamente sem carregar detalhes irrelevantes.' } },
|
||
validate: { number: '05', title: { en: 'Test behavior, then sharpen', pt: 'Teste comportamento, depois refine' }, question: { en: 'Did the skill improve a realistic outcome?', pt: 'A skill melhorou um resultado realista?' }, action: { en: 'Run structural validation, execute every new script, and forward-test realistic requests. Fix observed failures with the narrowest rule.', pt: 'Execute validação estrutural, rode cada script novo e teste pedidos realistas. Corrija falhas observadas com a regra mais estreita.' }, output: { en: 'Validated package plus evidence from real use.', pt: 'Pacote validado mais evidência de uso real.' }, proof: { en: 'quick_validate passes and behavior improves without unrelated side effects.', pt: 'quick_validate passa e o comportamento melhora sem efeitos colaterais.' } }
|
||
},
|
||
commonSkills: {
|
||
ponytail: { number: '01', kind: { en: 'SIMPLIFICATION INSTINCT', pt: 'INSTINTO DE SIMPLIFICAÇÃO' }, title: 'ponytail-lite', rule: { en: 'Stop at the first rung that holds.', pt: 'Pare no primeiro degrau que sustenta.' }, use: { en: 'Use when a request invites frameworks, dependencies, abstractions, or speculative scaffolding. It checks reuse, standard library, and native platform features before adding code.', pt: 'Use quando um pedido convida frameworks, dependências, abstrações ou scaffolding especulativo. Verifica reúso, biblioteca padrão e recursos nativos antes de adicionar código.' }, example: { en: 'Date picker? Start with <input type="date">.', pt: 'Seletor de data? Comece com <input type="date">.' }, caution: { en: 'Never simplify away security, accessibility, validation, or real edge cases.', pt: 'Nunca simplifique segurança, acessibilidade, validação ou casos extremos reais.' } },
|
||
caveman: { number: '02', kind: { en: 'COMMUNICATION STYLE', pt: 'ESTILO DE COMUNICAÇÃO' }, title: 'caveman', rule: { en: 'Signal first. Drop filler.', pt: 'Sinal primeiro. Corte o excesso.' }, use: { en: 'Use for routine status, handoffs, and technical summaries where speed matters. Short fragments make actions and evidence easy to scan.', pt: 'Use em status, handoffs e resumos técnicos rotineiros onde velocidade importa. Fragmentos curtos facilitam localizar ações e evidências.' }, example: { en: 'Built. Tests pass. Published.', pt: 'Feito. Testes passaram. Publicado.' }, caution: { en: 'Drop the style for security warnings, irreversible actions, and sequences where terse wording can be misread.', pt: 'Abandone o estilo em alertas de segurança, ações irreversíveis e sequências onde concisão pode causar erro.' } },
|
||
unlazy: { number: '03', kind: { en: 'COMPLETION DISCIPLINE', pt: 'DISCIPLINA DE CONCLUSÃO' }, title: 'unlazy', rule: { en: 'Define observable gates. Finish against evidence.', pt: 'Defina gates observáveis. Termine com evidências.' }, use: { en: 'Use for substantial autonomous builds, audits, and parallel work where quiet omissions are expensive. It turns “done” into runnable acceptance checks.', pt: 'Use em builds autônomos grandes, auditorias e trabalho paralelo onde omissões custam caro. Transforma “pronto” em verificações executáveis.' }, example: { en: 'Gate: language toggle persists. Check: browser reload. Expect: pt-BR.', pt: 'Gate: idioma persiste. Check: recarregar navegador. Esperado: pt-BR.' }, caution: { en: 'Too heavy for trivial edits or factual answers.', pt: 'Pesado demais para edições triviais ou respostas factuais.' } },
|
||
research: { number: '04', kind: { en: 'SOURCE DISCIPLINE', pt: 'DISCIPLINA DE FONTES' }, title: 'research', rule: { en: 'Trace claims to owners.', pt: 'Leve afirmações até suas fontes.' }, use: { en: 'Use when APIs, standards, architecture facts, or current behavior must be verified. Capture findings in a cited note, prioritizing primary sources.', pt: 'Use quando APIs, padrões, fatos de arquitetura ou comportamento atual precisam ser verificados. Registre achados citados, priorizando fontes primárias.' }, example: { en: 'Git behavior → git-scm.com docs, not a remembered blog summary.', pt: 'Comportamento do Git → documentação git-scm.com, não memória de um blog.' }, caution: { en: 'Practitioner articles add context; they do not override official behavior.', pt: 'Artigos de praticantes dão contexto; não substituem comportamento oficial.' } },
|
||
debug: { number: '05', kind: { en: 'DIAGNOSTIC LOOP', pt: 'CICLO DE DIAGNÓSTICO' }, title: 'diagnosing-bugs', rule: { en: 'No red-capable loop, no theory.', pt: 'Sem ciclo capaz de falhar, sem teoria.' }, use: { en: 'Use for hard bugs, flakes, and regressions. First build a fast deterministic reproduction, then minimize, rank hypotheses, instrument, and fix the root cause.', pt: 'Use para bugs difíceis, flakes e regressões. Primeiro crie reprodução rápida e determinística; depois minimize, ranqueie hipóteses, instrumente e corrija a causa raiz.' }, example: { en: 'One command reproduces the exact symptom before any fix.', pt: 'Um comando reproduz o sintoma exato antes de qualquer correção.' }, caution: { en: 'Do not jump from error message straight to a patch.', pt: 'Não pule da mensagem de erro direto para um patch.' } },
|
||
review: { number: '06', kind: { en: 'INDEPENDENT REVIEW', pt: 'REVISÃO INDEPENDENTE' }, title: 'code-review', rule: { en: 'Check standards and intent separately.', pt: 'Verifique padrões e intenção separadamente.' }, use: { en: 'Use on a branch or PR. One axis checks repository standards; another checks whether the change actually satisfies its originating specification.', pt: 'Use em branch ou PR. Um eixo verifica padrões do repositório; outro verifica se a mudança realmente satisfaz a especificação original.' }, example: { en: 'Clean code can still solve the wrong problem.', pt: 'Código limpo ainda pode resolver o problema errado.' }, caution: { en: 'Pin the comparison point and source specification before reviewing.', pt: 'Fixe o ponto de comparação e a especificação antes de revisar.' } },
|
||
tokens: { number: '07', kind: { en: 'CONTEXT ECONOMY', pt: 'ECONOMIA DE CONTEXTO' }, title: 'token-saver', rule: { en: 'Keep signal. Strip command noise.', pt: 'Mantenha sinal. Corte ruído de comandos.' }, use: { en: 'Use around verbose tests, builds, Git output, and logs. Filtering preserves context for reasoning while retaining full failure output for recovery.', pt: 'Use em testes, builds, saídas Git e logs verbosos. Filtragem preserva contexto para raciocínio e mantém falhas completas para recuperação.' }, example: { en: '200 passing-test lines → one result; failures keep their trace.', pt: '200 linhas de testes passando → um resultado; falhas mantêm o trace.' }, caution: { en: 'Read raw output when exact wording or full diffs matter.', pt: 'Leia saída bruta quando texto exato ou diffs completos importarem.' } }
|
||
}
|
||
};
|
||
|
||
const translations = {
|
||
pt: {
|
||
'.chapter-links a:nth-child(1)': '01 frota', '.chapter-links a:nth-child(2)': '02 worktrees', '.chapter-links a:nth-child(3)': '03 modelos', '.chapter-links a:nth-child(4)': '04 skills', '.chapter-links a:nth-child(5)': '05 criar', '.chapter-links a:nth-child(6)': '06 kit de campo', '.chapter-links a:nth-child(7)': '07 prática', '.edition': 'ENGENHARIA DE IA <i></i> 01 / 2026',
|
||
'.hero .eyebrow': 'Uma apresentação para quem entrega software', '.lede': 'Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.', '.hero-index span': 'NOTA DE CAMPO / 001', '.hero-index strong': 'Entregue o<br /><em>sistema.</em>', '.hero-index small': 'Skills · agentes · worktrees · evidências',
|
||
'.hero-stats div:nth-child(1) span': 'modelo forte<br />para ambiguidade', '.hero-stats div:nth-child(2) span': 'workers delimitados<br />em paralelo', '.hero-stats div:nth-child(3) span': 'iterações<br />com evidências', '.hero-stats p': 'Leia isto como um mapa de rota, não como uma receita de prompt.',
|
||
'.thesis span': 'REGRA ZERO', '.thesis strong': 'Modelo forte para ambiguidade.<br />Modelo leve para trabalho delimitado.', '.fleet .section-label span:nth-child(1)': 'Uma pequena frota', '.fleet .section-label span:nth-child(2)': 'coordenação antes do paralelismo', '.captain span': 'ORQUESTRADOR', '.captain h2': 'Decide o que<br />precisa acontecer.', '.worker-card[data-worker="ui"] strong': 'Componentes e estados visuais', '.worker-card[data-worker="tests"] strong': 'Casos de aceitação', '.worker-card[data-worker="docs"] strong': 'Guia e exemplos', '.caption': 'O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.',
|
||
'.failure-map .section-label span:nth-child(1)': 'Por que a fronteira importa', '.failure-map .section-label span:nth-child(2)': 'uma tarefa vaga / três falhas previsíveis', '.failure-grid article:nth-child(1) strong': 'Sopa de contexto', '.failure-grid article:nth-child(1) p': 'Cada worker lê tudo. Ninguém sabe quais fatos são essenciais.', '.failure-grid article:nth-child(2) strong': 'Colisão de branches', '.failure-grid article:nth-child(2) p': 'Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de conflitos.', '.failure-grid article:nth-child(3) strong': 'Desvio confiante', '.failure-grid article:nth-child(3) p': 'O diff parece ótimo, mas ninguém verifica se resolveu o problema original.',
|
||
'.workflow .eyebrow': 'O ciclo de subagentes', '#workflow-title': 'Clique em uma fase.<br /><em>Veja a passagem.</em>', '.workflow .copy > p:last-child': 'Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da responsabilidade.', '.handoff .section-label span:nth-child(1)': 'O que atravessa contextos', '.handoff .section-label span:nth-child(2)': 'brief → diff → evidência', '.handoff thead th:nth-child(1)': 'Pacote', '.handoff thead th:nth-child(2)': 'Contém', '.handoff thead th:nth-child(3)': 'Por que importa',
|
||
'.worktrees .eyebrow': 'Git worktrees', '.worktrees h2': 'Uma branch<br />por <em>mão.</em>', '.worktree-intro > p:nth-of-type(2)': 'Um worktree é outro diretório ligado ao mesmo repositório. Cada agente recebe seu próprio checkout e índice; o histórico continua compartilhado.', '.worktree-intro .interaction-hint': 'Selecione um nó para inspecionar checkout, responsável e próxima ação.', '.tree-toolbar > span:first-child': 'topologia do repositório', '.tree-live': '<i></i> 4 checkouts', '.tree-node.root span': 'RAIZ', '.tree-node.ui span': 'AGENTE DE UI', '.tree-node.tests span': 'AGENTE DE TESTES', '.tree-node.docs span': 'AGENTE DE DOCS', '.tree-node.root small': '● limpo', '.tree-node.ui small': '3 arquivos · trabalhando', '.tree-node.tests small': '8 verificações · pronto', '.tree-node.docs small': '2 páginas · revisão', '.routing .eyebrow': 'Roteamento de modelos', '.routing h2': 'Não pague por<br />raciocínio onde precisa<br />de <em>ritmo.</em>', '.routing .interaction-hint': 'Escolha um trabalho para entender por que o perfil do modelo muda.', '.route-table .head span:nth-child(1)': 'Trabalho', '.route-table .head span:nth-child(2)': 'Perfil', '.route-table .head span:nth-child(3)': 'Formato do prompt', '.route-table [data-route="plan"] strong': 'Planejar', '.route-table [data-route="build"] strong': 'Construir', '.route-table [data-route="explore"] strong': 'Explorar', '.route-table [data-route="review"] strong': 'Revisar', '.skills .eyebrow': 'Skills', '.skills h2': 'Escreva do jeito certo<br /><em>uma vez.</em>', '.skills > div:first-child > p': 'Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências, scripts e assets. Não é memória mágica e não substitui critérios de aceitação.',
|
||
'.skill-principles span:nth-child(1)': '01 / defina o gatilho', '.skill-principles span:nth-child(2)': '02 / carregue detalhes sob demanda', '.skill-principles span:nth-child(3)': '03 / devolva evidências', '.skill-package > span': 'PACOTE DE SKILL', '.skill-catalog .section-label span:nth-child(1)': 'Skills comuns', '.skill-catalog .section-label span:nth-child(2)': 'escolha o comportamento antes do modelo', '.catalog-intro .eyebrow': 'O kit de campo', '.catalog-intro h2': 'Trabalhos diferentes.<br />Instintos <em>diferentes.</em>', '.catalog-intro > p': 'Uma skill muda como o agente aborda o trabalho. Algumas moldam a comunicação. Outras impõem pesquisa, diagnóstico, revisão ou disciplina de conclusão. Selecione uma para inspecionar sua regra operacional.', '[data-common-skill="ponytail"] span': 'SIMPLIFICAR', '[data-common-skill="ponytail"] small': 'código mínimo que funciona', '[data-common-skill="caveman"] span': 'COMUNICAR', '[data-common-skill="caveman"] small': 'sinal sem excesso', '[data-common-skill="unlazy"] span': 'CONCLUIR', '[data-common-skill="unlazy"] small': 'gates e evidências', '[data-common-skill="research"] span': 'INVESTIGAR', '[data-common-skill="research"] small': 'fontes primárias primeiro', '[data-common-skill="debug"] span': 'DIAGNOSTICAR', '[data-common-skill="debug"] small': 'ciclo curto de feedback', '[data-common-skill="review"] span': 'REVISAR', '[data-common-skill="review"] small': 'padrões × especificação', '[data-common-skill="tokens"] span': 'ECONOMIZAR', '[data-common-skill="tokens"] small': 'comprima saídas ruidosas', '.skill-loadout > span': 'UM LOADOUT PRÁTICO', '.skill-loadout > div': '<b>PLANEJAR</b> unlazy <i>→</i> <b>CONSTRUIR</b> ponytail-lite <i>→</i> <b>DIAGNOSTICAR</b> diagnosing-bugs <i>→</i> <b>REPORTAR</b> caveman', '.rule span': 'O PAPEL HUMANO', '.rule strong': 'O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo seus.', '.callout span': 'COMECE AQUI', '.callout strong': 'Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem realmente independentes.', '.sources .section-label span:nth-child(1)': 'Continue aprendendo', '.sources .section-label span:nth-child(2)': '12 novas leituras + documentação primária', '.sources p': 'Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes. <a href="rules/">Estudo de caso sobre regras e enforcement →</a> <a href="docs/references/README.md">Referências primárias →</a> <a href="docs/references/additional-reading.md">Trilha com 12 leituras →</a>'
|
||
}
|
||
};
|
||
|
||
Object.assign(translations.pt, {
|
||
'.model-gearbox .section-label span:nth-child(1)': 'Câmbio de modelos', '.model-gearbox .section-label span:nth-child(2)': 'nível de capacidade × esforço de raciocínio',
|
||
'.gearbox-intro .eyebrow': 'Dois controles separados', '.gearbox-intro h2': 'Escolha o motor.<br />Depois escolha a <em>marcha.</em>',
|
||
'.gearbox-intro > p': 'Um modelo mais forte muda o teto de capacidade. Mais esforço de raciocínio dá mais espaço para esse modelo trabalhar. Comece com a combinação mais leve que passa seus checks e mova um controle por vez.',
|
||
'.effort-rail > span': 'RACIOCÍNIO / PENSAMENTO', '[data-effort="low"] b': 'BAIXO', '[data-effort="low"] small': 'delimitado + rápido', '[data-effort="medium"] b': 'MÉDIO', '[data-effort="medium"] small': 'ponto inicial', '[data-effort="high"] b': 'ALTO', '[data-effort="high"] small': 'complexo + custoso',
|
||
'.gearbox-rule span': 'REGRA DE ROTEAMENTO', '.gearbox-rule strong': 'Use modelos fortes para ambiguidade e julgamento. Use modelos leves para execução delimitada. Aumente o esforço apenas quando a avaliação mostrar ganho.',
|
||
'.skill-builder .section-label span:nth-child(1)': 'Criar uma skill',
|
||
'.skill-builder .section-label span:nth-child(2)': 'atrito repetido → julgamento reutilizável',
|
||
'.builder-intro .eyebrow': 'A forja de skills',
|
||
'.builder-intro h2': 'Ensine a decisão.<br />Mantenha o contexto <em>leve.</em>',
|
||
'.builder-intro > p': 'Não empacote tudo o que você sabe. Capture as escolhas não óbvias que melhoram resultados repetidamente e prove que a skill muda o comportamento.',
|
||
'[data-skill-step="observe"] span': 'Observar', '[data-skill-step="observe"] small': 'encontre atrito repetido',
|
||
'[data-skill-step="trigger"] span': 'Definir gatilho', '[data-skill-step="trigger"] small': 'roteie com precisão',
|
||
'[data-skill-step="scaffold"] span': 'Escolher anatomia', '[data-skill-step="scaffold"] small': 'apenas arquivos necessários',
|
||
'[data-skill-step="write"] span': 'Escrever orientação', '[data-skill-step="write"] small': 'decisões, não trivialidades',
|
||
'[data-skill-step="validate"] span': 'Validar', '[data-skill-step="validate"] small': 'teste comportamento real',
|
||
'.artifact-head span': 'SAÍDA / PACOTE DE SKILL', '.artifact-command span': 'VALIDAR',
|
||
'.builder-loop > span': 'APÓS USO REAL',
|
||
'.builder-loop > div': '<b>observar falha</b><i>→</i><b>refinar uma regra</b><i>→</i><b>retestar comportamento</b><i>→</i><b>manter estreita</b>',
|
||
'.install-skills header span': 'PACOTE DE INSTALAÇÃO', '.install-skills header strong': 'Peça ao seu agente para verificar, instalar e validar as skills.',
|
||
'.install-skills footer': 'Revise cada fonte antes da instalação. Skills locais existentes devem ser preservadas.',
|
||
'.hands-on .section-label span:nth-child(1)': 'Prática', '.hands-on .section-label span:nth-child(2)': '10 minutos / uma feature ausente',
|
||
'.hands-intro .eyebrow': 'Laboratório Tiny Tasks', '.hands-intro h2': 'Mesma tarefa.<br />Melhor <em>sistema operacional.</em>',
|
||
'.hands-intro > div:last-child > p': 'Comece com um quadro estático propositalmente incompleto. Execute um prompt, restaure e execute a versão com skills. Compare tamanho do diff, evidências e complexidade desnecessária.',
|
||
'.starter-link': 'Abrir o projeto inicial →', '.exercise-brief > span': 'A FEATURE AUSENTE',
|
||
'.exercise-brief > strong': 'Adicione filtros Todos / Abertos / Concluídos que sobrevivem reload e navegação.',
|
||
'.exercise-brief > div': '<b>STACK</b> HTML · CSS · JavaScript <b>DEPENDÊNCIAS</b> nenhuma <b>ARQUIVOS</b> 3',
|
||
'.prompt-card:first-child header strong': 'Bom prompt', '.prompt-card.enhanced header strong': 'Bom prompt + skills',
|
||
'.prompt-card:first-child footer': 'Contexto claro · restrições · aceitação · evidência', '.prompt-card.enhanced footer': 'Mesmo contrato · métodos explícitos · prova mais forte',
|
||
'.comparison-strip > span': 'COMPARE AS EXECUÇÕES', '.comparison-strip > div:nth-child(2)': '<b>01</b> Arquivos alterados', '.comparison-strip > div:nth-child(3)': '<b>02</b> Novas dependências', '.comparison-strip > div:nth-child(4)': '<b>03</b> Checks executados', '.comparison-strip > div:nth-child(5)': '<b>04</b> Evidências retornadas'
|
||
});
|
||
|
||
const panel = document.querySelector('#phase-panel');
|
||
const buttons = document.querySelectorAll('[data-phase]');
|
||
const originals = new Map();
|
||
let currentLanguage = 'en';
|
||
|
||
function setText(selector, value) {
|
||
const nodes = document.querySelectorAll(selector);
|
||
if (!nodes.length) return;
|
||
if (!originals.has(selector)) originals.set(selector, [...nodes].map((node) => node.innerHTML));
|
||
nodes.forEach((node) => { node.innerHTML = value; });
|
||
}
|
||
|
||
function render(id) {
|
||
const phase = phases[id];
|
||
panel.innerHTML = `<div class="phase-meta"><span>${phase.model[currentLanguage]}</span><small>${currentLanguage === 'pt' ? 'contexto: isolado' : 'context: isolated'}</small></div><h3>${phase.title[currentLanguage]}</h3><p>${phase.copy[currentLanguage]}</p><code>${phase.code[currentLanguage]}</code>`;
|
||
buttons.forEach((button) => { const active = button.dataset.phase === id; button.classList.toggle('active', active); button.setAttribute('aria-selected', String(active)); });
|
||
}
|
||
|
||
function selectButtons(selector, activeValue, key) {
|
||
document.querySelectorAll(selector).forEach((button) => {
|
||
const active = button.dataset[key] === activeValue;
|
||
button.classList.toggle('active', active);
|
||
button.setAttribute(button.hasAttribute('aria-selected') ? 'aria-selected' : 'aria-pressed', String(active));
|
||
});
|
||
}
|
||
|
||
function renderWorker(id) {
|
||
const item = interactiveCopy.workers[id][currentLanguage];
|
||
document.querySelector('#worker-detail').innerHTML = `<span>${item[0]}</span><strong>${item[1]}</strong><small>${item[2]}</small>`;
|
||
selectButtons('[data-worker]', id, 'worker');
|
||
}
|
||
|
||
function renderTree(id) {
|
||
const item = interactiveCopy.trees[id];
|
||
const language = currentLanguage;
|
||
document.querySelector('#tree-detail').innerHTML = `<div><span>${language === 'pt' ? 'RESPONSÁVEL' : 'OWNER'}</span><strong>${item.owner[language]}</strong></div><div><span>CHECKOUT</span><strong>${item.path}</strong></div><p>${item.note[language]}</p><code>${item.command}</code>`;
|
||
selectButtons('[data-tree]', id, 'tree');
|
||
}
|
||
|
||
function renderRoute(id) {
|
||
const item = interactiveCopy.routes[id];
|
||
const language = currentLanguage;
|
||
document.querySelector('#route-detail').innerHTML = `<div class="route-meter"><span style="--score:${item.score}%"></span></div><div><small>${language === 'pt' ? 'CARGA DE RACIOCÍNIO' : 'REASONING LOAD'} · ${item.score}</small><strong>${item.label[language]}</strong><p>${item.why[language]}</p></div>`;
|
||
selectButtons('[data-route]', id, 'route');
|
||
}
|
||
|
||
function renderModelProvider(id) {
|
||
const item = modelGuide.providers[id];
|
||
const language = currentLanguage;
|
||
const sourceLabel = language === 'pt' ? 'FONTE OFICIAL ↗' : 'OFFICIAL SOURCE ↗';
|
||
const kindLabels = language === 'pt' ? { STRONG: 'FORTE', BALANCED: 'EQUILÍBRIO', FAST: 'RÁPIDO' } : {};
|
||
const tiers = item.tiers.map(([kind, name, note]) => `<div><span>${kindLabels[kind] || kind}</span><strong>${name}</strong><small>${note[language]}</small></div>`).join('');
|
||
document.querySelector('#provider-detail').innerHTML = `<header><span>${item.label}</span><a href="${item.source}" target="_blank" rel="noopener">${sourceLabel}</a></header><h3>${item.title[language]}</h3><p>${item.copy[language]}</p><div class="model-ladder">${tiers}</div>`;
|
||
selectButtons('[data-model-provider]', id, 'modelProvider');
|
||
}
|
||
|
||
function renderEffort(id) {
|
||
const item = modelGuide.efforts[id][currentLanguage];
|
||
const provider = modelGuide.providers[document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai'];
|
||
document.querySelector('#effort-detail').innerHTML = `<span>${item[0]}</span><p>${item[1]}</p><code>${provider.config}</code>`;
|
||
selectButtons('[data-effort]', id, 'effort');
|
||
}
|
||
|
||
function renderSkillFile(id) {
|
||
const item = interactiveCopy.skillFiles[id];
|
||
document.querySelector('#skill-detail').innerHTML = `<span>${item.icon}</span><div><strong>${item.title}</strong><p>${item[currentLanguage]}</p><small>${currentLanguage === 'pt' ? 'clique em outro arquivo para explorar' : 'select another file to explore'}</small></div>`;
|
||
selectButtons('[data-skill-file]', id, 'skillFile');
|
||
}
|
||
|
||
function renderSkillWorkflow(id) {
|
||
const item = interactiveCopy.skillWorkflow[id];
|
||
const language = currentLanguage;
|
||
const labels = language === 'pt'
|
||
? ['PERGUNTA', 'AÇÃO', 'ARTEFATO', 'PROVA']
|
||
: ['QUESTION', 'ACTION', 'ARTIFACT', 'PROOF'];
|
||
document.querySelector('#builder-detail').innerHTML = `<header><span>${item.number}</span><small>${labels[0]}</small></header><h3>${item.title[language]}</h3><blockquote>${item.question[language]}</blockquote><div class="builder-action"><span>${labels[1]}</span><p>${item.action[language]}</p></div><footer><div><span>${labels[2]}</span><strong>${item.output[language]}</strong></div><div><span>${labels[3]}</span><strong>${item.proof[language]}</strong></div></footer>`;
|
||
selectButtons('[data-skill-step]', id, 'skillStep');
|
||
}
|
||
|
||
function renderCommonSkill(id) {
|
||
const item = interactiveCopy.commonSkills[id];
|
||
const language = currentLanguage;
|
||
const labels = language === 'pt'
|
||
? ['QUANDO USAR', 'EXEMPLO', 'CUIDADO']
|
||
: ['WHEN TO USE', 'EXAMPLE', 'WATCH OUT'];
|
||
const sourceLabel = language === 'pt' ? 'FONTE NO GITHUB ↗' : 'GITHUB SOURCE ↗';
|
||
document.querySelector('#common-skill-detail').innerHTML = `<header><span>${item.number}</span><small>${item.kind[language]}</small></header><h3>${item.title}</h3><blockquote>${item.rule[language]}</blockquote><div class="common-skill-notes"><div><span>${labels[0]}</span><p>${item.use[language]}</p></div><div><span>${labels[1]}</span><p>${item.example[language]}</p></div><div><span>${labels[2]}</span><p>${item.caution[language]}</p></div></div><a class="skill-source" href="${skillSources[id]}" target="_blank" rel="noopener">${sourceLabel}</a>`;
|
||
selectButtons('[data-common-skill]', id, 'commonSkill');
|
||
}
|
||
|
||
function renderHandsOn() {
|
||
document.querySelector('#prompt-basic').textContent = handsOnPrompts[currentLanguage].basic;
|
||
document.querySelector('#prompt-skills').textContent = handsOnPrompts[currentLanguage].skills;
|
||
document.querySelector('#prompt-install-skills').textContent = skillInstallPrompts[currentLanguage];
|
||
document.querySelectorAll('[data-copy-target] span').forEach((label) => { label.textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; });
|
||
}
|
||
|
||
async function copyPrompt(button) {
|
||
const text = document.querySelector(`#${button.dataset.copyTarget}`).textContent;
|
||
let copied = false;
|
||
try {
|
||
await navigator.clipboard.writeText(text);
|
||
copied = true;
|
||
} catch (error) {
|
||
const helper = document.createElement('textarea');
|
||
helper.value = text;
|
||
helper.setAttribute('readonly', '');
|
||
helper.style.position = 'fixed';
|
||
helper.style.opacity = '0';
|
||
document.body.appendChild(helper);
|
||
helper.select();
|
||
copied = document.execCommand('copy');
|
||
helper.remove();
|
||
}
|
||
const status = document.querySelector('#copy-status');
|
||
status.textContent = copied
|
||
? (currentLanguage === 'pt' ? 'Prompt copiado. Cole em uma nova sessão de agente.' : 'Prompt copied. Paste it into a fresh agent session.')
|
||
: (currentLanguage === 'pt' ? 'Não foi possível copiar. Selecione o texto manualmente.' : 'Copy unavailable. Select the text manually.');
|
||
if (copied) {
|
||
button.classList.add('copied');
|
||
button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIADO' : 'COPIED';
|
||
window.setTimeout(() => { button.classList.remove('copied'); button.querySelector('span').textContent = currentLanguage === 'pt' ? 'COPIAR' : 'COPY'; }, 1800);
|
||
}
|
||
}
|
||
|
||
function renderInteractive() {
|
||
renderWorker(document.querySelector('[data-worker].active')?.dataset.worker || 'ui');
|
||
renderTree(document.querySelector('[data-tree].active')?.dataset.tree || 'main');
|
||
renderRoute(document.querySelector('[data-route].active')?.dataset.route || 'plan');
|
||
renderModelProvider(document.querySelector('[data-model-provider].active')?.dataset.modelProvider || 'openai');
|
||
renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium');
|
||
renderSkillFile(document.querySelector('[data-skill-file].active')?.dataset.skillFile || 'skill');
|
||
renderSkillWorkflow(document.querySelector('[data-skill-step].active')?.dataset.skillStep || 'observe');
|
||
renderCommonSkill(document.querySelector('[data-common-skill].active')?.dataset.commonSkill || 'ponytail');
|
||
renderHandsOn();
|
||
}
|
||
|
||
function applyLanguage(language) {
|
||
currentLanguage = language === 'pt' ? 'pt' : 'en';
|
||
document.documentElement.lang = currentLanguage === 'pt' ? 'pt-BR' : 'en';
|
||
if (currentLanguage === 'pt') Object.entries(translations.pt).forEach(([selector, value]) => setText(selector, value));
|
||
else originals.forEach((values, selector) => document.querySelectorAll(selector).forEach((node, index) => { node.innerHTML = values[index]; }));
|
||
document.querySelectorAll('[data-lang]').forEach((button) => { const active = button.dataset.lang === currentLanguage; button.classList.toggle('active', active); button.setAttribute('aria-pressed', String(active)); });
|
||
render(document.querySelector('[data-phase].active')?.dataset.phase || 'plan');
|
||
renderInteractive();
|
||
try { localStorage.setItem('ai-for-dummies-language', currentLanguage); } catch (error) { /* previews may disable storage */ }
|
||
}
|
||
|
||
buttons.forEach((button) => button.addEventListener('click', () => render(button.dataset.phase)));
|
||
document.querySelectorAll('[data-lang]').forEach((button) => button.addEventListener('click', () => applyLanguage(button.dataset.lang)));
|
||
document.querySelectorAll('[data-worker]').forEach((button) => button.addEventListener('click', () => renderWorker(button.dataset.worker)));
|
||
document.querySelectorAll('[data-tree]').forEach((button) => button.addEventListener('click', () => renderTree(button.dataset.tree)));
|
||
document.querySelectorAll('[data-route]').forEach((button) => button.addEventListener('click', () => renderRoute(button.dataset.route)));
|
||
document.querySelectorAll('[data-model-provider]').forEach((button) => button.addEventListener('click', () => { renderModelProvider(button.dataset.modelProvider); renderEffort(document.querySelector('[data-effort].active')?.dataset.effort || 'medium'); }));
|
||
document.querySelectorAll('[data-effort]').forEach((button) => button.addEventListener('click', () => renderEffort(button.dataset.effort)));
|
||
document.querySelectorAll('[data-skill-file]').forEach((button) => button.addEventListener('click', () => renderSkillFile(button.dataset.skillFile)));
|
||
document.querySelectorAll('[data-skill-step]').forEach((button) => button.addEventListener('click', () => renderSkillWorkflow(button.dataset.skillStep)));
|
||
document.querySelectorAll('[data-common-skill]').forEach((button) => button.addEventListener('click', () => renderCommonSkill(button.dataset.commonSkill)));
|
||
document.querySelectorAll('[data-copy-target]').forEach((button) => button.addEventListener('click', () => copyPrompt(button)));
|
||
window.addEventListener('scroll', () => { const height = document.documentElement.scrollHeight - window.innerHeight; document.querySelector('.reading-progress span').style.width = `${height > 0 ? (window.scrollY / height) * 100 : 0}%`; }, { passive: true });
|
||
|
||
let savedLanguage = 'en';
|
||
try { savedLanguage = localStorage.getItem('ai-for-dummies-language') || 'en'; } catch (error) { /* previews may disable storage */ }
|
||
render('plan');
|
||
applyLanguage(savedLanguage);
|