omni-ai-cli 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +563 -2
- package/bin/ai.js +84 -0
- package/docs/README.en.md +563 -0
- package/docs/README.es.md +566 -0
- package/docs/README.fr.md +578 -0
- package/docs/diagrams/flow1.en.mmd +47 -0
- package/docs/diagrams/flow1.es.mmd +47 -0
- package/docs/diagrams/flow1.fr.mmd +47 -0
- package/docs/diagrams/flow1.pt.mmd +47 -0
- package/docs/diagrams/flow2.en.mmd +14 -0
- package/docs/diagrams/flow2.es.mmd +14 -0
- package/docs/diagrams/flow2.fr.mmd +14 -0
- package/docs/diagrams/flow2.pt.mmd +14 -0
- package/omni_ai/__init__.py +1 -0
- package/omni_ai/__main__.py +6 -0
- package/omni_ai/chat.py +1458 -0
- package/omni_ai/cli.py +1083 -0
- package/omni_ai/config.py +531 -0
- package/omni_ai/deepseek.py +57 -0
- package/omni_ai/grok.py +111 -0
- package/omni_ai/onboarding.py +196 -0
- package/omni_ai/openrouter.py +335 -0
- package/omni_ai/osutils.py +86 -0
- package/omni_ai/pii.py +281 -0
- package/omni_ai/profiles.json +69 -0
- package/omni_ai/skills.py +124 -0
- package/omni_ai/sync.py +199 -0
- package/omni_ai/tools.py +75 -0
- package/omni_ai/ui.py +595 -0
- package/omni_ai/usage.py +305 -0
- package/omni_ai/vendor/prompt_toolkit/LICENSE +27 -0
- package/omni_ai/vendor/prompt_toolkit/__init__.py +52 -0
- package/omni_ai/vendor/prompt_toolkit/application/__init__.py +32 -0
- package/omni_ai/vendor/prompt_toolkit/application/application.py +1626 -0
- package/omni_ai/vendor/prompt_toolkit/application/current.py +195 -0
- package/omni_ai/vendor/prompt_toolkit/application/dummy.py +55 -0
- package/omni_ai/vendor/prompt_toolkit/application/run_in_terminal.py +114 -0
- package/omni_ai/vendor/prompt_toolkit/auto_suggest.py +177 -0
- package/omni_ai/vendor/prompt_toolkit/buffer.py +2029 -0
- package/omni_ai/vendor/prompt_toolkit/cache.py +127 -0
- package/omni_ai/vendor/prompt_toolkit/clipboard/__init__.py +17 -0
- package/omni_ai/vendor/prompt_toolkit/clipboard/base.py +109 -0
- package/omni_ai/vendor/prompt_toolkit/clipboard/in_memory.py +44 -0
- package/omni_ai/vendor/prompt_toolkit/clipboard/pyperclip.py +42 -0
- package/omni_ai/vendor/prompt_toolkit/completion/__init__.py +43 -0
- package/omni_ai/vendor/prompt_toolkit/completion/base.py +438 -0
- package/omni_ai/vendor/prompt_toolkit/completion/deduplicate.py +45 -0
- package/omni_ai/vendor/prompt_toolkit/completion/filesystem.py +118 -0
- package/omni_ai/vendor/prompt_toolkit/completion/fuzzy_completer.py +213 -0
- package/omni_ai/vendor/prompt_toolkit/completion/nested.py +109 -0
- package/omni_ai/vendor/prompt_toolkit/completion/word_completer.py +94 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/__init__.py +0 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/completers/__init__.py +5 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/completers/system.py +64 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/__init__.py +80 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/compiler.py +579 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/completion.py +100 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/lexer.py +94 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/regex_parser.py +279 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/regular_languages/validation.py +60 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/ssh/__init__.py +8 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/ssh/server.py +178 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/telnet/__init__.py +7 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/telnet/log.py +13 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/telnet/protocol.py +209 -0
- package/omni_ai/vendor/prompt_toolkit/contrib/telnet/server.py +428 -0
- package/omni_ai/vendor/prompt_toolkit/cursor_shapes.py +117 -0
- package/omni_ai/vendor/prompt_toolkit/data_structures.py +18 -0
- package/omni_ai/vendor/prompt_toolkit/document.py +1182 -0
- package/omni_ai/vendor/prompt_toolkit/enums.py +19 -0
- package/omni_ai/vendor/prompt_toolkit/eventloop/__init__.py +31 -0
- package/omni_ai/vendor/prompt_toolkit/eventloop/async_generator.py +125 -0
- package/omni_ai/vendor/prompt_toolkit/eventloop/inputhook.py +191 -0
- package/omni_ai/vendor/prompt_toolkit/eventloop/utils.py +101 -0
- package/omni_ai/vendor/prompt_toolkit/eventloop/win32.py +72 -0
- package/omni_ai/vendor/prompt_toolkit/filters/__init__.py +71 -0
- package/omni_ai/vendor/prompt_toolkit/filters/app.py +419 -0
- package/omni_ai/vendor/prompt_toolkit/filters/base.py +261 -0
- package/omni_ai/vendor/prompt_toolkit/filters/cli.py +65 -0
- package/omni_ai/vendor/prompt_toolkit/filters/utils.py +41 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/__init__.py +59 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/ansi.py +297 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/base.py +179 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/html.py +145 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/pygments.py +32 -0
- package/omni_ai/vendor/prompt_toolkit/formatted_text/utils.py +103 -0
- package/omni_ai/vendor/prompt_toolkit/history.py +306 -0
- package/omni_ai/vendor/prompt_toolkit/input/__init__.py +14 -0
- package/omni_ai/vendor/prompt_toolkit/input/ansi_escape_sequences.py +344 -0
- package/omni_ai/vendor/prompt_toolkit/input/base.py +153 -0
- package/omni_ai/vendor/prompt_toolkit/input/defaults.py +79 -0
- package/omni_ai/vendor/prompt_toolkit/input/posix_pipe.py +118 -0
- package/omni_ai/vendor/prompt_toolkit/input/posix_utils.py +97 -0
- package/omni_ai/vendor/prompt_toolkit/input/typeahead.py +78 -0
- package/omni_ai/vendor/prompt_toolkit/input/vt100.py +309 -0
- package/omni_ai/vendor/prompt_toolkit/input/vt100_parser.py +250 -0
- package/omni_ai/vendor/prompt_toolkit/input/win32.py +749 -0
- package/omni_ai/vendor/prompt_toolkit/input/win32_pipe.py +156 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/__init__.py +22 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/__init__.py +0 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/auto_suggest.py +66 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/basic.py +257 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/completion.py +206 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/cpr.py +30 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/emacs.py +563 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/focus.py +26 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/mouse.py +348 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/named_commands.py +691 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/open_in_editor.py +52 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/page_navigation.py +85 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/scroll.py +190 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/search.py +96 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/bindings/vi.py +2233 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/defaults.py +63 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/digraphs.py +1378 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/emacs_state.py +36 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/key_bindings.py +670 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/key_processor.py +526 -0
- package/omni_ai/vendor/prompt_toolkit/key_binding/vi_state.py +107 -0
- package/omni_ai/vendor/prompt_toolkit/keys.py +222 -0
- package/omni_ai/vendor/prompt_toolkit/layout/__init__.py +147 -0
- package/omni_ai/vendor/prompt_toolkit/layout/containers.py +2738 -0
- package/omni_ai/vendor/prompt_toolkit/layout/controls.py +945 -0
- package/omni_ai/vendor/prompt_toolkit/layout/dimension.py +220 -0
- package/omni_ai/vendor/prompt_toolkit/layout/dummy.py +40 -0
- package/omni_ai/vendor/prompt_toolkit/layout/layout.py +412 -0
- package/omni_ai/vendor/prompt_toolkit/layout/margins.py +304 -0
- package/omni_ai/vendor/prompt_toolkit/layout/menus.py +748 -0
- package/omni_ai/vendor/prompt_toolkit/layout/mouse_handlers.py +56 -0
- package/omni_ai/vendor/prompt_toolkit/layout/processors.py +1010 -0
- package/omni_ai/vendor/prompt_toolkit/layout/screen.py +323 -0
- package/omni_ai/vendor/prompt_toolkit/layout/scrollable_pane.py +494 -0
- package/omni_ai/vendor/prompt_toolkit/layout/utils.py +80 -0
- package/omni_ai/vendor/prompt_toolkit/lexers/__init__.py +21 -0
- package/omni_ai/vendor/prompt_toolkit/lexers/base.py +85 -0
- package/omni_ai/vendor/prompt_toolkit/lexers/pygments.py +328 -0
- package/omni_ai/vendor/prompt_toolkit/log.py +13 -0
- package/omni_ai/vendor/prompt_toolkit/mouse_events.py +85 -0
- package/omni_ai/vendor/prompt_toolkit/output/__init__.py +15 -0
- package/omni_ai/vendor/prompt_toolkit/output/base.py +332 -0
- package/omni_ai/vendor/prompt_toolkit/output/color_depth.py +64 -0
- package/omni_ai/vendor/prompt_toolkit/output/conemu.py +65 -0
- package/omni_ai/vendor/prompt_toolkit/output/defaults.py +106 -0
- package/omni_ai/vendor/prompt_toolkit/output/flush_stdout.py +87 -0
- package/omni_ai/vendor/prompt_toolkit/output/plain_text.py +143 -0
- package/omni_ai/vendor/prompt_toolkit/output/vt100.py +748 -0
- package/omni_ai/vendor/prompt_toolkit/output/win32.py +683 -0
- package/omni_ai/vendor/prompt_toolkit/output/windows10.py +128 -0
- package/omni_ai/vendor/prompt_toolkit/patch_stdout.py +297 -0
- package/omni_ai/vendor/prompt_toolkit/py.typed +0 -0
- package/omni_ai/vendor/prompt_toolkit/renderer.py +814 -0
- package/omni_ai/vendor/prompt_toolkit/search.py +226 -0
- package/omni_ai/vendor/prompt_toolkit/selection.py +58 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/__init__.py +46 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/dialogs.py +330 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/progress_bar/__init__.py +33 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/progress_bar/base.py +449 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/progress_bar/formatters.py +431 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/prompt.py +1513 -0
- package/omni_ai/vendor/prompt_toolkit/shortcuts/utils.py +239 -0
- package/omni_ai/vendor/prompt_toolkit/styles/__init__.py +67 -0
- package/omni_ai/vendor/prompt_toolkit/styles/base.py +184 -0
- package/omni_ai/vendor/prompt_toolkit/styles/defaults.py +236 -0
- package/omni_ai/vendor/prompt_toolkit/styles/named_colors.py +162 -0
- package/omni_ai/vendor/prompt_toolkit/styles/pygments.py +70 -0
- package/omni_ai/vendor/prompt_toolkit/styles/style.py +401 -0
- package/omni_ai/vendor/prompt_toolkit/styles/style_transformation.py +374 -0
- package/omni_ai/vendor/prompt_toolkit/token.py +9 -0
- package/omni_ai/vendor/prompt_toolkit/utils.py +327 -0
- package/omni_ai/vendor/prompt_toolkit/validation.py +192 -0
- package/omni_ai/vendor/prompt_toolkit/widgets/__init__.py +63 -0
- package/omni_ai/vendor/prompt_toolkit/widgets/base.py +995 -0
- package/omni_ai/vendor/prompt_toolkit/widgets/dialogs.py +108 -0
- package/omni_ai/vendor/prompt_toolkit/widgets/menus.py +374 -0
- package/omni_ai/vendor/prompt_toolkit/widgets/toolbars.py +370 -0
- package/omni_ai/vendor/prompt_toolkit/win32_types.py +229 -0
- package/omni_ai/vendor/wcwidth/LICENSE +27 -0
- package/omni_ai/vendor/wcwidth/__init__.py +29 -0
- package/omni_ai/vendor/wcwidth/table_vs16.py +125 -0
- package/omni_ai/vendor/wcwidth/table_wide.py +1493 -0
- package/omni_ai/vendor/wcwidth/table_zero.py +4843 -0
- package/omni_ai/vendor/wcwidth/unicode_versions.py +38 -0
- package/omni_ai/vendor/wcwidth/wcwidth.py +345 -0
- package/package.json +44 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 samuelrms
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,564 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/samuelrms/omni-ai-cli/main/docs/assets/omni-ai-cli-logo.png" alt="Omni AI CLI" width="440">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# Omni AI CLI
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/omni-ai-cli)
|
|
8
|
+
[](https://www.npmjs.com/package/omni-ai-cli)
|
|
9
|
+
[](https://docs.npmjs.com/trusted-publishers)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
> [English](docs/README.en.md) · [Español](docs/README.es.md) · [Français](docs/README.fr.md)
|
|
15
|
+
|
|
16
|
+
Um terminal de chat que junta **Claude Code, Codex, Cursor, Antigravity, Grok, DeepSeek e
|
|
17
|
+
OpenRouter** num lugar só. Você escreve a pergunta, o `ai` decide quem responde e, se a
|
|
18
|
+
ferramenta falhar ou estourar a cota, passa para a próxima sem você fazer nada.
|
|
19
|
+
|
|
20
|
+
Enquanto uma resposta está sendo gerada, a caixa de entrada continua livre: você segue
|
|
21
|
+
mandando pedidos, eles entram numa fila visível acima da caixa e rodam em ordem.
|
|
22
|
+
|
|
23
|
+
Perguntas simples vão para as ferramentas mais baratas. Tarefas pesadas vão para o
|
|
24
|
+
Claude, com troca automática entre várias contas quando uma bate no limite. O histórico
|
|
25
|
+
fica salvo, a interface fala português, inglês, espanhol e francês, e modelos e contas
|
|
26
|
+
se configuram de dentro do próprio chat.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Índice
|
|
31
|
+
|
|
32
|
+
- [Como funciona](#como-funciona)
|
|
33
|
+
- [Instalação](#instalação)
|
|
34
|
+
- [Como usar](#como-usar)
|
|
35
|
+
- [Comandos do chat](#comandos-do-chat)
|
|
36
|
+
- [Modelos](#modelos)
|
|
37
|
+
- [Contas Claude](#contas-claude)
|
|
38
|
+
- [Chaves de API](#chaves-de-api)
|
|
39
|
+
- [Modos](#modos)
|
|
40
|
+
- [Skills e MCP](#skills-e-mcp)
|
|
41
|
+
- [Uso direto, sem chat](#uso-direto-sem-chat)
|
|
42
|
+
- [Onde ficam os arquivos](#onde-ficam-os-arquivos)
|
|
43
|
+
- [Estrutura do projeto](#estrutura-do-projeto)
|
|
44
|
+
- [Testes](#testes)
|
|
45
|
+
- [Troubleshooting](#troubleshooting)
|
|
46
|
+
- [Limitações conhecidas](#limitações-conhecidas)
|
|
47
|
+
- [Privacidade](#privacidade)
|
|
48
|
+
- [Licença](#licença)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Como funciona
|
|
53
|
+
|
|
54
|
+
Da linha digitada até a resposta salva. O chat (`chat.py`) não chama nenhuma IA
|
|
55
|
+
direto: ele dispara o roteador (`cli.py`) como subprocesso e lê os eventos que ele
|
|
56
|
+
emite, linha a linha, para mostrar quem está atendendo e o que está fazendo.
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
Três detalhes que o diagrama torna explícitos:
|
|
61
|
+
|
|
62
|
+
- **Cada conta Claude tem sua própria pasta de login.** Contas extras rodam com
|
|
63
|
+
`CLAUDE_CONFIG_DIR` apontando para `~/.claude-accounts/<nome>`. A conta principal
|
|
64
|
+
nunca é deslogada, e nenhuma credencial é copiada de um lugar para outro. Copiar o
|
|
65
|
+
`.credentials.json` não funciona de forma confiável no macOS, onde o login fica no
|
|
66
|
+
Keychain.
|
|
67
|
+
- **Ctrl-C mata a árvore inteira.** O roteador roda num grupo de processos próprio, então
|
|
68
|
+
cancelar encerra também o `claude`, o `codex` ou o `agy` que estiver rodando embaixo,
|
|
69
|
+
em vez de deixar processos órfãos gastando cota.
|
|
70
|
+
- **A fila não bloqueia nada.** Acima da caixa aparece o pedido em execução, o tempo e
|
|
71
|
+
os próximos da fila. Um pedido que esperou passa por um classificador grátis antes de
|
|
72
|
+
rodar: se a resposta anterior já o resolve, ele é pulado. Comandos e skills sempre
|
|
73
|
+
rodam. Ctrl-C com a fila ativa cancela tudo sem sair do chat. Se você fechar o chat com
|
|
74
|
+
pedidos na fila, eles ficam salvos na sessão e o `ai --resume ID` pergunta se quer continuar.
|
|
75
|
+
- **Cada resposta mostra quanto custou.** No fim de cada pedido aparecem os tokens e, quando
|
|
76
|
+
a ferramenta informa (OpenRouter, Grok, Claude com chave de API), o custo em dólar. O
|
|
77
|
+
`/usage` soma o gasto do dia por ferramenta.
|
|
78
|
+
- **Você vê por que a ferramenta foi escolhida.** No modo `auto`, uma linha curta diz se o
|
|
79
|
+
pedido foi tratado como tarefa de código ou pergunta simples e quem classificou (Groq,
|
|
80
|
+
Cerebras, OpenRouter, Grok ou as palavras-chave).
|
|
81
|
+
- **Só a resposta entra no histórico.** Linhas de status e diagnóstico aparecem na tela,
|
|
82
|
+
mas não são enviadas como contexto na próxima pergunta. Raciocínio interno dos modelos
|
|
83
|
+
também é descartado.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Instalação
|
|
88
|
+
|
|
89
|
+
Um comando instala tudo e, rodado de novo, atualiza. Suas contas, chaves e modelos ficam em `~/.config/smart-ai` e não são tocados.
|
|
90
|
+
|
|
91
|
+
**macOS e Linux**:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl -fsSL https://raw.githubusercontent.com/samuelrms/omni-ai-cli/main/get.sh | bash
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Windows** (PowerShell):
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
irm https://raw.githubusercontent.com/samuelrms/omni-ai-cli/main/get.ps1 | iex
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**No npm, yarn, pnpm ou bun:**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install -g omni-ai-cli
|
|
107
|
+
yarn global add omni-ai-cli
|
|
108
|
+
pnpm add -g omni-ai-cli
|
|
109
|
+
bun add -g omni-ai-cli
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Qualquer um dos comandos acima (script ou gerenciador de pacotes) instala o mesmo `ai`.
|
|
113
|
+
|
|
114
|
+
Para atualizar depois: `ai --update` (ou `/update` no chat).
|
|
115
|
+
|
|
116
|
+
Pelo código-fonte, para desenvolver:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git clone https://github.com/samuelrms/omni-ai-cli ~/dev/omni-ai-cli
|
|
120
|
+
cd ~/dev/omni-ai-cli && ./scripts/install.sh # Windows: powershell -File scripts\install.ps1
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
O instalador:
|
|
124
|
+
|
|
125
|
+
1. instala o **Claude Code** pelo instalador oficial se ele ainda não estiver na máquina;
|
|
126
|
+
2. abre o login da conta principal se ainda não houver um;
|
|
127
|
+
3. cria o comando `ai` em `~/.local/bin`;
|
|
128
|
+
4. mostra as contas Claude configuradas.
|
|
129
|
+
|
|
130
|
+
Requisitos: nenhum além de `curl` (macOS/Linux) ou PowerShell (Windows). Se não houver Python 3.11+, o instalador baixa um Python 3.12 só para o Omni AI, sem sudo e sem mexer no Python do sistema.
|
|
131
|
+
Codex (`codex`) e Antigravity (`agy`) são opcionais e entram na fila de alternativas quando estão instalados.
|
|
132
|
+
|
|
133
|
+
Se `~/.local/bin` não estiver no `PATH`, adicione ao `~/.zshrc`:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
No Windows, o instalador cria `ai.cmd` em `%USERPROFILE%\.local\bin` e adiciona essa pasta ao `PATH`; abra um terminal novo depois.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Como usar
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
ai
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Na primeira vez ele pergunta o idioma e oferece instalar as ferramentas que faltam
|
|
150
|
+
(Claude Code, Codex, Cursor, Grok Build) com os instaladores oficiais. O Antigravity se
|
|
151
|
+
instala pela página dele. Depois é só escrever e teclar Enter.
|
|
152
|
+
|
|
153
|
+
| Tecla | Ação |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| Enter | Envia |
|
|
156
|
+
| ↑ / ↓ | Linhas anteriores |
|
|
157
|
+
| Ctrl-C | Durante a resposta: cancela. No prompt: salva e sai |
|
|
158
|
+
| Ctrl-D | Salva e sai |
|
|
159
|
+
| `\` + Enter · Shift+Enter · Option+Enter | Nova linha sem enviar (Shift+Enter no iTerm2, Ghostty, Kitty, WezTerm e VS Code; no Terminal do Mac use `\` + Enter) |
|
|
160
|
+
| Tab | Completa comandos e skills |
|
|
161
|
+
|
|
162
|
+
No menu do Tab, cada item termina dizendo de quem é: `omni`, `claude`, `codex` ou `agy`. Ao escolher
|
|
163
|
+
uma skill que tem argumentos (como `/pr-review [pr-number | --branch]`), o chat pergunta os
|
|
164
|
+
argumentos: Tab mostra as opções, dá para escrever o próprio texto, e Enter sem nada roda sem argumentos.
|
|
165
|
+
|
|
166
|
+
A tela segue o estilo do Claude Code e do Codex: caixa de entrada no rodapé com a barra de status
|
|
167
|
+
(ferramenta, modelo, modo e pasta), Tab para completar comandos e skills, cada ação da IA numa linha
|
|
168
|
+
(`● Ran …`, `● Edited …`, `● Called …`) com a saída recolhida, respostas em markdown e um indicador
|
|
169
|
+
"Trabalhando…" com o tempo. Ao abrir, uma splash de 1,5 s em tela cheia (Ctrl-C pula;
|
|
170
|
+
`SMART_AI_NO_SPLASH=1` desliga).
|
|
171
|
+
|
|
172
|
+
Ao sair, ele mostra o comando para voltar à mesma conversa:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
Sessão salva. Retome com: ai --resume 1f3a9c2b7d40
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Comandos do chat
|
|
181
|
+
|
|
182
|
+
| Comando | O que faz |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| `/tool auto\|claude\|codex\|cursor\|agy\|openrouter\|grok\|deepseek` | Fixa uma ferramenta (`auto` decide sozinho) |
|
|
185
|
+
| `/model` | Lista os modelos de cada ferramenta |
|
|
186
|
+
| `/model codex gpt-6-astra` | Troca um modelo |
|
|
187
|
+
| `/model codex default` | Volta ao padrão |
|
|
188
|
+
| `/account` | Lista as contas Claude e se estão logadas |
|
|
189
|
+
| `/account add pessoal` | Cria uma conta e abre o login do Claude ali mesmo |
|
|
190
|
+
| `/account login NOME` | Refaz o login de uma conta |
|
|
191
|
+
| `/account sync NOME` | Recompartilha skills, plugins e MCPs da conta principal |
|
|
192
|
+
| `/account disable\|enable\|remove NOME` | Desativa, reativa ou remove |
|
|
193
|
+
| `/key` | Lista as chaves de API configuradas (mascaradas) |
|
|
194
|
+
| `/key claude\|codex\|cursor\|agy\|openrouter\|grok\|deepseek\|groq\|cerebras` | Adiciona uma chave, sem mostrar o que você digita |
|
|
195
|
+
| `/key add FERRAMENTA [NOME]` | Mais uma chave para a mesma ferramenta (vale para todas, inclusive Claude); as chaves são tentadas em ordem quando uma falha ou bate no limite |
|
|
196
|
+
| `/key remove FERRAMENTA [NOME]` | Remove a chave (sem nome, a principal) |
|
|
197
|
+
| `/status` · `/reset` | Status das contas · zera cooldowns |
|
|
198
|
+
| `/lang pt\|en\|es\|fr` | Muda o idioma |
|
|
199
|
+
| `/mode default\|plan\|edits\|auto\|yolo` | Modo de permissão, igual nas três ferramentas |
|
|
200
|
+
| `/sync` | Compartilha MCPs e skills entre Claude, Codex e Antigravity |
|
|
201
|
+
| `/usage [watch]` · `/limits` | Uso real de cada conta em barras (e-mail, plano, % da janela de 5 h e da semana, horário de reset) para Claude e Codex; gasto da chave no OpenRouter; nas demais, o aviso de limite que a ferramenta deu. `watch` atualiza a cada 30 s |
|
|
202
|
+
| `/queue [rm\|up\|down N\|clear]` | Mostra a fila; tira (`rm`), sobe (`up`), desce (`down`) um pedido ou esvazia (`clear`) o que espera |
|
|
203
|
+
| `/versions [N]` | O que mudou em cada versão, mais recente primeiro; `N` abre a nota técnica. Antes da primeira release, mostra os `feat`/`fix` do `main`. Os títulos saem no idioma do chat (traduzidos pelos classificadores grátis e guardados). Ao abrir o chat, uma linha avisa quando há versão nova. |
|
|
204
|
+
| `/clear` · `/compact` | Limpar a conversa · resumir a conversa num único bloco para economizar contexto |
|
|
205
|
+
| `/claude:usage` · `/codex:status` · `/agy:usage` … | Comandos nativos de cada ferramenta: abre a tela dela e volta ao Omni ao sair |
|
|
206
|
+
| `/setup [install]` | Guia de primeiro acesso: o que falta instalar, logar e configurar; `install` instala as ferramentas que faltam |
|
|
207
|
+
| `/terminal-setup` | Mostra como ter Shift+Enter (ou Option+Enter) para nova linha no seu terminal |
|
|
208
|
+
| `/skills` | Lista skills e comandos do projeto e do usuário, de Claude, Codex e Antigravity |
|
|
209
|
+
| `/nome args` | Roda a skill ou comando (ex.: `/pr-review 237`). Tab completa |
|
|
210
|
+
| `/history` · `/sent` · `/trace` | Conversa · o que foi enviado · eventos da execução |
|
|
211
|
+
| `/sessions` · `/resume ID` · `/new` | Sessões salvas |
|
|
212
|
+
| `/parallel codex: tarefa \| deepseek: tarefa` | 2 a 4 análises em paralelo, somente leitura (codex, openrouter, grok, deepseek) |
|
|
213
|
+
| `/help` · `/exit` | Ajuda · sair |
|
|
214
|
+
|
|
215
|
+
Enquanto a fila roda, `/clear`, `/new`, `/resume`, `/key`, `/usage` e os comandos nativos
|
|
216
|
+
esperam: eles tomam o terminal ou trocam a sessão. O resto funciona na hora.
|
|
217
|
+
|
|
218
|
+
Os comandos de configuração também funcionam fora do chat: `ai --model`,
|
|
219
|
+
`ai --account add pessoal`, `ai --key claude`, `ai --status`, `ai --reset`.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Modelos
|
|
224
|
+
|
|
225
|
+
| Chave | Padrão | Valores aceitos |
|
|
226
|
+
| --- | --- | --- |
|
|
227
|
+
| `claude` | `opus` (o mais capaz) | `opus`, `sonnet`, `haiku` ou ID completo |
|
|
228
|
+
| `codex` | `gpt-6-astra` | ID de modelo do Codex |
|
|
229
|
+
| `codex_effort` | `high` | `minimal`, `low`, `medium`, `high`, `xhigh` |
|
|
230
|
+
| `agy` | padrão do Antigravity | ID de modelo do Antigravity |
|
|
231
|
+
| `openrouter` | automático (gratuito) | ID do modelo; pago só com `openrouter_paid on` |
|
|
232
|
+
| `openrouter_paid` | `off` | `on` autoriza modelos pagos do OpenRouter |
|
|
233
|
+
| `grok` | padrão do Grok | ID de modelo da xAI |
|
|
234
|
+
| `deepseek` | `deepseek-chat` | `deepseek-chat`, `deepseek-reasoner` |
|
|
235
|
+
| `cursor` | padrão da conta Cursor | ID de modelo do Cursor CLI |
|
|
236
|
+
|
|
237
|
+
A ordem de prioridade é: variável de ambiente, depois o que foi salvo com `/model` (em
|
|
238
|
+
`~/.config/smart-ai/models.json`), depois o padrão. As variáveis são
|
|
239
|
+
`SMART_AI_CLAUDE_MODEL`, `SMART_AI_CODEX_MODEL`, `SMART_AI_CODEX_EFFORT`,
|
|
240
|
+
`SMART_AI_AGY_MODEL`, `SMART_AI_OPENROUTER_MODEL`, `SMART_AI_GROK_MODEL`,
|
|
241
|
+
`SMART_AI_DEEPSEEK_MODEL` e `SMART_AI_CURSOR_MODEL`. Quando uma delas está definida, o
|
|
242
|
+
`/model` avisa que ela tem precedência.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Contas Claude
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
ai [auto] › /account add trabalho
|
|
250
|
+
Abrindo login do Claude para a conta "trabalho" (pasta isolada ~/.claude-accounts/trabalho).
|
|
251
|
+
Sua conta principal não é afetada.
|
|
252
|
+
✓ Conta "trabalho" adicionada com prioridade 3.
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
As contas são tentadas por ordem de prioridade. A que bate no limite entra em cooldown
|
|
256
|
+
(60 minutos por padrão, ajustável em `~/.config/smart-ai/profiles.json`) e a próxima assume. A conta
|
|
257
|
+
`primary` usa o login normal do Claude (`claude auth login`).
|
|
258
|
+
|
|
259
|
+
Skills, plugins, agentes, `CLAUDE.md`, `settings.json` e os servidores MCP da conta
|
|
260
|
+
principal são compartilhados com cada conta extra no login. Só o login fica separado.
|
|
261
|
+
Se você instalar uma skill ou um MCP novo depois, rode `/account sync NOME`.
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Chaves de API
|
|
267
|
+
|
|
268
|
+
Além das assinaturas, cada ferramenta aceita uma chave de API. **A assinatura vem
|
|
269
|
+
primeiro**; a chave só entra quando a assinatura falha ou bate no limite, porque chave é
|
|
270
|
+
cobrada por uso. O OpenRouter funciona só com chave.
|
|
271
|
+
|
|
272
|
+
| Ferramenta | Comando | Variável que o CLI recebe | Onde criar |
|
|
273
|
+
| --- | --- | --- | --- |
|
|
274
|
+
| Claude | `/key claude` | `ANTHROPIC_API_KEY` | console.anthropic.com/settings/keys |
|
|
275
|
+
| Codex | `/key codex` | `CODEX_API_KEY` | platform.openai.com/api-keys |
|
|
276
|
+
| Antigravity | `/key agy` | `GEMINI_API_KEY` | aistudio.google.com/apikey |
|
|
277
|
+
| OpenRouter | `/key openrouter` | `OPENROUTER_API_KEY` | openrouter.ai/keys |
|
|
278
|
+
| Cursor | `/key cursor` | `CURSOR_API_KEY` | cursor.com/dashboard → Integrations |
|
|
279
|
+
| Grok | `/key grok` | `XAI_API_KEY` | console.x.ai |
|
|
280
|
+
| DeepSeek | `/key deepseek` | `DEEPSEEK_API_KEY` | platform.deepseek.com/api_keys |
|
|
281
|
+
| Groq (classificador) | `/key groq` | `GROQ_API_KEY` | console.groq.com/keys |
|
|
282
|
+
| Cerebras (classificador) | `/key cerebras` | `CEREBRAS_API_KEY` | cloud.cerebras.ai |
|
|
283
|
+
|
|
284
|
+
O Cursor usa primeiro o login da conta (`cursor-agent login`) e só depois as chaves. O
|
|
285
|
+
DeepSeek, como o OpenRouter, funciona só com chave e mascara dados pessoais antes do
|
|
286
|
+
envio. Groq e Cerebras não respondem perguntas: só classificam cada pedido (grátis) e
|
|
287
|
+
checam se um pedido da fila já foi resolvido.
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
ai [auto] › /key claude
|
|
291
|
+
Chave de claude (console.anthropic.com/settings/keys). Cole e tecle Enter; ela não aparece na tela.
|
|
292
|
+
>
|
|
293
|
+
✓ Chave de claude salva em ~/.config/smart-ai/keys/claude (permissão 600).
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
As chaves ficam em `~/.config/smart-ai/keys/`, uma por arquivo, com permissão 600, e
|
|
297
|
+
são passadas só ao processo da ferramenta, nunca ao histórico nem ao log. Se a variável
|
|
298
|
+
já estiver definida no ambiente, ela tem precedência. `/key` lista o que está
|
|
299
|
+
configurado, mostrando só o início e o fim de cada chave.
|
|
300
|
+
|
|
301
|
+
No Claude, a chave funciona como uma conta extra depois de todas as assinaturas: se ela
|
|
302
|
+
também bater no limite, entra em cooldown como as outras.
|
|
303
|
+
|
|
304
|
+
> **Nunca cole uma chave como comando no shell.** Ela fica gravada no histórico do zsh.
|
|
305
|
+
> Se isso acontecer, revogue a chave no site da ferramenta e apague a linha de
|
|
306
|
+
> `~/.zsh_history`.
|
|
307
|
+
|
|
308
|
+
No OpenRouter, o padrão é usar só modelos gratuitos: o cliente escolhe até três
|
|
309
|
+
variantes `:free` do catálogo e usa `openrouter/free` como última alternativa. Modelos
|
|
310
|
+
pagos ficam bloqueados até você autorizar:
|
|
311
|
+
|
|
312
|
+
```text
|
|
313
|
+
/model openrouter_paid on # autoriza; avisa que será cobrado na sua conta
|
|
314
|
+
/model openrouter anthropic/claude-opus-5.5
|
|
315
|
+
/model openrouter_paid off # bloqueia de novo e volta ao gratuito
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Com a autorização ligada, o modelo escolhido vem primeiro e os gratuitos ficam como
|
|
319
|
+
reserva. Desligada, o limite de preço zero é enviado em toda chamada, então nenhum
|
|
320
|
+
modelo pago responde.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Modos
|
|
325
|
+
|
|
326
|
+
O modo vale para Claude, Codex e Antigravity ao mesmo tempo e aparece no prompt
|
|
327
|
+
(`ai [auto · plan] ›`). Cada sessão guarda o seu.
|
|
328
|
+
|
|
329
|
+
| Modo | O que faz | Claude | Codex | Antigravity |
|
|
330
|
+
| --- | --- | --- | --- | --- |
|
|
331
|
+
| `default` | Padrão de cada ferramenta | — | — | — |
|
|
332
|
+
| `plan` | Só lê e planeja, não altera nada | `plan` | `--sandbox read-only` | `--mode plan` |
|
|
333
|
+
| `edits` | Aceita edições de arquivo | `acceptEdits` | `--sandbox workspace-write` | `--mode accept-edits` |
|
|
334
|
+
| `auto` | Age sozinho, com as proteções da ferramenta | `auto` | `workspace-write`, sem aprovação | `--mode auto` |
|
|
335
|
+
| `yolo` | Sem permissões nem sandbox | `bypassPermissions` | `--dangerously-bypass-approvals-and-sandbox` | `--dangerously-skip-permissions` |
|
|
336
|
+
|
|
337
|
+
`/mode yolo` pede confirmação, porque a IA passa a poder apagar arquivos e rodar qualquer
|
|
338
|
+
comando. Fora do chat: `ai --mode plan "pergunta"`. O `/parallel` roda sempre em `plan`.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Skills e MCP
|
|
343
|
+
|
|
344
|
+
O `ai` chama os próprios CLIs, então cada um usa as skills e os MCPs que já tem. O
|
|
345
|
+
`/sync` junta tudo e instala em cada ferramenta o que só existe nas outras:
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
ai [auto] › /sync
|
|
349
|
+
Ferramentas encontradas: claude, codex, agy
|
|
350
|
+
MCP github codex → claude
|
|
351
|
+
MCP obsidian claude → codex
|
|
352
|
+
skill humanizer claude → codex (~/.agents/skills/humanizer)
|
|
353
|
+
Aplicar? [s/N]
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
- Os MCPs são adicionados pelo comando `mcp add` de cada CLI, não editando arquivos na mão.
|
|
357
|
+
- O mesmo servidor com nomes diferentes é reconhecido pelo pacote ou URL e não é duplicado.
|
|
358
|
+
- MCPs que vêm dentro de um app (como os do Codex.app) ficam só nele.
|
|
359
|
+
- As skills são compartilhadas por atalho: `~/.claude/skills`, `~/.agents/skills` (Codex) e
|
|
360
|
+
`~/.gemini/antigravity/skills`. Editar uma vale para as três.
|
|
361
|
+
- As contas Claude extras são atualizadas junto.
|
|
362
|
+
- Variáveis de ambiente dos MCPs, que podem ter tokens, são copiadas para a config da
|
|
363
|
+
outra ferramenta. O `/sync` avisa antes.
|
|
364
|
+
- `/sync --dry-run` só mostra o que faria.
|
|
365
|
+
|
|
366
|
+
**Skills e comandos do projeto:** o chat lê `.claude/commands`, `.claude/skills`, `.agents/skills` e
|
|
367
|
+
`.agent/workflows` do projeto aberto, além das pastas do usuário. `/pr-review 237` vai para o
|
|
368
|
+
Claude do jeito que foi digitado, e ele executa o comando nativamente. Uma skill que só existe
|
|
369
|
+
no Codex ou no Antigravity vai para essa ferramenta, com as instruções do arquivo no pedido.
|
|
370
|
+
|
|
371
|
+
Como o chat não repassa pedidos de aprovação, uma ferramenta de MCP só roda se já estiver
|
|
372
|
+
liberada nas permissões do CLI ou se o modo for `auto` ou `yolo`. O OpenRouter não tem
|
|
373
|
+
skills nem MCP.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Uso direto, sem chat
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
ai "o que é um design pattern?" # roteamento automático
|
|
381
|
+
ai --claude "refatorar o módulo X" # força Claude
|
|
382
|
+
ai --codex "explicar REST" # força Codex
|
|
383
|
+
ai --agy "pesquisar React hooks" # força Antigravity
|
|
384
|
+
ai --openrouter "explicar Docker" # força OpenRouter
|
|
385
|
+
ai --cursor "corrigir o teste X" # força Cursor
|
|
386
|
+
ai --deepseek "explicar CAP theorem" # força DeepSeek
|
|
387
|
+
ai --install # instala as ferramentas que faltam (pergunta antes)
|
|
388
|
+
ai --classify "prompt" # mostra como seria classificado
|
|
389
|
+
ai --log # últimas chamadas
|
|
390
|
+
ai doctor # diagnóstico de instalação, contas e chaves
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Onde ficam os arquivos
|
|
396
|
+
|
|
397
|
+
| Caminho | Conteúdo |
|
|
398
|
+
| --- | --- |
|
|
399
|
+
| `~/.config/smart-ai/profiles.json` | Contas Claude e regras de roteamento |
|
|
400
|
+
| `~/.config/smart-ai/models.json` | Modelos escolhidos com `/model` |
|
|
401
|
+
| `~/.config/smart-ai/lang` | Idioma do chat |
|
|
402
|
+
| `~/.config/smart-ai/keys/<ferramenta>` | Chaves de API, uma por arquivo (permissão 600) |
|
|
403
|
+
| `~/.claude-accounts/<nome>/` | Login isolado de cada conta Claude extra |
|
|
404
|
+
| `~/.local/share/smart-ai/sessions/` | Sessões do chat |
|
|
405
|
+
| `~/.cache/smart-ai/state.json` | Cooldowns ativos |
|
|
406
|
+
| `~/.cache/smart-ai/usage.log` | Log de uso, só metadados |
|
|
407
|
+
| `~/.cache/smart-ai/costs.jsonl` | Custo e tokens de cada pedido do chat, sem pergunta nem resposta (soma do dia no `/usage`) |
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Estrutura do projeto
|
|
412
|
+
|
|
413
|
+
```text
|
|
414
|
+
omni-ai-cli/
|
|
415
|
+
├── omni_ai/ # o programa (pacote Python)
|
|
416
|
+
│ ├── tools.py # cadastro único das ferramentas: nome, cor, CLI, modelo, chave
|
|
417
|
+
│ ├── cli.py # comando ai: classifica, roteia e executa cada ferramenta
|
|
418
|
+
│ ├── chat.py # chat: sessões, eventos, Ctrl-C, idiomas
|
|
419
|
+
│ ├── ui.py # visual: caixa de entrada, barra de status, ações e respostas
|
|
420
|
+
│ ├── skills.py # skills e comandos do projeto, do usuário e nativos
|
|
421
|
+
│ ├── config.py # modelos, chaves e contas Claude (/model, /key, /account)
|
|
422
|
+
│ ├── sync.py # /sync: MCPs e skills entre as três ferramentas
|
|
423
|
+
│ ├── openrouter.py # cliente OpenRouter: gratuitos, pagos só se autorizados
|
|
424
|
+
│ ├── grok.py # cliente xAI, reaproveitado pelo DeepSeek
|
|
425
|
+
│ ├── deepseek.py # cliente DeepSeek com dados pessoais mascarados
|
|
426
|
+
│ ├── pii.py # mascara e restaura dados pessoais
|
|
427
|
+
│ ├── osutils.py # diferenças entre macOS, Linux e Windows
|
|
428
|
+
│ ├── profiles.json # modelo inicial; o seu fica em ~/.config/smart-ai
|
|
429
|
+
│ └── vendor/ # prompt_toolkit e wcwidth embutidos (sem pip)
|
|
430
|
+
├── bin/ai.js # lançador npm: acha ou instala o Python e roda o pacote
|
|
431
|
+
├── scripts/ # install.sh, install.ps1 e ai.cmd
|
|
432
|
+
├── docs/ # README em inglês, espanhol e francês
|
|
433
|
+
├── tests/ # executáveis fictícios, sem consumir cota
|
|
434
|
+
├── postman/ # simulação HTTP do OpenRouter
|
|
435
|
+
├── get.sh / get.ps1 # instalação com um comando
|
|
436
|
+
└── package.json · LICENSE
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
O roteador não sabe nada de chat: ele só executa uma pergunta e, com
|
|
440
|
+
`SMART_AI_EVENTS=1`, emite eventos JSON. O chat não sabe nada de credenciais: contas e
|
|
441
|
+
modelos passam pelo `config.py`.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Testes
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
python3 -m unittest discover -s tests -v
|
|
449
|
+
bash -n scripts/install.sh
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**106 testes**: persistência e retomada de sessão, limite de contexto, permissão dos
|
|
453
|
+
arquivos, sessão aberta em dois terminais, leitura de eventos, cancelamento da árvore de
|
|
454
|
+
processos, paralelismo real, sandbox somente leitura, mascaramento de dados pessoais,
|
|
455
|
+
fallback OpenRouter → Codex, validação de modelos, armazenamento das chaves e o ciclo de vida das contas. Todos os
|
|
456
|
+
executáveis são fictícios: **nenhum teste autentica nem consome assinatura.**
|
|
457
|
+
|
|
458
|
+
Para testar o HTTP do OpenRouter contra a simulação Postman:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
postman simulation run postman/simulations/openrouter.sim.yaml --port 4901 --no-history --output ndjson
|
|
462
|
+
SMART_AI_SIM_URL=http://127.0.0.1:4901/openrouter python3 -m unittest discover -s tests -v
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## Releases e branches
|
|
468
|
+
|
|
469
|
+
Publicação no npm é automática, feita pelo GitHub Actions com semantic-release. A versão
|
|
470
|
+
sai das mensagens de commit (Conventional Commits) — ninguém edita `version` no
|
|
471
|
+
`package.json` à mão.
|
|
472
|
+
|
|
473
|
+
| Branch | Publica como | Exemplo |
|
|
474
|
+
|---|---|---|
|
|
475
|
+
| `main` | `latest` | `1.4.0` |
|
|
476
|
+
| `next` | `next` (release candidate) | `1.5.0-rc.1` |
|
|
477
|
+
| `beta` | `beta` | `2.0.0-beta.1` |
|
|
478
|
+
| outras / PR | nada, só CI | — |
|
|
479
|
+
|
|
480
|
+
| Commit | Efeito na versão |
|
|
481
|
+
|---|---|
|
|
482
|
+
| `fix: ...` | patch (`1.4.0` → `1.4.1`) |
|
|
483
|
+
| `feat: ...` | minor (`1.4.0` → `1.5.0`) |
|
|
484
|
+
| `feat!: ...` ou `BREAKING CHANGE:` no corpo | major (`1.4.0` → `2.0.0`) |
|
|
485
|
+
| `docs:`, `ci:`, `chore:`, `test:`, `refactor:` | sem release |
|
|
486
|
+
|
|
487
|
+
Use squash merge: o título do PR vira o commit, e o workflow `PR title` bloqueia título
|
|
488
|
+
fora do padrão.
|
|
489
|
+
|
|
490
|
+
### Mensagens de commit
|
|
491
|
+
|
|
492
|
+
O `/versions` mostra o título de cada `feat`/`fix` como a "visão simples" e o corpo como
|
|
493
|
+
"nota técnica". Por isso:
|
|
494
|
+
|
|
495
|
+
- **Título:** o que muda para quem usa, em inglês, no presente, sem nome de arquivo ou
|
|
496
|
+
função. `feat: keep the input free and queue new requests while one runs`, não
|
|
497
|
+
`feat: add Queue class to chat.py`.
|
|
498
|
+
- **Corpo:** o porquê e o comportamento novo em frases curtas; detalhes técnicos só no
|
|
499
|
+
fim, se ajudarem.
|
|
500
|
+
- Um commit por mudança perceptível. Refatoração e testes vão em `refactor:`/`test:`,
|
|
501
|
+
que não aparecem em `/versions`.
|
|
502
|
+
|
|
503
|
+

|
|
504
|
+
|
|
505
|
+
A publicação usa **OIDC / trusted publishing**: o GitHub Actions se autentica no npm sem
|
|
506
|
+
token, e o pacote sai com *provenance*. Configuração única:
|
|
507
|
+
|
|
508
|
+
1. **npm → página do pacote → Settings → Trusted Publisher**: adicionar *GitHub Actions*
|
|
509
|
+
com `samuelrms/omni-ai-cli` e o workflow `release.yml`.
|
|
510
|
+
2. **GitHub → Settings → Branches**: proteger `main` (e `next`/`beta`, se usar) exigindo PR
|
|
511
|
+
e os checks `Tests (...)`, `npm package` e `conventional`.
|
|
512
|
+
3. **GitHub → Settings → General → Pull Requests**: deixar só *Allow squash merging* e
|
|
513
|
+
marcar *Default to pull request title*.
|
|
514
|
+
|
|
515
|
+
O workflow já pede `id-token: write` e `NPM_CONFIG_PROVENANCE`; não há `NPM_TOKEN` para
|
|
516
|
+
guardar nem rotacionar.
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## Troubleshooting
|
|
521
|
+
|
|
522
|
+
| Problema | Solução |
|
|
523
|
+
| --- | --- |
|
|
524
|
+
| `OPENROUTER_API_KEY não configurada` | `/key openrouter` no chat ou `ai --key openrouter`. |
|
|
525
|
+
| `Arquivo de credenciais não encontrado` ou `Conta 'X' sem login` | `/account login X`. |
|
|
526
|
+
| `You've hit your session limit` | Cota da conta. O `ai` passa para a próxima; adicione outra com `/account add` ou uma chave com `/key claude`. |
|
|
527
|
+
| `Individual quota reached` (Antigravity) | Cota do Antigravity; ele é pulado até a renovação. |
|
|
528
|
+
| `Not inside a trusted directory` (Codex) | Instalação antiga. Rode o instalador de novo. |
|
|
529
|
+
| `Claude Code não instalado` | `./scripts/install.sh` (ou `scripts\install.ps1` no Windows) instala pelo instalador oficial. |
|
|
530
|
+
| Windows: atalhos de skills falham | Ative o Modo de Desenvolvedor ou rode como administrador; sem isso o `ai` usa junções de pasta. |
|
|
531
|
+
| `/model` não muda nada | Uma variável `SMART_AI_*_MODEL` está definida e tem precedência. |
|
|
532
|
+
| `/key` não muda nada | A variável da ferramenta (`ANTHROPIC_API_KEY` etc.) está definida e tem precedência. |
|
|
533
|
+
| `Sessão já aberta em outro terminal` | Feche o outro terminal ou use `/new`. |
|
|
534
|
+
| `ai: command not found` | Adicione `~/.local/bin` ao `PATH`. |
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Limitações conhecidas
|
|
539
|
+
|
|
540
|
+
- O chat usa os modos não interativos dos CLIs. Pedidos de aprovação nativos (por
|
|
541
|
+
exemplo, "posso editar este arquivo?") não aparecem; nesses casos, use o CLI nativo.
|
|
542
|
+
- `/parallel` só analisa: Codex em `--sandbox read-only` ou OpenRouter, que não acessa os
|
|
543
|
+
arquivos. Os agentes não editam o projeto ao mesmo tempo.
|
|
544
|
+
- A classificação simples/complexa é por palavras-chave e tamanho, não por um modelo.
|
|
545
|
+
- Trocar de modelo não renova a cota da conta.
|
|
546
|
+
- O OpenRouter não tem streaming nesta versão: a resposta aparece inteira no final.
|
|
547
|
+
- No Windows, o código usa os equivalentes nativos (grupo de processos e `taskkill`, `msvcrt`, junções de pasta), mas foi testado de verdade só no macOS.
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Privacidade
|
|
552
|
+
|
|
553
|
+
- Sessões, chaves de API e logs ficam só na sua máquina, com permissão restrita ao seu usuário.
|
|
554
|
+
- O log de uso grava ferramenta, conta e status, **nunca o texto da pergunta**.
|
|
555
|
+
- Antes de ir para o OpenRouter, e-mails, telefones, documentos e outros dados pessoais
|
|
556
|
+
são trocados por marcadores e restaurados na resposta. O mascaramento é por padrões e
|
|
557
|
+
não garante anonimização total.
|
|
558
|
+
- Claude, Codex e Antigravity recebem o texto original, sob os termos de cada serviço.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Licença
|
|
563
|
+
|
|
564
|
+
[MIT](LICENSE). Bibliotecas embutidas em `vendor/`: prompt_toolkit (BSD-3-Clause) e wcwidth (MIT), com as licenças em cada pasta.
|
package/bin/ai.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// npm launcher: finds Python 3.11+ and runs the bundled omni_ai package with the same arguments.
|
|
3
|
+
'use strict';
|
|
4
|
+
const { spawnSync, spawn } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const root = path.resolve(__dirname, '..');
|
|
8
|
+
const candidates = process.env.SMART_AI_PYTHON
|
|
9
|
+
? [[process.env.SMART_AI_PYTHON]]
|
|
10
|
+
: process.platform === 'win32'
|
|
11
|
+
? [['py', '-3'], ['python'], ['python3']]
|
|
12
|
+
: [['python3'], ['python']];
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
const home = os.homedir();
|
|
17
|
+
const dataDir = process.platform === 'win32'
|
|
18
|
+
? path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'smart-ai')
|
|
19
|
+
: path.join(home, '.local', 'share', 'smart-ai');
|
|
20
|
+
const cachedPython = path.join(home, '.config', 'smart-ai', 'python-path');
|
|
21
|
+
|
|
22
|
+
function works(cmd, pre) {
|
|
23
|
+
const probe = spawnSync(cmd, [...pre, '-c', 'import sys; print(sys.version_info >= (3, 11))'], { encoding: 'utf8' });
|
|
24
|
+
return probe.status === 0 && probe.stdout.trim() === 'True';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findPython() {
|
|
28
|
+
try {
|
|
29
|
+
const saved = fs.readFileSync(cachedPython, 'utf8').trim();
|
|
30
|
+
if (saved && works(saved, [])) return [saved, []];
|
|
31
|
+
} catch (_) { /* first run */ }
|
|
32
|
+
for (const [cmd, ...pre] of candidates) {
|
|
33
|
+
if (works(cmd, pre)) return [cmd, pre];
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// No usable Python: fetch a private Python 3.12 with uv (no sudo, system Python untouched)
|
|
39
|
+
function installPython() {
|
|
40
|
+
if (process.env.SMART_AI_NO_AUTO_PYTHON === '1') return null;
|
|
41
|
+
console.error('Python 3.11+ não encontrado. Instalando um Python 3.12 só para o Omni AI (sem sudo)...');
|
|
42
|
+
const uvDir = path.join(dataDir, 'uv');
|
|
43
|
+
const uv = path.join(uvDir, process.platform === 'win32' ? 'uv.exe' : 'uv');
|
|
44
|
+
const env = { ...process.env, UV_INSTALL_DIR: uvDir, UV_NO_MODIFY_PATH: '1',
|
|
45
|
+
UV_PYTHON_INSTALL_DIR: path.join(dataDir, 'python') };
|
|
46
|
+
if (!fs.existsSync(uv)) {
|
|
47
|
+
const setup = process.platform === 'win32'
|
|
48
|
+
? spawnSync('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command',
|
|
49
|
+
'irm https://astral.sh/uv/install.ps1 | iex'], { stdio: 'inherit', env })
|
|
50
|
+
: spawnSync('sh', ['-c', 'curl -LsSf https://astral.sh/uv/install.sh | sh'], { stdio: 'inherit', env });
|
|
51
|
+
if (setup.status !== 0 || !fs.existsSync(uv)) return null;
|
|
52
|
+
}
|
|
53
|
+
// --no-bin: keep the private Python out of the user's PATH
|
|
54
|
+
if (spawnSync(uv, ['python', 'install', '--no-bin', '3.12'], { stdio: 'inherit', env }).status !== 0 &&
|
|
55
|
+
spawnSync(uv, ['python', 'install', '3.12'], { stdio: 'inherit', env }).status !== 0) return null;
|
|
56
|
+
const found = spawnSync(uv, ['python', 'find', '3.12'], { encoding: 'utf8', env });
|
|
57
|
+
const exe = found.stdout.trim();
|
|
58
|
+
if (found.status !== 0 || !exe || !works(exe, [])) return null;
|
|
59
|
+
fs.mkdirSync(path.dirname(cachedPython), { recursive: true });
|
|
60
|
+
fs.writeFileSync(cachedPython, exe);
|
|
61
|
+
console.error(`✓ Python pronto: ${exe}`);
|
|
62
|
+
return [exe, []];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const python = findPython() || installPython();
|
|
66
|
+
if (!python) {
|
|
67
|
+
console.error('Omni AI precisa do Python 3.11 ou mais novo e não conseguiu instalá-lo sozinho.');
|
|
68
|
+
console.error(process.platform === 'win32'
|
|
69
|
+
? 'Instale em https://www.python.org/downloads/ (marque "Add to PATH").'
|
|
70
|
+
: 'macOS: brew install python · Ubuntu/Debian: sudo apt install python3');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Claude Code installed as this package's optional dependency is used when there's no global one
|
|
75
|
+
const env = { ...process.env, SMART_AI_INSTALLER: 'npm',
|
|
76
|
+
PYTHONPATH: [root, process.env.PYTHONPATH].filter(Boolean).join(path.delimiter) };
|
|
77
|
+
env.PATH = [process.env.PATH, path.join(root, 'node_modules', '.bin')].join(path.delimiter);
|
|
78
|
+
|
|
79
|
+
const [cmd, pre] = python;
|
|
80
|
+
const child = spawn(cmd, [...pre, '-m', 'omni_ai', ...process.argv.slice(2)], { stdio: 'inherit', env });
|
|
81
|
+
// Ctrl-C reaches the child through the shared terminal; just don't die before it does
|
|
82
|
+
process.on('SIGINT', () => {});
|
|
83
|
+
process.on('SIGTERM', () => child.kill('SIGTERM'));
|
|
84
|
+
child.on('exit', (code, signal) => process.exit(signal ? 130 : code ?? 1));
|