diff --git a/README.md b/README.md index 436b267..ae003bd 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,41 @@ ## Install +Install the binary in `~/.local/bin` (it must be on your `PATH`). The commands below use `v0.3.0`, which exists only once that release is published; until then, use the latest tag on [GitHub Releases](https://github.com/BasisTI/taiga-cli/releases). + +Download the tarball for your platform together with `SHA256SUMS`; the binary is installed only if its checksum matches. The block runs in a subshell with `set -eu`, so any failing step (download, a missing or failed checksum, extraction) stops it before anything is installed, and your shell stays open. On macOS, where `sha256sum` may be missing, it uses `shasum -a 256`: + +```sh +( + set -eu + V=0.3.0 + P=linux_amd64 # linux_arm64, darwin_amd64 or darwin_arm64 + F="taiga_${V}_${P}.tar.gz" + cd "$(mktemp -d)" + gh release download "v$V" -R BasisTI/taiga-cli -p "$F" -p SHA256SUMS + awk -v f="$F" '$2 == f' SHA256SUMS > checksum + test -s checksum || { echo "$F is not in SHA256SUMS" >&2; exit 1; } + if command -v sha256sum >/dev/null; then sha256sum -c checksum; else shasum -a 256 -c checksum; fi + tar -xzf "$F" taiga + mkdir -p "$HOME/.local/bin" + install -m 0755 taiga "$HOME/.local/bin/taiga" + "$HOME/.local/bin/taiga" version +) +``` + +Or, with Go: + ```sh -go install github.com/BasisTI/taiga-cli/cmd/taiga@latest +GOBIN="$HOME/.local/bin" go install github.com/BasisTI/taiga-cli/cmd/taiga@v0.3.0 ``` -Or download the tarball for your platform from [GitHub Releases](https://github.com/BasisTI/taiga-cli/releases), together with `SHA256SUMS`, and check it: +### Agent skill + +[`skills/taiga-cli/SKILL.md`](skills/taiga-cli/SKILL.md) teaches coding agents to use the CLI (in Portuguese): which command to use, how to stop and ask a person on authentication errors, and what to check after an uncertain write instead of repeating it. Install it with the [skills CLI](https://github.com/vercel-labs/skills): ```sh -sha256sum -c SHA256SUMS --ignore-missing -tar -xzf taiga_*_linux_amd64.tar.gz taiga && sudo install taiga /usr/local/bin/ +npx skills add BasisTI/taiga-cli --global # for every project +npx skills update --global --yes # the installed copy never updates itself ``` ## Quickstart @@ -283,6 +309,8 @@ The last check, `project`, reads the selected project (it only reads) and report ## Coding agents and sandboxes +The [agent skill](#agent-skill) carries these rules for agents. + - A person runs `taiga auth login` once per machine; agents only read the session cache and never see the password. - An agent that gets `session_expired` inside a sandbox cannot renew the session there: run `taiga auth refresh` outside the sandbox and retry. `taiga` never spends the stored refresh token when it cannot save the new one, and with a read-only cache it logs in only with `TAIGA_PASSWORD`/`TAIGA_PASSWORD_FILE` (keeping that token in memory), never with the keyring, `secret_command` or the `--insecure-storage` file, whether or not a session exists. Without a session and without an env password it fails with `session_cache_readonly`: run `taiga auth login` outside the sandbox. - Codex: enable `network_access = true` and add the state dir to `writable_roots`: diff --git a/docs/examples/taiga-project.toml b/docs/examples/taiga-project.toml index 2c39564..190afed 100644 --- a/docs/examples/taiga-project.toml +++ b/docs/examples/taiga-project.toml @@ -1,8 +1,10 @@ -# Exemplo de configuração de projeto para `taiga project plan|apply -f`. +# Configuração de projeto para `taiga project plan|apply -f`. # -# Reproduz o que o configurar-taiga-projeto.sh da skill basis-ci-gitlab prepara: os status -# opcionais do fluxo da story e os campos de registro de início e de teste. Nomes, cores e -# descrições são exemplo, não valores embutidos na CLI: confirme a convenção do seu projeto. +# Este arquivo é a convenção canônica do fluxo da skill basis-ci-gitlab: os status +# `In revision` e `Waiting for deployment` e os 6 campos de registro de início e de teste. +# Substitui o configurar-taiga-projeto.sh; as diferenças de comportamento em relação ao +# script foram aprovadas na US #254 (decisão D5). Mude nomes, cores ou descrições aqui, e +# não num board isolado, para o fluxo continuar igual em todos os projetos. # # - Nada é removido nem alterado: status e campos fora do arquivo ficam como estão, e um # declarado que já existe com outra cor, outro `closed`, outro tipo ou outra descrição é diff --git a/docs/guia.md b/docs/guia.md index d49879c..c226200 100644 --- a/docs/guia.md +++ b/docs/guia.md @@ -2,6 +2,19 @@ Guia curto para a equipe. A referência completa está no [README](../README.md) (em inglês) e os códigos de erro em [errors.md](errors.md). +## Instalação + +O binário vai em `~/.local/bin`, como o `sgo`. O caminho principal é o tar.gz da [release](https://github.com/BasisTI/taiga-cli/releases), conferido pelo `SHA256SUMS` antes de instalar; a alternativa é `GOBIN="$HOME/.local/bin" go install github.com/BasisTI/taiga-cli/cmd/taiga@v0.3.0`. Os comandos estão na seção "Install" do README. Os exemplos com `v0.3.0` só funcionam depois de publicada essa release. + +Agentes usam a CLI pela skill [`taiga-cli`](../skills/taiga-cli/SKILL.md), que mora neste repositório e é instalada pelo skills CLI: + +```sh +npx skills add BasisTI/taiga-cli --global # para todos os projetos +npx skills update --global --yes # a cópia instalada não se atualiza sozinha +``` + +A skill diz ao agente qual comando usar, que ele para e pede o login a uma pessoa nos erros de autenticação, e o que conferir depois de uma escrita incerta em vez de repeti-la. Quando a CLI e o MCP do Taiga estão disponíveis, a CLI tem precedência; não é aconselhado manter os dois. + ## Login com a conta de serviço Em cada máquina onde agentes vão usar o Taiga, uma pessoa faz o login uma vez com a conta de serviço: @@ -172,14 +185,14 @@ taiga story field set 246 --unset "Testado em staging" --unset "Data de entrega" - `--unset NOME` (repetível, nome ou id) limpa um campo `checkbox` ou `date`: o Taiga guarda `null` na chave do campo, que continua no dicionário mesmo quando é o último (o Taiga recusa dicionário vazio). Campo sem valor, ou já `null`, não gera escrita. Campo `text` não se limpa (erro de uso); para esvaziar, `"Notas="`. No JSON o valor limpo aparece como `null`; no texto, vazio, como campo sem valor. - `story field set` junta os campos informados aos atuais e grava o dicionário inteiro com o `version` do recurso de valores, que é outro, independente do `version` da story. `story get` mostra os dois: o da story no topo e o dos valores em `custom_attributes`. - O Taiga não recusa `version` antigo nos valores de campos customizados. A CLI confere a resposta: se outra gravação caiu junto da nossa, sai `field_values_postcondition_failed` (exit 4) e a alteração **foi gravada**; confira com `taiga story field list` antes de repetir. -- Campos de task: as definições já têm `field list/create --kind task`; o comando de valores de task vem com a #253. +- Campos de task: as definições têm `field list/create --kind task`, e os valores, `task field list/set`. ## Status e campos do projeto como código Substitui o `configurar-taiga-projeto.sh` da skill basis-ci-gitlab. O arquivo TOML declara status de story e campos -customizados de story; [docs/examples/taiga-project.toml](examples/taiga-project.toml) traz o equivalente ao script -(`In revision`, `Waiting for deployment` e os seis campos de registro). Nomes, cores e descrições do exemplo são -exemplo, não configuração embutida na CLI. +customizados de story. [docs/examples/taiga-project.toml](examples/taiga-project.toml) é a convenção canônica do fluxo +da `basis-ci-gitlab` (`In revision`, `Waiting for deployment` e os seis campos de registro); as diferenças de +comportamento em relação ao script foram aprovadas na US #254. A CLI não embute esses valores: eles vêm do arquivo. ```sh taiga status list --output text @@ -215,7 +228,7 @@ taiga project apply -f taiga-project.toml # exige admin do projeto ## `taiga api` para o que ainda não tem comando próprio -Enquanto os comandos curados das próximas fases não chegam, `taiga api` cobre as receitas que hoje estão na skill `basis-ci-gitlab`. O caminho é relativo a `/api/v1/`, e `--auto-version` lê o `version` atual antes de gravar. +`taiga api` é a saída de emergência para o que não tem comando curado; bloqueio e campos customizados, por exemplo, já têm (`story update --block`, `story field set`). O caminho é relativo a `/api/v1/`, e `--auto-version` lê o `version` atual antes de gravar. O `taiga api` não confere a escrita: um exit 7 depois do envio pode ter gravado, então confira com um `GET` antes de repetir. ```sh # bloquear com motivo diff --git a/docs/superpowers/plans/2026-10-04-fase-4-skill-e-migracao.md b/docs/superpowers/plans/2026-10-04-fase-4-skill-e-migracao.md new file mode 100644 index 0000000..199a6bf --- /dev/null +++ b/docs/superpowers/plans/2026-10-04-fase-4-skill-e-migracao.md @@ -0,0 +1,157 @@ +# taiga-cli — Fase 4 (skill da CLI e migração da basis-ci-gitlab) — plano + +> **For agentic workers:** cada PR abaixo é executado por um agente a partir de um pacote do coordenador (Assistente Global), que define escopo, autorizações e devolução. Este documento registra o plano aprovado e as decisões; não autoriza push, PR, merge nem mudança de status no Taiga. + +**Goal:** Fechar a US #254 ("Skill da taiga-cli e migração da basis-ci-gitlab"): publicar uma skill que ensina agentes a usar a CLI, lançar a `v0.3.0` e trocar o MCP do Taiga e os scripts `curl` da skill `basis-ci-gitlab` pela CLI. + +**Spec:** `docs/superpowers/specs/2026-09-30-taiga-cli-design.md` ("MVP fecha em `v0.x`"). Base: `docs/superpowers/plans/2026-10-01-fase-3-anexos-swimlanes-paridade.md`, seção "Fase 4 (#254) — registro de escopo". + +**Fontes lidas no planejamento (2026-10-04, só leitura):** os repositórios `BasisTI/taiga-cli` e `BasisTI/skills`; o projeto 37 (`infra-2025`) pelo MCP (`taiga_projects_get`); um build local da CLI. O `project plan` não rodou por falta de config e login na máquina do planejamento. + +## Situação + +| PR | US | Estado | +|---|---|---| +| PR 1 | #273, redigir tokens na saída do completion | mesclado: #17, `369bf02` | +| PR 2 | #274, redirect em escritas como resultado incerto | mesclado: #16, `557596d` | +| PR 3 | #254 parte A, skill no taiga-cli | este PR (branch `TG-254`) | +| — | release `v0.3.0` | pendente (Cedric) | +| PR 4 | #254 parte B, `BasisTI/skills` | pendente, depois da `v0.3.0` | + +## Achados que mudaram o plano + +1. **O board 37 não está configurado.** Os status de US são New, Ready, In progress, Ready for test, Done e Archived: faltam `In revision` e `Waiting for deployment`. O único campo customizado de US é `Ocorrência SGO`; nenhum dos 6 campos de registro existe. Antes de "`project plan` sem diferenças" valer alguma coisa, é preciso um `taiga project apply` no 37, rodado pelo Cedric, que é admin (`admin_project_values`). Será o primeiro apply real contra produção. +2. **O projeto 37 está com o módulo de épicos desligado** (`is_epics_activated: false`), embora o épico #242 seja citado. Não afeta a #254; fica para o coordenador conferir. +3. **O state dir padrão é `~/.local/state/taiga`**, não `~/.local/share`. O `~/.codex/config.toml` da máquina do Cedric tem `network_access = true`, mas não tem `writable_roots` para o state dir. +4. **O `go install …@latest` só serve depois da `v0.3.0`.** No planejamento ele resolvia para a `v0.2.0`, sem a fase 3. A release usa goreleaser: tar.gz linux/darwin amd64/arm64 e `SHA256SUMS`. + +## Global Constraints + +- Docs e skill em PT-BR; README, comandos, flags e mensagens em inglês. +- A skill só cita comando, flag e código que existem no `main`, conferidos contra `docs/errors.md` e o `--help` de um binário compilado da branch. +- Nunca escrever em `agile.basis.com.br` a partir dos PRs; `auth login` e `project apply` no board 37 são passos humanos. +- Desligar o MCP do Taiga nas configs dos agentes fica fora da #254. + +## PR 1 — #273 (branch `TG-273`) + +Redigir tokens na saída do completion. +**Aceite:** nenhum token ou segredo na saída do completion, com teste que prove; CI `ci` verde. + +## PR 2 — #274 (branch `TG-274`) + +Tratar redirect em escritas como resultado incerto. +**Aceite:** uma escrita que recebe 3xx sai com código "gravado, ou talvez gravado: não repetir às cegas", documentado em `docs/errors.md`, com teste; CI verde. + +## PR 3 — #254 parte A (branch `TG-254`) + +Arquivos: + +- `skills/taiga-cli/SKILL.md` (PT-BR), novo, e `skills/taiga-cli/references/instalacao.md`: + - instalação: binário em `~/.local/bin`; principal, o tar.gz da release conferido pelo `SHA256SUMS`; alternativa, `go install github.com/BasisTI/taiga-cli/cmd/taiga@v0.3.0` (D7, D12); skill por `npx skills add BasisTI/taiga-cli`; + - autenticação: a pessoa faz `taiga auth login`, o agente lê o cache; Codex com o state dir em `writable_roots` e rede (D9); `auth_rejected`, `session_expired` e `session_cache_readonly` = parar e pedir à pessoa login ou refresh fora do sandbox (D8); + - keyring headless: resumo com link para o README (decisão de 2026-09-29); + - uso por agentes: comandos curados (story, task, comentário, anexo, status, swimlane, épico, `field`, `project plan|apply`, `auth status --diagnose`), `taiga api` como saída de emergência, decisão pelo `code` e não pelo exit, `--dry-run` antes de escrita em lote; + - os códigos "gravado, ou talvez gravado: não repetir às cegas", derivados de `docs/errors.md`, cada um com a conferência; as exceções que convergem (`epic_link_unconfirmed`, `epic_replace_incomplete`); + - precedência sobre o MCP e o aviso de que não é aconselhado ter os dois (D6); + - completion: o script gerado não grava o debug do shell (#17). +- `docs/examples/taiga-project.toml`: cabeçalho conforme a linha 9 da tabela D5; dados inalterados. +- `README.md` e `docs/guia.md`: apontar para a skill e para a instalação em `~/.local/bin`; a receita do keyring continua no README. +- Este plano. + +**Aceite:** todo comando e código citado na skill existe no `main`; a instalação por `npx skills add BasisTI/taiga-cli` é conferida pelo Cedric depois do merge (D14); links internos resolvem; CI verde. + +## Passo humano 1 — release `v0.3.0` (Cedric) + +Depois dos PRs 1 a 3 mesclados: tag `v0.3.0` no `main`; o workflow `release.yml` publica os tar.gz e o `SHA256SUMS`. +**Aceite:** release `v0.3.0` com 4 tar.gz e `SHA256SUMS`; `go install …@latest` resolve para `v0.3.0`. + +## Passo humano 2 — instalação e board (Cedric) + +1. Instalar o `taiga` `v0.3.0` em `~/.local/bin` e a skill pelo skills CLI (global). +2. `taiga auth login --url https://agile.basis.com.br`. +3. Acrescentar `writable_roots = ["/home/cedric/.local/state/taiga"]` ao `[sandbox_workspace_write]` do `~/.codex/config.toml`. +4. `taiga project plan -f docs/examples/taiga-project.toml --project 37`, revisar, depois `taiga project apply` no board 37 (cria 2 status e 6 campos). + +**Aceite:** `taiga auth status --diagnose` ok, inclusive dentro do Codex; `project plan` sem ações nem drift no 37. + +## PR 4 — #254 parte B, `BasisTI/skills` (branch `TG-254`) + +Arquivos em `skills/basis-ci-gitlab/`: + +- remover `references/taiga-mcp.md`, `scripts/taiga-env.sh` e `scripts/configurar-taiga-projeto.sh`; +- criar `references/taiga.md` enxuto (D10): receitas do fluxo com comandos `taiga`, mantendo as âncoras `#atualizar-o-status`, `#a-story-pode-começar`, `#retomar-story-bloqueada`, `#mudança-de-configuração-tag-config`, `#revisão-global-tag-review` e as demais citadas; preparar o board passa a ser `taiga project apply` com o TOML canônico do taiga-cli; +- `SKILL.md`: trocar a seção "## 2. O Taiga pelo MCP" por "O Taiga pela CLI", que exige a skill `taiga-cli` e a CLI instaladas e dá precedência à CLI sobre o MCP; tirar os dois scripts da tabela; trocar o link de `taiga-mcp.md` por `taiga.md`; +- `references/ciclo-da-mr.md`, `revisao-da-mr.md`, `worktree.md` e `cadeia-de-entrega.md`: trocar `taiga-mcp.md#…` por `taiga.md#…`. + +**Aceite:** `grep -rn "taiga-mcp\|taiga-env\|configurar-taiga-projeto\|mcp__taiga\|curl" skills/basis-ci-gitlab` sem ocorrências ligadas ao Taiga; todos os links e âncoras resolvem; nenhuma instrução da skill pede curl/jq para o Taiga. + +## Prova de aceite da #254 + +Depois do PR 4 mesclado e do `npx skills update --global --yes`, um agente (Codex no sandbox, D13), seguindo a `basis-ci-gitlab` migrada e a skill `taiga-cli`, numa US de teste no board 37: + +1. cria a US; +2. comenta; +3. anexa um arquivo; +4. muda o status (por exemplo New → In progress → In revision) e grava os campos de registro de início (`Início da implementação`, `Executor`, `Worktree`); +5. não usa curl, jq nem o MCP do Taiga; +6. o Cedric confere na interface e apaga a US. + +Também: `taiga project plan` sem diferenças no 37. + +## Dependências + +- PR 3 depende de #273 e #274 mesclados (D4); a skill cita os códigos novos da #274. +- `v0.3.0` depende dos PRs 1 a 3. +- PR 4 depende da `v0.3.0`, da skill instalável a partir do `main` do `BasisTI/taiga-cli` e do passo humano 2 (D2). +- A prova depende do PR 4 mesclado e do `npx skills update --global --yes`: a cópia instalada não se atualiza sozinha. + +## Decisões + +### Decisões tomadas (com origem) + +Origem de D1–D14: Cedric, conversa de planejamento de 2026-10-04, encaminhada pelo Assistente Global. + +| # | Decisão | +|---|---| +| D1 | A skill mora no repositório taiga-cli (`skills/taiga-cli/SKILL.md`), versionada com o binário e instalável pelo skills CLI a partir de `BasisTI/taiga-cli`. | +| D2 | Ordem: primeiro o PR no taiga-cli, depois o PR no `BasisTI/skills`, este só após a CLI e a skill instaladas. | +| D3 | Release **`v0.3.0`**, normal (não RC), antes da migração. A tag é criada pelo Cedric. Descartadas `v1.0.0-rc.1` e a mudança para 1.0; o spec ("MVP fecha em `v0.x`") segue valendo. | +| D4 | A #273 e a #274 são implementadas **antes** da skill, e portanto antes da `v0.3.0`. | +| D5 | Tabela de diferenças TOML × script **aprovada inteira** (abaixo), inclusive a mudança do comentário do TOML. Cumpre a exigência da decisão de 2026-10-01. | +| D6 | O que é do MCP antigo é **removido** da `basis-ci-gitlab`. Se o MCP e a CLI estiverem instalados, **a CLI tem precedência**; a skill diz que não é aconselhado ter os dois. Desligar o MCP das configs continua fora da #254. | +| D7 | O binário é instalado em `~/.local/bin`, como o `sgo`. | +| D8 | Em erro de autenticação, o agente para e pede ao usuário que refaça o login (`taiga auth login`, ou `taiga auth refresh` quando é `session_expired`) fora do sandbox. O agente nunca pede senha nem usa `TAIGA_PASSWORD`. | +| D9 | `TAIGA_STATE_DIR` no Codex: **opção a**. Fica o padrão `~/.local/state/taiga`, incluído em `[sandbox_workspace_write] writable_roots`, com `network_access = true`. Descartados o cache somente leitura como modo normal e o state dir em `/tmp` ou no workspace. | +| D10 | A `basis-ci-gitlab` mantém um `references/taiga.md` enxuto com as receitas do fluxo (status, início, bloqueio, tags config/review), agora com comandos `taiga`, **preservando as âncoras** usadas por `ciclo-da-mr.md`, `revisao-da-mr.md`, `worktree.md` e `cadeia-de-entrega.md`. O uso geral da CLI fica na skill taiga-cli. | +| D11 | Prova de aceite: um agente cria, comenta, anexa e muda o status de uma US de teste no Taiga; o Cedric apaga a US pela interface no fim. | +| D12 | Instalação na skill: o tar.gz da release, conferido pelo `SHA256SUMS`, é o caminho principal; o `go install …@v0.3.0` fica como alternativa. | +| D13 | A prova é feita pelo Codex dentro do sandbox, para validar a D9. | +| D14 | A instalação da skill a partir do `BasisTI/taiga-cli` é testada pelo Cedric com `npx skills add` depois do merge do PR 3. A estrutura `skills//SKILL.md` é a mesma do `BasisTI/skills`; o teste faz parte do passo humano 2. | + +Decisões tomadas durante a execução dos PRs 1 e 2 (origem: Cedric, 2026-10-04, via Assistente Global): + +| PR | Decisão | +|---|---| +| #16 (#274) | **Opção B estendida a comentário e anexo.** `story comment`, `task comment` e `attachment upload` com resposta inconclusiva (rede depois de aberta a conexão, timeout, 5xx ou redirect 3xx) saem **sempre** com `comment_unconfirmed` ou `attachment_unconfirmed` (exit 1), nomeando as candidatas, e nunca com sucesso, mesmo que o item apareça na releitura. Muda o contrato anterior ("achou = sucesso") e passa a ser visível para quem automatiza. | +| #17 (#273) | **Completion: sobrescrever o escritor de debug em vez de filtrar.** Os scripts gerados por `taiga completion` redefinem o `__taiga_debug` do Cobra para não fazer nada, em vez de filtrar a linha digitada; o `BASH_COMP_DEBUG_FILE` recebe só o log do binário, já redigido. | + +### Tabela aprovada (D5): `docs/examples/taiga-project.toml` × `configurar-taiga-projeto.sh` + +Os dados são idênticos: 2 status (mesmos nomes e cores, `closed=false`) e 6 campos (mesmos nomes, tipos e descrições). + +| # | Ponto | Script | CLI/TOML | Decisão | +|---|---|---|---|---| +| 1 | Âncora do `after` | slug (`in-progress`, `ready-for-test`) | nome (`In progress`, `Ready for test`) | aceitar | +| 2 | Comparação de nome | ignora maiúsculas | diferencia; nome igual só na caixa é drift | aceitar | +| 3 | Item existente com outros valores | mantém e segue | drift: o `plan` mostra, o `apply` recusa (exit 2) | aceitar | +| 4 | Âncora ausente | exit 4 | erro de uso, exit 2 | aceitar | +| 5 | Sem admin | exit 3 | `forbidden`, exit 6, também no `--dry-run` | aceitar | +| 6 | `is_archived` | `false` explícito | omitido (padrão do Taiga, `false`) | aceitar | +| 7 | Reordenação | sem conferência | relê antes e depois (`project_changed`, `status_order_postcondition_failed`) | aceitar | +| 8 | Credencial | `TAIGA_TOKEN` ou usuário/senha no env | sessão do `taiga auth login` | aceitar | +| 9 | Comentário do TOML | — | "Nomes, cores e descrições são exemplo…" | **mudar**: declarar que é a convenção canônica do fluxo da `basis-ci-gitlab` | + +### Propostas ainda pendentes + +- **Status da US de teste na prova.** A sequência exata (só In progress, ou passar também por In revision) fica com o executor; o mínimo é uma troca de status e os 3 campos de início. +- **Épicos desligados no projeto 37** (achado 2): conferir se o #242 é de fato épico do 37. Não foi discutido com o Cedric. diff --git a/skills/taiga-cli/SKILL.md b/skills/taiga-cli/SKILL.md new file mode 100644 index 0000000..fcb5fcd --- /dev/null +++ b/skills/taiga-cli/SKILL.md @@ -0,0 +1,181 @@ +--- +name: taiga-cli +description: >- + Ler e alterar o Taiga pela CLI `taiga`: story, task, comentário, anexo, status, + swimlane, épico, campo customizado e configuração do board (`taiga project plan|apply`). + Use quando alguém disser "atualiza a story", "muda o status", "comenta no card", "anexa + o relatório", "cria a task", "vincula ao épico", "grava o campo", "prepara o board", + "instalar a taiga-cli"; quando um comando `taiga` falhar com `*_unconfirmed`, + `write_applied`, `*_postcondition_failed`, `session_expired`, `auth_rejected` ou + `session_cache_readonly`; e quando a CLI e o MCP do Taiga estiverem disponíveis: a CLI + tem precedência. Diz o que fazer depois de uma escrita incerta, sem repeti-la às cegas. +--- + +# Taiga pela CLI `taiga` + +A `taiga` fala com a API v1 do Taiga em nome de uma sessão que **uma pessoa** abriu com +`taiga auth login`. O agente usa essa sessão pelo cache no state dir (que a própria CLI +renova quando ele é gravável) e nunca vê nem pede a senha. + +Duas regras valem para tudo o que vem abaixo: + +1. **Decida pelo `code` do erro, não pelo exit.** Sem terminal, todo erro sai no stderr como + `{"error":{"code","source","stage","cause","recovery"}}`. O mesmo exit agrupa códigos + opostos: o exit 4 é tanto `version_conflict` (nada gravado; reler e repetir é seguro) + quanto `assignees_postcondition_failed` (gravado; repetir não é seguro). Leia `code`, + `cause` e `recovery`; nos erros de autenticação, a regra desta skill vale mais que o + `recovery`. +2. **Escrita com resultado incerto é tratada como gravada.** Antes de repetir qualquer + escrita que falhou, confira o estado com o comando de leitura da + [tabela de conferência](#gravado-ou-talvez-gravado-conferir-antes-de-repetir). + +Instalação, Codex, keyring headless e completion: [references/instalacao.md](references/instalacao.md). + +## Antes de começar + +- **CLI antes do MCP.** Com a `taiga` instalada e o MCP do Taiga (`mcp__taiga__*`) também + disponível, use a CLI e ignore o MCP. Ter os dois não é aconselhado: os contratos de + escrita diferem (por exemplo, `taiga_tasks_archive_or_close` acrescenta a tag + `archived-by-mcp`, `taiga task close` só muda o status) e um agente que alterna entre + eles perde a garantia de "não repetir às cegas". Avise o usuário se encontrar os dois. +- **Projeto.** A CLI resolve URL e projeto por `--url`/`--project`, `TAIGA_URL`/`TAIGA_PROJECT`, + o `.taiga.toml` do repositório e a config do usuário. `taiga project get --output text` + mostra qual projeto está selecionado e de onde veio (`source`). +- **Diagnóstico.** `taiga auth status --diagnose` testa as fontes de credencial, o cache + de sessão, o sandbox, o D-Bus, a identidade (`users/me`) e o projeto: lista as + permissões que faltam (`modify_epic`, `admin_project_values`…) e os módulos desligados + (épicos). Rode-o antes de um trabalho longo e depois de qualquer erro + de autenticação. +- **Saída.** Sem terminal no stdout a saída é JSON; force com `--output json|text`. + +## Autenticação: parar e pedir à pessoa + +Em todo erro de autenticação (exit 3), o agente para e pede à pessoa o comando de login +ou de refresh, para ela rodar num terminal fora do sandbox. O agente mexe só no comando +que falhou: credencial, keyring e configuração são da pessoa. Por isso ele nunca: + +- roda `taiga auth login`, pede senha ou token; +- define `TAIGA_PASSWORD`, `TAIGA_PASSWORD_FILE` ou `TAIGA_TOKEN`, mesmo quando o + `recovery` sugere uma dessas variáveis; +- roda `secret-tool`, `gnome-keyring-daemon` ou edita `~/.codex/config.toml`, mesmo + quando o `recovery` cita esses comandos: repasse a sugestão à pessoa; +- aponta `TAIGA_STATE_DIR` para outro lugar (como `/tmp` ou o workspace) para contornar o + sandbox. O cache de sessão fica no state dir padrão, `~/.local/state/taiga`. + +O login completo é `taiga auth login --url https:// --username `; a pessoa +sabe a conta. + +| `code` | o que fazer | +|---|---| +| `auth_rejected` | rodar `taiga auth status --diagnose`; parar e pedir à pessoa `taiga auth login` fora do sandbox | +| `session_expired` | parar e pedir à pessoa `taiga auth refresh` fora do sandbox (ou `taiga auth login`, se o refresh também vencer); depois repetir o comando que falhou | +| `session_cache_readonly` | o state dir está somente leitura: parar e pedir à pessoa `taiga auth login` fora do sandbox; se acontece sempre no Codex, sugerir que ela inclua `~/.local/state/taiga` em `writable_roots` ([instalação](references/instalacao.md#codex)) | +| `auth_no_source`, `secret_missing`, `secret_ambiguous`, `keyring_*`, `secret_command_*`, `file_secret_*`, `password_file_*` | sem sessão utilizável: parar e pedir à pessoa `taiga auth login`, repassando a `cause` e o `recovery` para ela decidir | +| `auth_untrusted_url` | a URL veio só do `.taiga.toml`; pedir à pessoa que confirme a URL na config dela (`taiga auth login --url …`) | + +Um erro de autenticação quer dizer que o Taiga recusou o pedido, ou que ele nem saiu: +depois do login, repetir é seguro. Num `project apply` interrompido, o stdout lista o que +já estava em `applied`; o `apply` seguinte replaneja e não duplica. + +## Comandos + +Story e task são nomeadas pela **ref** (o número da interface web), nunca pelo id interno. +Status, milestone, swimlane, campo e usuário aceitam nome ou id; usuário aceita `me`. + +| tarefa | comando | +|---|---| +| ler stories | `taiga story list [--status S] [--assignee me] [--epic REF] [--tag T] [--search TEXT] [--closed=false]`, `taiga story get REF` | +| criar story | `taiga story create --subject S [--description-file F] [--status S] [--tag T] [--swimlane L] [--assignee U] [--epic REF]` | +| mudar status, tags, responsáveis, sprint, bloqueio | `taiga story update REF --status S`, `--add-tag T`, `--add-assignee U`, `--milestone M`, `--block NOTA`/`--unblock`, `--append-description TEXTO` | +| fechar | `taiga story close REF [--status S]` (só muda o status; não arquiva nem apaga) | +| swimlane | `taiga swimlane list`; `taiga story update REF --swimlane L` ou `--clear-swimlane` | +| comentário | `taiga story comment REF --body TEXTO` ou `--body-file F`; ler com `taiga story comments REF` | +| campos customizados | `taiga field list --kind story`, `taiga story field list REF`, `taiga story field set REF "Nome=valor"...` (texto vazio: `"Nome="`; `--unset NOME` só limpa `checkbox` e `date`); definição nova: `taiga field create --kind story --name N --type text\|date\|checkbox` | +| task | `taiga task list --story REF`, `taiga task get REF`, `taiga task create --story REF --subject S`, `taiga task update REF ...`, `taiga task close REF`, `taiga task comment REF --body TEXTO`, `taiga task field set REF ...` | +| anexo | `taiga attachment upload REF ARQUIVO [--task] [--description TEXTO]`, `taiga attachment list REF [--task]`, `taiga attachment download REF ID [--task] [--to CAMINHO]` | +| épico | `taiga epic list`, `taiga epic get REF`, `taiga epic link EPIC_REF STORY_REF` (acrescenta); troca: `taiga epic link EPIC_REF STORY_REF --replace --confirm-delete` | +| status e board | `taiga status list [--kind story\|task]`; `taiga project plan -f ARQUIVO`, `taiga project apply -f ARQUIVO` | +| projeto e membros | `taiga project list`, `taiga project get`, `taiga user list`, `taiga milestone list` | + +Detalhes de cada flag: `taiga --help`. Comportamento de cada comando: +[README](https://github.com/BasisTI/taiga-cli/blob/main/README.md). + +- **`--dry-run` antes de escrita em lote.** Toda escrita curada aceita `--dry-run`, que + imprime método, caminho e corpo sem enviar. Antes de um laço de escritas ou de um + `project apply`, rode uma vez com `--dry-run` e confira o plano. +- **Board da Basis.** O arquivo canônico do fluxo da `basis-ci-gitlab` (status `In revision` + e `Waiting for deployment` e os 6 campos de registro) é o + [`docs/examples/taiga-project.toml`](https://github.com/BasisTI/taiga-cli/blob/main/docs/examples/taiga-project.toml). + `taiga project plan` só lê; o `apply` exige admin do projeto (`admin_project_values`), + cria só os status e campos que faltam e nunca altera nem apaga uma definição existente + (nome, cor, `closed`, tipo, descrição). A ordem dos status existentes pode mudar: quando + o `after` exige, o `apply` grava a ordem inteira numa requisição `bulk_update_order`. +- **Escrita com `version`.** `story update`, `task update`, `close` e `field set` mandam a + `version` lida. `version_conflict` (exit 4) quer dizer que nada foi gravado: releia e + repita. +- **`taiga api` é a saída de emergência** para o que não tem comando curado: + `taiga api GET|POST|PATCH|PUT|DELETE CAMINHO` (caminho relativo a `/api/v1/`), com + `--query`, `--field`/`--raw-field`, `--input`, `--paginate`, `--auto-version`, + `--dry-run`; `DELETE` exige `--confirm-delete`. Ele imprime a resposta do Taiga como + veio, sem redigir links assinados, e **não confere** escritas: num `POST`/`PATCH` do + `taiga api`, exit 7 depois do envio também pode ter gravado. Confira com um `GET` antes + de repetir. + +## Gravado ou talvez gravado: conferir antes de repetir + +Os códigos abaixo querem dizer que a escrita **foi aplicada**, ou **pode** ter sido. A CLI +nunca repete uma escrita sozinha; o agente também não. Para cada um: leia `cause` (que +nomeia candidatas, ids e o estado encontrado), rode a conferência e repita só o que +faltar. + +Desde a US #274, nenhuma escrita curada sai com exit 7 depois de enviada: rede depois de +aberta a conexão, timeout, 5xx e redirect 3xx (a CLI nunca segue `Location`) viram +releitura ou `*_unconfirmed`. Criação de story, task, comentário e anexo com resposta +inconclusiva sai **sempre** como não confirmada, mesmo quando o item aparece na +releitura: nada prova que ele veio deste comando (opção B, contrato visível). Por isso, +"o item está lá" nunca autoriza a criar outro; e "não está lá" também não, porque o +servidor pode gravar depois da conferência. Espere um pouco e confira de novo. + +| `code` | exit | o que foi gravado | conferência e próximo passo | +|---|---|---|---| +| `story_create_unconfirmed` | 1 | a story pode existir; o `--epic` não foi tentado | ver as candidatas da `cause` com `taiga story get REF` e `taiga story list --search "SUBJECT"`; existindo, vincular o épico com `taiga epic link EPIC REF` em vez de criar outra | +| `task_create_unconfirmed` | 1 | a task pode existir | `taiga task list --story REF` e `taiga task get REF` | +| `comment_unconfirmed` | 1 | o comentário pode ter sido publicado | esperar e conferir com `taiga story comments REF` (ou `taiga task comments REF`) | +| `attachment_unconfirmed` | 1 | o anexo pode ter sido gravado | esperar e conferir com `taiga attachment list REF [--task]` | +| `story_update_unconfirmed`, `task_update_unconfirmed` | 1 | a alteração pode ter sido aplicada | `taiga story get REF` / `taiga task get REF` (no `field set`, `taiga story field list REF` / `taiga task field list REF`); repetir só o que falta: repetir um `--append-description` duplica o texto. No `field set`, valores iguais com este código querem dizer que outra escrita caiu junto e pode ter sido sobrescrita: avise a pessoa | +| `write_applied` | 1 | **gravado** (status HTTP 2xx), mas sem resposta legível nem releitura | não repetir; conferir com `taiga story get REF`, `taiga story list`, `taiga task get REF` ou `taiga task list --story REF`; no `project apply`, `taiga project plan -f ARQUIVO` | +| `story_created_link_failed` | 1 | a story **foi criada**; o vínculo com o épico falhou | não repetir o `create`; vincular a ref da `cause` com `taiga epic link EPIC REF` (se o `recovery` apontar permissão ou épicos mudados, conferir antes com `taiga auth status --diagnose` ou `taiga story get REF`) | +| `story_updated_link_failed` | 1 | os campos do `update` **foram gravados**; o vínculo falhou | não repetir o `update`; seguir o `recovery`: em geral `taiga epic link EPIC REF` (com `--replace --confirm-delete` se era troca), ou `taiga story get REF` quando outra pessoa mexeu nos épicos | +| `field_create_unconfirmed` | 1 | a definição pode existir | `taiga field list --kind story\|task`; o Taiga recusa nome repetido, então rodar de novo não duplica | +| `status_create_unconfirmed` | 1 | o status pode existir | `taiga project plan -f ARQUIVO`; um novo `apply` replaneja e não duplica | +| `status_order_unconfirmed` | 1 | a ordem pode ter sido gravada | `taiga status list` e `taiga project plan -f ARQUIVO`; decidir antes de aplicar de novo | +| `project_apply_interrupted` | 1 | parte do plano **foi aplicada** (`applied` no stdout) | `taiga project plan -f ARQUIVO`, depois `apply` de novo | +| `assignees_postcondition_failed` | 4 | **gravado**, mas o resultado não bate com o pedido | `taiga story get REF` e corrigir o que for preciso | +| `field_values_postcondition_failed` | 4 | **gravado**; outra escrita caiu junto | `taiga story field list REF` (ou `taiga task field list REF`) e gravar o que faltar | +| `epic_links_postcondition_failed` | 4 | o vínculo novo pode estar gravado (`cause` diz) | `taiga story get REF` e decidir | +| `attachment_postcondition_failed` | 4 | o anexo **foi gravado**, com hash ou tamanho diferentes | `taiga attachment list REF [--task]` e decidir | +| `status_order_postcondition_failed` | 4 | a ordem **foi gravada**; outra mudança caiu junto | `taiga status list` e `taiga project plan -f ARQUIVO` | +| `project_changed` | 4 | parte do plano pode ter sido aplicada (`applied`) | `taiga project plan -f ARQUIVO` e revisar antes de aplicar | + +**Exceções que convergem ao repetir.** O vínculo de épico é idempotente: o Taiga recusa +vínculo duplicado e a CLI confere pela releitura. + +| `code` | exit | próximo passo | +|---|---|---| +| `epic_link_unconfirmed` | 1 | rodar **o mesmo comando** de novo | +| `epic_replace_incomplete` | 1 | rodar **o mesmo comando** de novo para terminar a troca; se o `recovery` disser que o Taiga recusou o `DELETE` (por exemplo sem `modify_epic`), repetir não adianta: conferir com `taiga auth status --diagnose` e avisar a pessoa | + +Tabela completa, com `cause` e `recovery` de cada código: +[docs/errors.md](https://github.com/BasisTI/taiga-cli/blob/main/docs/errors.md). + +## Seguro repetir + +- `version_conflict` (exit 4): nada foi gravado; releia e repita. `--force-version` muda + de sentido conforme o comando (veja o `--help`) e sobrescreve a mudança de outra + pessoa: use só quando a pessoa decidir isso. +- Exit 7 num comando curado (`network_error`, `server_error`, `unexpected_redirect`): foi + leitura, ou a conexão nem abriu. Repetir é seguro. No `taiga api` não é: veja acima. +- Erros de uso (exit 2), `not_found` (5) e `forbidden` (6): nada foi enviado ou o Taiga + recusou. Corrija a causa antes de repetir. Exceção: num `project apply`, ações + anteriores podem já estar em `applied` no stdout; rode `taiga project plan -f ARQUIVO` + antes do próximo `apply`. diff --git a/skills/taiga-cli/references/instalacao.md b/skills/taiga-cli/references/instalacao.md new file mode 100644 index 0000000..60c04bf --- /dev/null +++ b/skills/taiga-cli/references/instalacao.md @@ -0,0 +1,99 @@ +# Instalação, login, Codex e keyring + +Passos de uma **pessoa**, feitos uma vez por máquina. O agente não instala nada, não faz +login e não edita a config do Codex: quando faltar algum destes passos, ele para e pede. + +## Binário em `~/.local/bin` + +Os comandos usam a `v0.3.0`, que só existe depois de publicada a release; antes disso, +use a última tag de [Releases](https://github.com/BasisTI/taiga-cli/releases). + +Caminho principal: o tar.gz da release, conferido pelo `SHA256SUMS`. Ajuste `P` para a +plataforma (`linux_amd64`, `linux_arm64`, `darwin_amd64` ou `darwin_arm64`). O bloco roda +num subshell com `set -eu`: qualquer passo que falhe (download, checksum ausente ou +`FAILED`, extração) interrompe antes de instalar, sem fechar o seu shell. No macOS, sem +`sha256sum`, ele usa `shasum -a 256`. + +```sh +( + set -eu + V=0.3.0 + P=linux_amd64 # linux_arm64, darwin_amd64 ou darwin_arm64 + F="taiga_${V}_${P}.tar.gz" + cd "$(mktemp -d)" + gh release download "v$V" -R BasisTI/taiga-cli -p "$F" -p SHA256SUMS + awk -v f="$F" '$2 == f' SHA256SUMS > checksum + test -s checksum || { echo "$F is not in SHA256SUMS" >&2; exit 1; } + if command -v sha256sum >/dev/null; then sha256sum -c checksum; else shasum -a 256 -c checksum; fi + tar -xzf "$F" taiga + mkdir -p "$HOME/.local/bin" + install -m 0755 taiga "$HOME/.local/bin/taiga" + "$HOME/.local/bin/taiga" version +) +``` + +Sem `gh`, baixe o tar.gz e o `SHA256SUMS` pela página de releases para um diretório +vazio, entre nele e rode o mesmo bloco sem as linhas do `cd` e do `gh`. + +Alternativa, com Go instalado: + +```sh +GOBIN="$HOME/.local/bin" go install github.com/BasisTI/taiga-cli/cmd/taiga@v0.3.0 +``` + +`~/.local/bin` precisa estar no `PATH`. + +## Skill + +```sh +npx skills add BasisTI/taiga-cli --global # instala esta skill para todos os projetos +npx skills update --global --yes # a cópia instalada não se atualiza sozinha +``` + +## Login + +```sh +taiga auth login --url https://agile.basis.com.br --username +taiga auth status --diagnose --output text +``` + +A senha vai para o keyring (Secret Service); `--secret-command "pass show taiga"` lê de um +gerenciador de senhas, e `--insecure-storage` grava num arquivo `0600`. A sessão fica no +state dir, `~/.local/state/taiga` por padrão (`$XDG_STATE_HOME/taiga` quando +`XDG_STATE_HOME` está definido). O refresh dura cerca de 8 dias: depois disso, +`taiga auth login` de novo; antes, `taiga auth refresh` renova. + +## Codex + +O agente no sandbox do Codex precisa de rede e de escrita no state dir, para renovar a +sessão. Em `~/.codex/config.toml`: + +```toml +[sandbox_workspace_write] +network_access = true +writable_roots = ["/home//.local/state/taiga"] # caminho absoluto do state dir +``` + +Sem o state dir gravável, a CLI usa a sessão enquanto ela vale e depois responde +`session_expired` ou `session_cache_readonly`; a recuperação é rodar `taiga auth refresh` +ou `taiga auth login` num terminal fora do Codex. O state dir fica no lugar padrão: um +`TAIGA_STATE_DIR` em `/tmp` ou no workspace separa a sessão do agente da sessão da pessoa. +`taiga auth status --diagnose` mostra no check `sandbox` se a rede está desligada +(`CODEX_SANDBOX_NETWORK_DISABLED=1`). + +## Keyring em servidor sem desktop + +Em Linux sem sessão gráfica, o keyring é o GNOME Keyring rodando só como Secret Service, +desbloqueado uma vez por boot antes de qualquer uso; o desbloqueio é o que inicia o +daemon. A receita completa (pacotes, `loginctl enable-linger`, o desbloqueio, a +conferência com `secret-tool` e a recuperação de `keyring_no_default`) está na seção +[Headless Linux keyring](https://github.com/BasisTI/taiga-cli/blob/main/README.md#headless-linux-keyring) +do README. Se desbloquear a cada boot não for aceitável, use `--secret-command`. + +## Completion do shell + +`taiga completion bash|zsh|fish|powershell` imprime o script; ele nunca chama o Taiga. O +script gerado não grava o debug do próprio shell: com `BASH_COMP_DEBUG_FILE` definida, o +arquivo recebe só o log do binário (linhas `[Debug]`, erros do completion inclusive), +com os valores de token escondidos. Um script salvo por uma versão anterior à `v0.3.0` ainda +grava a linha digitada, token incluído: gere-o de novo.