@seniorsistemas/angular-upgrade-mcp
v1.0.2
Published
MCP Server para automação de upgrades do Angular (v9 até v18)
Readme
angular-upgrade-mcp
Servidor MCP (Model Context Protocol) para automação de upgrades do Angular de v9 até v18, incluindo migração automática de componentes @seniorsistemas/angular-components e PrimeNG via análise estática com tree-sitter.
Índice
- O que faz
- Arquitetura
- Pré-requisitos
- Instalação
- Configuração nos editores
- Tools MCP disponíveis
- Motor de migração
- API Express (playground)
- Playground Angular
- Scripts
- Testes
- Extensibilidade
O que faz
O angular-upgrade-mcp expõe ferramentas MCP que um agente de IA (Claude, Cursor, etc.) pode chamar para:
- Analisar compatibilidade de dependências com Angular 18 antes de iniciar, bloqueando o upgrade se alguma lib não tiver caminho de atualização conhecido.
- Fazer o upgrade sequencial de Angular v9 → v10 → ... → v18, cada etapa com validação de Node.js via NVM.
- Aplicar os ajustes pós-upgrade necessários para funcionar no Angular 18 (package.json, tailwind, tsconfig, eslint, Prettier).
- Migrar automaticamente todos os componentes
@seniorsistemas/angular-componentse PrimeNG no projeto usando análise de AST (tree-sitter), sem regex frágeis — e provar com um motor de verificação independente que os padrões antigos catalogados realmente desapareceram (ver Motor de verificação). - Corrigir breaking changes obrigatórias do Angular 18 / RxJS 7 (
.toPromise(),import moment,RouterEvent, retornos deHttpClientsem tipo). - Atualizar libs
@seniorsistemas/*para a versão mais nova compatível com Angular 18 que tenha schematic de migração (ng update), mediante confirmação do usuário. - Corrigir erros TypeScript strict mode opcionalmente, via análise estática.
Arquitetura
angular-upgrade-mcp/
│
├── src/
│ ├── server.ts # Registra todas as tools MCP
│ ├── index.ts # Entrypoint (stdio transport)
│ │
│ ├── migration/ # ENGINE GENÉRICO (lib-agnóstico)
│ │ ├── index.ts # migrateAngularComponent(ts, html, ctx, config, registry)
│ │ ├── ts-migrator.ts # Pipeline de migração TypeScript
│ │ ├── html-migrator.ts # Pipeline de migração HTML
│ │ ├── verification.ts # "Prova real" pós-migração (ver seção própria)
│ │ ├── inheritance-discovery.ts # Descoberta de herança entre componentes
│ │ ├── module-declarations.ts # Análise de declarations/imports de NgModule
│ │ ├── ts-entry-classification.ts # classifyTsEntries() — puro, compartilhado com o playground
│ │ ├── eol.ts # Normalização de fim de linha (CRLF/LF)
│ │ ├── rules.ts # Interfaces: MigrationConfig, ComponentDefinition, etc.
│ │ ├── parser-adapter.ts # Bridge tree-sitter nativo → TsLanguage/TsTree
│ │ ├── parser-types.ts # Tipos internos do tree-sitter
│ │ └── custom-rules/
│ │ └── types.ts # CustomRule, CustomRuleContext, TextEdit, Warning
│ │
│ ├── migration-angular-components-v18/ # REGRAS: @seniorsistemas/angular-components
│ │ ├── rules.generated.v18.json # Inventário auto-gerado (npm run scan)
│ │ ├── rules.manual.ts # Renames manuais (inputs/outputs/selectors)
│ │ ├── enum-literal-migrations.ts # Inventário de enums migrados p/ literal string
│ │ ├── merge.ts # Combina JSON + manual → MigrationConfig
│ │ └── custom-rules/
│ │ ├── registry.ts # Mapa nome → função de regra
│ │ ├── verify-registry.ts # Mapa nome → verificador ("prova real")
│ │ ├── accordion-switch-manual.ts
│ │ ├── button-model-to-menu-options.ts
│ │ ├── dynamic-form-fields-to-configs.ts
│ │ ├── enum-member-to-literal.ts
│ │ ├── file-upload-validate-errors.ts
│ │ ├── lookup-labels-to-labeloptions.ts
│ │ ├── lookup-removed-inputs.ts
│ │ └── steps-description-to-label.ts
│ │
│ ├── migration-primeng-v18/ # REGRAS: PrimeNG
│ │ ├── rules.manual.ts # Regras manuais (sem auto-scan — lib externa)
│ │ ├── config.ts # Constrói MigrationConfig a partir de rules.manual
│ │ └── custom-rules/
│ │ ├── registry.ts
│ │ ├── verify-registry.ts # Mapa nome → verificador ("prova real")
│ │ ├── datepicker-locale.ts
│ │ ├── dropdown-onchange.ts
│ │ ├── p-button-component.ts
│ │ ├── p-button-directive.ts
│ │ ├── p-tabMenu-to-p-tabs.ts
│ │ ├── p-tabView-to-s-tabs.ts
│ │ ├── p-tooltip-disabled.ts
│ │ ├── primeng-dialog-templates.ts
│ │ ├── primeng-messages-to-message-for.ts
│ │ ├── primeng-numeric-attrs.ts
│ │ ├── primeng-radiobutton-checkbox-label.ts
│ │ ├── primeng-table.ts
│ │ └── primeng-translation-config.ts
│ │
│ └── tools/
│ ├── v18-adjustments.ts # Re-exports + guia de uso (v18-adjustments)
│ ├── start-upgrade.ts # Ponto de entrada do fluxo
│ ├── check-prerequisites.ts
│ ├── prepare-upgrade.ts
│ ├── analyze-dependencies.ts # Análise de compatibilidade de deps com Angular 18
│ ├── report-writer.ts # appendToolSection() — grava UPGRADE-REPORT.md
│ ├── upgrade-shared.ts # Utilitários compartilhados do fluxo de upgrade
│ ├── env-debug.ts # isLocalDryRunEnabled() — dry-run local via env-debug.json (debug, nunca via MCP)
│ ├── platform.ts # Detecção de SO / shell para comandos NVM
│ ├── upgrade-to-10.ts … upgrade-to-18.ts # Uma tool por etapa do ng update
│ └── v18/
│ ├── migrate-angular-components.ts # Tool MCP de migração (usa engine acima)
│ ├── fix-runtime.ts # Tool MCP de fixes obrigatórios RxJS 7 / Angular 18
│ ├── fix-strict-ts.ts # Tool MCP de fix strict mode (estático, opcional)
│ ├── restore-package-json.ts
│ ├── package-versions.ts # KNOWN_V18_VERSIONS + regras de merge
│ ├── fix-lib.ts
│ ├── v18-setup-lib.ts
│ ├── install-tailwind.ts
│ ├── fix-tsconfig.ts
│ ├── remove-polyfills.ts
│ ├── migrate-eslint.ts
│ ├── run-prettier.ts # Tool MCP de formatação (Prettier)
│ ├── npm-install.ts
│ └── update-senior-libs.ts # Tool MCP de update de libs @seniorsistemas/*
│
├── playground/ # Ferramentas de debug (não entra no MCP)
│ ├── api/
│ │ ├── api.ts # Bootstrap fino: importa app.ts, faz app.listen()
│ │ ├── app.ts # App Express + rotas — mesmo engine do MCP, testável via supertest (não faz listen)
│ │ ├── lib-paths.ts
│ │ └── project-explorer.ts
│ └── src/ # Frontend Angular 22 com Monaco Editor
│
├── scripts/
│ ├── extract-rules.ts # Auto-scan do angular-components via git worktree
│ ├── scan.config.ts # Branches/versões a scanear
│ ├── lib-paths.ts # Resolução do caminho do repo da lib
│ ├── ensure-angular-components-src.sh # Garante ANGULAR_COMPONENTS_SRC — clona snr-angular-components (GitHub) se ausente
│ ├── copy-dist-assets.mjs # Copia assets (WASM/grammars) pro dist/ no build
│ ├── update-grammars.mjs # Atualiza gramáticas tree-sitter (.wasm)
│ └── debug-mcp-local.sh # Liga/desliga o dry-run local (env-debug.json) com trap EXIT
│
└── test/
├── helpers.ts # makeConfig() + migrate() para testes
├── setup.ts # Setup global do vitest
├── html-migrator.test.ts
├── ts-migrator.test.ts
├── merge.test.ts
├── integration.test.ts
├── custom-rules.test.ts
├── custom-rules-verify.test.ts # Paridade customRule ↔ verificador ("prova real")
├── verification.test.ts # Motor de verificação genérico
├── migrate-angular-components.test.ts # Relatório verificado (prova real) + E2E do handler em tmpdir
├── ts-entry-classification.test.ts # classifyTsEntries — módulo puro compartilhado com o playground
├── playground-migration-parity.test.ts # Trava paridade MCP ↔ playground (mesmo arquivo modificado/warnings)
├── env-debug.test.ts # isLocalDryRunEnabled (flag local, expiração de 1h)
├── scan-signals.test.ts
├── extract-rules-worktree.test.ts
├── lib-paths.test.ts
├── fix-strict-ts.test.ts
├── fix-strict.test.ts
├── restore-package-json.test.ts
├── analyze-dependencies.test.ts
├── inheritance-discovery.test.ts
├── module-declarations.test.ts
├── eol.test.ts
├── npm-install.test.ts
├── migrate-eslint.test.ts
├── run-prettier.test.ts
├── update-senior-libs.test.ts
├── primeng-config.test.ts
└── upgrade-shared.test.tsPré-requisitos
- Node.js 20+
- npm 10+
- Gerenciador de versões de Node (para as tools de upgrade que alternam versões): fnm (recomendado) ou nvm
- Linux/macOS: fnm ou nvm
- Windows: fnm (recomendado —
winget install Schniz.fnm, não requer Administrador) ou nvm-windows (onvm usealtera symlinks em Program Files e exige terminal Administrador)
O parser do servidor usa tree-sitter via WASM — nenhuma compilação nativa é necessária na instalação (sem Visual Studio Build Tools/Python no Windows).
Instalação
O pacote é executado via npx — não é necessário instalar globalmente. Veja a seção Configuração nos editores para conectá-lo ao seu editor.
Para desenvolvimento deste repositório:
git clone <repositorio>
cd angular-upgrade-mcp
npm install
npm run buildPara usar o auto-scan do @seniorsistemas/angular-components (regenerar rules.generated.v18.json), npm run scan precisa de ANGULAR_COMPONENTS_SRC apontando para um checkout do repositório da lib (senior-sistemas/snr-angular-components, privado). Se a variável não estiver definida, npm run scan clona esse repositório automaticamente (via gh repo clone, ou git clone como fallback) em _angular-components/ na raiz do projeto — veja scripts/ensure-angular-components-src.sh. Requer estar autenticado no GitHub (gh auth login) com acesso à org senior-sistemas.
Se preferir apontar pra um checkout próprio (ex: já tem a lib clonada em outro lugar), defina a variável manualmente antes:
# Linux/macOS
export ANGULAR_COMPONENTS_SRC=/caminho/para/angular-components
npm run scan# Windows (PowerShell)
$env:ANGULAR_COMPONENTS_SRC = "C:\caminho\para\angular-components"
npm run scanA variável aceita tanto a raiz do repositório quanto uma subpasta interna (projects/angular-components) — o script detecta automaticamente.
Git é necessário para contribuir com o projeto ou rodar o
npm run scan(o scan cria um git worktree temporário para não alterar a working tree do repositório da lib).
Configuração nos editores
Windows: alguns clientes MCP no Windows não conseguem spawnar o
npxdiretamente. Se o servidor não iniciar, envolva o comando comcmd /c:{ "command": "cmd", "args": ["/c", "npx", "-y", "@seniorsistemas/angular-upgrade-mcp"] }
VS Code
Crie .vscode/mcp.json na raiz do projeto:
{
"servers": {
"angular-upgrade-mcp": {
"command": "npx",
"args": ["-y", "@seniorsistemas/angular-upgrade-mcp"]
}
}
}Kiro
Crie .kiro/settings/mcp.json na raiz do projeto (workspace) ou ~/.kiro/settings/mcp.json (global):
{
"mcpServers": {
"angular-upgrade-mcp": {
"command": "npx",
"args": ["-y", "@seniorsistemas/angular-upgrade-mcp"]
}
}
}Claude Code
Via CLI — adiciona ao .mcp.json do projeto (compartilhado com o time):
claude mcp add --scope project angular-upgrade-mcp -- npx -y @seniorsistemas/angular-upgrade-mcpOu crie .mcp.json na raiz do projeto diretamente:
{
"mcpServers": {
"angular-upgrade-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@seniorsistemas/angular-upgrade-mcp"]
}
}
}Build local (desenvolvimento)
Para usar o build local em qualquer editor, substitua o command/args por:
{
"command": "node",
"args": ["/caminho/para/angular-upgrade-mcp/dist/index.js"]
}Tools MCP disponíveis
Fluxo de upgrade (v9 → v18)
| Tool | O que faz |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| start-upgrade | Ponto de entrada obrigatório. Retorna o plano completo com a sequência exata de tools a chamar. |
| check-prerequisites | Verifica se o projeto está em Angular v9 e se NVM está disponível. |
| analyze-dependencies | Analisa compatibilidade de dependências com Angular 18; bloqueia o upgrade se alguma lib não tiver versão compatível conhecida; pode gerar angular-upgrade-overrides.jsonc e angular-upgrade-deps-report.md. |
| prepare-upgrade | Salva backup do package.json e prepara a base limpa para o upgrade. |
| upgrade-to-10 … upgrade-to-18 | Executa ng update para cada versão usando o Node correto via NVM. |
Ajustes pós-upgrade Angular 18
Chame v18-adjustments para receber o guia completo com a sequência e as instruções de uso. As tools individuais são:
| # | Tool | O que faz |
| --- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | v18-restore-package-json | Restaura o package.json do backup e aplica versões v18-compatíveis: Storybook 6→8 (com react-is→^16.13.1), injeta ngx-mask (peer dep de @seniorsistemas/angular-components), atualiza rxjs 6→7 e remove rxjs-compat. Roda npm install --force (Node 20) ao final. |
| 2 | v18-setup-lib | Corrige builder ng-packagr; remove ~ do SCSS e @import primeng/resources/; separa lib/ng-package.json; remove CSS PrimeNG legado e garante angular-components/styles.css. |
| 3 | v18-install-tailwind | Instala e configura TailwindCSS (tailwind.config.js, tailwind.css, angular.json). |
| 4 | v18-fix-tsconfig | Remove test.ts/polyfills.ts dos files dos tsconfigs e allowJs; normaliza compilerOptions.module desatualizado (es2015/commonjs/etc. → es2022, necessário pro dynamic import de lazy loading) em todo tsconfig*.json do projeto; atualiza karma.conf.js para headless Chrome (detectando karma-coverage vs karma-coverage-istanbul-reporter); desativa strict: false + todas as flags granulares angularCompilerOptions (medida temporária — ver aviso no UPGRADE-REPORT.md). |
| 5 | v18-migrate-angular-components | Migra automaticamente os componentes AC e PrimeNG (veja abaixo). |
| 6 | v18-migrate-eslint | Migra de TSLint para ESLint compatível com Angular 18; busca recursiva de tslint.json em todos os subdiretórios. |
| 7 | run-prettier | Instala o Prettier e formata .ts/.html/.scss do projeto; cria .prettierrc.json com parser: "angular" para *.component.html (veja abaixo). |
| 8 | v18-npm-install | Roda npm install com Node 20 com fallback progressivo (--legacy-peer-deps, --force); reporta conflitos de peer deps detectados. |
| 9 | v18-fix-runtime | OBRIGATÓRIO. Corrige breaking changes RxJS 7 / Angular 18 em todos os .ts do projeto (veja abaixo). |
| 10 | v18-update-senior-libs (opcional — abrirá diálogo de confirmação) | Atualiza libs @seniorsistemas/*/@senior-gestao-empresarial/* via ng update para a versão mais nova compatível com Angular 18 que tenha schematic de migração. Só executar após confirmação do usuário (veja abaixo). |
| 11 | v18-fix-strict-ts (opcional — abrirá diálogo de confirmação) | Corrige erros TypeScript strict mode via análise estática. Só executar após confirmação do usuário (veja abaixo). |
v18-fix-runtime
Corrige breaking changes obrigatórias do Angular 18 / RxJS 7 em todos os .ts do projeto (exceto *.spec.ts/*.d.ts/node_modules).
Fixes aplicados:
.toPromise()→firstValueFrom()— via tree-sitter; cobre.pipe(...).toPromise<any>()e similares; adicionaimport { firstValueFrom } from 'rxjs'automaticamente se necessário.import * as moment_ from 'moment'→import moment from 'moment'— namespace import não é callable no RxJS/momento moderno.- Remove
RouterEventde@angular/routerquando não utilizado no corpo do arquivo (deprecated Angular 18). - Adiciona
: Observable<any>em métodos de serviço que usamthis.X.(get|post|put|delete|patch)()sem tipo de retorno explícito; adicionaimport { Observable } from 'rxjs'se necessário. Itens marcados comorequiresReview: truepara o desenvolvedor refinar o tipo depois. new Subject()sem generic →new Subject<void>()— só quando toda chamada.next()desseSubjectno arquivo é sem argumento (padrão "sinal de destroy"); se houver.next(valor)em algum lugar, não altera (ambíguo).import * as X from '.../package.json'→import X from '...'(default import) — named export de módulo default-exporting não é permitido pelo TS moderno.- Scripts
.jsna raiz do projeto que citam@angular/cli: dasheriza flags camelCase conhecidas do dev-server builder (--publicHost→--public-host,--disableHostCheck→--disable-host-check, etc.) — Angular CLI 18 registra as opções no yargs sempre em kebab-case.
Parâmetros:
{
"projectPath": "/caminho/absoluto/do/projeto"
}run-prettier
Instala o Prettier no projeto (devDependencies + script format) e formata todo .ts/.html/.scss do código-fonte, ignorando node_modules/dist/.angular. Cria .prettierrc.json com tabWidth: 4 e parser: "angular" para *.component.html (necessário para formatar templates Angular corretamente — o parser HTML genérico não entende *ngIf/interpolação/bindings). Não sobrescreve configuração do Prettier já existente. Deve ser chamada logo após v18-migrate-eslint e antes de v18-npm-install.
Parâmetros:
{
"projectPath": "/caminho/absoluto/do/projeto"
}v18-update-senior-libs (opcional)
⛔ Consulte o usuário antes de executar. Pergunte: "Deseja verificar e atualizar libs da Senior (
@seniorsistemas/*,@senior-gestao-empresarial/*) que tenham versões mais novas com schematics de migração disponíveis? Isso rodang updatee pode quebrar a aplicação se algum schematic não cobrir 100% do seu caso de uso."
Escaneia dependencies/devDependencies do package.json por libs com prefixo Senior. Para cada uma, consulta o npm registry subindo major a major (18→19→20...) enquanto o peerDependencies de @angular/* continuar aceitando Angular 18, e só considera a versão final se ela tiver o campo ng-update (schematics). Roda ng update <lib>@<versão> --allow-dirty uma vez por lib qualificada (Node 20, @angular/cli@18).
Depois de cada ng update bem-sucedido, roda duas verificações e instala o que faltar num único npm install:
- peerDependencies novas (3rd-party) que a versão-alvo passou a declarar e que a versão antiga não tinha — ex.:
[email protected]passou a exigirngx-cookie-service/cropperjsque a18.1.3não exigia. Se ainda não estiverem nopackage.jsondo projeto, são adicionadas automaticamente. - Peer dependencies Senior-para-Senior que ficaram inválidas (
npm ls --all --json) por causa de outra lib Senior já atualizada — ex.:platform-componentsexigido em^7.0.0peloangular-componentsatualizado. Corrige só o número da versão nopackage.json(semng update— a lib não tem schematics).
Isso cobre dependência declarada (peerDependencies). Não cobre dependência que a lib usa no bundle sem declarar em lugar nenhum (bug de empacotamento da lib publicada) — se aparecer Module not found pontual após o build, é preciso investigar/instalar manualmente.
Parâmetros:
{
"projectPath": "/caminho/absoluto/do/projeto",
"confirmed": true
}v18-migrate-angular-components
A tool principal de migração. Faz o walk recursivo do projeto, processa cada par .component.ts + .component.html (e arquivos .ts sem HTML companion — services, módulos, pipes, guards — pelo pipeline TS-only) e aplica:
- Quebra de imports do barrel em entry-points específicos (com deduplicação de
@NgModulearrays) - Renames de inputs/outputs e de tipos TypeScript
- Substituições de seletores HTML
- Migrações de
importPath - Regras customizadas via AST (accordion, dynamic-form, lookup, p-table, p-dialog, etc.)
A seção rica que essa tool escreve em UPGRADE-REPORT.md (ver abaixo) é verificada contra a "prova real" (src/migration/verification.ts): cada claim declarativo ("Atributo X removido automaticamente", etc.) só é afirmado se nenhum achado confirmed de resíduo contradizer aquele arquivo — quando contradiz, o relatório contesta o claim numa seção "Divergências encontradas pela prova real" em vez de afirmá-lo incondicionalmente.
Parâmetros:
{
"projectPath": "/caminho/absoluto/do/projeto"
}dryRun não é exposto no schema do MCP de propósito (nenhum client/agente deve conseguir pedir uma prévia sem gravar — commit 6de96c2). Pra debug local via prompt sem gravar num projeto real, existe um mecanismo fora do protocolo: npm run dryrun:local (ou dryrun:on/dryrun:off) cria um env-debug.json na raiz do repo (nunca commitado, expira sozinho em 1h) que só você, localmente, consegue ativar — ver src/tools/env-debug.ts.
v18-fix-strict-ts (opcional)
⛔ Consulte o usuário antes de executar. O build já funciona com
strict: false— esta tool é opcional. Pergunte: "Deseja executar o v18-fix-strict-ts? Ele tenta corrigir erros de strict mode automaticamente mas não cobre 100% dos casos — itens restantes ficam no STRICT-REVIEW.md."
Corrige erros TypeScript strict mode via análise estática. Percorre todos os .ts do projeto e aplica correções em uma única passada.
Fixes aplicados:
- TS2564 (propriedade sem inicializador), conforme o caso:
- atribuída em
ngOnInit→prop!: T(definite assignment) - tipo primitivo sem decorator → inicializador padrão (
boolean→= false,string→= '',number→= 0) @Input/@ViewChildsemimplements→prop?: T@Input/@ViewChildem classe comimplements(contrato exige a prop) →prop!: T- tipo complexo sem decorator e sem ngOnInit →
prop: T | undefined
- atribuída em
- TS2769 — callback de
.subscribe()com parâmetro tipado → remove a anotação de tipo do parâmetro - TS2345 —
this.prop(T[] | undefined) passado como argumento →this.prop ?? [] - Usos de
this.prop.X→this.prop?.Xapós a propriedade virar| undefined .find()sem null-check →?.nos acessos subsequentesgetElementById()/querySelector()→?.method()- Template
$event.target.value→$any($event.target).value - HTML: propriedade que virou
| undefinedcom 2+ bindings na mesma linha → envolve o elemento em@if (prop) { ... }; binding único →?.
Parâmetros:
{
"projectPath": "/caminho/absoluto/do/projeto",
"confirmed": true
}Saída: lista de arquivos modificados + as correções com risco de falso positivo gravadas em STRICT-REVIEW.md na raiz do projeto.
Relatório de Upgrade (UPGRADE-REPORT.md)
Cada tool MCP grava automaticamente uma seção em UPGRADE-REPORT.md na raiz do projeto do cliente. O arquivo é criado pela start-upgrade e incrementado pelas tools subsequentes.
Estrutura do relatório
O relatório contém:
- Cabeçalho — projeto, diretório e data de geração
- Resumo da Migração (topo) — bloco inserido ao final do fluxo, com tempo total gasto, arquivos Angular Components modificados e arquivos PrimeNG migrados
- Uma seção por tool — status (✅/❌/⚠️), duração, detalhes, avisos e erros
- Seção rica de migração — para
v18-migrate-angular-components, inclui métricas (incluindo separação AC vs PrimeNG e o total de divergências confirmadas pela prova real), distribuição de warnings, um<details>por componente migrado, e — quando a "prova real" encontra um resíduo confirmado para um claim declarado — uma subseção "⚠️ Divergências encontradas pela prova real" listando arquivo e mensagem - Aviso de strict mode desativado — a
v18-fix-tsconfigregistra no relatório questrict: falseé temporário e orienta como ativar gradualmente
Extensibilidade para produtos
Tools de produto terceiro podem escrever no mesmo relatório usando appendToolSection de src/tools/report-writer.ts:
import { appendToolSection } from "./report-writer.js";
appendToolSection(projectPath, {
toolName: "migrate-produto-x-components",
displayName: "Migração Produto X — entry-points",
status: "success",
responsible: "Equipe Produto X",
startTime: t0,
endTime: Date.now(),
details: ["42 entry-points convertidos"],
warnings: [],
errors: [],
customMarkdown: buildProductComponentDetails(results),
});O campo responsible aparece como atribuição de equipe na seção — cada tool declara o seu.
Motor de migração
O engine em src/migration/ é completamente genérico — não tem nenhuma referência a angular-components ou primeng. Ele recebe um MigrationConfig e um customRulesRegistry e sabe como aplicar qualquer conjunto de regras.
Interface principal
migrateAngularComponent(
tsSource: string,
htmlSource: string,
context: MigrationContext, // árvores parseadas (tree-sitter)
config: MigrationConfig, // regras da lib (entry-points + externalRules)
customRulesRegistry: Record<string, CustomRule>, // regras AST customizadas
): MigrationResult // { ts, html, warnings }MigrationConfig
interface MigrationConfig {
sourceModule: string; // ex: '@seniorsistemas/angular-components'
entryPoints: EntryPointDefinition[]; // componentes com entry-point específico
externalRules?: ComponentDefinition[]; // componentes de libs externas (PrimeNG)
}CustomRule
Função que recebe o contexto completo (AST do TS + AST do HTML) e retorna edits indexados:
type CustomRule = (ctx: CustomRuleContext) => CustomRuleResult;
interface CustomRuleResult {
tsEdits: TextEdit[];
htmlEdits: TextEdit[];
warnings: Warning[];
}As edits são aplicadas em ordem decrescente de startIndex para não deslocar posições.
Motor de verificação ("prova real")
Migrar sem quebrar a sintaxe não é o mesmo que migrar corretamente. Depois que migrateAngularComponent produz o TS/HTML migrado, src/migration/verification.ts reparseia esse resultado do zero e confere, para cada padrão antigo catalogado (attributeRenames, propertyRenames, selectorReplacement, attributeRemovals, importPathMigration, typeMigration), se ele realmente desapareceu — e se o padrão novo esperado apareceu no lugar.
O princípio central é independência: este módulo nunca importa a lógica de transformação do ts-migrator.ts/html-migrator.ts/custom rules para decidir onde procurar. Ele usa sua própria lógica de matching (tree-sitter + regex de segurança), deliberadamente separada — assim, um bug de matching na migração não se propaga silenciosamente para a verificação, o que evitaria detectar um arquivo que "passou" mas continua com o padrão antigo.
Cada achado (VerificationFinding) tem duas camadas de confiança:
- Camada 1 —
confirmed: query tree-sitter escopada à tag/seletor resultante (AST). - Camada 2 —
needs-review: rede de segurança por regex sobre o texto (comentários/strings mascarados), ativada só quando a Camada 1 não confirma nada mas há sinal independente (ex: import do componente) de que ele está em jogo no arquivo.
Regras puramente declarativas (as quatro acima, definidas em rules.manual.ts) são cobertas automaticamente pelo motor genérico — nenhum código extra é necessário ao adicionar uma.
Toda customRule nova (registrada em custom-rules/registry.ts) precisa de um verificador correspondente com a mesma chave em custom-rules/verify-registry.ts (exportado em acCustomRuleVerifiers/primengCustomRuleVerifiers), também escrito sem importar a customRule que verifica. Sem isso, test/custom-rules-verify.test.ts tem um teste de paridade que falha propositalmente — mesmo espírito de "fail loud" do merge.ts.
Tanto o "Migrar Projeto" do playground quanto a tool MCP v18-migrate-angular-components rodam essa verificação após cada migração. O playground expõe os achados numa aba própria ("Prova real"); a tool MCP usa achados confirmed de resíduo/possible-silent-removal pra contestar (em vez de afirmar incondicionalmente) claims de "alteração aplicada" na seção rica do UPGRADE-REPORT.md — ver seção "Relatório de Upgrade" acima.
Como o angular-components usa o engine
merge.tslêrules.generated.v18.json(inventário auto-scan) +rules.manual.ts(renames manuais) e produz oMigrationConfig.custom-rules/registry.tsmapeia nomes de regra ('accordion-switch-manual') para funções.- A tool MCP combina AC config + PrimeNG config e os dois registries antes de chamar o engine.
Performance: cache de discoverProjectInheritance e memoização de índices por config
O "Migrar Projeto" do playground chama o engine uma vez por arquivo do projeto (via HTTP, num processo Express single-thread e síncrono). Sem cuidado, isso faria com que trabalho que só depende do projeto como um todo — não do arquivo sendo migrado — fosse recalculado do zero a cada arquivo:
discoverProjectInheritance(src/migration/inheritance-discovery.ts) varre e reparseia com tree-sitter todos os.tsdo projeto pra descobrir componentes queextendsuma classe conhecida da lib. Chamar isso a cada arquivo custa O(N arquivos migrados × M arquivos do projeto) — em projetos com centenas/milhares de arquivos, isso é a causa raiz de requests de/migrate//migrate-ts-onlylevando dezenas de segundos.buildClassIndex/typeMigByName(ts-migrator.ts) e a listaallComponents(verification.ts) são reconstruídos a partir do mesmoMigrationConfiga cada chamada.
Por isso:
discoverProjectInheritanceCached(projectDir, config)cacheia o resultado por(config, projectDir). A invalidação é automática: uma assinatura barata (contagem + path +mtime/sizede cada.ts, viastat, sem ler conteúdo) é recalculada a cada chamada — se qualquer arquivo do projeto mudar, o cache é invalidado e o resultado recomputado é idêntico ao que se teria sem cache.discoverProjectInheritance(não-cacheada) continua existindo e é o que a tool MCPv18-migrate-angular-componentsusa (ela já só chama uma vez por run, antes do loop de arquivos — não sofre desse problema).playground/api/app.tstambém cacheia porprojectDiro objetoconfigaumentado (config base +externalRulesdescobertas), reusando a mesma referência entre requests do mesmo projeto — isso é o que permitebuildRuleIndexes(ts-migrator.ts) egetAllComponents(verification.ts) memoizarem por identidade deconfig(WeakMap) sem nenhuma mudança de contrato HTTP.
Auto-scan (npm run scan)
Precisa da env var ANGULAR_COMPONENTS_SRC apontando para o repositório do angular-components. Se ela não estiver definida, scripts/ensure-angular-components-src.sh (encadeado antes do scan no próprio npm run scan) clona automaticamente senior-sistemas/snr-angular-components (privado — requer acesso via gh/git já autenticado) em _angular-components/ na raiz do projeto e exporta a variável, sem passo manual:
npm run scanPra apontar pra um checkout próprio em vez de deixar o auto-clone rodar:
# Linux/macOS
export ANGULAR_COMPONENTS_SRC=/caminho/para/angular-components
npm run scan# Windows (PowerShell)
$env:ANGULAR_COMPONENTS_SRC = "C:\caminho\para\angular-components"
npm run scanDepois de resolvida a env var, o script cria um git worktree temporário na branch configurada em scripts/scan.config.ts, extrai classNames/selectors/inputs/outputs via tree-sitter e grava src/migration-angular-components-v18/rules.generated.v18.json.
API Express (playground)
API de debug que expõe o mesmo engine do MCP via HTTP.
npm run start:api
# http://localhost:3001| Endpoint | Descrição |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| GET /health | Status + versão do parser |
| GET /config | Config de migração carregada (AC + PrimeNG combinados) |
| POST /migrate | { ts, html } → { ts, html, warnings, verification, crossFileImportRequests } |
| POST /migrate-ts-only | { ts } → { code, warnings, verification } — espelha o pipeline TS-only da tool MCP (services/módulos/pipes/guards, sem HTML) |
| POST /project/resolve-cross-file-imports | { projectPath, requests } → resolve, pra cada crossFileImportRequest, qual *.module.ts declara o componente (read-only, nunca escreve) |
| POST /project/migration-report | { projectPath, files } → { markdown } — mesma seção rica (collectComponentSummaries/buildMigrationCustomMarkdown) que a tool MCP escreveria em UPGRADE-REPORT.md, sempre em modo dry-run (o playground nunca escreve) |
| GET /project?path=abs | Árvore de arquivos de um projeto |
| POST /project/check-syntax | { ts, html } → detecta erro de sintaxe via tree-sitter (hasError/contagem de nós ERROR) |
| POST /project/verify-cross-file | Roda a "prova real" para regras cross-file (que afetam um arquivo diferente do par migrado, ex: setTranslation( → providePrimeNG) |
| GET /search?q=termo[&path=abs] | Busca texto nos arquivos do angular-components |
| GET /project/file?path=abs&file=rel | Lê um arquivo do projeto |
| POST /analyze-dependencies | { projectPath, confirmOverrides? } → análise de deps vs Angular 18 |
| POST /senior-libs-check | { projectPath } → analisa libs @seniorsistemas/* com update disponível (contraparte web de v18-update-senior-libs) |
| POST /strict-analyze | { ts, html, fileName? } → detecta erros strict sem aplicar (TS2564, EventTarget, .find()) |
| POST /strict-fix | { ts, html, fileName? } → { ts, html, issues } aplica as correções strict |
| POST /runtime-fix | { ts, fileName? } → { ts, issues } aplica fixes de runtime (toPromise, moment, RouterEvent, HttpClient return types) |
| POST /prettier-format | { ts, html, fileName?, projectDir? } → formata o resultado via Prettier (contraparte web de run-prettier) |
| GET /project/branches?path=abs | Branches git ordenadas por commit mais recente |
| POST /project/checkout | { path, branch } → checkout de branch |
A env var ANGULAR_COMPONENTS_SRC é opcional — se ausente, a API sobe usando apenas rules.generated.v18.json (sem busca no código-fonte da lib).
Playground Angular
Frontend com Monaco Editor para testar a migração interativamente.
npm run start:playground # sobe API + frontend juntos
npm run start:frontend # só o Angular (porta 4200)
npm run start:api # só a API Express (porta 3001)Funcionalidades:
- Modo upload: arraste
.ts/.htmle veja o resultado da migração lado a lado - Modo project: aponte para um caminho absoluto, navegue na árvore e migre arquivo a arquivo ou em lote ("Migrar Projeto")
- Branch selector: lista branches git do projeto ordenadas pela mais recente; ao trocar a branch o projeto é recarregado automaticamente
- Migrar Projeto (lote): processa todo
.tsdo projeto (não só.component.ts— services/módulos/pipes/guards vão pelo pipeline TS-only via/migrate-ts-only, igual a tool MCP), checa sintaxe, roda a "prova real" pós-migração e análise strict (/strict-analyze) em paralelo - Batch report: filtros por erros, warnings, inputs removidos, achados de verificação (
confirmed/needs-review, incluindo preview de imports de NgModule pendentes) e strict issues (TS2564, EventTarget, .find()) - Aba "Relatório MCP": mostra a mesma seção rica que a tool MCP escreveria em
UPGRADE-REPORT.md(reaproveitacollectComponentSummaries/buildMigrationCustomMarkdowndo servidor), renderizada como markdown de verdade viangx-markdown(sanitizado por padrão viaDomSanitizer) — é a forma de conferir, sem rodar a tool MCP de verdade, que o relatório não vai afirmar algo que a "prova real" contradiz - Analisar Deps: verifica compatibilidade de dependências com Angular 18
- Runtime Fix: aplica fixes de runtime (toPromise, moment, RouterEvent, HttpClient return types) no resultado da migração atual
- Prettier: formata o resultado da migração via
/prettier-format - Senior Libs Check: lista libs
@seniorsistemas/*com versão mais nova disponível viang update - Busca por arquivo (Ctrl+E) e busca por conteúdo (Ctrl+Shift+F) no projeto carregado
Scripts
| Script | Descrição |
| -------------------------- | --------------------------------------------------------------------- |
| npm start | Compila e sobe o MCP server (build + node dist/index.js) |
| npm run build | Compila o MCP server (TypeScript → dist/) |
| npm run update:grammars | Atualiza as gramáticas tree-sitter (.wasm) usadas pelo parser |
| npm run dev | MCP server em modo watch (tsx) |
| npm run start:mcp | Sobe o MCP server compilado |
| npm run start:api | Sobe a API Express (playground) |
| npm run start:frontend | Sobe o Angular playground |
| npm run start:playground | Sobe API + Angular juntos |
| npm run scan | Regenera rules.generated.v18.json (clona snr-angular-components automaticamente se ANGULAR_COMPONENTS_SRC não estiver definida) |
| npm test | Roda todos os testes (vitest) |
| npm run test:watch | Testes em modo watch |
| npm run inspector | Abre o MCP Inspector para testar as tools manualmente |
| npm run dryrun:local | Liga o dry-run local (env-debug.json), espera você testar via prompt e desliga sozinho (trap EXIT) |
| npm run dryrun:on / dryrun:off | Liga/desliga manualmente o dry-run local (expira sozinho em 1h) |
| npm run prepublishOnly | Rodado automaticamente antes de npm publish: install + scan + build |
Testes
577 testes (28 arquivos) cobrindo:
| Arquivo | O que testa |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| html-migrator.test.ts | Renames de atributos, seletor de atributo, attributeRemovals, sCalendarMask, herança, fixStrayGt, balance check, limite de tag sensível a aspas em applyComponentTransforms (> literal num atributo anterior não corta attributeRemovals cedo demais) |
| ts-migrator.test.ts | Import split, propertyRenames, typeMigration, importPathMigration, herança, deduplicação de @NgModule arrays, remoção de @Injectable() com parâmetros não-injetáveis (Tier 1/2, incluindo resolução via node_modules), memoização de classIndex/typeMigByName por referência de config (buildRuleIndexes) sem vazar estado entre arquivos |
| merge.test.ts | Merge JSON + manual, validação de renames, externalRules |
| integration.test.ts | Fluxo completo com rules.generated.v18.json real |
| custom-rules.test.ts | Todas as 21 regras customizadas (8 AC + 13 PrimeNG), incluindo lazyLoadLookup→onCompleteMethod e showDialog→async showDialog |
| custom-rules-verify.test.ts | Paridade customRule ↔ verificador em verify-registry.ts (AC e PrimeNG); regressão do gap p-footer/p-header fora de <p-dialog>; regressão do falso positivo de pTemplate em componente não relacionado (ex: <p-table>) na regra p-tabView-to-s-tabs |
| verification.test.ts | Motor genérico da "prova real" pós-migração (verifyMigration): camada AST confirmed + camada textual needs-review; memoização de allComponents por referência de config (getAllComponents) sem vazar findings entre arquivos |
| migrate-angular-components.test.ts | Relatório da tool MCP verificado contra a prova real (collectComponentSummaries/describeComponentChanges/buildMigrationCustomMarkdown não afirmam remoção/rename quando há resíduo confirmed, e não contaminam claims de outros atributos) + E2E do handler completo em diretório descartável (dryRun: false grava e não afirma remoção que não aconteceu; dryRun: true não escreve nada e a seção rica aparece na resposta de texto) |
| ts-entry-classification.test.ts | classifyTsEntries — exclusão de .d.ts/.spec.ts, pareamento com .html presente/ausente, paths absolutos/relativos — módulo puro compartilhado entre walkTsFiles (backend) e o playground |
| playground-migration-parity.test.ts | Trava contra divergência futura entre a tool MCP e o playground: mesmo pareamento de arquivo, mesmo conjunto de arquivos modificados e tipos de warning por arquivo, e que /project/migration-report reusa collectComponentSummaries/buildMigrationCustomMarkdown do servidor |
| env-debug.test.ts | isLocalDryRunEnabled — arquivo ausente/JSON inválido/dryRun falsy/activatedAt expirado → false; só true com flag recente |
| scan-signals.test.ts | Extração de inputs/outputs via tree-sitter (decorators + signal API) |
| extract-rules-worktree.test.ts | Resolução de branch/ref do git worktree usado pelo npm run scan |
| lib-paths.test.ts | Resolução do ANGULAR_COMPONENTS_SRC em diferentes formatos |
| fix-strict-ts.test.ts | Fixes strict mode estáticos (TS2564, TS2769, TS2345, primitivos, implements, ngOnInit→!), fixes de runtime (RouterEvent, HttpClient return types, toPromise tree-sitter, new Subject() sem generic, import * as X de package.json, dasherize de flags @angular/cli em scripts .js) |
| fix-strict.test.ts | Fixes pontuais adicionais: acesso encadeado em parâmetro opcional, argumentos de parâmetro parcial, import * as moment_ → import moment |
| fix-tsconfig.test.ts | Normalização de compilerOptions.module desatualizado (es2015/commonjs/etc. → es2022) em todo tsconfig*.json do projeto |
| restore-package-json.test.ts | Merge de deps, ngx-mask, rxjs 6→7, pacotes removidos |
| analyze-dependencies.test.ts | Análise de compatibilidade de dependências com Angular 18 |
| inheritance-discovery.test.ts | Descoberta de herança entre componentes; discoverProjectInheritanceCached — cache hit devolve mesma referência, invalidação automática quando o projeto muda, paridade com a versão não-cacheada, isolamento por config |
| module-declarations.test.ts | Descoberta de declarations de @NgModule e aplicação de imports cross-file |
| eol.test.ts | Detecção/conversão de fim de linha (CRLF/LF) e preservação durante a migração |
| npm-install.test.ts | Parsing/iteração do npm install |
| migrate-eslint.test.ts | Elicitation do selectorPrefix na migração TSLint → ESLint |
| run-prettier.test.ts | Instalação/config do Prettier, descoberta e formatação de arquivos, formatSource (reutilizada pelo playground) |
| update-senior-libs.test.ts | Resolução de versão instalada/major mais recente estável, descoberta de update via npm registry para libs @seniorsistemas/*, diff de peerDependencies novas (diffNewPeerDeps) e reconciliação Senior-para-Senior pós-schematics |
| primeng-config.test.ts | Conversão de PrimengManualEntry.selector → ComponentDefinition.selectors |
| upgrade-shared.test.ts | Utilitários compartilhados do fluxo de upgrade |
npm testExtensibilidade
A arquitetura foi projetada para adicionar novas libs sem tocar no engine.
Adicionar uma nova lib (ex: migration-platform-components-v18/)
- Criar
src/migration-platform-components-v18/com:rules.manual.ts— entradas de rename/seletor/importPathconfig.ts— constróiMigrationConfiga partir das regras manuaiscustom-rules/registry.ts+ arquivos de regras customizadas (se necessário)
- Em
src/tools/v18/migrate-angular-components.ts, combinar o novo config e registry junto com os existentes. - Zero alteração no engine
src/migration/.
O engine não sabe nada sobre nenhuma lib específica — ele só executa o que o
MigrationConfige ocustomRulesRegistrydescrevem.
Usando o engine em outro projeto
⚠️ Experimental — esta abordagem não foi testada com repositórios externos, mas é exatamente o mesmo mecanismo que o
migration-angular-components-v18e omigration-primeng-v18usam internamente.
O engine em src/migration/ é completamente genérico. Para usá-lo com uma lib de terceiros:
1. Descrever as regras de migração
Crie um MigrationConfig descrevendo os componentes da lib:
import type { MigrationConfig } from "./src/migration/rules";
const myLibConfig: MigrationConfig = {
sourceModule: "@minha-empresa/minha-lib",
entryPoints: [
{
path: "button", // entry-point: @minha-empresa/minha-lib/button
components: [
{
classNames: ["ButtonComponent"],
selectors: ["ml-button"],
propertyRenames: { onClick: "clicked" },
attributeRenames: { "(onClick)": "(clicked)" },
},
],
},
],
externalRules: [
// componentes sem entry-point dedicado (ex: dependências de outras libs)
],
};2. Regras customizadas (opcional)
Para transformações estruturais que vão além de renames simples, implemente a interface CustomRule:
import type { CustomRule } from "./src/migration/custom-rules/types";
const myRule: CustomRule = (ctx) => {
// ctx.tsSource, ctx.htmlSource — código original
// ctx.tsTree, ctx.tsLang — AST TypeScript (tree-sitter)
// ctx.htmlTree, ctx.htmlLang — AST Angular HTML (tree-sitter)
// ctx.component — ComponentDefinition que ativou esta regra
return {
tsEdits: [], // TextEdit[] aplicados no .ts
htmlEdits: [], // TextEdit[] aplicados no .html
warnings: [], // Warning[] reportados ao usuário
};
};3. Rodar a migração
import {
parseTypeScript,
parseAngularHtml,
} from "./src/migration/parser-adapter";
import { migrateAngularComponent } from "./src/migration";
const tsParsed = parseTypeScript(tsSource);
const htmlParsed = parseAngularHtml(htmlSource);
const result = migrateAngularComponent(
tsSource,
htmlSource,
{ ts: tsParsed, html: htmlParsed },
myLibConfig,
{ "my-custom-rule": myRule },
);
console.log(result.ts); // TypeScript migrado
console.log(result.html); // HTML migrado
console.log(result.warnings); // Avisos para revisão manual