pokt-cli 1.0.6 → 1.0.8

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 CHANGED
@@ -1,119 +1,112 @@
1
- # Pokt CLI
2
-
3
- CLI de **Vibe Coding** com IA: OpenRouter, Ollama (local e cloud), Gemini e provedor Pokt (controller).
4
-
5
- ## Requisitos
6
-
7
- - **Node.js** >= 18.0.0
8
-
9
- ## Instalação
10
-
11
- O pacote no npm chama-se **`pokt-cli`**; o comando no terminal é **`pokt`** (ou **`pokt-cli`**).
12
-
13
- ```bash
14
- # Instalação global (recomendado para o comando `pokt` no PATH)
15
- npm install -g pokt-cli
16
-
17
- # Sem instalar (sempre funciona em qualquer máquina)
18
- npx --yes pokt-cli
19
- ```
20
-
21
- ### O comando `pokt` não é reconhecido?
22
-
23
- 1. **Instalação local** (`npm install pokt-cli` dentro de um projeto): o `pokt` **não** fica no PATH global. Use:
24
- - `npx pokt-cli` ou
25
- - `npm exec pokt-cli` ou
26
- - `./node_modules/.bin/pokt` (Git Bash / WSL / macOS/Linux).
27
-
28
- 2. **Windows – pasta do npm no PATH**: após `npm install -g pokt-cli`, o executável fica na pasta global do npm. Confira:
29
- ```bash
30
- npm config get prefix
31
- ```
32
- O diretório **`prefix`** e a subpasta **`prefix\bin`** (ou no Windows muitas vezes **`%AppData%\npm`**) precisam estar no **PATH** do sistema. Reinicie o terminal após alterar.
33
-
34
- 3. **Windows PowerShell – “running scripts is disabled”**: o npm cria `pokt.ps1`. Se a política bloquear scripts, use **Prompt de Comando (cmd)** para rodar `pokt`, ou:
35
- ```powershell
36
- Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
37
- ```
38
- Ou continue usando **`npx pokt-cli`**, que costuma contornar o problema.
39
-
40
- 4. **Linux/macOS “bad interpreter” ou comando não encontrado**: em versões antigas publicadas com **CRLF** no arquivo da CLI, reinstale a versão mais recente (`npm install -g pokt-cli@latest`).
41
-
42
- ## Uso
43
-
44
- Sem argumentos, o Pokt abre um menu interativo:
45
-
46
- ```bash
47
- pokt
48
- ```
49
-
50
- Ou use os comandos diretamente:
51
-
52
- ```bash
53
- pokt chat # Iniciar chat (Vibe Coding)
54
- pokt models list # Listar modelos
55
- pokt provider use openrouter
56
- pokt config show
57
- pokt --help
58
- ```
59
-
60
- ## Comandos
61
-
62
- | Comando | Descrição |
63
- |--------|-----------|
64
- | `pokt` | Menu interativo |
65
- | `pokt chat` | Iniciar sessão de chat com a IA |
66
- | `pokt config <action>` | Configurar chaves e tokens |
67
- | `pokt models <action>` | Gerenciar modelos (listar, adicionar, trocar) |
68
- | `pokt provider use <provider>` | Trocar provedor de API |
69
- | `pokt mcp [action]` | Gerenciar servidores MCP (ferramentas externas) |
70
-
71
- ### Config (`config`)
72
-
73
- - `pokt config show` — Mostra a configuração atual (tokens mascarados).
74
- - `pokt config set-openrouter -v <token>` — Token OpenRouter.
75
- - `pokt config set-ollama -v <url>` — URL base do Ollama local.
76
- - `pokt config set-ollama-cloud -v <key>` API key Ollama Cloud.
77
- - `pokt config set-gemini -v <key>` API key Google Gemini.
78
- - `pokt config set-pokt-token -v <token>` Token do controller Pokt.
79
- - `pokt config clear-openrouter` — Remove o token OpenRouter.
80
-
81
- ### Modelos (`models`)
82
-
83
- - `pokt models list` — Lista modelos registrados e o ativo.
84
- - `pokt models fetch-openrouter` — Busca modelos disponíveis no OpenRouter.
85
- - `pokt models fetch-ollama` Busca modelos do Ollama local.
86
- - `pokt models fetch-ollama-cloud` — Busca modelos do Ollama Cloud.
87
- - `pokt models add-openrouter`, `add-ollama`, `add-ollama-cloud` — Adiciona modelo (use `-i <id>`).
88
- - `pokt models use -i <id> -p <provider>` — Define o modelo ativo.
89
-
90
- ### Provedores (`provider`)
91
-
92
- Provedores suportados: `controller` (Pokt), `openrouter`, `gemini`, `ollama`, `ollama-cloud`.
93
-
94
- ```bash
95
- pokt provider use openrouter
96
- pokt provider use ollama
97
- ```
98
-
99
- ## Desenvolvimento
100
-
101
- ```bash
102
- # Clonar e instalar
103
- git clone https://github.com/PoktWeb/Pokt_CLI.git
104
- cd Pokt_CLI
105
- npm install
106
-
107
- # Desenvolvimento (executa o TypeScript direto)
108
- npm run dev
109
-
110
- # Build
111
- npm run build
112
-
113
- # Executar após o build
114
- npm start
115
- ```
116
-
117
- ## Licença
118
-
119
- ISC · **PoktWeb**
1
+ # Pokt CLI
2
+
3
+ CLI de **Vibe Coding** com IA: OpenRouter, Ollama (local e cloud), Gemini e provedor Pokt (controller).
4
+
5
+ ## Requisitos
6
+
7
+ - **Node.js** >= 18.0.0
8
+
9
+ ## Instalação
10
+
11
+ ```bash
12
+ # Instalação principal (global)
13
+ npm install -g pokt-cli
14
+
15
+ # Alternativa: usar sem instalar
16
+ npx pokt-cli
17
+ ```
18
+
19
+ ## Uso
20
+
21
+ Sem argumentos, o Pokt abre um menu interativo:
22
+
23
+ ```bash
24
+ pokt
25
+ ```
26
+
27
+ ## Se o `pokt` não for reconhecido (Windows)
28
+
29
+ Se você instalou com `npm install -g pokt-cli` mas o PowerShell diz que `pokt` não existe, normalmente é **PATH do prefixo global do npm**.
30
+
31
+ ```bash
32
+ # Veja onde o npm instala pacotes globais
33
+ npm config get prefix
34
+
35
+ # Verifique se o comando foi criado (PowerShell)
36
+ where pokt
37
+ ```
38
+
39
+ - **Garanta no PATH**: adicione o `prefix` acima (geralmente algo como `%AppData%\\npm`) ao PATH do Windows.
40
+ - **Reinicie o terminal**: feche e abra o PowerShell/Terminal novamente.
41
+ - **Reinstale**: `npm uninstall -g pokt-cli && npm install -g pokt-cli`
42
+
43
+ Ou use os comandos diretamente:
44
+
45
+ ```bash
46
+ pokt chat # Iniciar chat (Vibe Coding)
47
+ pokt models list # Listar modelos
48
+ pokt provider use openrouter
49
+ pokt config show
50
+ pokt --help
51
+ ```
52
+
53
+ ## Comandos
54
+
55
+ | Comando | Descrição |
56
+ |--------|-----------|
57
+ | `pokt` | Menu interativo |
58
+ | `pokt chat` | Iniciar sessão de chat com a IA |
59
+ | `pokt config <action>` | Configurar chaves e tokens |
60
+ | `pokt models <action>` | Gerenciar modelos (listar, adicionar, trocar) |
61
+ | `pokt provider use <provider>` | Trocar provedor de API |
62
+ | `pokt mcp [action]` | Gerenciar servidores MCP (ferramentas externas) |
63
+
64
+ ### Config (`config`)
65
+
66
+ - `pokt config show` Mostra a configuração atual (tokens mascarados).
67
+ - `pokt config set-openrouter -v <token>` Token OpenRouter.
68
+ - `pokt config set-ollama -v <url>` URL base do Ollama local.
69
+ - `pokt config set-ollama-cloud -v <key>` API key Ollama Cloud.
70
+ - `pokt config set-gemini -v <key>` — API key Google Gemini.
71
+ - `pokt config set-pokt-token -v <token>` — Token do controller Pokt.
72
+ - `pokt config clear-openrouter` — Remove o token OpenRouter.
73
+
74
+ ### Modelos (`models`)
75
+
76
+ - `pokt models list` Lista modelos registrados e o ativo.
77
+ - `pokt models fetch-openrouter` Busca modelos disponíveis no OpenRouter.
78
+ - `pokt models fetch-ollama` Busca modelos do Ollama local.
79
+ - `pokt models fetch-ollama-cloud` — Busca modelos do Ollama Cloud.
80
+ - `pokt models add-openrouter`, `add-ollama`, `add-ollama-cloud` — Adiciona modelo (use `-i <id>`).
81
+ - `pokt models use -i <id> -p <provider>` — Define o modelo ativo.
82
+
83
+ ### Provedores (`provider`)
84
+
85
+ Provedores suportados: `controller` (Pokt), `openrouter`, `gemini`, `ollama`, `ollama-cloud`.
86
+
87
+ ```bash
88
+ pokt provider use openrouter
89
+ pokt provider use ollama
90
+ ```
91
+
92
+ ## Desenvolvimento
93
+
94
+ ```bash
95
+ # Clonar e instalar
96
+ git clone https://github.com/PoktWeb/Pokt_CLI.git
97
+ cd Pokt_CLI
98
+ npm install
99
+
100
+ # Desenvolvimento (executa o TypeScript direto)
101
+ npm run dev
102
+
103
+ # Build
104
+ npm run build
105
+
106
+ # Executar após o build
107
+ npm start
108
+ ```
109
+
110
+ ## Licença
111
+
112
+ ISC · **PoktWeb**
package/dist/bin/pokt.js CHANGED
@@ -37,7 +37,7 @@ async function showMenu() {
37
37
  const active = getEffectiveActiveModel();
38
38
  const providerLabel = active ? (PROVIDER_LABELS[active.provider] ?? active.provider) : 'No provider';
39
39
  console.log('');
40
- console.log(ui.banner());
40
+ await ui.printBanner({ animate: true });
41
41
  console.log(ui.statusLine(providerLabel));
42
42
  console.log('');
43
43
  console.log(ui.separator());
package/dist/chat/loop.js CHANGED
@@ -2,9 +2,11 @@ import prompts from 'prompts';
2
2
  import ora from 'ora';
3
3
  import { ui } from '../ui.js';
4
4
  import { runProFlow } from '../commands/pro.js';
5
+ import { PROVIDER_LABELS, ALL_PROVIDERS } from '../config.js';
5
6
  import { config } from '../config.js';
6
7
  import { getClient } from './client.js';
7
8
  import { tools, executeTool } from './tools.js';
9
+ import { saveAuto, loadAuto, listCheckpoints, saveCheckpoint, loadCheckpoint, deleteCheckpoint, exportConversation, getSessionsDir } from './sessions.js';
8
10
  import { connectMcpServer, getAllMcpToolsOpenAI, callMcpTool, isMcpTool, disconnectAllMcp, } from '../mcp/client.js';
9
11
  const SYSTEM_PROMPT = `You are Pokt CLI, an elite AI Software Engineer.
10
12
  Your goal is to help the user build, fix, and maintain software projects with high quality.
@@ -46,7 +48,8 @@ async function loadProjectStructure() {
46
48
  }
47
49
  }
48
50
  export async function startChatLoop(modelConfig) {
49
- const client = await getClient(modelConfig);
51
+ let activeModel = modelConfig;
52
+ let client = await getClient(activeModel);
50
53
  // Conectar servidores MCP configurados e montar lista de tools (nativas + MCP)
51
54
  const mcpServers = config.get('mcpServers') ?? [];
52
55
  for (const server of mcpServers) {
@@ -59,14 +62,174 @@ export async function startChatLoop(modelConfig) {
59
62
  ...tools,
60
63
  ...getAllMcpToolsOpenAI(),
61
64
  ];
62
- const messages = [
63
- { role: 'system', content: SYSTEM_PROMPT }
64
- ];
65
+ const messages = [{ role: 'system', content: SYSTEM_PROMPT }];
66
+ // Auto-resume do projeto (estilo gemini): se existir, adiciona mensagens anteriores (sem duplicar system prompt)
67
+ const prev = loadAuto();
68
+ if (prev && prev.length > 0) {
69
+ for (const m of prev) {
70
+ if (m.role === 'system')
71
+ continue;
72
+ messages.push({ role: m.role, content: m.content });
73
+ }
74
+ console.log(ui.dim(`Sessão anterior carregada (projeto). Use /chat list | /chat save <tag>.`));
75
+ }
76
+ function modelLabel(m) {
77
+ const provider = PROVIDER_LABELS[m.provider] ?? m.provider;
78
+ return `[${provider}] ${m.id}`;
79
+ }
80
+ async function switchModelFlow(mode = 'model') {
81
+ const models = config.get('registeredModels') ?? [];
82
+ if (!Array.isArray(models) || models.length === 0) {
83
+ console.log(ui.error('Nenhum modelo registrado. Rode: pokt models list'));
84
+ return;
85
+ }
86
+ const providerChoices = ALL_PROVIDERS.map((p) => {
87
+ const label = PROVIDER_LABELS[p] ?? p;
88
+ const hasAny = models.some((m) => m.provider === p);
89
+ const star = activeModel.provider === p ? '★ ' : '';
90
+ return {
91
+ title: `${star}${label}${hasAny ? '' : ' (sem modelos)'}`,
92
+ value: p,
93
+ disabled: !hasAny,
94
+ };
95
+ });
96
+ const providerPick = await prompts({
97
+ type: 'select',
98
+ name: 'provider',
99
+ message: mode === 'provider' ? 'Trocar provedor:' : 'Selecione o provedor:',
100
+ choices: [...providerChoices, { title: '🔙 Cancelar', value: 'cancel' }],
101
+ });
102
+ if (!providerPick.provider || providerPick.provider === 'cancel')
103
+ return;
104
+ const provider = providerPick.provider;
105
+ const providerModels = models.filter((m) => m.provider === provider);
106
+ if (providerModels.length === 0) {
107
+ console.log(ui.error(`Sem modelos para ${PROVIDER_LABELS[provider] ?? provider}.`));
108
+ return;
109
+ }
110
+ let selected = null;
111
+ if (mode === 'provider') {
112
+ selected = providerModels[0];
113
+ }
114
+ else {
115
+ const pick = await prompts({
116
+ type: 'select',
117
+ name: 'idx',
118
+ message: `Modelos em ${PROVIDER_LABELS[provider] ?? provider}:`,
119
+ choices: [
120
+ ...providerModels.map((m, i) => ({
121
+ title: `${activeModel.provider === m.provider && activeModel.id === m.id ? '★ ' : ''}${m.id}`,
122
+ value: i,
123
+ })),
124
+ { title: '🔙 Cancelar', value: 'cancel' },
125
+ ],
126
+ });
127
+ if (pick.idx === 'cancel' || typeof pick.idx !== 'number')
128
+ return;
129
+ selected = providerModels[pick.idx] ?? null;
130
+ }
131
+ if (!selected)
132
+ return;
133
+ // Validar chaves/credenciais necessárias (reaproveita mesma lógica do providerCommand / getClient)
134
+ try {
135
+ const newClient = await getClient(selected);
136
+ client = newClient;
137
+ }
138
+ catch (e) {
139
+ console.log(ui.error(e?.message ?? String(e)));
140
+ return;
141
+ }
142
+ activeModel = selected;
143
+ config.set('activeModel', selected);
144
+ console.log(ui.success(`Modelo ativo atualizado: ${modelLabel(selected)}`));
145
+ console.log(ui.dim('Dica: o histórico do chat foi mantido; apenas o modelo/provedor mudou.'));
146
+ }
147
+ function printHelp() {
148
+ console.log(ui.dim(`
149
+ Comandos do chat:
150
+ ${ui.accent('/help')} — mostra esta ajuda
151
+ ${ui.accent('/clear')} — limpa a tela
152
+ ${ui.accent('/status')} — mostra modelo/provider atual
153
+ ${ui.accent('/model')} — trocar modelo (menu interativo)
154
+ ${ui.accent('/provider')} — trocar provedor (usa o 1º modelo disponível)
155
+ ${ui.accent('/chat')} — checkpoints/sessões (list/save/resume/delete/share)
156
+ ${ui.accent('/resume')} — alias de /chat
157
+ ${ui.accent('/copy')} — copia a última resposta do Pokt (Windows: clip)
158
+ ${ui.accent('/pro')} — abrir Pokt Pro no navegador
159
+ ${ui.accent('/quit')} ou ${ui.accent('exit')} — sair do chat
160
+ `));
161
+ }
162
+ let lastAssistantText = '';
163
+ async function handleChatCommand(raw) {
164
+ const parts = raw.trim().split(/\s+/);
165
+ const cmd = parts[0].toLowerCase();
166
+ const sub = (parts[1] ?? 'list').toLowerCase();
167
+ const arg = parts.slice(2).join(' ').trim();
168
+ if (sub === 'dir') {
169
+ console.log(ui.dim(`Sessões: ${getSessionsDir()}`));
170
+ return;
171
+ }
172
+ if (sub === 'list') {
173
+ const items = listCheckpoints();
174
+ if (items.length === 0) {
175
+ console.log(ui.dim('Nenhum checkpoint salvo ainda. Use: /chat save <tag>'));
176
+ return;
177
+ }
178
+ console.log(ui.dim('Checkpoints:'));
179
+ for (const it of items) {
180
+ console.log(`- ${ui.accent(it.tag)} ${it.updatedAt ? ui.dim(`(${it.updatedAt})`) : ''}`);
181
+ }
182
+ return;
183
+ }
184
+ if (sub === 'save') {
185
+ if (!arg) {
186
+ console.log(ui.error('Uso: /chat save <tag>'));
187
+ return;
188
+ }
189
+ saveCheckpoint(arg, messages);
190
+ console.log(ui.success(`Checkpoint salvo: ${arg}`));
191
+ return;
192
+ }
193
+ if (sub === 'resume' || sub === 'load') {
194
+ if (!arg) {
195
+ console.log(ui.error('Uso: /chat resume <tag>'));
196
+ return;
197
+ }
198
+ const loaded = loadCheckpoint(arg);
199
+ // mantém system prompt; substitui resto
200
+ const sys = messages[0];
201
+ messages.length = 0;
202
+ messages.push(sys);
203
+ for (const m of loaded) {
204
+ if (m.role === 'system')
205
+ continue;
206
+ messages.push({ role: m.role, content: m.content });
207
+ }
208
+ console.log(ui.success(`Checkpoint carregado: ${arg}`));
209
+ return;
210
+ }
211
+ if (sub === 'delete' || sub === 'rm') {
212
+ if (!arg) {
213
+ console.log(ui.error('Uso: /chat delete <tag>'));
214
+ return;
215
+ }
216
+ deleteCheckpoint(arg);
217
+ console.log(ui.success(`Checkpoint removido: ${arg}`));
218
+ return;
219
+ }
220
+ if (sub === 'share' || sub === 'export') {
221
+ const filename = arg || `pokt-chat-${Date.now()}.md`;
222
+ const out = exportConversation(filename, messages);
223
+ console.log(ui.success(`Exportado: ${out}`));
224
+ return;
225
+ }
226
+ console.log(ui.warn(`Subcomando desconhecido: ${sub}. Use /chat list|save|resume|delete|share`));
227
+ }
65
228
  while (true) {
66
229
  console.log('');
67
230
  const cwd = process.cwd();
68
231
  console.log(ui.dim(`Diretório atual: ${cwd}`));
69
- console.log(ui.shortcutsLine('shift+tab to accept edits', '? · /pro (Torne-se Pro)'));
232
+ console.log(ui.shortcutsLine(undefined, '? · /help · /pro'));
70
233
  const response = await prompts({
71
234
  type: 'text',
72
235
  name: 'input',
@@ -82,6 +245,55 @@ export async function startChatLoop(modelConfig) {
82
245
  }
83
246
  const trimmed = userInput.trim();
84
247
  const low = trimmed.toLowerCase();
248
+ if (low === '/help' || low === '/?' || low === 'help') {
249
+ printHelp();
250
+ continue;
251
+ }
252
+ if (low === '/clear') {
253
+ console.clear();
254
+ continue;
255
+ }
256
+ if (low === '/status') {
257
+ console.log(ui.statusBar({ cwd: process.cwd(), model: `/model ${activeModel.provider} (${activeModel.id})` }));
258
+ continue;
259
+ }
260
+ if (low.startsWith('/chat') || low.startsWith('/resume')) {
261
+ await handleChatCommand(trimmed.replace(/^\/resume/i, '/chat'));
262
+ continue;
263
+ }
264
+ if (low === '/copy') {
265
+ try {
266
+ if (!lastAssistantText) {
267
+ console.log(ui.warn('Nada para copiar ainda.'));
268
+ continue;
269
+ }
270
+ // Sem interpolar conteúdo no comando (evita quebra por aspas/newlines).
271
+ // Escreve para um arquivo temporário e copia via Get-Content | clip (Windows).
272
+ const tmp = `.pokt_copy_${Date.now()}.txt`;
273
+ await executeTool('write_file', JSON.stringify({ path: tmp, content: lastAssistantText }));
274
+ await executeTool('run_command', JSON.stringify({ command: `powershell -NoProfile -Command "Get-Content -Raw '${tmp}' | clip"` }));
275
+ // best-effort cleanup
276
+ try {
277
+ await executeTool('delete_file', JSON.stringify({ path: tmp }));
278
+ }
279
+ catch {
280
+ // ignore
281
+ }
282
+ console.log(ui.success('Copiado para a área de transferência.'));
283
+ }
284
+ catch {
285
+ console.log(ui.warn('Falha ao copiar. Se estiver no Windows, verifique se o comando "clip" está disponível.'));
286
+ }
287
+ continue;
288
+ }
289
+ if (low === '/model') {
290
+ await switchModelFlow('model');
291
+ continue;
292
+ }
293
+ if (low === '/provider') {
294
+ await switchModelFlow('provider');
295
+ continue;
296
+ }
85
297
  if (low === '/pro' || low === '/torne-se-pro' || low === 'torne-se pro') {
86
298
  runProFlow();
87
299
  continue;
@@ -91,10 +303,12 @@ export async function startChatLoop(modelConfig) {
91
303
  Atalhos:
92
304
  ${ui.accent('/pro')} ou ${ui.accent('/torne-se-pro')} — abrir Pokt Pro no navegador (pagamento + chave)
93
305
  exit, ${ui.accent('/quit')} — sair do chat
306
+ ${ui.accent('/help')} — ver comandos do chat
94
307
  `));
95
308
  continue;
96
309
  }
97
310
  messages.push({ role: 'user', content: userInput });
311
+ saveAuto(messages);
98
312
  // Primeiro o modelo vê o pedido; depois carregamos a estrutura do projeto para ele entender e então criar/editar
99
313
  const isFirstUserMessage = messages.filter(m => m.role === 'user').length === 1;
100
314
  if (isFirstUserMessage) {
@@ -103,7 +317,18 @@ Atalhos:
103
317
  loadSpinner.stop();
104
318
  messages.push({ role: 'system', content: `Current Project Structure:\n${projectStructure}` });
105
319
  }
106
- await processLLMResponse(client, modelConfig.id, messages, allTools);
320
+ await processLLMResponse(client, activeModel.id, messages, allTools);
321
+ // Atualiza auto-save após resposta
322
+ saveAuto(messages);
323
+ // Captura última resposta do assistente para /copy (melhor esforço)
324
+ for (let i = messages.length - 1; i >= 0; i--) {
325
+ const m = messages[i];
326
+ if (m?.role === 'assistant') {
327
+ const c = m.content;
328
+ lastAssistantText = typeof c === 'string' ? c : Array.isArray(c) ? c.map((p) => (p && p.text ? p.text : String(p))).join('') : String(c ?? '');
329
+ break;
330
+ }
331
+ }
107
332
  }
108
333
  await disconnectAllMcp();
109
334
  }
@@ -0,0 +1,18 @@
1
+ import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions/completions.js';
2
+ type PersistedMessage = {
3
+ role: 'system' | 'user' | 'assistant' | 'tool';
4
+ content: string;
5
+ };
6
+ export declare function getProjectHash(cwd?: string): string;
7
+ export declare function getSessionsDir(): string;
8
+ export declare function saveAuto(messages: ChatCompletionMessageParam[]): void;
9
+ export declare function loadAuto(): PersistedMessage[] | null;
10
+ export declare function listCheckpoints(): Array<{
11
+ tag: string;
12
+ updatedAt?: string;
13
+ }>;
14
+ export declare function saveCheckpoint(tag: string, messages: ChatCompletionMessageParam[]): void;
15
+ export declare function loadCheckpoint(tag: string): PersistedMessage[];
16
+ export declare function deleteCheckpoint(tag: string): void;
17
+ export declare function exportConversation(filename: string, messages: ChatCompletionMessageParam[]): string;
18
+ export {};
@@ -0,0 +1,120 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import crypto from 'crypto';
5
+ function toPersisted(messages) {
6
+ return messages
7
+ .filter((m) => !!m && typeof m === 'object' && 'role' in m)
8
+ .map((m) => {
9
+ const role = m.role;
10
+ const content = m.content;
11
+ if (typeof content === 'string')
12
+ return { role, content };
13
+ if (Array.isArray(content)) {
14
+ const text = content
15
+ .map((part) => (part && typeof part === 'object' && 'text' in part ? String(part.text) : String(part)))
16
+ .join('');
17
+ return { role, content: text };
18
+ }
19
+ return { role, content: content != null ? String(content) : '' };
20
+ });
21
+ }
22
+ function ensureDir(p) {
23
+ fs.mkdirSync(p, { recursive: true });
24
+ }
25
+ function safeTag(tag) {
26
+ return tag.trim().replace(/[<>:"/\\|?*\x00-\x1F]/g, '_').slice(0, 80);
27
+ }
28
+ export function getProjectHash(cwd = process.cwd()) {
29
+ const h = crypto.createHash('sha1');
30
+ h.update(path.resolve(cwd));
31
+ return h.digest('hex').slice(0, 12);
32
+ }
33
+ export function getSessionsDir() {
34
+ // compatível Windows/mac/linux
35
+ const base = path.join(os.homedir(), '.pokt', 'tmp');
36
+ const proj = getProjectHash();
37
+ const dir = path.join(base, proj);
38
+ ensureDir(dir);
39
+ return dir;
40
+ }
41
+ function getAutoPath() {
42
+ return path.join(getSessionsDir(), 'auto.json');
43
+ }
44
+ export function saveAuto(messages) {
45
+ const payload = {
46
+ updatedAt: new Date().toISOString(),
47
+ cwd: process.cwd(),
48
+ messages: toPersisted(messages),
49
+ };
50
+ fs.writeFileSync(getAutoPath(), JSON.stringify(payload, null, 2), 'utf8');
51
+ }
52
+ export function loadAuto() {
53
+ try {
54
+ const raw = fs.readFileSync(getAutoPath(), 'utf8');
55
+ const parsed = JSON.parse(raw);
56
+ return Array.isArray(parsed?.messages) ? parsed.messages : null;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ function tagPath(tag) {
63
+ return path.join(getSessionsDir(), `checkpoint.${safeTag(tag)}.json`);
64
+ }
65
+ export function listCheckpoints() {
66
+ const dir = getSessionsDir();
67
+ const files = fs.readdirSync(dir).filter((f) => f.startsWith('checkpoint.') && f.endsWith('.json'));
68
+ return files
69
+ .map((f) => {
70
+ const tag = f.replace(/^checkpoint\./, '').replace(/\.json$/, '');
71
+ try {
72
+ const raw = fs.readFileSync(path.join(dir, f), 'utf8');
73
+ const parsed = JSON.parse(raw);
74
+ return { tag, updatedAt: parsed.updatedAt };
75
+ }
76
+ catch {
77
+ return { tag };
78
+ }
79
+ })
80
+ .sort((a, b) => String(b.updatedAt ?? '').localeCompare(String(a.updatedAt ?? '')));
81
+ }
82
+ export function saveCheckpoint(tag, messages) {
83
+ const t = safeTag(tag);
84
+ if (!t)
85
+ throw new Error('Tag inválida.');
86
+ const payload = {
87
+ tag: t,
88
+ updatedAt: new Date().toISOString(),
89
+ cwd: process.cwd(),
90
+ messages: toPersisted(messages),
91
+ };
92
+ fs.writeFileSync(tagPath(t), JSON.stringify(payload, null, 2), 'utf8');
93
+ }
94
+ export function loadCheckpoint(tag) {
95
+ const t = safeTag(tag);
96
+ const raw = fs.readFileSync(tagPath(t), 'utf8');
97
+ const parsed = JSON.parse(raw);
98
+ if (!Array.isArray(parsed?.messages))
99
+ throw new Error('Checkpoint inválido.');
100
+ return parsed.messages;
101
+ }
102
+ export function deleteCheckpoint(tag) {
103
+ const t = safeTag(tag);
104
+ fs.unlinkSync(tagPath(t));
105
+ }
106
+ export function exportConversation(filename, messages) {
107
+ const out = path.isAbsolute(filename) ? filename : path.join(process.cwd(), filename);
108
+ const ext = path.extname(out).toLowerCase();
109
+ if (ext === '.json') {
110
+ fs.writeFileSync(out, JSON.stringify({ exportedAt: new Date().toISOString(), messages: toPersisted(messages) }, null, 2), 'utf8');
111
+ return out;
112
+ }
113
+ // markdown
114
+ const md = toPersisted(messages)
115
+ .filter((m) => m.role !== 'system')
116
+ .map((m) => `## ${m.role}\n\n${m.content}\n`)
117
+ .join('\n');
118
+ fs.writeFileSync(out, `# Pokt CLI chat export\n\nExportado em: ${new Date().toISOString()}\n\n${md}`, 'utf8');
119
+ return out;
120
+ }
@@ -27,7 +27,7 @@ export const chatCommand = {
27
27
  }
28
28
  // Se veio do menu interativo, não repetir banner/tips (já foram exibidos)
29
29
  if (!fromMenu) {
30
- console.log(ui.banner());
30
+ await ui.printBanner({ animate: true });
31
31
  console.log(ui.statusLine(`[${activeModel.provider}] ${activeModel.id}`));
32
32
  console.log('');
33
33
  console.log(ui.tips());
package/dist/ui.d.ts CHANGED
@@ -9,8 +9,13 @@ export declare const ui: {
9
9
  labelPokt: () => any;
10
10
  accent: (text: string) => any;
11
11
  muted: (text: string) => any;
12
- /** Banner principal estilo Gemini CLI: logo + nome + versão */
12
+ /** Banner principal em ASCII (Pokt CLI) */
13
13
  banner: (customVersion?: string) => string;
14
+ /** Imprime o banner com “typewriter” (letra por letra) */
15
+ printBanner: (opts?: {
16
+ animate?: boolean;
17
+ version?: string;
18
+ }) => Promise<void>;
14
19
  /** Status de login / provider (uma linha) */
15
20
  statusLine: (providerLabel: string, configPath?: string) => any;
16
21
  /** Seção "Tips for getting started" */
package/dist/ui.js CHANGED
@@ -2,15 +2,101 @@ import chalk from 'chalk';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { execSync } from 'child_process';
5
- const VERSION = '1.0.5';
6
- /** Logo em estilo chevron com gradiente (azul → rosa → roxo) */
7
- function logo() {
8
- const c = (s, color) => color(s);
9
- const block = (char, col) => col(char);
10
- const blue = chalk.rgb(100, 149, 237);
11
- const pink = chalk.rgb(255, 105, 180);
12
- const purple = chalk.rgb(147, 112, 219);
13
- return block('▸', blue) + ' ' + block('▸', pink) + ' ' + block('▸', purple);
5
+ function sleep(ms) {
6
+ return new Promise(resolve => setTimeout(resolve, ms));
7
+ }
8
+ function lerp(a, b, t) {
9
+ return a + (b - a) * t;
10
+ }
11
+ function gradientColor(idx, len, start, end) {
12
+ if (len <= 1)
13
+ return start;
14
+ const t = idx / (len - 1);
15
+ return [
16
+ Math.round(lerp(start[0], end[0], t)),
17
+ Math.round(lerp(start[1], end[1], t)),
18
+ Math.round(lerp(start[2], end[2], t)),
19
+ ];
20
+ }
21
+ function gradientChars(text, start, end) {
22
+ const chars = [...text];
23
+ return chars.map((ch, i) => {
24
+ if (ch === ' ')
25
+ return ' ';
26
+ const [r, g, b] = gradientColor(i, chars.length, start, end);
27
+ return chalk.rgb(r, g, b).bold(ch);
28
+ });
29
+ }
30
+ function getVersion() {
31
+ // Prioridade: env de empacotamento → package.json → fallback
32
+ const envVer = process.env.POKT_CLI_VERSION || process.env.npm_package_version;
33
+ if (envVer)
34
+ return envVer;
35
+ try {
36
+ const pkgPath = path.resolve(process.cwd(), 'package.json');
37
+ if (fs.existsSync(pkgPath)) {
38
+ const raw = fs.readFileSync(pkgPath, 'utf8');
39
+ const parsed = JSON.parse(raw);
40
+ if (parsed?.version)
41
+ return parsed.version;
42
+ }
43
+ }
44
+ catch {
45
+ // ignore
46
+ }
47
+ return 'dev';
48
+ }
49
+ function bannerLines(ver) {
50
+ const left = [
51
+ '██████╗ ██████╗ ██╗ ██╗████████╗',
52
+ '██╔══██╗██╔═══██╗██║ ██╔╝╚══██╔══╝',
53
+ '██████╔╝██║ ██║█████╔╝ ██║',
54
+ '██╔═══╝ ██║ ██║██╔═██╗ ██║',
55
+ '██║ ╚██████╔╝██║ ██╗ ██║',
56
+ '╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝',
57
+ ];
58
+ const right = [
59
+ ' ██████╗██╗ ██╗',
60
+ ' ██╔════╝██║ ██║',
61
+ ' ██║ ██║ ██║',
62
+ ' ██║ ██║ ██║',
63
+ ' ╚██████╗███████╗██║',
64
+ ' ╚═════╝╚══════╝╚═╝',
65
+ ];
66
+ const lines = left.map((l, i) => `${l}${right[i] ?? ''}`);
67
+ lines.push(' '.repeat(18) + `CLI Version v${ver}`);
68
+ return lines;
69
+ }
70
+ function bannerAscii(ver) {
71
+ const start = [0, 205, 255]; // azul/ciano
72
+ const end = [155, 89, 255]; // roxo
73
+ const lines = bannerLines(ver);
74
+ return lines
75
+ .map((line, i) => {
76
+ if (i === lines.length - 1)
77
+ return chalk.gray(line);
78
+ return gradientChars(line, start, end).join('');
79
+ })
80
+ .join('\n');
81
+ }
82
+ async function printBannerAnimated(customVersion) {
83
+ const ver = customVersion ?? getVersion();
84
+ const start = [0, 205, 255];
85
+ const end = [155, 89, 255];
86
+ const lines = bannerLines(ver);
87
+ for (let li = 0; li < lines.length; li++) {
88
+ const line = lines[li] ?? '';
89
+ if (li === lines.length - 1) {
90
+ process.stdout.write(chalk.gray(line) + '\n');
91
+ continue;
92
+ }
93
+ const colored = gradientChars(line, start, end);
94
+ for (let i = 0; i <= colored.length; i++) {
95
+ process.stdout.write('\r' + colored.slice(0, i).join(''));
96
+ await sleep(1);
97
+ }
98
+ process.stdout.write('\n');
99
+ }
14
100
  }
15
101
  export const ui = {
16
102
  title: (text) => chalk.whiteBright.bold(text),
@@ -19,33 +105,40 @@ export const ui = {
19
105
  error: (text) => chalk.red(text),
20
106
  warn: (text) => chalk.yellow(text),
21
107
  dim: (text) => chalk.gray(text),
22
- labelYou: () => chalk.cyan('You:'),
108
+ labelYou: () => chalk.cyan('Você:'),
23
109
  labelPokt: () => chalk.green('Pokt:'),
24
110
  accent: (text) => chalk.blue(text),
25
111
  muted: (text) => chalk.gray(text),
26
- /** Banner principal estilo Gemini CLI: logo + nome + versão */
112
+ /** Banner principal em ASCII (Pokt CLI) */
27
113
  banner: (customVersion) => {
28
- const ver = customVersion ?? VERSION;
29
- const line1 = logo() + ' ' + chalk.whiteBright.bold('Pokt CLI') + chalk.gray(` v${ver}`);
30
- return line1;
114
+ const ver = customVersion ?? getVersion();
115
+ return bannerAscii(ver);
116
+ },
117
+ /** Imprime o banner com “typewriter” (letra por letra) */
118
+ printBanner: async (opts) => {
119
+ if (opts?.animate) {
120
+ await printBannerAnimated(opts.version);
121
+ return;
122
+ }
123
+ console.log(ui.banner(opts?.version));
31
124
  },
32
125
  /** Status de login / provider (uma linha) */
33
126
  statusLine: (providerLabel, configPath = '/config') => {
34
- const auth = chalk.gray(`Logged in with ${providerLabel} ${chalk.underline(configPath)}`);
127
+ const auth = chalk.gray(`Ativo: ${providerLabel} ${chalk.underline(configPath)}`);
35
128
  return auth;
36
129
  },
37
130
  /** Seção "Tips for getting started" */
38
131
  tips: () => {
39
- const title = chalk.white('Tips for getting started:');
132
+ const title = chalk.white('Dicas para começar:');
40
133
  const tips = [
41
- chalk.gray('1. /help for more information'),
42
- chalk.gray('2. Ask coding questions, edit code or run commands'),
43
- chalk.gray('3. Be specific for the best results'),
134
+ chalk.gray('1. Digite /help para ver comandos'),
135
+ chalk.gray('2. Peça ajuda para codar, editar arquivos ou rodar comandos'),
136
+ chalk.gray('3. Seja específico para melhores resultados'),
44
137
  ].join('\n');
45
138
  return title + '\n' + tips;
46
139
  },
47
140
  /** Linha de atalhos acima do input */
48
- shortcutsLine: (left = 'shift+tab to accept edits', right = '? for shortcuts', center) => {
141
+ shortcutsLine: (left = 'shift+tab para aceitar edições', right = '? para atalhos', center) => {
49
142
  const l = chalk.gray(left);
50
143
  const r = chalk.gray(right);
51
144
  const c = center ? chalk.gray(center) : '';
@@ -55,7 +148,7 @@ export const ui = {
55
148
  return l + ' '.repeat(Math.max(0, 60 - left.length - right.length)) + r;
56
149
  },
57
150
  /** Placeholder do input */
58
- inputPlaceholder: () => chalk.gray('Type your message or @path/to/file'),
151
+ inputPlaceholder: () => chalk.gray('Digite sua mensagem (ou /help)'),
59
152
  /** Barra de status inferior: path, branch, sandbox, model */
60
153
  statusBar: (opts) => {
61
154
  const cwd = opts.cwd ?? process.cwd();
package/package.json CHANGED
@@ -1,62 +1,61 @@
1
- {
2
- "name": "pokt-cli",
3
- "version": "1.0.6",
4
- "description": "Vibe Coding AI CLI for OpenRouter and Ollama",
5
- "main": "./dist/bin/pokt.js",
6
- "type": "module",
7
- "bin": {
8
- "pokt": "dist/bin/pokt.js",
9
- "pokt-cli": "dist/bin/pokt.js"
10
- },
11
- "scripts": {
12
- "build": "tsc && node scripts/ensure-bin-lf.js",
13
- "start": "node ./dist/bin/pokt.js",
14
- "dev": "tsx ./src/bin/pokt.ts",
15
- "prepublishOnly": "npm run build"
16
- },
17
- "files": [
18
- "dist",
19
- "package.json",
20
- "README.md"
21
- ],
22
- "keywords": [
23
- "ai",
24
- "cli",
25
- "coding",
26
- "agent",
27
- "openrouter",
28
- "ollama"
29
- ],
30
- "author": "PoktWeb",
31
- "license": "ISC",
32
- "engines": {
33
- "node": ">=18.0.0"
34
- },
35
- "repository": {
36
- "type": "git",
37
- "url": "git+https://github.com/PoktWeb/Pokt_CLI.git"
38
- },
39
- "homepage": "https://github.com/PoktWeb/Pokt_CLI#readme",
40
- "bugs": {
41
- "url": "https://github.com/PoktWeb/Pokt_CLI/issues"
42
- },
43
- "dependencies": {
44
- "@modelcontextprotocol/sdk": "^1.27.1",
45
- "chalk": "^5.3.0",
46
- "conf": "^12.0.0",
47
- "diff": "^8.0.3",
48
- "openai": "^4.28.4",
49
- "ora": "^8.0.1",
50
- "prompts": "^2.4.2",
51
- "yargs": "^17.7.2",
52
- "zod": "^3.23.8"
53
- },
54
- "devDependencies": {
55
- "@types/diff": "^7.0.2",
56
- "@types/node": "^20.11.24",
57
- "@types/prompts": "^2.4.9",
58
- "@types/yargs": "^17.0.32",
59
- "tsx": "^4.7.1",
60
- "typescript": "^5.3.3"
61
- }
62
- }
1
+ {
2
+ "name": "pokt-cli",
3
+ "version": "1.0.8",
4
+ "description": "Vibe Coding AI CLI for OpenRouter and Ollama",
5
+ "main": "./dist/bin/pokt.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "pokt": "dist/bin/pokt.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node ./dist/bin/pokt.js",
13
+ "dev": "tsx ./src/bin/pokt.ts",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "package.json",
19
+ "README.md"
20
+ ],
21
+ "keywords": [
22
+ "ai",
23
+ "cli",
24
+ "coding",
25
+ "agent",
26
+ "openrouter",
27
+ "ollama"
28
+ ],
29
+ "author": "PoktWeb",
30
+ "license": "ISC",
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/PoktWeb/Pokt_CLI.git"
37
+ },
38
+ "homepage": "https://github.com/PoktWeb/Pokt_CLI#readme",
39
+ "bugs": {
40
+ "url": "https://github.com/PoktWeb/Pokt_CLI/issues"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.27.1",
44
+ "chalk": "^5.3.0",
45
+ "conf": "^12.0.0",
46
+ "diff": "^8.0.3",
47
+ "openai": "^4.28.4",
48
+ "ora": "^8.0.1",
49
+ "prompts": "^2.4.2",
50
+ "yargs": "^17.7.2",
51
+ "zod": "^3.23.8"
52
+ },
53
+ "devDependencies": {
54
+ "@types/diff": "^7.0.2",
55
+ "@types/node": "^20.11.24",
56
+ "@types/prompts": "^2.4.9",
57
+ "@types/yargs": "^17.0.32",
58
+ "tsx": "^4.7.1",
59
+ "typescript": "^5.3.3"
60
+ }
61
+ }