Metadata-Version: 2.4
Name: datatool-cli
Version: 0.1.0
Summary: Diagnostica, perfila, limpa e converte arquivos de dados (CSV, Excel, JSON, Parquet) pela linha de comando, com suporte nativo a CSV do Excel brasileiro, CPF/CNPJ e R$.
Author-email: WVCode <contato@wvcode.com.br>
License-Expression: GPL-3.0-only
Project-URL: Homepage, https://github.com/wvcode/cli-python
Project-URL: Bug Tracker, https://github.com/wvcode/cli-python/issues
Project-URL: Changelog, https://github.com/wvcode/cli-python/blob/main/CHANGELOG.md
Keywords: csv,excel,parquet,data-cleaning,data-quality,profiling,polars,cpf,cnpj,mcp
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Natural Language :: Portuguese (Brazilian)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.16
Requires-Dist: polars>=1.27.1
Requires-Dist: fastexcel>=0.10
Requires-Dist: xlsxwriter>=3.0
Requires-Dist: platformdirs>=3.0
Provides-Extra: mcp
Requires-Dist: mcp>=2.0; extra == "mcp"
Provides-Extra: dev
Requires-Dist: datatool-cli[mcp]; extra == "dev"
Requires-Dist: click>=8.2.1; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: pyright>=1.1.380; extra == "dev"
Dynamic: license-file

# datatool

Diagnostica, perfila, limpa e converte arquivos de dados pela linha de comando: CSV, Excel, JSON, Parquet e outros. Aponta valores nulos, duplicidades, datas em formatos diferentes, números guardados como texto e CPF/CNPJ inválido, e sugere o comando que corrige cada problema. Entende nativamente CSV exportado pelo Excel em português (`;`, `cp1252`), CPF/CNPJ e valores em R$.

```bash
pip install datatool-cli
datatool info clientes.csv
```

O roadmap completo, com o status de cada funcionalidade, está em [specs/README.md](https://github.com/wvcode/cli-python/blob/main/specs/README.md).

## Status

- **Implementado**: `convert`, `info`, `profile` (+ `--columns`/`--max-columns`), `clean` (diagnóstico + `--trim`/`--lowercase`/`--uppercase`/`--normalize-case`/`--remove-duplicates`/`--fill-null`/`--drop-null`/`--normalize-dates`/`--fix-types`/`--rename-columns`/`--remove-columns`/`--normalize-documents`), saída em JSON (`--format json`) em `info`/`profile`/`clean`/`convert`, `--redact-values` em `info`/`profile`/`clean`, detecção de delimitador/encoding de CSV (`--sep`/`--encoding`), log de execução (no diretório de logs do usuário, configurável), validação de CPF/CNPJ, servidor MCP (`datatool-mcp`) para agentes de IA
- **Ainda não implementado**: relatório HTML de profiling, pipelines YAML, inspeção e limpeza de planilhas Excel, IA opcional, licenciamento Pro — veja [specs/README.md](https://github.com/wvcode/cli-python/blob/main/specs/README.md) para o detalhamento spec a spec

## Requisitos

- Python >= 3.10

## Instalação

```bash
pip install datatool-cli
```

O pacote se chama `datatool-cli`, mas o comando é `datatool`. `datatool --version` mostra a versão instalada, e `datatool --help` lista os comandos.

Para o servidor MCP, usado por agentes de IA (ver a [seção própria](#servidor-mcp-datatool-mcp)), que traz dependências a mais:

```bash
pip install "datatool-cli[mcp]"
```

Para instalar a partir do código-fonte, veja [Desenvolvimento](#desenvolvimento).

## Comandos disponíveis

### `convert` — converter arquivo entre formatos

```bash
datatool convert vendas.csv vendas.parquet
datatool convert vendas.xlsx vendas.csv
```

- Formatos suportados: **CSV, JSON, JSONL, Excel (xlsx), Parquet, SQLite**, além de Feather e Avro.
- SQLite: a tabela tem o nome do arquivo (`vendas.db` → tabela `vendas`). Com `--overwrite` num `.db` que já existe, a tabela é substituída numa única transação: se a gravação falhar, a tabela original fica intacta. As demais tabelas do arquivo não são tocadas. Booleanos, datas e data-hora são gravados com o tipo declarado (`BOOLEAN`, `DATE`, `TIMESTAMP`) e voltam com o mesmo tipo na leitura. Data-hora com fuso volta em UTC, no mesmo instante.
- Os tipos das colunas são inferidos com o arquivo inteiro, não só com as primeiras linhas: um valor como `N/D` numa coluna numérica, em qualquer posição, faz a coluna ser lida como texto (o `info` aponta e o `clean --fix-types` corrige), em vez de impedir a leitura ou virar nulo.
- O formato de entrada e saída é inferido pela extensão do arquivo. Use `--from-type`/`--to-type` para sobrescrever quando a extensão não é reconhecida ou é ambígua.
- Com `to_filename`, uma linha de confirmação vai para o stderr: `Gravado vendas.parquet (parquet): 1.500 linhas, 8 colunas`.
- Se `to_filename` for omitido, o dataset inteiro é impresso no stdout em CSV, pronto para pipe (`datatool convert vendas.xlsx | head`).
- Se o destino já existir, o comando recusa (exit code 2) e não altera nada; use `--overwrite` para substituí-lo.
- `--show-stats` imprime (linhas, colunas) da origem e do destino; sem `to_filename`, vai para o stderr, para não se misturar ao CSV.
- `--format json` imprime um documento com `source` e `target` (exige `to_filename`; não combina com `--show-stats`).

```bash
datatool convert vendas.csv vendas.parquet --show-stats
```

Arquivos inexistentes, formatos não suportados ou diretórios sem permissão de escrita retornam uma mensagem clara e exit code diferente de zero.

### `info` — diagnóstico automático de um arquivo

```bash
datatool info clientes.csv
```

Mostra linhas, colunas e tamanho do arquivo, além de detectar automaticamente:

- valores nulos por coluna
- linhas duplicadas
- colunas com múltiplos formatos de data
- colunas numéricas armazenadas como texto

Para cada problema encontrado, sugere o comando `datatool clean` correspondente. Todas as sugestões (`--fix-types`, `--remove-duplicates`, `--normalize-dates`, `--drop-null`) já estão implementadas. Funciona para CSV, JSON, Excel e Parquet.

```text
Arquivo: clientes.csv
Linhas: 10
Colunas: 6
Tamanho: 711 bytes

Problemas encontrados:
  ⚠ 3 valores nulos em "email"
  ⚠ 1 linhas duplicadas
  ⚠ "data_nascimento" contém 5 formatos de data diferentes
  ⚠ "idade" está armazenada como texto mas parece numérica

Sugestões:
  1. Corrigir tipos → datatool clean clientes.csv --fix-types
  2. Remover duplicidades → datatool clean clientes.csv --remove-duplicates
  3. Normalizar datas → datatool clean clientes.csv --normalize-dates
  4. Tratar valores nulos → datatool clean clientes.csv --drop-null
```

Um arquivo de exemplo que dispara todos esses problemas está em [examples/clientes.csv](https://github.com/wvcode/cli-python/blob/main/examples/clientes.csv).

### `profile` — profiling estatístico de um dataset

```bash
datatool profile vendas.csv
datatool profile vendas.csv --key cpf
```

Para cada coluna, mostra contagem e percentual de nulos, e:

- **colunas numéricas**: min, max, média, mediana, desvio padrão, percentis (25/50/75) e outliers (método IQR)
- **colunas categóricas/texto**: cardinalidade e top 5 valores mais frequentes (com percentual)

Também reporta a quantidade de linhas totalmente duplicadas e, com `--key coluna1,coluna2`, a quantidade de duplicidades considerando apenas essas colunas como chave.

```text
Arquivo: clientes.csv
Linhas: 10
Colunas: 6
Linhas duplicadas: 1
Linhas duplicadas (chave: cpf): 1

Coluna "cpf" (numérica)
  Nulos: 0 (0.00%)
  Min: 11122233344.00  Max: 99900011122.00  Média: 52222222221.70  Mediana: 50011122232.50  Desvio padrão: 30544672345.70
  Percentis: p25=25011122232.75  p50=50011122232.50  p75=75011122149.75
  Outliers (IQR): 0

Coluna "cidade" (categórica)
  Nulos: 0 (0.00%)
  Cardinalidade: 9
  Top 5 valores:
    Rio de Janeiro: 2 (20.00%)
    São Paulo: 1 (10.00%)
    ...
```

Em datasets largos, `--columns` e `--max-columns` restringem o tamanho da saída:

```bash
datatool profile vendas.csv --columns preco,quantidade
datatool profile vendas.csv --max-columns 50
```

- `--columns col1,col2` perfila só essas colunas (a contagem de linhas duplicadas continua do dataset inteiro); coluna inexistente é erro claro, igual a `--key`
- `--max-columns N` perfila só as N primeiras colunas **na ordem do dataset** (mesmo que `--columns` peça outra ordem); o que ficou de fora aparece no fim do texto, ou em `columns_returned`/`columns_total`/`truncated_columns` no JSON
- Sem `--max-columns`, nada muda — a flag é só um limite opcional

### `clean` — detectar e corrigir problemas de qualidade

```bash
datatool clean clientes.csv
```

Sem nenhuma flag, é somente leitura/diagnóstico — **não grava nenhum arquivo**. Detecta, por coluna:

- valores inválidos (ex.: e-mail fora do formato)
- variação de formato (ex.: telefone em formatos diferentes)
- espaços extras nas bordas dos valores
- duplicidade por chave (coluna majoritariamente única com alguns valores repetidos, ex.: CPF)
- inconsistência de capitalização (ex.: "Porto Alegre" / "PORTO ALEGRE" / "porto alegre")

```text
Arquivo: clientes_sujos.csv
Linhas: 20
Colunas: 5

nome
  1 registros com espaços extras

email
  1 valores inválidos

telefone
  3 formatos diferentes

cpf
  1 valores duplicados

cidade
  "PORTO ALEGRE"
  "Porto Alegre"
  "porto alegre"
```

Um arquivo de exemplo que dispara todos esses problemas está em [examples/clientes_sujos.csv](https://github.com/wvcode/cli-python/blob/main/examples/clientes_sujos.csv).

Com pelo menos uma flag de operação, o comando passa a transformar os dados (só em colunas de texto — colunas numéricas, por exemplo, não são alteradas) e mostra o resultado: grava em `--output arquivo` (formato inferido pela extensão, igual ao `convert`) ou, se omitido, imprime o dataset inteiro em CSV no stdout, com o relatório das operações no stderr. Nada é gravado por padrão, e um `--output` que já existe só é substituído com `--overwrite`. Ao gravar, uma linha de confirmação vai para o stderr, depois do relatório: `Gravado clientes_limpo.csv (csv): 20 linhas, 5 colunas`.

Opções que só configuram uma operação exigem essa operação: `--key` precisa de `--remove-duplicates`, `--drop-null-columns` de `--drop-null`, `--document-columns` de `--normalize-documents`, `--date-columns` de `--normalize-dates` e `--decimal-separator` de `--fix-types`. Sem ela, o comando falha (exit code 2) em vez de ignorar a opção. Pelo mesmo motivo, `--output`/`--overwrite` sem nenhuma operação é erro: sem operação, o `clean` só faz o diagnóstico e não grava nada.

```bash
datatool clean clientes.csv --trim --normalize-case --output clientes_limpo.csv
datatool clean clientes.csv --lowercase --output clientes_limpo.csv
datatool clean clientes.csv --uppercase --output clientes_limpo.csv
```

- `--trim` remove espaços extras nas bordas
- `--lowercase` / `--uppercase` convertem a caixa de todo o texto da coluna
- `--normalize-case` unifica variações de capitalização em title case (`"PORTO ALEGRE"`/`"porto alegre"` → `"Porto Alegre"`)
- `--trim` combina com qualquer uma das outras e roda primeiro; `--lowercase`, `--uppercase` e `--normalize-case` são mutuamente exclusivas (passar duas é erro, exit code 2)

```text
nome,email,telefone,cpf,cidade
Pessoa 1,Pessoa1@Example.Com,(11) 91234-5678,10000000001,Porto Alegre
Pessoa 2,Pessoa2@Example.Com,11 91234-5678,10000000002,Porto Alegre
Pessoa 3,Invalido-Sem-Arroba,11912345678,10000000003,Porto Alegre
```

`--remove-duplicates` remove linhas duplicadas, mantendo a primeira ocorrência, e reporta quantas foram removidas:

```bash
datatool clean clientes.csv --remove-duplicates
datatool clean clientes.csv --remove-duplicates --key cpf --output clientes_limpo.csv
```

- Por padrão considera a linha inteira (todas as colunas iguais); `--key coluna1,coluna2` deduplica por um subconjunto de colunas
- A quantidade de linhas removidas é sempre impressa, mesmo gravando em arquivo via `--output`
- Combinável com as flags de texto acima — quando combinadas, `--trim`/`--lowercase`/`--uppercase`/`--normalize-case` rodam **antes** da remoção de duplicidades, então linhas que só diferiam por espaço ou capitalização também são deduplicadas

`--fill-null`/`--drop-null` tratam valores nulos e reportam quantas células/linhas foram afetadas:

```bash
datatool clean clientes.csv --fill-null "N/A"
datatool clean clientes.csv --fill-null "idade:0" --output clientes_limpo.csv
datatool clean clientes.csv --drop-null
datatool clean clientes.csv --drop-null --drop-null-columns email --output clientes_limpo.csv
```

- `--fill-null valor` (sem `:`) preenche nulos só nas colunas de texto — evita converter uma coluna numérica inteira para texto ao preencher com um valor não numérico
- `--fill-null coluna:valor` preenche só essa coluna, convertendo o valor para o tipo da coluna quando ela é numérica; um valor incompatível com o tipo (ex.: `idade:abc`) é erro, exit code 2, em vez de transformar a coluna em texto; é repetível (`--fill-null "N/A" --fill-null "idade:0"`)
- `--drop-null` remove linhas com nulos em qualquer coluna por padrão, ou só nas colunas de `--drop-null-columns coluna1,coluna2` (`--columns` continua aceito como nome antigo)

`--normalize-dates` converte datas em formatos variados para ISO 8601 (`yyyy-mm-dd`):

```bash
datatool clean clientes.csv --normalize-dates
datatool clean clientes.csv --normalize-dates --date-columns data_nascimento,data_cadastro --output clientes_limpo.csv
```

- Sem `--date-columns`, detecta automaticamente as colunas de texto em que a maioria dos valores (≥ 60% de uma amostra) parece data
- Formatos reconhecidos: `yyyy-mm-dd`, `yyyy/mm/dd`, `yyyy.mm.dd`, `yyyy-mm-ddThh:mm:ss` (a hora é descartada), `dd/mm/yyyy`, `dd-mm-yyyy`, `dd.mm.yyyy`, `dd/mm/yy`, `dd-mm-yy` e as variantes `mm/dd`
- Datas ambíguas como `01/02/1990` são lidas como `dd/mm`; a coluna só é tratada como `mm/dd` quando tem valores que só fazem sentido assim (ex.: `12/31/1990`) e nenhum que só faça sentido como `dd/mm`
- Anos com 2 dígitos seguem a regra do Python: `00`–`68` → 2000–2068, `69`–`99` → 1969–1999
- Valores não reconhecidos são mantidos como estão e listados na saída:

```text
"data_nascimento": 6 datas normalizadas
"data_nascimento": 1 valores não reconhecidos como data, mantidos sem alteração:
  "20261301"
```

`--fix-types` converte para número as colunas numéricas armazenadas como texto, inclusive valores monetários em formato brasileiro:

```bash
datatool clean relatorio.csv --fix-types --output relatorio_limpo.parquet
datatool clean relatorio.csv --fix-types --decimal-separator , --output relatorio_limpo.parquet
```

- Detecta automaticamente as colunas de texto em que ≥ 90% de uma amostra dos valores é numérica
- Remove `R$` e espaços antes de converter
- `--decimal-separator ,` ou `--decimal-separator .` define o separador decimal de todas as colunas; o de milhar é o outro caractere. Com `,`: `1.234,56` → `1234.56` e `1.500` → `1500`. Com `.`: `1,234.56` → `1234.56` e `1.500` → `1.5`
- Sem `--decimal-separator`, decide por coluna: se algum valor tem vírgula ou `R$`, usa `,` como decimal; senão, `.`. Nesse modo, uma coluna só com valores como `1.500` é lida como `1.5` — informe `--decimal-separator ,` para ler como `1500`
- A coluna vira `int` quando nenhum valor tem parte decimal, senão `float`
- Colunas com zeros à esquerda (`01001000`, típico de CEP/CPF) são ignoradas, porque a conversão perderia os zeros
- Valores que não puderam ser convertidos viram nulo e são listados na saída, sem interromper as demais colunas:

```text
"valor": convertida para float, 1 valores não convertidos (viraram nulo):
  "a combinar"
"idade": convertida para int, 1 valores não convertidos (viraram nulo):
  "N/D"
```

`--rename-columns`/`--remove-columns` ajustam o schema:

```bash
datatool clean vendas.csv --rename-columns old_name:new_name,foo:bar --output vendas_limpo.csv
datatool clean vendas.csv --remove-columns coluna_interna,coluna_temp --output vendas_limpo.csv
```

- Coluna inexistente, entrada sem `:` em `--rename-columns` ou renomeação que geraria nomes repetidos são erro claro, sem gravar nada
- São aplicadas **antes** das demais operações (primeiro remove, depois renomeia): `--key`, `--drop-null-columns`, `--date-columns` e `--fill-null coluna:valor` usam os nomes já renomeados, e colunas removidas não entram na deduplicação

`--normalize-documents` valida e padroniza CPF/CNPJ, inclusive o CNPJ alfanumérico (letras nas 12 primeiras posições, emitido pela Receita desde julho de 2026):

```bash
datatool clean clientes.csv                                    # diagnóstico passa a incluir CPF/CNPJ
datatool clean clientes.csv --normalize-documents digits       # 123.456.789-09 → 12345678909
datatool clean clientes.csv --normalize-documents masked        # 12345678909 → 123.456.789-09
datatool clean clientes.csv --normalize-documents masked --document-columns cpf,cnpj_empresa
```

- Detecta colunas de texto ou numéricas que "parecem" CPF/CNPJ (formato batendo numa amostra e dígito verificador majoritariamente válido, máscara presente, ou `cpf`/`cnpj`/`documento` no nome da coluna) — sem `--document-columns`, aplica a todas elas
- Diagnóstico (`clean` sem flags e `info`) reporta, por coluna: dígito verificador inválido, todos os dígitos iguais (ex.: `111.111.111-11`), valores fora do formato, mistura de máscara/sem máscara e, em coluna numérica, zeros à esquerda perdidos na leitura
- `digits` remove a máscara; `masked` aplica `XXX.XXX.XXX-XX` (CPF) ou `XX.XXX.XXX/XXXX-XX` (CNPJ), com letras sempre maiúsculas
- Coluna numérica (CPF lido como `Int64` perde zeros à esquerda) é completada com `0` e sempre vira texto
- Normalizar não valida: documentos com dígito inválido são formatados do mesmo jeito, e o comando informa quantos continuam inválidos; só valores fora do formato (nem CPF nem CNPJ) ficam como estão
- Nunca mostra o CPF/CNPJ no terminal fora do próprio dataset — o diagnóstico e o log só trazem contagens

```text
"cpf": 1.180 documentos normalizados
"cpf": 23 com dígito verificador inválido (formatados, mas continuam inválidos)
"cpf": 2 valores fora do formato de CPF/CNPJ, mantidos sem alteração
```

### CSV com `;` ou em `cp1252` (Excel em português)

`convert`, `info`, `profile` e `clean` detectam sozinhos o delimitador (`,`, `;`, tab ou `|`) e o encoding (UTF-8, com ou sem BOM, ou `cp1252`) do CSV de entrada. Um CSV exportado pelo Excel em português funciona sem nenhuma opção. Para forçar:

```bash
datatool info vendas.csv --sep ";" --encoding latin-1
datatool convert vendas.csv vendas.parquet --sep "\t"
```

- `--sep` aceita um caractere (ou `\t` para tab); `--encoding` aceita qualquer codec do Python (`cp1252`, `latin-1`, `utf-16`, ...)
- Valem só para o arquivo de entrada e só para CSV; a saída CSV continua com `,` e UTF-8
- Números no formato brasileiro (`1.234,56`) chegam como texto: use `datatool clean ... --fix-types --decimal-separator ,`

### Log de execução

Toda execução registra em `datatool.log`, no diretório de logs do usuário (`~/Library/Logs/datatool` no macOS, `~/.local/state/datatool/log` no Linux, `%LOCALAPPDATA%\datatool\Logs` no Windows), o que foi feito: comando e opções, delimitador/encoding usados, arquivos lidos e gravados, operações do `clean` com as contagens, erros, exit code e duração. O terminal não muda.

```text
2026-09-25 15:40:46,811 INFO    [41227a] clean: início — args: filename=vendas.csv, fix_types=True, decimal_separator=,, output=limpo.csv, output_format=text
2026-09-25 15:40:46,811 INFO    [41227a] clean: encoding detectado: cp1252 (arquivo não é UTF-8 válido)
2026-09-25 15:40:46,812 INFO    [41227a] clean: delimitador detectado: ';'
2026-09-25 15:40:46,814 INFO    [41227a] clean: --fix-types: "valor" convertida para float
2026-09-25 15:40:46,815 INFO    [41227a] clean: gravado limpo.csv (csv) — 2 linhas, 2 colunas
2026-09-25 15:40:46,815 INFO    [41227a] clean: fim — exit code 0, 0,00 s
```

- O log nunca contém valores das células (só caminhos, nomes de coluna, contagens e opções)
- Rotação a cada 5 MB, mantendo até 3 arquivos antigos (`datatool.log.1` a `.3`)
- Se o log não puder ser gravado (ex.: diretório somente leitura), o comando roda normalmente, sem log
- `DATATOOL_LOG_DIR=caminho` grava em outro diretório (relativo ao diretório atual, se não for absoluto; ex.: `DATATOOL_LOG_DIR=logs` volta ao comportamento antigo, em `./logs`)
- `DATATOOL_NO_LOG=1` desliga o log

### Saída em JSON (`--format json`)

`info`, `profile`, `clean` e `convert` aceitam `--format json` (o padrão é `--format text`, a saída descrita acima). O stdout passa a ser um único documento JSON, para uso em scripts, notebooks e CI:

```bash
datatool info clientes.csv --format json | jq '.problems[] | select(.category == "nulls")'
datatool profile clientes.csv --format json > perfil.json
datatool clean clientes.csv --fix-types --remove-duplicates --output limpo.parquet --format json
```

```json
{
  "schema_version": 1,
  "command": "info",
  "status": "ok",
  "file": {"path": "examples/clientes.csv", "format": "csv", "rows": 10, "columns": 6, "size_bytes": 711},
  "problems": [
    {"category": "nulls", "column": "email", "count": 3, "message": "3 valores nulos em \"email\""}
  ],
  "suggestions": [
    {"category": "nulls", "label": "Tratar valores nulos", "command": "datatool clean examples/clientes.csv --drop-null"}
  ]
}
```

- Cada problema traz `count` e `count_unit` (`rows`, `values`, `formats` ou `variants`), que diz o que está sendo contado
- Todo documento traz `schema_version`, `command` e `status`; `info` traz `problems`/`suggestions`, `profile` traz `duplicates`/`columns`, o `clean` traz `problems` (diagnóstico) ou `operations`/`output` (com operações), e o `convert` traz `source`/`target`
- Números sem formatação pt-BR nem arredondamento; `NaN` vira `null`
- Erros também saem em JSON (`"status": "error"`), com o mesmo exit code do modo texto
- No `clean` com operações, `--output` é obrigatório, e no `convert`, o `to_filename`: o JSON é só o relatório, os dados vão para o arquivo
- O formato completo, campo a campo, está em [specs/019-saida-json.md](https://github.com/wvcode/cli-python/blob/main/specs/019-saida-json.md)

### Ocultar valores de célula (`--redact-values`)

`info`, `profile` e `clean` aceitam `--redact-values` (padrão desligado), nos dois formatos, para tirar valores de célula do relatório sem perder as contagens — útil ao colar a saída num chat de IA, ticket de suporte ou log de CI:

```bash
datatool profile clientes.csv --redact-values
datatool clean clientes.csv --redact-values
```

- Em `profile`, os valores mais frequentes viram `<valor 1>`, `<valor 2>`, ...; `count`/`percent` continuam reais
- No diagnóstico, a inconsistência de capitalização passa a mostrar só a contagem (`"3 variações de capitalização"`), em vez de listar as variantes
- Nas operações do `clean` (`--normalize-dates`, `--fix-types`, `--normalize-documents`), os valores não reconhecidos somem da lista — fica só a contagem e `(valores ocultos por --redact-values)`
- Nunca muda o arquivo gravado por `--output`, só o que é impresso ou volta em `--format json`

### Servidor MCP (`datatool-mcp`)

Expõe `info`, `profile`, `clean` (diagnóstico e operação) e `convert` como ferramentas [MCP](https://modelcontextprotocol.io/) para um agente de IA (Claude Code, Claude Desktop, Cursor, ...) chamar diretamente, em vez de escrever código de análise do zero a cada conversa. Requer `pip install "datatool-cli[mcp]"` (ver [Instalação](#instalação)) — quem só usa o CLI não precisa dessa dependência.

```json
{
  "mcpServers": {
    "datatool": {
      "command": "datatool-mcp",
      "args": ["--root", "/caminho/do/projeto"]
    }
  }
}
```

- Ferramentas: `datatool_info`, `datatool_profile` (+ `columns`/`max_columns`, padrão 50), `datatool_clean_diagnose`, `datatool_clean_apply` (grava em `output`) e `datatool_convert` (grava em `to_filename`) — cada uma devolve o mesmo JSON de [`--format json`](#saída-em-json---format-json)
- Todo caminho de arquivo fica restrito ao diretório passado em `--root`; `output`/`to_filename` nunca pode ser o mesmo arquivo da entrada, e uma saída já existente exige `overwrite: true`
- `redact_values` vem **ligado por padrão** nas ferramentas (diferente do CLI, onde vem desligado): os valores de célula não trafegam para o modelo de IA a menos que a pessoa configure a ferramenta com `redact_values: false`
- Cada chamada é registrada em `<root>/logs/datatool.log` (ou onde `DATATOOL_LOG_DIR` apontar), com o nome prefixado (`mcp info`, `mcp clean`, ...)
- Só ferramentas Community por enquanto — o detalhamento completo está em [specs/020-mcp-server.md](https://github.com/wvcode/cli-python/blob/main/specs/020-mcp-server.md)

## Desenvolvimento

Instalar a partir do código-fonte, em modo editável (alterações em `src/datatool/` valem sem reinstalar), com as ferramentas de teste, lint e checagem de tipos, que já incluem o servidor MCP:

```bash
git clone https://github.com/wvcode/cli-python.git
cd cli-python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

Para apenas rodar via módulo, sem instalar: `PYTHONPATH=src python -m datatool.main <comando> ...`

Rodar os testes (com cobertura; o CI exige pelo menos 95%):

```bash
pytest --cov
```

Rodar o lint, o formatador e a checagem de tipos:

```bash
ruff check src/ tests/
ruff format --check src/ tests/
pyright
```

- Os testes ficam em `tests/`, um arquivo por comando/assunto; `conftest.py` e `helpers.py` têm o que é compartilhado
- Durante os testes, o log vai para `./logs` do diretório temporário de cada teste (`DATATOOL_LOG_DIR`, no `conftest.py`)
- As versões mínimas declaradas em `pyproject.toml` são verificadas no CI (job `test-minimum`, com `uv pip install --resolution lowest-direct`)
- A versão vem da tag do git (`setuptools-scm`): publicar uma release com a tag `v0.2.0` gera o pacote `0.2.0`

## Estrutura do projeto

```text
src/datatool/   código-fonte do CLI (comandos, leitura/gravação, heurísticas)
tests/          suíte de testes (pytest)
specs/          uma spec por user story, com critérios de aceite e status
examples/       arquivos de exemplo para testar os comandos manualmente
```
