@noclaf/cli 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -0
- package/dist/auth.js +66 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli/commands/auth.js +76 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/init.js +291 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/skills.js +22 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/sync.js +170 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/copy/auth.json +6 -0
- package/dist/cli/copy/flows.json +6 -0
- package/dist/cli/copy/init.json +19 -0
- package/dist/cli/copy/sync.json +3 -0
- package/dist/cli/copy.js +23 -0
- package/dist/cli/copy.js.map +1 -0
- package/dist/cli/flows/clients.js +108 -0
- package/dist/cli/flows/clients.js.map +1 -0
- package/dist/cli/flows/dependencies.js +68 -0
- package/dist/cli/flows/dependencies.js.map +1 -0
- package/dist/cli/flows/sections.js +45 -0
- package/dist/cli/flows/sections.js.map +1 -0
- package/dist/cli/ui/render.js +167 -0
- package/dist/cli/ui/render.js.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/database.types.js +11 -0
- package/dist/database.types.js.map +1 -0
- package/dist/lib/active-project.js +28 -0
- package/dist/lib/active-project.js.map +1 -0
- package/dist/lib/client-configs.js +179 -0
- package/dist/lib/client-configs.js.map +1 -0
- package/dist/lib/client-install.js +40 -0
- package/dist/lib/client-install.js.map +1 -0
- package/dist/lib/colors.js +67 -0
- package/dist/lib/colors.js.map +1 -0
- package/dist/lib/cowork-extension.js +131 -0
- package/dist/lib/cowork-extension.js.map +1 -0
- package/dist/lib/dependencies.js +159 -0
- package/dist/lib/dependencies.js.map +1 -0
- package/dist/lib/duration.js +5 -0
- package/dist/lib/duration.js.map +1 -0
- package/dist/lib/file-merge.js +33 -0
- package/dist/lib/file-merge.js.map +1 -0
- package/dist/lib/hooks.js +231 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/project-context.js +105 -0
- package/dist/lib/project-context.js.map +1 -0
- package/dist/lib/prompts.js +62 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/provision.js +222 -0
- package/dist/lib/provision.js.map +1 -0
- package/dist/lib/require-config.js +31 -0
- package/dist/lib/require-config.js.map +1 -0
- package/dist/lib/sections.js +85 -0
- package/dist/lib/sections.js.map +1 -0
- package/dist/lib/skill-serve.js +71 -0
- package/dist/lib/skill-serve.js.map +1 -0
- package/dist/lib/skills-cache.js +89 -0
- package/dist/lib/skills-cache.js.map +1 -0
- package/dist/lib/skills.js +219 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/targets.js +150 -0
- package/dist/lib/targets.js.map +1 -0
- package/dist/lib/task-history.js +14 -0
- package/dist/lib/task-history.js.map +1 -0
- package/dist/lib/telemetry.js +32 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/tool-result.js +12 -0
- package/dist/lib/tool-result.js.map +1 -0
- package/dist/lib/version-check.js +88 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/logo.js +17 -0
- package/dist/logo.js.map +1 -0
- package/dist/mcp-server.js +162 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/spinner.js +12 -0
- package/dist/spinner.js.map +1 -0
- package/dist/supabase.js +95 -0
- package/dist/supabase.js.map +1 -0
- package/dist/tools/comment-task.js +80 -0
- package/dist/tools/comment-task.js.map +1 -0
- package/dist/tools/create-task.js +194 -0
- package/dist/tools/create-task.js.map +1 -0
- package/dist/tools/end-allocation.js +59 -0
- package/dist/tools/end-allocation.js.map +1 -0
- package/dist/tools/end-task-allocation.js +51 -0
- package/dist/tools/end-task-allocation.js.map +1 -0
- package/dist/tools/get-active-allocation.js +106 -0
- package/dist/tools/get-active-allocation.js.map +1 -0
- package/dist/tools/get-context.js +47 -0
- package/dist/tools/get-context.js.map +1 -0
- package/dist/tools/get-task.js +223 -0
- package/dist/tools/get-task.js.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-my-allocations.js +87 -0
- package/dist/tools/list-my-allocations.js.map +1 -0
- package/dist/tools/list-projects.js +34 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-tasks.js +213 -0
- package/dist/tools/list-tasks.js.map +1 -0
- package/dist/tools/move-task.js +98 -0
- package/dist/tools/move-task.js.map +1 -0
- package/dist/tools/set-project.js +47 -0
- package/dist/tools/set-project.js.map +1 -0
- package/dist/tools/start-allocation.js +45 -0
- package/dist/tools/start-allocation.js.map +1 -0
- package/dist/tools/start-task-allocation.js +141 -0
- package/dist/tools/start-task-allocation.js.map +1 -0
- package/dist/tools/update-task.js +256 -0
- package/dist/tools/update-task.js.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# noclaf-cli
|
|
2
|
+
|
|
3
|
+
MCP server + CLI pra controlar o **NOS** (Noclaf Operation System) a partir do Claude Code, Codex CLI e GitHub Copilot.
|
|
4
|
+
|
|
5
|
+
## O que faz
|
|
6
|
+
|
|
7
|
+
Permite ao assistente de IA:
|
|
8
|
+
|
|
9
|
+
- Ler contexto do projeto (membros, sprint ativa, repositórios vinculados)
|
|
10
|
+
- Listar, criar, atualizar, mover entre colunas e comentar tarefas
|
|
11
|
+
- Bater ponto e cronometrar tempo em tarefas específicas
|
|
12
|
+
|
|
13
|
+
## Instalação (mac / linux / windows)
|
|
14
|
+
|
|
15
|
+
Precisa apenas de **Node.js 20+**. Instala como pacote global:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm i -g @noclaf/cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Pronto. Os comandos `noclaf` (CLI) e `noclaf-cli` (servidor MCP) ficam disponíveis no PATH em qualquer SO.
|
|
22
|
+
|
|
23
|
+
## Configurar em qualquer repo
|
|
24
|
+
|
|
25
|
+
Na raiz do repositório:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
noclaf init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Esse único comando faz tudo:
|
|
32
|
+
|
|
33
|
+
1. Pede o seu **PAT** se você ainda não está autenticado (gera em https://nos.noclaf.com.br/profile#mcp).
|
|
34
|
+
2. Lista os projetos do NOS e você escolhe um (ou cola o UUID).
|
|
35
|
+
3. (Opcional) Escolhe o repositório vinculado.
|
|
36
|
+
4. Escreve `noclaf.json` na raiz.
|
|
37
|
+
5. Mostra o contexto carregado pra você validar.
|
|
38
|
+
6. Pergunta quais clientes MCP configurar — global ou por-repo.
|
|
39
|
+
|
|
40
|
+
Reinicie o cliente de IA depois pra carregar as tools.
|
|
41
|
+
|
|
42
|
+
## Comandos do CLI
|
|
43
|
+
|
|
44
|
+
| Comando | Descrição |
|
|
45
|
+
| ---------------------------------------------------- | -------------------------------------------------------- |
|
|
46
|
+
| `noclaf init` | Faz tudo: autentica, vincula projeto, configura clientes |
|
|
47
|
+
| `noclaf login [pat]` | Salva o PAT (interativo se omitido) |
|
|
48
|
+
| `noclaf logout` | Apaga as credenciais |
|
|
49
|
+
| `noclaf whoami` | Mostra o usuário autenticado |
|
|
50
|
+
| `noclaf skills status` | Lista os docs do pacote `@noclaf/skills` |
|
|
51
|
+
| `noclaf sync` | Provisiona skills/commands/agents/hooks pros clientes configurados (do pacote `@noclaf/skills`); checa atualização |
|
|
52
|
+
| `noclaf --version` | Imprime a versão |
|
|
53
|
+
|
|
54
|
+
## Tools disponíveis
|
|
55
|
+
|
|
56
|
+
15 tools no MCP, divididas em quatro grupos.
|
|
57
|
+
|
|
58
|
+
### Projeto
|
|
59
|
+
|
|
60
|
+
| Tool | O que faz |
|
|
61
|
+
| ------------------- | ------------------------------------------------------------------------ |
|
|
62
|
+
| `nos_get_context` | Snapshot do projeto ativo: nome, repos, sprint ativa, membros, você |
|
|
63
|
+
| `nos_list_projects` | Lista os projetos que você acessa (marca o ativo) |
|
|
64
|
+
| `nos_set_project` | Define o **projeto ativo da sessão** (só em memória, dura enquanto o servidor roda) |
|
|
65
|
+
|
|
66
|
+
> **Escopo do projeto.** As tools de tarefa/alocação resolvem o projeto nesta ordem:
|
|
67
|
+
> argumento `project_id` da chamada → projeto ativo da sessão (`nos_set_project`) →
|
|
68
|
+
> default opcional de CLI de código (`noclaf.json`/`NOCLAF_PROJECT_ID`).
|
|
69
|
+
>
|
|
70
|
+
> O projeto ativo é **por sessão**: vive só em memória, no processo do servidor MCP,
|
|
71
|
+
> e não é gravado em disco — ao reiniciar volta ao default. No Cowork, onde não há
|
|
72
|
+
> `noclaf.json`, é sempre assim que se escolhe o projeto: `nos_list_projects` →
|
|
73
|
+
> `nos_set_project`. (Como um processo do Claude Desktop pode ser compartilhado entre
|
|
74
|
+
> conversas, passe `project_id` direto na chamada quando quiser garantir o escopo de
|
|
75
|
+
> uma conversa específica.)
|
|
76
|
+
|
|
77
|
+
### Tarefas
|
|
78
|
+
|
|
79
|
+
| Tool | O que faz |
|
|
80
|
+
| ------------------ | ----------------------------------------------------------------------------- |
|
|
81
|
+
| `nos_list_tasks` | Lista tarefas filtradas por status, assignee, sprint, prioridade, tipo, busca |
|
|
82
|
+
| `nos_get_task` | Detalhes completos de uma tarefa |
|
|
83
|
+
| `nos_create_task` | Cria nova tarefa (default: status=todo, sprint=active, assignee=você) |
|
|
84
|
+
| `nos_update_task` | Atualiza campos de uma tarefa |
|
|
85
|
+
| `nos_move_task` | Atalho pra mudar o status |
|
|
86
|
+
| `nos_comment_task` | Adiciona comentário em uma tarefa |
|
|
87
|
+
|
|
88
|
+
### Alocações (ponto + cronometragem)
|
|
89
|
+
|
|
90
|
+
| Tool | O que faz |
|
|
91
|
+
| --------------------------- | ------------------------------------------------- |
|
|
92
|
+
| `nos_start_allocation` | Bate o ponto |
|
|
93
|
+
| `nos_end_allocation` | Encerra o ponto do dia |
|
|
94
|
+
| `nos_start_task_allocation` | Começa a cronometrar uma task |
|
|
95
|
+
| `nos_end_task_allocation` | Para de cronometrar a task atual |
|
|
96
|
+
| `nos_get_active_allocation` | Estado atual: ponto, task ativa, segmentos do dia |
|
|
97
|
+
| `nos_list_my_allocations` | Histórico de pontos |
|
|
98
|
+
|
|
99
|
+
## Skills, commands e dependências
|
|
100
|
+
|
|
101
|
+
Além das tools, o noclaf entrega skills/commands/agents/hooks pros clientes. O conteúdo
|
|
102
|
+
vive num **repo aberto** — [`Falcao-Tech/noclaf-skills`](https://github.com/Falcao-Tech/noclaf-skills) —
|
|
103
|
+
e **não** é um pacote npm. O CLI baixa o repo (zipball do GitHub, sem precisar de `git`)
|
|
104
|
+
pra um cache local em **`~/.noclaf/skills`** e lê de lá. `noclaf sync` **atualiza o cache**
|
|
105
|
+
(pull da branch) toda vez, então as skills evoluem sem republicar o CLI; e **auto-detecta**
|
|
106
|
+
quais clientes têm o noclaf configurado, provisionando pra cada um conforme a seleção de
|
|
107
|
+
perfil/área do `noclaf.json`.
|
|
108
|
+
|
|
109
|
+
Overrides por ambiente:
|
|
110
|
+
|
|
111
|
+
| Variável | Efeito |
|
|
112
|
+
| --------------------- | ------------------------------------------------------------------ |
|
|
113
|
+
| `NOCLAF_SKILLS_DIR` | Aponta pra um checkout local do repo (dev) — vence tudo |
|
|
114
|
+
| `NOCLAF_SKILLS_REPO` | Outro `owner/repo` (default `Falcao-Tech/noclaf-skills`) |
|
|
115
|
+
| `NOCLAF_SKILLS_REF` | Outra branch/tag (default `main`) |
|
|
116
|
+
|
|
117
|
+
Cada cliente recebe no formato que entende:
|
|
118
|
+
|
|
119
|
+
| Cliente | Onde | Formato |
|
|
120
|
+
| --------------- | ----------------------------- | ----------------------------------------------------------------------- |
|
|
121
|
+
| Claude Code | `~/.claude/{commands,skills,agents}` | arquivos nativos — slash-commands e skills |
|
|
122
|
+
| Codex CLI | `~/.codex/` | **dual-write**: cada command/skill vira uma **skill** (`skills/<id>/SKILL.md`, auto-selecionada) **e** um **custom prompt** (`prompts/<id>.md`, no menu `/prompts:<id>`) |
|
|
123
|
+
| Cowork/Desktop | — | via **MCP prompts** + resources, servidos ao vivo (ignora `~/.claude`) |
|
|
124
|
+
|
|
125
|
+
> Configurar o Codex grava `NOCLAF_CLIENT=codex` no `config.toml`, o que faz o servidor
|
|
126
|
+
> provisionar pra `~/.codex` e filtrar os docs pelo surface `codex`. Reinicie o cliente
|
|
127
|
+
> depois do sync pra carregar skills/prompts novos.
|
|
128
|
+
|
|
129
|
+
### Visibilidade por cliente
|
|
130
|
+
|
|
131
|
+
Um doc pode ser restrito a clientes específicos via frontmatter:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
clients: claude-code, cowork # vazio/ausente = todos os clientes
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Valores: `claude-code`, `codex`, `cowork`. O MCP filtra por esse campo, então um skill
|
|
138
|
+
marcado `cowork` não é provisionado pro `~/.claude`, e um `claude-code` não aparece como
|
|
139
|
+
prompt no Cowork.
|
|
140
|
+
|
|
141
|
+
### Dependências externas
|
|
142
|
+
|
|
143
|
+
Commands/skills podem depender de libs externas (declaradas como nós `dependency` e
|
|
144
|
+
linkadas por wikilink `[[nome]]`). No fim do `init`/`sync`, a CLI lista cada uma e:
|
|
145
|
+
|
|
146
|
+
- **instalador estruturado** (`npm:`, `skills:` = `npx skills add`, `git:`) → oferece
|
|
147
|
+
rodar com `[y/N]` (comando montado a partir do campo validado, **sem shell**);
|
|
148
|
+
- **`manual:`** (plugin de marketplace, passos no cliente, UI) → imprime os passos por
|
|
149
|
+
cliente, com os comandos destacados. Ex.: o Ponytail é instalado por `/plugin` no Claude
|
|
150
|
+
Code e `codex plugin …` + `/hooks` no Codex — a CLI não automatiza, só orienta.
|
|
151
|
+
|
|
152
|
+
A string `install:` (se houver) é **só exibição** — nunca é executada.
|
|
153
|
+
|
|
154
|
+
### Checagem de cliente
|
|
155
|
+
|
|
156
|
+
Ao escolher um cliente no `init` ou `install-client`, a CLI verifica se ele está de fato
|
|
157
|
+
instalado na máquina. Se não estiver:
|
|
158
|
+
|
|
159
|
+
- **CLIs** (Claude Code, Codex) → mostra e oferece rodar o instalador:
|
|
160
|
+
`npm i -g @anthropic-ai/claude-code` · `npm i -g @openai/codex`;
|
|
161
|
+
- **apps** (Claude Desktop/Cowork, VS Code) → mostra o link de download
|
|
162
|
+
(ex.: https://claude.ai/download) — reabra o app depois de instalar.
|
|
163
|
+
|
|
164
|
+
## Claude Desktop / Cowork (Desktop Extension `.mcpb`)
|
|
165
|
+
|
|
166
|
+
Pra usar no app de desktop do Claude ou no Cowork, gere uma extensão `.mcpb`:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
noclaf install-client cowork
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Isso empacota o servidor (dist + dependências + `manifest.json`) num arquivo
|
|
173
|
+
`~/Downloads/noclaf-cli-<versão>.mcpb`. Pra instalar:
|
|
174
|
+
|
|
175
|
+
**Configurações → Extensões → Configurações avançadas → seção "Extension Developer" →
|
|
176
|
+
"Install Extension…"** → selecione o `.mcpb` → preencha **só o PAT**.
|
|
177
|
+
|
|
178
|
+
Não há projeto pra configurar na instalação: o Cowork não tem `noclaf.json` (não tem
|
|
179
|
+
working dir/repo), então o projeto é **escolhido por sessão** — peça pro assistente rodar
|
|
180
|
+
`nos_list_projects` e depois `nos_set_project` com o id desejado.
|
|
181
|
+
|
|
182
|
+
Diferente da CLI, a extensão não usa `noclaf login`/`noclaf.json`: a única config vem do
|
|
183
|
+
`user_config` da extensão, injetada como variável de ambiente:
|
|
184
|
+
|
|
185
|
+
| Variável | Origem (`user_config`) | Sensível |
|
|
186
|
+
| ------------ | ---------------------- | -------- |
|
|
187
|
+
| `NOCLAF_PAT` | PAT (`noc_…`) | ✅ sim |
|
|
188
|
+
|
|
189
|
+
> **Override de default (avançado, opcional).** Fora do Cowork, em CLIs de código, você
|
|
190
|
+
> ainda pode definir `NOCLAF_PROJECT_ID`/`NOCLAF_REPOSITORY_ID` no env do cliente MCP pra
|
|
191
|
+
> fixar um projeto default — equivalente ao `noclaf.json`. O projeto da sessão
|
|
192
|
+
> (`nos_set_project`) e o `project_id` por chamada sempre têm precedência sobre esse default.
|
|
193
|
+
|
|
194
|
+
## Atualizando
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm i -g @noclaf/cli@latest
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
O `noclaf sync` **checa a versão publicada** no início e, se houver uma mais nova, mostra
|
|
201
|
+
um aviso e **oferece atualizar ali mesmo** (roda `npm i -g @noclaf/cli@latest` com sua
|
|
202
|
+
confirmação; depois é só rodar o sync de novo). Use `noclaf sync --yes` pra aceitar sem
|
|
203
|
+
prompt. A CLI também avisa em background em qualquer comando (via `update-notifier`).
|
|
204
|
+
|
|
205
|
+
## Troubleshooting
|
|
206
|
+
|
|
207
|
+
- **`noclaf-cli: command not found`** — confirme o install global com `npm i -g @noclaf/cli` e que `npm bin -g` está no PATH.
|
|
208
|
+
- **Tools não aparecem no Claude Code** — reinicie o Claude Code depois do `noclaf init`. Confira com `/mcp` dentro dele.
|
|
209
|
+
- **`Não autenticado`** — rode `noclaf init` (que já faz login inline) ou `noclaf login`. No Cowork, confira se o **PAT** foi preenchido na extensão.
|
|
210
|
+
- **`Nenhum projeto selecionado`** — escolha o projeto da sessão: `nos_list_projects` → `nos_set_project`. (Em CLI de código você também pode rodar `noclaf init` pra criar um `noclaf.json` default no repo.)
|
|
211
|
+
|
|
212
|
+
## Convenções
|
|
213
|
+
|
|
214
|
+
- **Idioma**: UI/CLI em pt-BR. Código (variáveis, funções, tipos) em inglês.
|
|
215
|
+
- **Backups**: qualquer escrita em arquivo de config existente gera `.bak.<timestamp>` ao lado.
|
|
216
|
+
- **stdout reservado pro JSON-RPC** no MCP server — logs vão pra stderr.
|
|
217
|
+
|
|
218
|
+
## Versão
|
|
219
|
+
|
|
220
|
+
v0.1.x — feature complete pra v1. Próximos passos: testes automatizados,
|
|
221
|
+
suporte a múltiplos perfis de usuário.
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile, chmod, rm } from 'node:fs/promises';
|
|
2
|
+
import { CREDENTIALS_DIR, CREDENTIALS_FILE, PAT_REGEX, SUPABASE_ANON_KEY, TOKEN_EXCHANGE_URL, } from './constants.js';
|
|
3
|
+
export async function loadCredentials() {
|
|
4
|
+
// Precedência pro ambiente: usado por Desktop Extensions / Cowork, onde o PAT
|
|
5
|
+
// chega via user_config (NOCLAF_PAT) em vez de `noclaf login`.
|
|
6
|
+
const envPat = process.env.NOCLAF_PAT?.trim();
|
|
7
|
+
if (envPat) {
|
|
8
|
+
return { pat: envPat };
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const raw = await readFile(CREDENTIALS_FILE, 'utf8');
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
if (!parsed || typeof parsed.pat !== 'string') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err.code === 'ENOENT') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function saveCredentials(creds) {
|
|
26
|
+
await mkdir(CREDENTIALS_DIR, { recursive: true });
|
|
27
|
+
await writeFile(CREDENTIALS_FILE, JSON.stringify(creds, null, 2) + '\n', 'utf8');
|
|
28
|
+
try {
|
|
29
|
+
await chmod(CREDENTIALS_FILE, 0o600);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// chmod pode falhar em Windows — ignoramos silenciosamente.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function clearCredentials() {
|
|
36
|
+
await rm(CREDENTIALS_FILE, { force: true });
|
|
37
|
+
}
|
|
38
|
+
export function validatePatFormat(pat) {
|
|
39
|
+
return PAT_REGEX.test(pat);
|
|
40
|
+
}
|
|
41
|
+
export async function exchangePatForJwt(pat) {
|
|
42
|
+
const response = await fetch(TOKEN_EXCHANGE_URL, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
apikey: SUPABASE_ANON_KEY,
|
|
46
|
+
Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({ token: pat }),
|
|
50
|
+
});
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
let message = response.statusText;
|
|
53
|
+
try {
|
|
54
|
+
const body = (await response.json());
|
|
55
|
+
if (body && typeof body.error === 'string' && body.error.length > 0) {
|
|
56
|
+
message = body.error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// body não era JSON — mantém statusText
|
|
61
|
+
}
|
|
62
|
+
throw new Error(message);
|
|
63
|
+
}
|
|
64
|
+
return (await response.json());
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { password } from "../../lib/prompts.js";
|
|
2
|
+
import { LOGO } from "../../logo.js";
|
|
3
|
+
import { startSpinner } from "../../spinner.js";
|
|
4
|
+
import { clearCredentials, exchangePatForJwt, loadCredentials, saveCredentials, validatePatFormat, } from "../../auth.js";
|
|
5
|
+
import { authCopy } from "../copy.js";
|
|
6
|
+
export function registerAuthCommands(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("login")
|
|
9
|
+
.description("Salva o token de acesso pessoal (PAT) localmente")
|
|
10
|
+
.argument("[pat]", "Token gerado em /profile#mcp do NOS (opcional — será pedido se omitido)")
|
|
11
|
+
.action(async (patArg) => {
|
|
12
|
+
let pat = patArg?.trim();
|
|
13
|
+
if (!pat) {
|
|
14
|
+
console.log("Gere um token pessoal em: https://nos.noclaf.com.br/profile#mcp");
|
|
15
|
+
const answer = await password({
|
|
16
|
+
message: authCopy.login.patPrompt,
|
|
17
|
+
mask: "*",
|
|
18
|
+
validate: (input) => validatePatFormat(input.trim()) || authCopy.login.patInvalid,
|
|
19
|
+
});
|
|
20
|
+
pat = answer.trim();
|
|
21
|
+
}
|
|
22
|
+
if (!validatePatFormat(pat)) {
|
|
23
|
+
console.error("Token inválido. Formato esperado: `noc_` + 64 caracteres hexadecimais.");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const spinner = startSpinner("Autenticando...");
|
|
27
|
+
try {
|
|
28
|
+
const result = await exchangePatForJwt(pat);
|
|
29
|
+
await saveCredentials({ pat });
|
|
30
|
+
spinner.stop();
|
|
31
|
+
const u = result.user;
|
|
32
|
+
console.log(LOGO);
|
|
33
|
+
console.log("[ok] Autenticado!");
|
|
34
|
+
console.log(`Nome: ${u.full_name || "(sem nome)"}`);
|
|
35
|
+
console.log(`Usuário: ${u.username ? `@${u.username}` : "(sem username)"}`);
|
|
36
|
+
console.log(`Email: ${u.email}`);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
spinner.fail(`Falha ao autenticar: ${err.message}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
program
|
|
44
|
+
.command("logout")
|
|
45
|
+
.description("Remove credenciais locais")
|
|
46
|
+
.action(async () => {
|
|
47
|
+
await clearCredentials();
|
|
48
|
+
console.log("Sessão removida.");
|
|
49
|
+
});
|
|
50
|
+
program
|
|
51
|
+
.command("whoami")
|
|
52
|
+
.description("Mostra o usuário autenticado")
|
|
53
|
+
.action(async () => {
|
|
54
|
+
const creds = await loadCredentials();
|
|
55
|
+
if (!creds) {
|
|
56
|
+
console.error("Não autenticado. Rode `noclaf login <PAT>`.");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const spinner = startSpinner("Validando sessão...");
|
|
60
|
+
try {
|
|
61
|
+
const result = await exchangePatForJwt(creds.pat);
|
|
62
|
+
spinner.stop();
|
|
63
|
+
const u = result.user;
|
|
64
|
+
console.log(LOGO);
|
|
65
|
+
console.log(`Nome: ${u.full_name || "(sem nome)"}`);
|
|
66
|
+
console.log(`Email: ${u.email}`);
|
|
67
|
+
console.log(`Usuário: ${u.username ? `@${u.username}` : "(sem username)"}`);
|
|
68
|
+
console.log(`ID: ${u.id}`);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
spinner.fail(`Falha ao validar token: ${err.message}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/cli/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { select, confirm, password, input, checkbox } from "../../lib/prompts.js";
|
|
3
|
+
import { exchangePatForJwt, loadCredentials, saveCredentials, validatePatFormat, } from "../../auth.js";
|
|
4
|
+
import { createAuthenticatedClient } from "../../supabase.js";
|
|
5
|
+
import { getProjectConfigPath, writeProjectConfig, UUID_REGEX, } from "../../config.js";
|
|
6
|
+
import { fetchProjectContext, ProjectContextError, } from "../../lib/project-context.js";
|
|
7
|
+
import { installClaudeCodeGlobal, installClaudeCodeRepo, installCodexGlobal, installVSCodeRepo, } from "../../lib/client-configs.js";
|
|
8
|
+
import { readDependencies, skillIdMap } from "../../lib/skills.js";
|
|
9
|
+
import { fetchSkills } from "../../lib/skills-cache.js";
|
|
10
|
+
import { claudeTarget, codexTarget, } from "../../lib/targets.js";
|
|
11
|
+
import { c, sym, sectionTitle } from "../../lib/colors.js";
|
|
12
|
+
import { CLIENTS } from "../../lib/client-install.js";
|
|
13
|
+
import { startSpinner } from "../../spinner.js";
|
|
14
|
+
import { printInstallResult, printProvisionResult, printContextSummary, printHookResult, } from "../ui/render.js";
|
|
15
|
+
import { offerDependencyInstall } from "../flows/dependencies.js";
|
|
16
|
+
import { promptSections } from "../flows/sections.js";
|
|
17
|
+
import { track, provisionedItems } from "../../lib/telemetry.js";
|
|
18
|
+
import { VERSION } from "../../version.js";
|
|
19
|
+
import { ensureClientInstalled, activateCowork, offerDisableCoAuthoredBy, } from "../flows/clients.js";
|
|
20
|
+
import { provision } from "../../lib/provision.js";
|
|
21
|
+
import { provisionHooks } from "../../lib/hooks.js";
|
|
22
|
+
import { initCopy } from "../copy.js";
|
|
23
|
+
export function registerInitCommand(program) {
|
|
24
|
+
program
|
|
25
|
+
.command("init")
|
|
26
|
+
.description("Cria noclaf.json no diretório atual vinculando a um projeto do NOS")
|
|
27
|
+
.action(async () => {
|
|
28
|
+
const configPath = getProjectConfigPath();
|
|
29
|
+
if (existsSync(configPath)) {
|
|
30
|
+
const overwrite = await confirm({
|
|
31
|
+
message: initCopy.overwrite,
|
|
32
|
+
default: false,
|
|
33
|
+
});
|
|
34
|
+
if (!overwrite) {
|
|
35
|
+
console.log("Operação cancelada.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const existingCreds = await loadCredentials();
|
|
40
|
+
if (!existingCreds) {
|
|
41
|
+
console.log("Você ainda não está autenticado.");
|
|
42
|
+
console.log("Gere um token pessoal em: https://nos.noclaf.com.br/profile#mcp");
|
|
43
|
+
const newPat = (await password({
|
|
44
|
+
message: initCopy.patPrompt,
|
|
45
|
+
mask: "*",
|
|
46
|
+
validate: (value) => validatePatFormat(value.trim()) || initCopy.patInvalid,
|
|
47
|
+
})).trim();
|
|
48
|
+
const loginSpinner = startSpinner("Validando token...");
|
|
49
|
+
try {
|
|
50
|
+
const result = await exchangePatForJwt(newPat);
|
|
51
|
+
await saveCredentials({ pat: newPat });
|
|
52
|
+
loginSpinner.stop();
|
|
53
|
+
const displayName = result.user.full_name || result.user.email;
|
|
54
|
+
console.log(`[ok] Autenticado como ${displayName}`);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
loginSpinner.fail(`Falha ao autenticar: ${err.message}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const authSpinner = startSpinner("Carregando sessão...");
|
|
62
|
+
let session;
|
|
63
|
+
try {
|
|
64
|
+
session = await createAuthenticatedClient();
|
|
65
|
+
authSpinner.stop();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
authSpinner.fail(err.message);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const { supabase } = session;
|
|
72
|
+
const projectsSpinner = startSpinner("Buscando projetos...");
|
|
73
|
+
const projectsResult = await supabase
|
|
74
|
+
.from("projects")
|
|
75
|
+
.select("id, name, is_active")
|
|
76
|
+
.eq("is_active", true)
|
|
77
|
+
.order("name");
|
|
78
|
+
if (projectsResult.error) {
|
|
79
|
+
projectsSpinner.fail(`Erro ao buscar projetos: ${projectsResult.error.message}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
projectsSpinner.stop();
|
|
83
|
+
const projects = projectsResult.data ?? [];
|
|
84
|
+
if (projects.length === 0) {
|
|
85
|
+
console.error("Você não é membro de nenhum projeto. Peça a um admin pra te adicionar.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const MANUAL = "__manual__";
|
|
89
|
+
let projectId = await select({
|
|
90
|
+
message: initCopy.pickProject,
|
|
91
|
+
choices: [
|
|
92
|
+
...projects.map((p) => ({ name: p.name, value: p.id })),
|
|
93
|
+
{ name: initCopy.manualProjectChoice, value: MANUAL },
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
if (projectId === MANUAL) {
|
|
97
|
+
projectId = await input({
|
|
98
|
+
message: initCopy.manualProjectPrompt,
|
|
99
|
+
validate: (value) => UUID_REGEX.test(value.trim()) || initCopy.manualProjectInvalid,
|
|
100
|
+
});
|
|
101
|
+
projectId = projectId.trim();
|
|
102
|
+
}
|
|
103
|
+
const selectedProject = projects.find((p) => p.id === projectId) ?? {
|
|
104
|
+
id: projectId,
|
|
105
|
+
name: projectId,
|
|
106
|
+
};
|
|
107
|
+
const reposSpinner = startSpinner("Buscando repositórios...");
|
|
108
|
+
const reposResult = await supabase
|
|
109
|
+
.from("repositories")
|
|
110
|
+
.select("id, name")
|
|
111
|
+
.eq("project_id", projectId)
|
|
112
|
+
.order("name");
|
|
113
|
+
if (reposResult.error) {
|
|
114
|
+
reposSpinner.fail(`Erro ao buscar repositórios: ${reposResult.error.message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
reposSpinner.stop();
|
|
118
|
+
const repos = reposResult.data ?? [];
|
|
119
|
+
let repositoryId = null;
|
|
120
|
+
if (repos.length > 0) {
|
|
121
|
+
const NO_REPO = "__none__";
|
|
122
|
+
const choice = await select({
|
|
123
|
+
message: initCopy.linkRepo,
|
|
124
|
+
choices: [
|
|
125
|
+
{
|
|
126
|
+
name: initCopy.noRepoChoice,
|
|
127
|
+
value: NO_REPO,
|
|
128
|
+
},
|
|
129
|
+
...repos.map((r) => ({ name: r.name, value: r.id })),
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
repositoryId = choice === NO_REPO ? null : choice;
|
|
133
|
+
}
|
|
134
|
+
const config = { project_id: projectId };
|
|
135
|
+
if (repositoryId) {
|
|
136
|
+
config.repository_id = repositoryId;
|
|
137
|
+
}
|
|
138
|
+
config.sections = await promptSections();
|
|
139
|
+
writeProjectConfig(config);
|
|
140
|
+
console.log(`[ok] Criado noclaf.json apontando para ${selectedProject.name}.`);
|
|
141
|
+
const contextSpinner = startSpinner("Carregando contexto do projeto...");
|
|
142
|
+
try {
|
|
143
|
+
const context = await fetchProjectContext(supabase, config, session.user);
|
|
144
|
+
contextSpinner.stop();
|
|
145
|
+
printContextSummary(context);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const message = err instanceof ProjectContextError
|
|
149
|
+
? err.message
|
|
150
|
+
: err.message;
|
|
151
|
+
contextSpinner.fail(`Não foi possível carregar o contexto: ${message}`);
|
|
152
|
+
console.error("O noclaf.json foi salvo. Rode `noclaf init` de novo se precisar revincular.");
|
|
153
|
+
}
|
|
154
|
+
console.log("");
|
|
155
|
+
const clientConfigs = await checkbox({
|
|
156
|
+
message: initCopy.clientsPrompt,
|
|
157
|
+
choices: [
|
|
158
|
+
{
|
|
159
|
+
name: initCopy.clientChoices.claudeGlobal,
|
|
160
|
+
value: "claude-code-global",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: initCopy.clientChoices.codexGlobal,
|
|
164
|
+
value: "codex-global",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: initCopy.clientChoices.cowork,
|
|
168
|
+
value: "cowork",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: initCopy.clientChoices.claudeRepo,
|
|
172
|
+
value: "claude-code-repo",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: initCopy.clientChoices.vscodeRepo,
|
|
176
|
+
value: "vscode-repo",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
const cwd = process.cwd();
|
|
181
|
+
for (const choice of clientConfigs) {
|
|
182
|
+
if (choice === "cowork")
|
|
183
|
+
continue; // ativação async + fallback, tratada abaixo
|
|
184
|
+
try {
|
|
185
|
+
let result;
|
|
186
|
+
let label;
|
|
187
|
+
switch (choice) {
|
|
188
|
+
case "claude-code-global":
|
|
189
|
+
result = installClaudeCodeGlobal();
|
|
190
|
+
label = "Claude Code (global)";
|
|
191
|
+
break;
|
|
192
|
+
case "codex-global":
|
|
193
|
+
result = installCodexGlobal();
|
|
194
|
+
label = "Codex CLI (global)";
|
|
195
|
+
break;
|
|
196
|
+
case "claude-code-repo":
|
|
197
|
+
result = installClaudeCodeRepo(cwd);
|
|
198
|
+
label = "Claude Code (per-repo)";
|
|
199
|
+
break;
|
|
200
|
+
case "vscode-repo":
|
|
201
|
+
result = installVSCodeRepo(cwd);
|
|
202
|
+
label = "VS Code Copilot (per-repo)";
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
printInstallResult(label, result);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
console.error(`[erro] Falha ao configurar ${choice}: ${err.message}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (clientConfigs.includes("cowork")) {
|
|
212
|
+
console.log("");
|
|
213
|
+
console.log(sectionTitle("Claude Desktop / Cowork"));
|
|
214
|
+
await activateCowork({ interactive: true });
|
|
215
|
+
}
|
|
216
|
+
const chosenClientIds = new Set();
|
|
217
|
+
for (const choice of clientConfigs) {
|
|
218
|
+
if (choice.startsWith("claude-code"))
|
|
219
|
+
chosenClientIds.add("claude-code");
|
|
220
|
+
if (choice === "codex-global")
|
|
221
|
+
chosenClientIds.add("codex");
|
|
222
|
+
if (choice === "vscode-repo")
|
|
223
|
+
chosenClientIds.add("vscode");
|
|
224
|
+
}
|
|
225
|
+
if (chosenClientIds.size > 0) {
|
|
226
|
+
console.log("");
|
|
227
|
+
console.log(sectionTitle("Clientes", "confirmando que estão instalados"));
|
|
228
|
+
for (const id of chosenClientIds) {
|
|
229
|
+
await ensureClientInstalled(CLIENTS[id], { interactive: true });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (chosenClientIds.has("claude-code"))
|
|
233
|
+
await offerDisableCoAuthoredBy();
|
|
234
|
+
// claude-code-* → ~/.claude (nativo); codex-global → ~/.codex (traduzido pra skills).
|
|
235
|
+
const provisionTargets = new Set();
|
|
236
|
+
for (const choice of clientConfigs) {
|
|
237
|
+
if (choice.startsWith("claude-code"))
|
|
238
|
+
provisionTargets.add(claudeTarget);
|
|
239
|
+
if (choice === "codex-global")
|
|
240
|
+
provisionTargets.add(codexTarget);
|
|
241
|
+
}
|
|
242
|
+
if (provisionTargets.size === 0)
|
|
243
|
+
provisionTargets.add(claudeTarget);
|
|
244
|
+
const uidByName = skillIdMap();
|
|
245
|
+
console.log("");
|
|
246
|
+
console.log(sectionTitle("Skills & commands", "provisionando pros clientes"));
|
|
247
|
+
// Baixa o repo aberto de skills pro cache local antes de provisionar.
|
|
248
|
+
const fetched = await fetchSkills({ force: true });
|
|
249
|
+
if (fetched.status === "failed") {
|
|
250
|
+
console.error(` ${c.red(sym.err)} não consegui baixar as skills: ${fetched.error}`);
|
|
251
|
+
}
|
|
252
|
+
else if (fetched.status === "fetched") {
|
|
253
|
+
console.log(` ${c.green(sym.ok)} skills baixadas ${c.dim(`(${fetched.repo}@${fetched.ref})`)}`);
|
|
254
|
+
}
|
|
255
|
+
for (const target of provisionTargets) {
|
|
256
|
+
console.log("");
|
|
257
|
+
console.log(` ${c.magenta(sym.dot)} ${c.bold(target.label)}`);
|
|
258
|
+
try {
|
|
259
|
+
const result = provision({ target, sections: config.sections });
|
|
260
|
+
printProvisionResult(result);
|
|
261
|
+
track(session?.supabase ?? null, {
|
|
262
|
+
type: "provision",
|
|
263
|
+
client: target.id,
|
|
264
|
+
sections: config.sections,
|
|
265
|
+
items: provisionedItems(result.files, uidByName),
|
|
266
|
+
version: VERSION,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
console.error(`[aviso] Não consegui sincronizar skills/commands: ${err.message}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Hooks (só Claude Code): registra os gatilhos no ~/.claude/settings.json.
|
|
274
|
+
if (provisionTargets.has(claudeTarget)) {
|
|
275
|
+
console.log("");
|
|
276
|
+
console.log(sectionTitle("Hooks", "registrando no Claude Code"));
|
|
277
|
+
try {
|
|
278
|
+
printHookResult(provisionHooks({
|
|
279
|
+
surface: claudeTarget.surface,
|
|
280
|
+
sections: config.sections,
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
console.error(`[aviso] Não consegui registrar hooks: ${err.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// `init` é sempre interativo — oferece instalar as libs externas do bundle.
|
|
288
|
+
await offerDependencyInstall(readDependencies(), { interactive: true });
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=init.js.map
|