@dynamicworks/br-openspec 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +11 -2
  2. package/README.pt-BR.md +11 -2
  3. package/dist/commands/config.js +4 -0
  4. package/dist/commands/schema.js +21 -21
  5. package/dist/core/artifact-graph/instruction-loader.js +4 -4
  6. package/dist/core/artifact-graph/schema.js +5 -4
  7. package/dist/core/completions/factory.js +3 -2
  8. package/dist/core/completions/installers/fish-installer.js +13 -12
  9. package/dist/core/completions/installers/powershell-installer.js +16 -17
  10. package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
  11. package/dist/core/completions/installers/zsh-installer.js +4 -32
  12. package/dist/core/config.js +3 -2
  13. package/dist/core/global-config.d.ts +6 -1
  14. package/dist/core/global-config.js +15 -16
  15. package/dist/core/parsers/change-parser.js +7 -6
  16. package/dist/core/parsers/requirement-blocks.js +5 -5
  17. package/dist/core/parsers/spec-structure.js +1 -1
  18. package/dist/core/profile-sync-drift.js +1 -0
  19. package/dist/core/profiles.d.ts +2 -2
  20. package/dist/core/profiles.js +2 -1
  21. package/dist/core/project-config.js +12 -13
  22. package/dist/core/shared/skill-generation.js +3 -1
  23. package/dist/core/shared/tool-detection.d.ts +2 -2
  24. package/dist/core/shared/tool-detection.js +2 -0
  25. package/dist/core/specs-apply.js +38 -39
  26. package/dist/core/templates/skill-templates.d.ts +1 -1
  27. package/dist/core/templates/skill-templates.js +1 -1
  28. package/dist/core/templates/workflows/code-review.d.ts +10 -0
  29. package/dist/core/templates/workflows/code-review.js +21 -0
  30. package/dist/core/templates/workflows/sync-specs.js +2 -2
  31. package/dist/core/tools-manager.js +3 -2
  32. package/dist/core/update.d.ts +6 -0
  33. package/dist/core/update.js +21 -0
  34. package/dist/core/validation/validator.js +2 -2
  35. package/dist/messages/index.d.ts +145 -2
  36. package/dist/messages/index.js +320 -12
  37. package/dist/utils/change-metadata.js +8 -7
  38. package/dist/utils/change-utils.js +12 -11
  39. package/package.json +1 -1
  40. package/schemas/spec-driven/schema.yaml +78 -78
  41. package/schemas/spec-driven/templates/design.md +5 -5
  42. package/schemas/spec-driven/templates/proposal.md +9 -9
  43. package/schemas/spec-driven/templates/spec.md +5 -5
  44. package/schemas/spec-driven/templates/tasks.md +6 -6
  45. package/dist/core/templates/workflows/upstream-sync.d.ts +0 -10
  46. package/dist/core/templates/workflows/upstream-sync.js +0 -116
@@ -3,6 +3,28 @@
3
3
  *
4
4
  * Este módulo reúne todas as mensagens exibidas ao usuário para facilitar
5
5
  * manutenção, revisão e consistência linguística.
6
+ *
7
+ * ─────────────────────────────────────────────────────────────────────────
8
+ * ⚠️ TERMOS RESERVADOS — NÃO TRADUZIR
9
+ * ─────────────────────────────────────────────────────────────────────────
10
+ * O BR-OpenSpec é PT-BR first, mas o FORMATO de spec é um protocolo lido pelo
11
+ * parser e pelo validador. Os marcadores estruturais e as palavras-chave
12
+ * normativas DEVEM permanecer em inglês e em CAIXA ALTA. Só o conteúdo
13
+ * descritivo (nomes, descrições, prosa) é escrito em português.
14
+ *
15
+ * - Palavras-chave normativas (RFC 2119): MUST, MUST NOT, REQUIRED, SHALL,
16
+ * SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, OPTIONAL.
17
+ * - Cabeçalhos de delta/spec: "## ADDED Requirements", "## MODIFIED Requirements",
18
+ * "## REMOVED Requirements", "## RENAMED Requirements", "## Requirements",
19
+ * "### Requirement:", "#### Scenario:".
20
+ * - Cláusulas de cenário (Gherkin): WHEN, THEN, AND, GIVEN, ELSE.
21
+ * - Auxiliares de RENAMED: FROM, TO.
22
+ *
23
+ * Regra geral: qualquer palavra em CAIXA ALTA que represente uma regra, uma
24
+ * operação de delta (ADD/REMOVE/RENAME) ou uma cláusula de cenário fica em
25
+ * inglês. Traduzir esses termos quebra o parsing/validação dos specs.
26
+ * Ver também AGENTS.md ("Termos reservados em inglês").
27
+ * ─────────────────────────────────────────────────────────────────────────
6
28
  */
7
29
  // ═══════════════════════════════════════════════════════════
8
30
  // CLI — Descrições de comandos (src/cli/index.ts)
@@ -514,6 +536,8 @@ export const CONFIG_MESSAGES = {
514
536
  workflowBulkArchiveDesc: 'Arquiva múltiplas alterações concluídas juntas',
515
537
  workflowVerifyName: 'Verificar alteração',
516
538
  workflowVerifyDesc: 'Executa verificações contra uma alteração',
539
+ workflowCodeReviewName: 'Code review',
540
+ workflowCodeReviewDesc: 'Revisa diffs, branches ou arquivos com contexto do projeto',
517
541
  workflowOnboardName: 'Onboarding',
518
542
  workflowOnboardDesc: 'Fluxo de onboarding guiado para o BR-OpenSpec',
519
543
  };
@@ -669,6 +693,38 @@ export const COMPLETION_MESSAGES = {
669
693
  warningCouldNotRemoveLegacy: (path, err) => `Aviso: Não foi possível remover arquivo legado ${path}: ${err}`,
670
694
  powershellCompletionHeader: '# Script de autocompletar PowerShell para a CLI do BR-OpenSpec',
671
695
  powershellCompletionNote: '# Gerado automaticamente - não edite manualmente',
696
+ // Fish installer messages
697
+ fishAlreadyInstalled: 'Script de autocomplete já está instalado (atualizado)',
698
+ fishAlreadyInstalledDetail: 'O script de autocomplete já está instalado e atualizado.',
699
+ fishAutoLoadsHint: 'O Fish carrega automaticamente os scripts de autocomplete - devem estar disponíveis imediatamente.',
700
+ fishUpdatedWithBackup: 'Script de autocomplete atualizado com sucesso (versão anterior salva em backup)',
701
+ fishUpdated: 'Script de autocomplete atualizado com sucesso',
702
+ fishInstalled: 'Script de autocomplete instalado com sucesso para Fish',
703
+ fishAutoLoadsDir: 'O Fish carrega automaticamente os scripts de autocomplete de ~/.config/fish/completions/',
704
+ fishAvailableImmediately: 'Os autocompletes estão disponíveis imediatamente - sem necessidade de reiniciar o shell.',
705
+ fishFailedToInstall: (error) => `Falha ao instalar script de autocomplete: ${error}`,
706
+ fishNotInstalled: 'Script de autocomplete não está instalado',
707
+ fishUninstalled: 'Script de autocomplete desinstalado com sucesso',
708
+ fishFailedToUninstall: (error) => `Falha ao desinstalar script de autocomplete: ${error}`,
709
+ // PowerShell installer messages
710
+ powershellUtf16BEUnsupported: 'Arquivo codificado em UTF-16 BE não é suportado. Salve novamente como UTF-8 ou UTF-16 LE e tente novamente.',
711
+ powershellAlreadyInstalled: 'Script de autocomplete já está instalado (atualizado)',
712
+ powershellAlreadyInstalledDetail: 'O script de autocomplete já está instalado e atualizado.',
713
+ powershellAlreadyInstalledHint: 'Se o autocomplete não estiver funcionando, tente reiniciar o PowerShell ou execute: . $PROFILE',
714
+ powershellUpdatedWithBackup: 'Script de autocomplete atualizado com sucesso (versão anterior salva em backup)',
715
+ powershellUpdated: 'Script de autocomplete atualizado com sucesso',
716
+ powershellInstalledWithProfile: 'Script de autocomplete instalado e perfil do PowerShell configurado com sucesso',
717
+ powershellInstalled: 'Script de autocomplete instalado com sucesso para PowerShell',
718
+ powershellFailedToInstall: (error) => `Falha ao instalar script de autocomplete: ${error}`,
719
+ powershellScriptInstalled: 'Script de autocomplete instalado com sucesso.',
720
+ powershellEnableCompletions: (profilePath) => `Para ativar o autocomplete, adicione o seguinte ao seu perfil PowerShell (${profilePath}):`,
721
+ powershellSourceComment: '# Carrega os autocompletes do BR-OpenSpec',
722
+ powershellThenRestart: 'Depois reinicie o PowerShell ou execute: . $PROFILE',
723
+ powershellNotInstalled: 'Script de autocomplete não está instalado',
724
+ powershellUninstalled: 'Script de autocomplete desinstalado com sucesso',
725
+ powershellFailedToUninstall: (error) => `Falha ao desinstalar script de autocomplete: ${error}`,
726
+ // Zsh installer (missing)
727
+ zshNotInstalled: 'Script de autocomplete não está instalado',
672
728
  };
673
729
  // ═══════════════════════════════════════════════════════════
674
730
  // Comandos — Feedback (src/commands/feedback.ts)
@@ -767,6 +823,8 @@ export const UPDATE_MESSAGES = {
767
823
  it: 'ela',
768
824
  them: 'elas',
769
825
  extraWorkflowsNote: (count) => `Nota: ${count} fluxos de trabalho extras não estão no perfil (use \`openspec config profile\` para gerenciar)`,
826
+ oldCoreProfileSyncNote: 'Nota: o perfil core agora inclui o fluxo de trabalho sync. Seu perfil personalizado está mantendo o conjunto antigo de fluxos de trabalho do core.',
827
+ oldCoreProfileSyncHint: 'Execute `openspec config profile core` e depois `openspec update` para adicionar o sync.',
770
828
  cleaningLegacy: 'Limpando arquivos legados...',
771
829
  legacyCleaned: 'Arquivos legados limpos',
772
830
  forceLegacyHint: '⚠ Execute com --force para limpar automaticamente arquivos legados, ou execute de forma interativa.',
@@ -809,11 +867,21 @@ export const VALIDATOR_MESSAGES = {
809
867
  unknownError: 'Erro desconhecido',
810
868
  duplicateRequirementAdded: (name) => `Requisito duplicado em ADDED: "${name}"`,
811
869
  missingRequirementTextAdded: (name) => `ADDED "${name}" está sem texto de requisito`,
812
- missingShallOrMustAdded: (name) => `ADDED "${name}" deve conter SHALL ou MUST`,
870
+ missingShallOrMustAdded: (name, keywordInHeader = false) => {
871
+ const base = `ADDED "${name}" deve conter SHALL ou MUST`;
872
+ return keywordInHeader
873
+ ? `${base} no corpo do requisito, não apenas no cabeçalho. Mova a declaração SHALL/MUST para a linha imediatamente após o cabeçalho "### Requirement: ...".`
874
+ : base;
875
+ },
813
876
  missingScenarioAdded: (name) => `ADDED "${name}" deve incluir pelo menos um cenário`,
814
877
  duplicateRequirementModified: (name) => `Requisito duplicado em MODIFIED: "${name}"`,
815
878
  missingRequirementTextModified: (name) => `MODIFIED "${name}" está sem texto de requisito`,
816
- missingShallOrMustModified: (name) => `MODIFIED "${name}" deve conter SHALL ou MUST`,
879
+ missingShallOrMustModified: (name, keywordInHeader = false) => {
880
+ const base = `MODIFIED "${name}" deve conter SHALL ou MUST`;
881
+ return keywordInHeader
882
+ ? `${base} no corpo do requisito, não apenas no cabeçalho. Mova a declaração SHALL/MUST para a linha imediatamente após o cabeçalho "### Requirement: ...".`
883
+ : base;
884
+ },
817
885
  missingScenarioModified: (name) => `MODIFIED "${name}" deve incluir pelo menos um cenário`,
818
886
  duplicateRequirementRemoved: (name) => `Requisito duplicado em REMOVED: "${name}"`,
819
887
  duplicateFromRenamed: (name) => `FROM duplicado em RENAMED: "${name}"`,
@@ -1289,11 +1357,11 @@ Aqui está um rascunho de proposal:
1289
1357
 
1290
1358
  ---
1291
1359
 
1292
- ## Por Que
1360
+ ## Why
1293
1361
 
1294
1362
  [1-2 frases explicando o problema/oportunidade]
1295
1363
 
1296
- ## O Que Muda
1364
+ ## What Changes
1297
1365
 
1298
1366
  [Bullet points do que será diferente]
1299
1367
 
@@ -1357,21 +1425,21 @@ Aqui está o spec:
1357
1425
 
1358
1426
  ---
1359
1427
 
1360
- ## Requisitos ADICIONADOS
1428
+ ## ADDED Requirements
1361
1429
 
1362
- ### Requisito: <Nome>
1430
+ ### Requirement: <Nome>
1363
1431
 
1364
- <Descrição do que o sistema deve fazer>
1432
+ O sistema SHALL <descrição do que o sistema deve fazer>
1365
1433
 
1366
- #### Cenário: <Nome do cenário>
1434
+ #### Scenario: <Nome do cenário>
1367
1435
 
1368
- - **QUANDO** <condição de gatilho>
1369
- - **ENTÃO** <resultado esperado>
1370
- - **E** <resultado adicional se necessário>
1436
+ - **WHEN** <condição de gatilho>
1437
+ - **THEN** <resultado esperado>
1438
+ - **AND** <resultado adicional se necessário>
1371
1439
 
1372
1440
  ---
1373
1441
 
1374
- Este formato - QUANDO/ENTÃO/E - torna os requisitos testáveis. Você pode literalmente lê-los como casos de teste.
1442
+ Este formato - WHEN/THEN/AND - torna os requisitos testáveis. Você pode literalmente lê-los como casos de teste. Os marcadores estruturais (ADDED Requirements, Requirement, Scenario) e as palavras-chave (WHEN/THEN/AND, SHALL/MUST) ficam SEMPRE em inglês — é o protocolo que o parser e o validador reconhecem. Apenas o conteúdo descritivo é escrito em português.
1375
1443
  \`\`\`
1376
1444
 
1377
1445
  Salve em \`openspec/changes/<nome>/specs/<capability>/spec.md\`.
@@ -1952,9 +2020,249 @@ Use markdown claro com:
1952
2020
  - Sem sugestões vagas como "consider reviewing"`,
1953
2021
  };
1954
2022
  // ═══════════════════════════════════════════════════════════
2023
+ // Templates de Workflow — Code Review (src/core/templates/workflows/code-review.ts)
2024
+ // ═══════════════════════════════════════════════════════════
2025
+ export const CODE_REVIEW_TEMPLATE_MESSAGES = {
2026
+ skillDescription: 'Realiza code review genérico e consciente do projeto. Use quando o usuário quiser revisar um diff, branch, PR, working tree ou conjunto de arquivos antes de mesclar ou continuar.',
2027
+ skillCompatibility: 'Requer acesso aos arquivos do projeto. Git é recomendado para revisar diffs e branches.',
2028
+ opsxDescription: 'Revisa diffs, branches, PRs ou arquivos usando contexto do projeto',
2029
+ instructions: `Realize um code review rigoroso, genérico e consciente do projeto. Seu objetivo é encontrar problemas reais antes do merge — não validar superficialmente, não comentar estilo, não elogiar.
2030
+
2031
+ **Entrada**: Opcionalmente especifique o alvo após \`/opsx:code-review\`: branch, PR, diff, working tree, staged changes, caminho de arquivo ou descrição de escopo. Se omitido, descubra o alvo com segurança.
2032
+
2033
+ **Postura**
2034
+
2035
+ - Você está revisando, não implementando. Não edite arquivos a menos que o usuário peça explicitamente para corrigir.
2036
+ - Aja como um revisor sênior cético: assuma que existe um bug até o código provar o contrário, e fundamente cada finding com evidência concreta no código.
2037
+ - Priorize nesta ordem: correção/regressões → segurança/privacidade → perda ou corrupção de dados → quebra de contrato/compatibilidade → concorrência → comportamento cross-platform → testes ausentes → performance → manutenibilidade.
2038
+ - Estilo e preferência pessoal só viram finding se tiverem impacto concreto (ex.: legibilidade que esconde um bug, violação de um padrão real do projeto).
2039
+ - Findings primeiro, com evidência. Resumo depois.
2040
+ - Calibre a profundidade ao tamanho do diff; em diffs grandes, revise por blocos e priorize as áreas de maior risco.
2041
+ - Sinalize incerteza explicitamente. Nunca invente número de linha, nome de símbolo ou comportamento que você não verificou.
2042
+
2043
+ **Passos**
2044
+
2045
+ 1. **Determine o alvo da review**
2046
+
2047
+ Use o argumento do usuário quando existir. Caso contrário, descubra com segurança:
2048
+ - Confirme se há Git: \`git rev-parse --is-inside-work-tree\`.
2049
+ - Inspecione o estado local: \`git status --short\`, \`git diff\` (unstaged) e \`git diff --staged\`.
2050
+ - Para revisar um branch contra a base: identifique a base provável (ex.: \`git merge-base HEAD origin/main\`) e use \`git diff <base>...HEAD\`.
2051
+ - Para um PR, prefira \`gh pr diff <numero>\` quando o \`gh\` estiver disponível.
2052
+ - Se não houver Git, peça arquivos ou escopo explícitos ao usuário.
2053
+ - Se o alvo continuar ambíguo, pergunte ao usuário o que revisar.
2054
+
2055
+ Não assuma review do repositório inteiro sem confirmação. Leia o diff completo (com contexto de linha) antes de julgar.
2056
+
2057
+ 2. **Entenda a intenção antes de criticar**
2058
+
2059
+ Antes de procurar defeitos, articule o que a mudança tenta fazer e por quê (a partir do título do PR/branch, mensagens de commit, artifacts OpenSpec ou do próprio diff). Um bom review compara o que o código faz com o que deveria fazer; sem a intenção, você só consegue revisar sintaxe.
2060
+
2061
+ 3. **Colete contexto do projeto**
2062
+
2063
+ Leia apenas os arquivos relevantes, priorizando:
2064
+ - \`README.md\`, \`README_*.md\` e docs de contribuição
2065
+ - \`AGENTS.md\`, instruções de agentes e skills existentes do projeto
2066
+ - \`openspec/config.yaml\` e docs de arquitetura/ADRs quando existirem
2067
+ - specs vivas em \`openspec/specs/\` quando relacionadas ao alvo
2068
+ - testes vizinhos ao código alterado (para entender o contrato esperado)
2069
+ - manifests e configs de stack (\`package.json\`, lockfiles, \`tsconfig.json\`, configs de lint/test/build, CI etc.)
2070
+
2071
+ Aplique as instruções encontradas. Em caso de conflito, prefira a orientação mais específica do repositório.
2072
+
2073
+ 4. **Entenda a stack e os comandos de verificação**
2074
+
2075
+ Infira linguagem, framework, package manager e comandos úteis a partir dos arquivos locais.
2076
+
2077
+ Exemplos:
2078
+ - Node/TypeScript: scripts de \`package.json\`, lockfile e \`tsconfig.json\`
2079
+ - Python: \`pyproject.toml\`, \`requirements*.txt\`, configs de pytest/ruff/mypy
2080
+ - Go/Rust: \`go.mod\`, \`Cargo.toml\` e scripts de CI
2081
+
2082
+ 5. **Inclua contexto OpenSpec quando existir**
2083
+
2084
+ Se houver uma change relacionada:
2085
+ - Leia \`proposal.md\`, \`design.md\`, \`tasks.md\` e delta specs disponíveis.
2086
+ - Verifique se o diff preserva a intenção dos artifacts.
2087
+ - Não transforme esta review em \`/opsx:verify\`; use os artifacts apenas como contexto adicional para revisar o código.
2088
+
2089
+ 6. **Revise o código em profundidade**
2090
+
2091
+ Método para cada mudança: leia além do diff (o código ao redor, chamadores e implementações chamadas), rastreie de onde vêm os dados e para onde vão, e teste mentalmente entradas adversárias (vazio, nulo, zero, negativo, muito grande, unicode, concorrente). Não confie no nome de uma função — confirme o que ela faz.
2092
+
2093
+ Procure, por categoria:
2094
+ - **Correção e lógica**: regressões, edge cases e limites (off-by-one), condições invertidas ou incompletas, \`switch\` sem \`break\`/default, retorno/await faltando, suposições falsas sobre a entrada.
2095
+ - **Dados e estado**: mutação de estado compartilhado, ordem de operações, idempotência, transações e atomicidade, invalidação de cache, lifecycle e limpeza de recursos.
2096
+ - **Concorrência**: race conditions, \`await\`/lock faltando, reentrância, deadlock, escrita concorrente.
2097
+ - **Segurança e privacidade**: injeção (SQL/command/path/template), validação e sanitização de entrada não confiável, authn/authz, segredos hardcoded, dados sensíveis vazando em logs/erros, deserialização insegura, SSRF/path traversal.
2098
+ - **Erros e resiliência**: erros engolidos, mensagens que vazam dados, ausência de rollback, retries/timeouts, recursos não liberados em caminho de erro.
2099
+ - **Contratos e compatibilidade**: mudança em API pública, assinatura, schema ou formato persistido; migrações reversíveis; compatibilidade retroativa e versionamento.
2100
+ - **Cross-platform**: separador de caminho, case sensitivity, line endings, shell e encoding quando filesystem/processo estiverem envolvidos.
2101
+ - **Performance**: N+1, complexidade quadrática, I/O ou alocação dentro de loop — reporte apenas quando o impacto for plausível.
2102
+ - **Testes**: cenários novos/quebráveis cobertos, testes negativos e de borda, testes frágeis ou que não exercitam de fato o código.
2103
+ - **Dependências**: nova dependência justificada, versão e licença sãs, risco de supply chain.
2104
+ - **Consistência com o projeto**: aderência a padrões, convenções e decisões reais do repositório (incluindo, quando aplicável, mensagens centralizadas/i18n em vez de texto hardcoded).
2105
+
2106
+ 7. **Valide quando for seguro**
2107
+
2108
+ Rode verificações focadas e proporcionais (typecheck, lint, testes do escopo afetado) quando forem seguras. Não rode comandos destrutivos, dependentes de serviços externos ou desproporcionalmente caros sem explicar/confirmar. Reporte o que rodou e o que pulou (e por quê).
2109
+
2110
+ 8. **Sugira contexto durável do projeto**
2111
+
2112
+ Se o projeto não tiver orientação durável suficiente para reviews (nenhum \`AGENTS.md\`, skill do projeto, docs de contribuição ou contexto em \`openspec/config.yaml\`):
2113
+ - Sugira criar ou enriquecer uma skill/contexto do projeto para futuras reviews, indicando um local concreto (ex.: \`AGENTS.md\` ou uma skill do projeto) e os pontos que ela deveria registrar (padrões, comandos de validação, armadilhas).
2114
+ - Explique brevemente o benefício.
2115
+ - Peça confirmação antes de escrever qualquer arquivo.
2116
+
2117
+ **Formato de Saída**
2118
+
2119
+ Se houver findings, comece por eles, ordenados por severidade. Para cada finding:
2120
+
2121
+ \`\`\`text
2122
+ Findings
2123
+ - [CRITICAL] caminho/arquivo.ext:123 — Título curto e específico
2124
+ Evidência: o que no código causa o problema (cite o trecho/símbolo)
2125
+ Impacto: o que quebra na prática e sob qual condição
2126
+ Correção: ação concreta e mínima
2127
+ Confiança: alta | média | baixa (se < alta, diga a suposição)
2128
+ \`\`\`
2129
+
2130
+ Depois inclua, de forma breve:
2131
+ - alvo revisado
2132
+ - contexto/instruções considerados
2133
+ - validações executadas
2134
+ - validações não executadas e por quê
2135
+ - risco residual ou perguntas abertas
2136
+
2137
+ Se não houver findings, diga claramente que nenhum problema foi encontrado e ainda informe as validações não executadas.
2138
+
2139
+ **Regras de Severidade**
2140
+
2141
+ - **CRITICAL**: bug provável, perda/corrupção de dados, falha de segurança, quebra de contrato, build/test claramente quebrado.
2142
+ - **WARNING**: risco real mas dependente de condição, cobertura ausente para comportamento importante, inconsistência que pode virar regressão.
2143
+ - **SUGGESTION**: melhoria útil, baixa urgência, refino de manutenção.
2144
+
2145
+ **Guardrails**
2146
+
2147
+ - Não altere arquivos durante uma review pura.
2148
+ - Todo finding precisa de evidência concreta; sem evidência, não reporte.
2149
+ - Não reporte o que linter/typecheck/formatter já pegam automaticamente — foque no que essas ferramentas não veem.
2150
+ - Não duplique o mesmo finding; agrupe ocorrências do mesmo problema.
2151
+ - Prefira poucos findings fortes a muitos comentários especulativos.
2152
+ - Não encha a saída com elogios genéricos.
2153
+ - Use referências no formato \`arquivo:linha\`; quando não houver linha exata, cite o menor escopo verificável.
2154
+ - Se a informação for incerta, diga o que verificou e qual suposição está fazendo — e marque a confiança.`,
2155
+ };
2156
+ // ═══════════════════════════════════════════════════════════
1955
2157
  // Telemetry (src/telemetry/index.ts)
1956
2158
  // ═══════════════════════════════════════════════════════════
1957
2159
  export const TELEMETRY_MESSAGES = {
1958
2160
  firstRunNotice: 'Aviso: o BR-OpenSpec coleta estatísticas de uso anônimas. Para optar por não participar, defina OPENSPEC_TELEMETRY=0',
1959
2161
  };
2162
+ // ═══════════════════════════════════════════════════════════
2163
+ // Parser — Change Parser (src/core/parsers/change-parser.ts)
2164
+ // ═══════════════════════════════════════════════════════════
2165
+ export const CHANGE_PARSER_MESSAGES = {
2166
+ mustHaveWhySection: 'A alteração deve ter uma seção Why',
2167
+ mustHaveWhatChangesSection: 'A alteração deve ter uma seção What Changes',
2168
+ addRequirement: (text) => `Adicionar requisito: ${text}`,
2169
+ modifyRequirement: (text) => `Modificar requisito: ${text}`,
2170
+ removeRequirement: (text) => `Remover requisito: ${text}`,
2171
+ renameRequirement: (from, to) => `Renomear requisito de "${from}" para "${to}"`,
2172
+ };
2173
+ // ═══════════════════════════════════════════════════════════
2174
+ // Core — Specs Apply (src/core/specs-apply.ts)
2175
+ // ═══════════════════════════════════════════════════════════
2176
+ export const SPECS_APPLY_MESSAGES = {
2177
+ duplicateInSection: (specName, section, reqName) => `${specName} validação falhou - requisito duplicado em ${section} para cabeçalho "### Requirement: ${reqName}"`,
2178
+ duplicateFromInRenamed: (specName, reqName) => `${specName} validação falhou - FROM duplicado em RENAMED para cabeçalho "### Requirement: ${reqName}"`,
2179
+ duplicateToInRenamed: (specName, reqName) => `${specName} validação falhou - TO duplicado em RENAMED para cabeçalho "### Requirement: ${reqName}"`,
2180
+ renamedModifiedMustReferenceNew: (specName, toName) => `${specName} validação falhou - quando existe um rename, MODIFIED deve referenciar o NOVO cabeçalho "### Requirement: ${toName}"`,
2181
+ renamedToCollidesWithAdded: (specName, toName) => `${specName} validação falhou - cabeçalho RENAMED TO colide com ADDED para "### Requirement: ${toName}"`,
2182
+ requirementInMultipleSections: (specName, sectionA, sectionB, reqName) => `${specName} validação falhou - requisito presente em múltiplas seções (${sectionA} e ${sectionB}) para cabeçalho "### Requirement: ${reqName}"`,
2183
+ noDeltaOperations: (capability) => `Análise de delta não encontrou operações para ${capability}. Forneça seções ADDED/MODIFIED/REMOVED/RENAMED no spec da change.`,
2184
+ targetSpecNotExists: (specName) => `${specName}: spec alvo não existe; somente requisitos ADDED são permitidos para specs novos. Operações MODIFIED e RENAMED requerem um spec existente.`,
2185
+ targetSpecStructurallyInvalid: (specName, details) => `${specName}: spec alvo é estruturalmente inválido e não pode ser atualizado até ser corrigido:\n${details}`,
2186
+ renamedFailedSourceNotFound: (specName, reqName) => `${specName} RENAMED falhou para cabeçalho "### Requirement: ${reqName}" - origem não encontrada`,
2187
+ renamedFailedTargetExists: (specName, reqName) => `${specName} RENAMED falhou para cabeçalho "### Requirement: ${reqName}" - destino já existe`,
2188
+ removedFailedNotFound: (specName, reqName) => `${specName} REMOVED falhou para cabeçalho "### Requirement: ${reqName}" - não encontrado`,
2189
+ modifiedFailedNotFound: (specName, reqName) => `${specName} MODIFIED falhou para cabeçalho "### Requirement: ${reqName}" - não encontrado`,
2190
+ modifiedFailedHeaderMismatch: (specName, reqName) => `${specName} MODIFIED falhou para cabeçalho "### Requirement: ${reqName}" - incompatibilidade de cabeçalho no conteúdo`,
2191
+ addedFailedAlreadyExists: (specName, reqName) => `${specName} ADDED falhou para cabeçalho "### Requirement: ${reqName}" - já existe`,
2192
+ applyingChangesTo: (specPath) => `Aplicando alterações em openspec/specs/${specPath}/spec.md:`,
2193
+ wouldApplyChangesTo: (specPath) => `Aplicaria alterações em openspec/specs/${specPath}/spec.md:`,
2194
+ countAdded: (n) => ` + ${n} adicionado(s)`,
2195
+ countModified: (n) => ` ~ ${n} modificado(s)`,
2196
+ countRemoved: (n) => ` - ${n} removido(s)`,
2197
+ countRenamed: (n) => ` → ${n} renomeado(s)`,
2198
+ skeletonPurpose: (changeName) => `A definir - criado ao arquivar alteração ${changeName}. Atualize o Purpose após o arquivamento.`,
2199
+ changeNotFound: (changeName) => `Alteração '${changeName}' não encontrada.`,
2200
+ validationErrorsInRebuiltSpec: (specName, errors) => `Erros de validação na especificação reconstruída para ${specName}:\n${errors}`,
2201
+ };
2202
+ // ═══════════════════════════════════════════════════════════
2203
+ // Core — Project Config (src/core/project-config.ts)
2204
+ // ═══════════════════════════════════════════════════════════
2205
+ export const PROJECT_CONFIG_SUGGEST_MESSAGES = {
2206
+ configNotValidYaml: 'openspec/config.yaml não é um objeto YAML válido',
2207
+ configFailedToParse: 'Falha ao analisar openspec/config.yaml:',
2208
+ unknownArtifactId: (artifactId, schemaName, validIds) => `ID de artefato desconhecido nas regras: "${artifactId}". IDs válidos para o schema "${schemaName}": ${validIds}`,
2209
+ schemaNotFound: (schemaName) => `Schema '${schemaName}' não encontrado em openspec/config.yaml\n\n`,
2210
+ didYouMean: 'Você quis dizer algum destes?\n',
2211
+ schemaType: (isBuiltIn) => isBuiltIn ? 'nativo' : 'local do projeto',
2212
+ availableSchemas: 'Schemas disponíveis:\n',
2213
+ builtInSchemas: (schemas) => ` Nativos: ${schemas}\n`,
2214
+ projectLocalSchemas: (schemas) => ` Locais do projeto: ${schemas}\n`,
2215
+ noProjectLocalSchemas: ' Locais do projeto: (nenhum encontrado)\n',
2216
+ fixSuggestion: (invalidName) => `\nCorreção: Edite openspec/config.yaml e altere 'schema: ${invalidName}' para um nome de schema válido`,
2217
+ };
2218
+ // ═══════════════════════════════════════════════════════════
2219
+ // Core — Tools Manager (src/core/tools-manager.ts)
2220
+ // ═══════════════════════════════════════════════════════════
2221
+ export const TOOLS_MANAGER_MESSAGES = {
2222
+ toolDoesNotSupportSkills: (toolValue) => `A ferramenta '${toolValue}' não suporta geração de skills.`,
2223
+ };
2224
+ // ═══════════════════════════════════════════════════════════
2225
+ // Core — Artifact Graph (src/core/artifact-graph/)
2226
+ // ═══════════════════════════════════════════════════════════
2227
+ export const ARTIFACT_GRAPH_MESSAGES = {
2228
+ invalidSchema: (errors) => `Schema inválido: ${errors}`,
2229
+ duplicateArtifactId: (id) => `ID de artefato duplicado: ${id}`,
2230
+ invalidDependencyReference: (artifactId, ref) => `Referência de dependência inválida no artefato '${artifactId}': '${ref}' não existe`,
2231
+ cyclicDependency: (cycle) => `Dependência cíclica detectada: ${cycle}`,
2232
+ templateNotFound: (path) => `Template não encontrado: ${path}`,
2233
+ failedToReadTemplate: (error) => `Falha ao ler template: ${error}`,
2234
+ artifactNotFound: (artifactId, schemaName) => `Artefato '${artifactId}' não encontrado no schema '${schemaName}'`,
2235
+ };
2236
+ // ═══════════════════════════════════════════════════════════
2237
+ // Utils — Change Metadata (src/utils/change-metadata.ts)
2238
+ // ═══════════════════════════════════════════════════════════
2239
+ export const CHANGE_METADATA_MESSAGES = {
2240
+ failedToWriteMetadata: (error) => `Falha ao escrever metadados: ${error}`,
2241
+ failedToReadMetadata: (error) => `Falha ao ler metadados: ${error}`,
2242
+ invalidMetadata: (error) => `Metadados inválidos: ${error}`,
2243
+ invalidYaml: (error) => `YAML inválido no arquivo de metadados: ${error}`,
2244
+ unknownSchema: (schema, available) => `Schema desconhecido '${schema}'. Disponíveis: ${available}`,
2245
+ };
2246
+ // ═══════════════════════════════════════════════════════════
2247
+ // Utils — Change Utils (src/utils/change-utils.ts)
2248
+ // ═══════════════════════════════════════════════════════════
2249
+ export const CHANGE_UTILS_MESSAGES = {
2250
+ changeAlreadyExists: (name, dir) => `A alteração '${name}' já existe em ${dir}`,
2251
+ nameEmpty: 'O nome da alteração não pode estar vazio',
2252
+ nameMustBeLowercase: 'O nome da alteração deve ser minúsculo (use kebab-case)',
2253
+ nameNoSpaces: 'O nome da alteração não pode conter espaços (use hífens)',
2254
+ nameNoUnderscores: 'O nome da alteração não pode conter underscores (use hífens)',
2255
+ nameNoStartHyphen: 'O nome da alteração não pode começar com hífen',
2256
+ nameNoEndHyphen: 'O nome da alteração não pode terminar com hífen',
2257
+ nameNoConsecutiveHyphens: 'O nome da alteração não pode conter hífens consecutivos',
2258
+ nameOnlyAllowedChars: 'O nome da alteração pode conter apenas letras minúsculas, números e hífens',
2259
+ nameMustStartWithLetter: 'O nome da alteração deve começar com uma letra',
2260
+ nameKebabCase: 'O nome da alteração deve seguir a convenção kebab-case (ex: add-auth, refactor-db)',
2261
+ };
2262
+ // ═══════════════════════════════════════════════════════════
2263
+ // Core — Completions Factory (src/core/completions/factory.ts)
2264
+ // ═══════════════════════════════════════════════════════════
2265
+ export const COMPLETIONS_FACTORY_MESSAGES = {
2266
+ unsupportedShell: (shell) => `Shell não suportado: ${shell}`,
2267
+ };
1960
2268
  //# sourceMappingURL=index.js.map
@@ -4,6 +4,7 @@ import * as yaml from 'yaml';
4
4
  import { ChangeMetadataSchema } from '../core/artifact-graph/types.js';
5
5
  import { listSchemas } from '../core/artifact-graph/resolver.js';
6
6
  import { readProjectConfig } from '../core/project-config.js';
7
+ import { CHANGE_METADATA_MESSAGES } from '../messages/index.js';
7
8
  const METADATA_FILENAME = '.openspec.yaml';
8
9
  /**
9
10
  * Error thrown when change metadata validation fails.
@@ -29,7 +30,7 @@ export class ChangeMetadataError extends Error {
29
30
  export function validateSchemaName(schemaName, projectRoot) {
30
31
  const availableSchemas = listSchemas(projectRoot);
31
32
  if (!availableSchemas.includes(schemaName)) {
32
- throw new Error(`Unknown schema '${schemaName}'. Available: ${availableSchemas.join(', ')}`);
33
+ throw new Error(CHANGE_METADATA_MESSAGES.unknownSchema(schemaName, availableSchemas.join(', ')));
33
34
  }
34
35
  return schemaName;
35
36
  }
@@ -48,7 +49,7 @@ export function writeChangeMetadata(changeDir, metadata, projectRoot) {
48
49
  // Validate with Zod
49
50
  const parseResult = ChangeMetadataSchema.safeParse(metadata);
50
51
  if (!parseResult.success) {
51
- throw new ChangeMetadataError(`Invalid metadata: ${parseResult.error.message}`, metaPath);
52
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.invalidMetadata(parseResult.error.message), metaPath);
52
53
  }
53
54
  // Write YAML file
54
55
  const content = yaml.stringify(parseResult.data);
@@ -57,7 +58,7 @@ export function writeChangeMetadata(changeDir, metadata, projectRoot) {
57
58
  }
58
59
  catch (err) {
59
60
  const ioError = err instanceof Error ? err : new Error(String(err));
60
- throw new ChangeMetadataError(`Failed to write metadata: ${ioError.message}`, metaPath, ioError);
61
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.failedToWriteMetadata(ioError.message), metaPath, ioError);
61
62
  }
62
63
  }
63
64
  /**
@@ -79,7 +80,7 @@ export function readChangeMetadata(changeDir, projectRoot) {
79
80
  }
80
81
  catch (err) {
81
82
  const ioError = err instanceof Error ? err : new Error(String(err));
82
- throw new ChangeMetadataError(`Failed to read metadata: ${ioError.message}`, metaPath, ioError);
83
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.failedToReadMetadata(ioError.message), metaPath, ioError);
83
84
  }
84
85
  let parsed;
85
86
  try {
@@ -87,17 +88,17 @@ export function readChangeMetadata(changeDir, projectRoot) {
87
88
  }
88
89
  catch (err) {
89
90
  const parseError = err instanceof Error ? err : new Error(String(err));
90
- throw new ChangeMetadataError(`Invalid YAML in metadata file: ${parseError.message}`, metaPath, parseError);
91
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.invalidYaml(parseError.message), metaPath, parseError);
91
92
  }
92
93
  // Validate with Zod
93
94
  const parseResult = ChangeMetadataSchema.safeParse(parsed);
94
95
  if (!parseResult.success) {
95
- throw new ChangeMetadataError(`Invalid metadata: ${parseResult.error.message}`, metaPath);
96
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.invalidMetadata(parseResult.error.message), metaPath);
96
97
  }
97
98
  // Validate that the schema exists
98
99
  const availableSchemas = listSchemas(projectRoot);
99
100
  if (!availableSchemas.includes(parseResult.data.schema)) {
100
- throw new ChangeMetadataError(`Unknown schema '${parseResult.data.schema}'. Available: ${availableSchemas.join(', ')}`, metaPath);
101
+ throw new ChangeMetadataError(CHANGE_METADATA_MESSAGES.unknownSchema(parseResult.data.schema, availableSchemas.join(', ')), metaPath);
101
102
  }
102
103
  return parseResult.data;
103
104
  }
@@ -2,6 +2,7 @@ import path from 'path';
2
2
  import { FileSystemUtils } from './file-system.js';
3
3
  import { writeChangeMetadata, validateSchemaName } from './change-metadata.js';
4
4
  import { readProjectConfig } from '../core/project-config.js';
5
+ import { CHANGE_UTILS_MESSAGES } from '../messages/index.js';
5
6
  const DEFAULT_SCHEMA = 'spec-driven';
6
7
  /**
7
8
  * Validates that a change name follows kebab-case conventions.
@@ -24,35 +25,35 @@ export function validateChangeName(name) {
24
25
  // optionally followed by hyphen + lowercase letters/numbers (repeatable)
25
26
  const kebabCasePattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
26
27
  if (!name) {
27
- return { valid: false, error: 'Change name cannot be empty' };
28
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameEmpty };
28
29
  }
29
30
  if (!kebabCasePattern.test(name)) {
30
31
  // Provide specific error messages for common mistakes
31
32
  if (/[A-Z]/.test(name)) {
32
- return { valid: false, error: 'Change name must be lowercase (use kebab-case)' };
33
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameMustBeLowercase };
33
34
  }
34
35
  if (/\s/.test(name)) {
35
- return { valid: false, error: 'Change name cannot contain spaces (use hyphens instead)' };
36
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameNoSpaces };
36
37
  }
37
38
  if (/_/.test(name)) {
38
- return { valid: false, error: 'Change name cannot contain underscores (use hyphens instead)' };
39
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameNoUnderscores };
39
40
  }
40
41
  if (name.startsWith('-')) {
41
- return { valid: false, error: 'Change name cannot start with a hyphen' };
42
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameNoStartHyphen };
42
43
  }
43
44
  if (name.endsWith('-')) {
44
- return { valid: false, error: 'Change name cannot end with a hyphen' };
45
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameNoEndHyphen };
45
46
  }
46
47
  if (/--/.test(name)) {
47
- return { valid: false, error: 'Change name cannot contain consecutive hyphens' };
48
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameNoConsecutiveHyphens };
48
49
  }
49
50
  if (/[^a-z0-9-]/.test(name)) {
50
- return { valid: false, error: 'Change name can only contain lowercase letters, numbers, and hyphens' };
51
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameOnlyAllowedChars };
51
52
  }
52
53
  if (/^[0-9]/.test(name)) {
53
- return { valid: false, error: 'Change name must start with a letter' };
54
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameMustStartWithLetter };
54
55
  }
55
- return { valid: false, error: 'Change name must follow kebab-case convention (e.g., add-auth, refactor-db)' };
56
+ return { valid: false, error: CHANGE_UTILS_MESSAGES.nameKebabCase };
56
57
  }
57
58
  return { valid: true };
58
59
  }
@@ -106,7 +107,7 @@ export async function createChange(projectRoot, name, options = {}) {
106
107
  const changeDir = path.join(projectRoot, 'openspec', 'changes', name);
107
108
  // Check if change already exists
108
109
  if (await FileSystemUtils.directoryExists(changeDir)) {
109
- throw new Error(`Change '${name}' already exists at ${changeDir}`);
110
+ throw new Error(CHANGE_UTILS_MESSAGES.changeAlreadyExists(name, changeDir));
110
111
  }
111
112
  // Create the directory (including parent directories if needed)
112
113
  await FileSystemUtils.createDirectory(changeDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamicworks/br-openspec",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "openspec",