@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.
Files changed (120) hide show
  1. package/README.md +221 -0
  2. package/dist/auth.js +66 -0
  3. package/dist/auth.js.map +1 -0
  4. package/dist/cli/commands/auth.js +76 -0
  5. package/dist/cli/commands/auth.js.map +1 -0
  6. package/dist/cli/commands/init.js +291 -0
  7. package/dist/cli/commands/init.js.map +1 -0
  8. package/dist/cli/commands/skills.js +22 -0
  9. package/dist/cli/commands/skills.js.map +1 -0
  10. package/dist/cli/commands/sync.js +170 -0
  11. package/dist/cli/commands/sync.js.map +1 -0
  12. package/dist/cli/copy/auth.json +6 -0
  13. package/dist/cli/copy/flows.json +6 -0
  14. package/dist/cli/copy/init.json +19 -0
  15. package/dist/cli/copy/sync.json +3 -0
  16. package/dist/cli/copy.js +23 -0
  17. package/dist/cli/copy.js.map +1 -0
  18. package/dist/cli/flows/clients.js +108 -0
  19. package/dist/cli/flows/clients.js.map +1 -0
  20. package/dist/cli/flows/dependencies.js +68 -0
  21. package/dist/cli/flows/dependencies.js.map +1 -0
  22. package/dist/cli/flows/sections.js +45 -0
  23. package/dist/cli/flows/sections.js.map +1 -0
  24. package/dist/cli/ui/render.js +167 -0
  25. package/dist/cli/ui/render.js.map +1 -0
  26. package/dist/cli.js +25 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/config.js +96 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/constants.js +10 -0
  31. package/dist/constants.js.map +1 -0
  32. package/dist/database.types.js +11 -0
  33. package/dist/database.types.js.map +1 -0
  34. package/dist/lib/active-project.js +28 -0
  35. package/dist/lib/active-project.js.map +1 -0
  36. package/dist/lib/client-configs.js +179 -0
  37. package/dist/lib/client-configs.js.map +1 -0
  38. package/dist/lib/client-install.js +40 -0
  39. package/dist/lib/client-install.js.map +1 -0
  40. package/dist/lib/colors.js +67 -0
  41. package/dist/lib/colors.js.map +1 -0
  42. package/dist/lib/cowork-extension.js +131 -0
  43. package/dist/lib/cowork-extension.js.map +1 -0
  44. package/dist/lib/dependencies.js +159 -0
  45. package/dist/lib/dependencies.js.map +1 -0
  46. package/dist/lib/duration.js +5 -0
  47. package/dist/lib/duration.js.map +1 -0
  48. package/dist/lib/file-merge.js +33 -0
  49. package/dist/lib/file-merge.js.map +1 -0
  50. package/dist/lib/hooks.js +231 -0
  51. package/dist/lib/hooks.js.map +1 -0
  52. package/dist/lib/project-context.js +105 -0
  53. package/dist/lib/project-context.js.map +1 -0
  54. package/dist/lib/prompts.js +62 -0
  55. package/dist/lib/prompts.js.map +1 -0
  56. package/dist/lib/provision.js +222 -0
  57. package/dist/lib/provision.js.map +1 -0
  58. package/dist/lib/require-config.js +31 -0
  59. package/dist/lib/require-config.js.map +1 -0
  60. package/dist/lib/sections.js +85 -0
  61. package/dist/lib/sections.js.map +1 -0
  62. package/dist/lib/skill-serve.js +71 -0
  63. package/dist/lib/skill-serve.js.map +1 -0
  64. package/dist/lib/skills-cache.js +89 -0
  65. package/dist/lib/skills-cache.js.map +1 -0
  66. package/dist/lib/skills.js +219 -0
  67. package/dist/lib/skills.js.map +1 -0
  68. package/dist/lib/targets.js +150 -0
  69. package/dist/lib/targets.js.map +1 -0
  70. package/dist/lib/task-history.js +14 -0
  71. package/dist/lib/task-history.js.map +1 -0
  72. package/dist/lib/telemetry.js +32 -0
  73. package/dist/lib/telemetry.js.map +1 -0
  74. package/dist/lib/tool-result.js +12 -0
  75. package/dist/lib/tool-result.js.map +1 -0
  76. package/dist/lib/version-check.js +88 -0
  77. package/dist/lib/version-check.js.map +1 -0
  78. package/dist/logo.js +17 -0
  79. package/dist/logo.js.map +1 -0
  80. package/dist/mcp-server.js +162 -0
  81. package/dist/mcp-server.js.map +1 -0
  82. package/dist/spinner.js +12 -0
  83. package/dist/spinner.js.map +1 -0
  84. package/dist/supabase.js +95 -0
  85. package/dist/supabase.js.map +1 -0
  86. package/dist/tools/comment-task.js +80 -0
  87. package/dist/tools/comment-task.js.map +1 -0
  88. package/dist/tools/create-task.js +194 -0
  89. package/dist/tools/create-task.js.map +1 -0
  90. package/dist/tools/end-allocation.js +59 -0
  91. package/dist/tools/end-allocation.js.map +1 -0
  92. package/dist/tools/end-task-allocation.js +51 -0
  93. package/dist/tools/end-task-allocation.js.map +1 -0
  94. package/dist/tools/get-active-allocation.js +106 -0
  95. package/dist/tools/get-active-allocation.js.map +1 -0
  96. package/dist/tools/get-context.js +47 -0
  97. package/dist/tools/get-context.js.map +1 -0
  98. package/dist/tools/get-task.js +223 -0
  99. package/dist/tools/get-task.js.map +1 -0
  100. package/dist/tools/index.js +34 -0
  101. package/dist/tools/index.js.map +1 -0
  102. package/dist/tools/list-my-allocations.js +87 -0
  103. package/dist/tools/list-my-allocations.js.map +1 -0
  104. package/dist/tools/list-projects.js +34 -0
  105. package/dist/tools/list-projects.js.map +1 -0
  106. package/dist/tools/list-tasks.js +213 -0
  107. package/dist/tools/list-tasks.js.map +1 -0
  108. package/dist/tools/move-task.js +98 -0
  109. package/dist/tools/move-task.js.map +1 -0
  110. package/dist/tools/set-project.js +47 -0
  111. package/dist/tools/set-project.js.map +1 -0
  112. package/dist/tools/start-allocation.js +45 -0
  113. package/dist/tools/start-allocation.js.map +1 -0
  114. package/dist/tools/start-task-allocation.js +141 -0
  115. package/dist/tools/start-task-allocation.js.map +1 -0
  116. package/dist/tools/update-task.js +256 -0
  117. package/dist/tools/update-task.js.map +1 -0
  118. package/dist/version.js +16 -0
  119. package/dist/version.js.map +1 -0
  120. 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
@@ -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