pokt-cli 1.0.2 → 1.0.4
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 +96 -96
- package/dist/auth/google.d.ts +1 -1
- package/dist/auth/google.js +49 -49
- package/dist/chat/loop.js +143 -7
- package/dist/commands/auth.d.ts +2 -2
- package/dist/commands/auth.js +16 -16
- package/dist/commands/chat.js +1 -1
- package/dist/ui.js +1 -1
- package/package.json +61 -61
package/README.md
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
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 global
|
|
13
|
-
npm install -g pokt-cli
|
|
14
|
-
|
|
15
|
-
# Ou use 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
|
-
Ou use os comandos diretamente:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
pokt chat # Iniciar chat (Vibe Coding)
|
|
31
|
-
pokt models list # Listar modelos
|
|
32
|
-
pokt provider use openrouter
|
|
33
|
-
pokt config show
|
|
34
|
-
pokt --help
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Comandos
|
|
38
|
-
|
|
39
|
-
| Comando | Descrição |
|
|
40
|
-
|--------|-----------|
|
|
41
|
-
| `pokt` | Menu interativo |
|
|
42
|
-
| `pokt chat` | Iniciar sessão de chat com a IA |
|
|
43
|
-
| `pokt config <action>` | Configurar chaves e tokens |
|
|
44
|
-
| `pokt models <action>` | Gerenciar modelos (listar, adicionar, trocar) |
|
|
45
|
-
| `pokt provider use <provider>` | Trocar provedor de API |
|
|
46
|
-
| `pokt mcp [action]` | Gerenciar servidores MCP (ferramentas externas) |
|
|
47
|
-
|
|
48
|
-
### Config (`config`)
|
|
49
|
-
|
|
50
|
-
- `pokt config show` — Mostra a configuração atual (tokens mascarados).
|
|
51
|
-
- `pokt config set-openrouter -v <token>` — Token OpenRouter.
|
|
52
|
-
- `pokt config set-ollama -v <url>` — URL base do Ollama local.
|
|
53
|
-
- `pokt config set-ollama-cloud -v <key>` — API key Ollama Cloud.
|
|
54
|
-
- `pokt config set-gemini -v <key>` — API key Google Gemini.
|
|
55
|
-
- `pokt config set-pokt-token -v <token>` — Token do controller Pokt.
|
|
56
|
-
- `pokt config clear-openrouter` — Remove o token OpenRouter.
|
|
57
|
-
|
|
58
|
-
### Modelos (`models`)
|
|
59
|
-
|
|
60
|
-
- `pokt models list` — Lista modelos registrados e o ativo.
|
|
61
|
-
- `pokt models fetch-openrouter` — Busca modelos disponíveis no OpenRouter.
|
|
62
|
-
- `pokt models fetch-ollama` — Busca modelos do Ollama local.
|
|
63
|
-
- `pokt models fetch-ollama-cloud` — Busca modelos do Ollama Cloud.
|
|
64
|
-
- `pokt models add-openrouter`, `add-ollama`, `add-ollama-cloud` — Adiciona modelo (use `-i <id>`).
|
|
65
|
-
- `pokt models use -i <id> -p <provider>` — Define o modelo ativo.
|
|
66
|
-
|
|
67
|
-
### Provedores (`provider`)
|
|
68
|
-
|
|
69
|
-
Provedores suportados: `controller` (Pokt), `openrouter`, `gemini`, `ollama`, `ollama-cloud`.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
pokt provider use openrouter
|
|
73
|
-
pokt provider use ollama
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Desenvolvimento
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
# Clonar e instalar
|
|
80
|
-
git clone https://github.com/PoktWeb/Pokt_CLI.git
|
|
81
|
-
cd Pokt_CLI
|
|
82
|
-
npm install
|
|
83
|
-
|
|
84
|
-
# Desenvolvimento (executa o TypeScript direto)
|
|
85
|
-
npm run dev
|
|
86
|
-
|
|
87
|
-
# Build
|
|
88
|
-
npm run build
|
|
89
|
-
|
|
90
|
-
# Executar após o build
|
|
91
|
-
npm start
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Licença
|
|
95
|
-
|
|
96
|
-
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 global
|
|
13
|
+
npm install -g pokt-cli
|
|
14
|
+
|
|
15
|
+
# Ou use 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
|
+
Ou use os comandos diretamente:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pokt chat # Iniciar chat (Vibe Coding)
|
|
31
|
+
pokt models list # Listar modelos
|
|
32
|
+
pokt provider use openrouter
|
|
33
|
+
pokt config show
|
|
34
|
+
pokt --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Comandos
|
|
38
|
+
|
|
39
|
+
| Comando | Descrição |
|
|
40
|
+
|--------|-----------|
|
|
41
|
+
| `pokt` | Menu interativo |
|
|
42
|
+
| `pokt chat` | Iniciar sessão de chat com a IA |
|
|
43
|
+
| `pokt config <action>` | Configurar chaves e tokens |
|
|
44
|
+
| `pokt models <action>` | Gerenciar modelos (listar, adicionar, trocar) |
|
|
45
|
+
| `pokt provider use <provider>` | Trocar provedor de API |
|
|
46
|
+
| `pokt mcp [action]` | Gerenciar servidores MCP (ferramentas externas) |
|
|
47
|
+
|
|
48
|
+
### Config (`config`)
|
|
49
|
+
|
|
50
|
+
- `pokt config show` — Mostra a configuração atual (tokens mascarados).
|
|
51
|
+
- `pokt config set-openrouter -v <token>` — Token OpenRouter.
|
|
52
|
+
- `pokt config set-ollama -v <url>` — URL base do Ollama local.
|
|
53
|
+
- `pokt config set-ollama-cloud -v <key>` — API key Ollama Cloud.
|
|
54
|
+
- `pokt config set-gemini -v <key>` — API key Google Gemini.
|
|
55
|
+
- `pokt config set-pokt-token -v <token>` — Token do controller Pokt.
|
|
56
|
+
- `pokt config clear-openrouter` — Remove o token OpenRouter.
|
|
57
|
+
|
|
58
|
+
### Modelos (`models`)
|
|
59
|
+
|
|
60
|
+
- `pokt models list` — Lista modelos registrados e o ativo.
|
|
61
|
+
- `pokt models fetch-openrouter` — Busca modelos disponíveis no OpenRouter.
|
|
62
|
+
- `pokt models fetch-ollama` — Busca modelos do Ollama local.
|
|
63
|
+
- `pokt models fetch-ollama-cloud` — Busca modelos do Ollama Cloud.
|
|
64
|
+
- `pokt models add-openrouter`, `add-ollama`, `add-ollama-cloud` — Adiciona modelo (use `-i <id>`).
|
|
65
|
+
- `pokt models use -i <id> -p <provider>` — Define o modelo ativo.
|
|
66
|
+
|
|
67
|
+
### Provedores (`provider`)
|
|
68
|
+
|
|
69
|
+
Provedores suportados: `controller` (Pokt), `openrouter`, `gemini`, `ollama`, `ollama-cloud`.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pokt provider use openrouter
|
|
73
|
+
pokt provider use ollama
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Desenvolvimento
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Clonar e instalar
|
|
80
|
+
git clone https://github.com/PoktWeb/Pokt_CLI.git
|
|
81
|
+
cd Pokt_CLI
|
|
82
|
+
npm install
|
|
83
|
+
|
|
84
|
+
# Desenvolvimento (executa o TypeScript direto)
|
|
85
|
+
npm run dev
|
|
86
|
+
|
|
87
|
+
# Build
|
|
88
|
+
npm run build
|
|
89
|
+
|
|
90
|
+
# Executar após o build
|
|
91
|
+
npm start
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Licença
|
|
95
|
+
|
|
96
|
+
ISC · **PoktWeb**
|
package/dist/auth/google.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function loginWithGoogle(): Promise<void>;
|
|
1
|
+
export declare function loginWithGoogle(): Promise<void>;
|
package/dist/auth/google.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import { OAuth2Client } from 'google-auth-library';
|
|
2
|
-
import http from 'http';
|
|
3
|
-
import url from 'url';
|
|
4
|
-
import open from 'open';
|
|
5
|
-
import { config } from '../config.js';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import ora from 'ora';
|
|
8
|
-
const REDIRECT_URI = 'http://localhost:3000/oauth2callback';
|
|
9
|
-
export async function loginWithGoogle() {
|
|
10
|
-
const clientId = config.get('googleClientId');
|
|
11
|
-
const clientSecret = config.get('googleClientSecret');
|
|
12
|
-
if (!clientId || !clientSecret) {
|
|
13
|
-
console.log(chalk.red('\nError: Google OAuth Client ID or Secret not configured.'));
|
|
14
|
-
console.log(chalk.yellow('To use Google Login, follow these steps:'));
|
|
15
|
-
console.log(chalk.gray('1. Create a project at: https://console.cloud.google.com/'));
|
|
16
|
-
console.log(chalk.gray('2. Enable the "Generative Language API"'));
|
|
17
|
-
console.log(chalk.gray('3. Create OAuth 2.0 Client ID (Type: Web application)'));
|
|
18
|
-
console.log(chalk.gray(`4. Add Authorized redirect URI: ${REDIRECT_URI}`));
|
|
19
|
-
console.log(chalk.yellow('\nThen configure Pokt CLI:'));
|
|
20
|
-
console.log(chalk.cyan(` pokt config set-google-client-id -v YOUR_CLIENT_ID`));
|
|
21
|
-
console.log(chalk.cyan(` pokt config set-google-client-secret -v YOUR_CLIENT_SECRET`));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const spinner = ora('Starting Google Login...').start();
|
|
25
|
-
const oAuth2Client = new OAuth2Client(clientId, clientSecret, REDIRECT_URI);
|
|
26
|
-
const authorizeUrl = oAuth2Client.generateAuthUrl({
|
|
27
|
-
access_type: 'offline',
|
|
28
|
-
scope: ['https://www.googleapis.com/auth/generative-language'],
|
|
29
|
-
});
|
|
30
|
-
const server = http.createServer(async (req, res) => {
|
|
31
|
-
try {
|
|
32
|
-
if (req.url && req.url.includes('/oauth2callback') && req.url.includes('code=')) {
|
|
33
|
-
const qs = new url.URL(req.url, 'http://localhost:3000').searchParams;
|
|
34
|
-
const code = qs.get('code');
|
|
35
|
-
res.end('Authentication successful! You can close this tab.');
|
|
36
|
-
server.close();
|
|
37
|
-
const { tokens } = await oAuth2Client.getToken(code);
|
|
38
|
-
config.set('googleToken', tokens);
|
|
39
|
-
console.log(chalk.green('\n✔ Google account connected successfully.'));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch (e) {
|
|
43
|
-
console.error(chalk.red(`\nAuthentication failed: ${e.message}`));
|
|
44
|
-
res.end('Authentication failed.');
|
|
45
|
-
}
|
|
46
|
-
}).listen(3000);
|
|
47
|
-
spinner.succeed(chalk.blue('Opening browser for Google login...'));
|
|
48
|
-
await open(authorizeUrl);
|
|
49
|
-
}
|
|
1
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import url from 'url';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import { config } from '../config.js';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
const REDIRECT_URI = 'http://localhost:3000/oauth2callback';
|
|
9
|
+
export async function loginWithGoogle() {
|
|
10
|
+
const clientId = config.get('googleClientId');
|
|
11
|
+
const clientSecret = config.get('googleClientSecret');
|
|
12
|
+
if (!clientId || !clientSecret) {
|
|
13
|
+
console.log(chalk.red('\nError: Google OAuth Client ID or Secret not configured.'));
|
|
14
|
+
console.log(chalk.yellow('To use Google Login, follow these steps:'));
|
|
15
|
+
console.log(chalk.gray('1. Create a project at: https://console.cloud.google.com/'));
|
|
16
|
+
console.log(chalk.gray('2. Enable the "Generative Language API"'));
|
|
17
|
+
console.log(chalk.gray('3. Create OAuth 2.0 Client ID (Type: Web application)'));
|
|
18
|
+
console.log(chalk.gray(`4. Add Authorized redirect URI: ${REDIRECT_URI}`));
|
|
19
|
+
console.log(chalk.yellow('\nThen configure Pokt CLI:'));
|
|
20
|
+
console.log(chalk.cyan(` pokt config set-google-client-id -v YOUR_CLIENT_ID`));
|
|
21
|
+
console.log(chalk.cyan(` pokt config set-google-client-secret -v YOUR_CLIENT_SECRET`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const spinner = ora('Starting Google Login...').start();
|
|
25
|
+
const oAuth2Client = new OAuth2Client(clientId, clientSecret, REDIRECT_URI);
|
|
26
|
+
const authorizeUrl = oAuth2Client.generateAuthUrl({
|
|
27
|
+
access_type: 'offline',
|
|
28
|
+
scope: ['https://www.googleapis.com/auth/generative-language'],
|
|
29
|
+
});
|
|
30
|
+
const server = http.createServer(async (req, res) => {
|
|
31
|
+
try {
|
|
32
|
+
if (req.url && req.url.includes('/oauth2callback') && req.url.includes('code=')) {
|
|
33
|
+
const qs = new url.URL(req.url, 'http://localhost:3000').searchParams;
|
|
34
|
+
const code = qs.get('code');
|
|
35
|
+
res.end('Authentication successful! You can close this tab.');
|
|
36
|
+
server.close();
|
|
37
|
+
const { tokens } = await oAuth2Client.getToken(code);
|
|
38
|
+
config.set('googleToken', tokens);
|
|
39
|
+
console.log(chalk.green('\n✔ Google account connected successfully.'));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error(chalk.red(`\nAuthentication failed: ${e.message}`));
|
|
44
|
+
res.end('Authentication failed.');
|
|
45
|
+
}
|
|
46
|
+
}).listen(3000);
|
|
47
|
+
spinner.succeed(chalk.blue('Opening browser for Google login...'));
|
|
48
|
+
await open(authorizeUrl);
|
|
49
|
+
}
|
package/dist/chat/loop.js
CHANGED
|
@@ -13,12 +13,17 @@ CORE CAPABILITIES:
|
|
|
13
13
|
2. **Autonomous Coding**: You can create new files, rewrite existing ones, and run terminal commands.
|
|
14
14
|
3. **Problem Solving**: You analyze errors and propose/apply fixes.
|
|
15
15
|
|
|
16
|
+
CRITICAL - FILE CREATION/EDITS (this API does NOT support tool calls):
|
|
17
|
+
- Do NOT reply with only "We will call read_file", "We will call write_file" or similar. Those tools will NOT run. The user will get no file.
|
|
18
|
+
- You MUST output the complete file content in a markdown code block so the CLI can create/edit the file. Format: mention the filename (e.g. hello.py or **hello.py**) then a newline then \`\`\`python then newline then the full file content then \`\`\`.
|
|
19
|
+
- For edits: first "read" the file by inferring its content from the user request and project context, then output the full updated file in a \`\`\`python (or correct language) block with the filename mentioned just above the block.
|
|
20
|
+
- Never end your response with only an intention to call a tool. Always include the actual code in a block.
|
|
21
|
+
|
|
16
22
|
GUIDELINES:
|
|
17
23
|
- You will receive the user request first, then the current project structure. Use the project structure to understand the context before creating or editing anything.
|
|
18
24
|
- When asked to fix something, first **read** the relevant files to understand the context.
|
|
19
|
-
- When creating a project, start by planning the structure, then use \`write_file\` to create
|
|
20
|
-
-
|
|
21
|
-
- You have full access to the current terminal. You can run \`npm install\`, \`tsc\`, or any other command.
|
|
25
|
+
- When creating a project, start by planning the structure, then use \`write_file\` to create each file.
|
|
26
|
+
- You have full access to the current terminal. You can run \`run_command\` for \`npm install\`, \`tsc\`, or any other command.
|
|
22
27
|
- Be extremely concise in your explanations.
|
|
23
28
|
- The current working directory is: ${process.cwd()}
|
|
24
29
|
`;
|
|
@@ -58,6 +63,8 @@ export async function startChatLoop(modelConfig) {
|
|
|
58
63
|
];
|
|
59
64
|
while (true) {
|
|
60
65
|
console.log('');
|
|
66
|
+
const cwd = process.cwd();
|
|
67
|
+
console.log(ui.dim(`Diretório atual: ${cwd}`));
|
|
61
68
|
console.log(ui.shortcutsLine('shift+tab to accept edits', '? for shortcuts'));
|
|
62
69
|
const response = await prompts({
|
|
63
70
|
type: 'text',
|
|
@@ -87,6 +94,83 @@ export async function startChatLoop(modelConfig) {
|
|
|
87
94
|
}
|
|
88
95
|
const MAX_429_RETRIES = 3;
|
|
89
96
|
const BASE_429_DELAY_MS = 5000;
|
|
97
|
+
/** Extensões que consideramos como arquivos de código para aplicar fallback */
|
|
98
|
+
const CODE_EXT = /\.(py|js|ts|tsx|jsx|html|css|json|md|txt|java|go|rs|c|cpp|rb|php)$/i;
|
|
99
|
+
/**
|
|
100
|
+
* Quando a API não retorna tool_calls, alguns backends só devolvem texto.
|
|
101
|
+
* Extrai blocos de código da resposta (```lang\n...\n```) e, se encontrar
|
|
102
|
+
* um nome de arquivo mencionado antes do bloco, aplica write_file.
|
|
103
|
+
*/
|
|
104
|
+
/** Blocos de comando "como rodar" (bash/sh de 1–2 linhas) não viram arquivo para não poluir. */
|
|
105
|
+
function isRunCommandOnly(lang, code) {
|
|
106
|
+
const shellLike = /^(bash|sh|shell|zsh)$/i.test(lang);
|
|
107
|
+
const lines = code.split('\n').filter((l) => l.trim().length > 0);
|
|
108
|
+
return shellLike && lines.length <= 2;
|
|
109
|
+
}
|
|
110
|
+
/** Remove markdown/formatting do nome de arquivo (ex: **hello.py** → hello.py). */
|
|
111
|
+
function cleanFilename(candidate) {
|
|
112
|
+
return candidate.replace(/^[\s*`'"]+/g, '').replace(/[\s*`'")\]\s]+$/g, '').trim();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Aplica blocos de código da resposta e retorna conteúdo para exibição (sem repetir o código).
|
|
116
|
+
* - Detecta nome de arquivo na mensagem (ex: "updated **hello.py**") para editar o existente.
|
|
117
|
+
* - Blocos já aplicados são substituídos por "[Código aplicado ao arquivo: path]" na mensagem exibida.
|
|
118
|
+
*/
|
|
119
|
+
/** Garante que o conteúdo da mensagem seja string (algumas APIs devolvem array). */
|
|
120
|
+
function messageContentToString(content) {
|
|
121
|
+
if (typeof content === 'string')
|
|
122
|
+
return content;
|
|
123
|
+
if (Array.isArray(content)) {
|
|
124
|
+
return content
|
|
125
|
+
.map((part) => (typeof part === 'object' && part != null && 'text' in part ? part.text : String(part)))
|
|
126
|
+
.join('');
|
|
127
|
+
}
|
|
128
|
+
return content != null ? String(content) : '';
|
|
129
|
+
}
|
|
130
|
+
async function applyCodeBlocksFromContent(content) {
|
|
131
|
+
const codeBlockRe = /```(\w*)\n([\s\S]*?)```/g;
|
|
132
|
+
const appliedBlocks = [];
|
|
133
|
+
let applied = false;
|
|
134
|
+
let m;
|
|
135
|
+
const matches = [];
|
|
136
|
+
while ((m = codeBlockRe.exec(content)) !== null) {
|
|
137
|
+
matches.push({
|
|
138
|
+
fullMatch: m[0],
|
|
139
|
+
index: m.index,
|
|
140
|
+
lang: m[1] || '',
|
|
141
|
+
code: m[2].replace(/\r\n/g, '\n').trimEnd(),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
for (const { fullMatch, index, lang, code } of matches) {
|
|
145
|
+
if (isRunCommandOnly(lang, code))
|
|
146
|
+
continue;
|
|
147
|
+
const beforeBlock = content.substring(0, index);
|
|
148
|
+
// Nome de arquivo: aceita "**hello.py**", "hello.py" antes de espaço/newline/backtick, etc.
|
|
149
|
+
const fileMatch = beforeBlock.match(/(\S+\.(?:py|js|ts|tsx|jsx|html|css|json|md|txt|java|go|rs|c|cpp|rb|php))(?=\s|$|[:.)\]*`"])/gi);
|
|
150
|
+
const rawCandidate = fileMatch ? fileMatch[fileMatch.length - 1].trim() : null;
|
|
151
|
+
const candidate = rawCandidate ? cleanFilename(rawCandidate) : null;
|
|
152
|
+
const path = candidate && CODE_EXT.test(candidate) ? candidate : (lang === 'python' ? 'generated.py' : lang ? `generated.${lang}` : null);
|
|
153
|
+
if (path && code) {
|
|
154
|
+
try {
|
|
155
|
+
console.log(ui.warn(`\n[Fallback] Aplicando código da resposta ao arquivo: ${path}`));
|
|
156
|
+
await executeTool('write_file', JSON.stringify({ path, content: code }));
|
|
157
|
+
applied = true;
|
|
158
|
+
appliedBlocks.push({ start: index, end: index + fullMatch.length, path });
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// ignora falha em um bloco
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Monta mensagem para exibição: blocos aplicados viram uma linha curta (evita repetir o código)
|
|
166
|
+
let displayContent = content;
|
|
167
|
+
for (let i = appliedBlocks.length - 1; i >= 0; i--) {
|
|
168
|
+
const { start, end, path } = appliedBlocks[i];
|
|
169
|
+
const placeholder = `\n${ui.dim('[Código aplicado ao arquivo: ' + path + ']')}\n`;
|
|
170
|
+
displayContent = displayContent.substring(0, start) + placeholder + displayContent.substring(end);
|
|
171
|
+
}
|
|
172
|
+
return { applied, displayContent };
|
|
173
|
+
}
|
|
90
174
|
async function createCompletionWithRetry(client, modelId, messages, toolsList) {
|
|
91
175
|
let lastError;
|
|
92
176
|
for (let attempt = 0; attempt <= MAX_429_RETRIES; attempt++) {
|
|
@@ -117,10 +201,13 @@ async function processLLMResponse(client, modelId, messages, toolsList) {
|
|
|
117
201
|
let completion = await createCompletionWithRetry(client, modelId, messages, toolsList);
|
|
118
202
|
let message = completion.choices[0].message;
|
|
119
203
|
spinner.stop();
|
|
204
|
+
let writeFileExecutedThisTurn = false;
|
|
120
205
|
while (message.tool_calls && message.tool_calls.length > 0) {
|
|
121
206
|
messages.push(message);
|
|
122
207
|
for (const toolCall of message.tool_calls) {
|
|
123
208
|
const name = toolCall.function.name;
|
|
209
|
+
if (name === 'write_file')
|
|
210
|
+
writeFileExecutedThisTurn = true;
|
|
124
211
|
const args = toolCall.function.arguments ?? '{}';
|
|
125
212
|
console.log(ui.warn(`\n[Executing Tool: ${name}]`));
|
|
126
213
|
console.log(ui.dim(`Arguments: ${args}`));
|
|
@@ -141,10 +228,59 @@ async function processLLMResponse(client, modelId, messages, toolsList) {
|
|
|
141
228
|
message = completion.choices[0].message;
|
|
142
229
|
spinner.stop();
|
|
143
230
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
231
|
+
const rawContent = message.content;
|
|
232
|
+
let contentStr = messageContentToString(rawContent);
|
|
233
|
+
let finalContent = rawContent ?? contentStr;
|
|
234
|
+
// Quando a API não executa tools, tentar aplicar blocos de código da resposta
|
|
235
|
+
if (!writeFileExecutedThisTurn) {
|
|
236
|
+
let result = await applyCodeBlocksFromContent(contentStr);
|
|
237
|
+
// Se a IA só disse "We will call read_file/write_file" e não há código, pedir o código em um follow-up
|
|
238
|
+
const looksLikeToolIntentOnly = /(We will call|We need to call|Let's call|I will call)\s+(read_file|write_file|run_command)/i.test(contentStr)
|
|
239
|
+
|| (/call\s+(read_file|write_file)/i.test(contentStr) && contentStr.length < 400);
|
|
240
|
+
if (!result.applied && looksLikeToolIntentOnly) {
|
|
241
|
+
messages.push({ role: 'assistant', content: rawContent ?? contentStr });
|
|
242
|
+
const followUpSystem = `This API does not support tool calls. You must NOT reply with "We will call X". Output the complete file content in a markdown code block so the user's CLI can create/edit the file. Format: mention the filename (e.g. hello.py) then newline then \`\`\`python then newline then the FULL file content then \`\`\`. Do that now for the user's last request.`;
|
|
243
|
+
messages.push({ role: 'system', content: followUpSystem });
|
|
244
|
+
spinner.start('Getting code...');
|
|
245
|
+
const followUp = await createCompletionWithRetry(client, modelId, messages, toolsList);
|
|
246
|
+
spinner.stop();
|
|
247
|
+
const followUpMsg = followUp.choices[0].message;
|
|
248
|
+
const followUpStr = messageContentToString(followUpMsg.content);
|
|
249
|
+
if (followUpStr.trim() !== '') {
|
|
250
|
+
result = await applyCodeBlocksFromContent(followUpStr);
|
|
251
|
+
contentStr = followUpStr;
|
|
252
|
+
finalContent = followUpMsg.content ?? followUpStr;
|
|
253
|
+
messages.push({ role: 'assistant', content: finalContent });
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
messages.push({ role: 'assistant', content: '' });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (contentStr.trim() !== '') {
|
|
260
|
+
let contentToShow = result.applied ? result.displayContent : contentStr;
|
|
261
|
+
console.log('\n' + ui.labelPokt());
|
|
262
|
+
console.log(contentToShow);
|
|
263
|
+
if (!messages.some((m) => m.role === 'assistant' && m.content === finalContent)) {
|
|
264
|
+
messages.push({ role: 'assistant', content: finalContent });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
console.log('\n' + ui.labelPokt());
|
|
269
|
+
console.log(ui.dim('(A IA não retornou código utilizável. Tente reformular o pedido.)'));
|
|
270
|
+
messages.push({ role: 'assistant', content: '' });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
if (contentStr.trim() !== '') {
|
|
275
|
+
console.log('\n' + ui.labelPokt());
|
|
276
|
+
console.log(contentStr);
|
|
277
|
+
messages.push({ role: 'assistant', content: finalContent });
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
console.log('\n' + ui.labelPokt());
|
|
281
|
+
console.log(ui.dim('(Sem resposta de texto.)'));
|
|
282
|
+
messages.push({ role: 'assistant', content: '' });
|
|
283
|
+
}
|
|
148
284
|
}
|
|
149
285
|
}
|
|
150
286
|
catch (error) {
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CommandModule } from 'yargs';
|
|
2
|
-
export declare const authCommand: CommandModule;
|
|
1
|
+
import { CommandModule } from 'yargs';
|
|
2
|
+
export declare const authCommand: CommandModule;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { loginWithGoogle } from '../auth/google.js';
|
|
2
|
-
export const authCommand = {
|
|
3
|
-
command: 'auth <action>',
|
|
4
|
-
describe: 'Manage authentication',
|
|
5
|
-
builder: (yargs) => yargs
|
|
6
|
-
.positional('action', {
|
|
7
|
-
describe: 'Action to perform',
|
|
8
|
-
type: 'string',
|
|
9
|
-
choices: ['login-google']
|
|
10
|
-
}),
|
|
11
|
-
handler: async (argv) => {
|
|
12
|
-
if (argv.action === 'login-google') {
|
|
13
|
-
await loginWithGoogle();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
};
|
|
1
|
+
import { loginWithGoogle } from '../auth/google.js';
|
|
2
|
+
export const authCommand = {
|
|
3
|
+
command: 'auth <action>',
|
|
4
|
+
describe: 'Manage authentication',
|
|
5
|
+
builder: (yargs) => yargs
|
|
6
|
+
.positional('action', {
|
|
7
|
+
describe: 'Action to perform',
|
|
8
|
+
type: 'string',
|
|
9
|
+
choices: ['login-google']
|
|
10
|
+
}),
|
|
11
|
+
handler: async (argv) => {
|
|
12
|
+
if (argv.action === 'login-google') {
|
|
13
|
+
await loginWithGoogle();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
package/dist/commands/chat.js
CHANGED
|
@@ -34,7 +34,7 @@ export const chatCommand = {
|
|
|
34
34
|
console.log('');
|
|
35
35
|
}
|
|
36
36
|
console.log(ui.dim('Type "exit" or /quit to end the session.'));
|
|
37
|
-
console.log(ui.statusBar({ model: `/model ${activeModel.provider} (${activeModel.id})` }));
|
|
37
|
+
console.log(ui.statusBar({ cwd: process.cwd(), model: `/model ${activeModel.provider} (${activeModel.id})` }));
|
|
38
38
|
console.log('');
|
|
39
39
|
await startChatLoop(activeModel);
|
|
40
40
|
}
|
package/dist/ui.js
CHANGED
|
@@ -2,7 +2,7 @@ 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
|
+
const VERSION = '1.0.4';
|
|
6
6
|
/** Logo em estilo chevron com gradiente (azul → rosa → roxo) */
|
|
7
7
|
function logo() {
|
|
8
8
|
const c = (s, color) => color(s);
|
package/package.json
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pokt-cli",
|
|
3
|
-
"version": "1.0.
|
|
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
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pokt-cli",
|
|
3
|
+
"version": "1.0.4",
|
|
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
|
+
}
|