anveesa 0.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/Cargo.toml ADDED
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "anveesa"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+ default-run = "anveesa"
6
+
7
+ [dependencies]
8
+ base64 = "0.22"
9
+ anyhow = "1.0"
10
+ clap = { version = "4.5", features = ["derive"] }
11
+ libc = "0.2"
12
+ reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] }
13
+ rustyline = "14"
14
+ serde = { version = "1.0", features = ["derive"] }
15
+ serde_json = "1.0"
16
+ tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "process", "time", "sync", "io-util"] }
17
+ toml = "0.8"
package/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # Anveesa
2
+
3
+ Anveesa is a Rust terminal wrapper for AI providers. It gives you one command,
4
+ `anveesa`, while each provider is configured as either:
5
+
6
+ - `openai-compatible`: HTTP chat completions providers such as OpenRouter and other compatible gateways.
7
+ - `command`: local CLIs such as Codex, Copilot, and Claude Code, where Anveesa spawns a command and passes the prompt.
8
+
9
+ ## Install locally
10
+
11
+ ```sh
12
+ cargo install --path . --force
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```sh
18
+ anveesa config init
19
+ export SUMOPOD_API_KEY="..."
20
+ anveesa config set-model "your-sumopod-model"
21
+ anveesa
22
+ ```
23
+
24
+ Running `anveesa` with no prompt opens an interactive prompt for the default provider:
25
+
26
+ ```text
27
+ anveesa | provider: sumopod | model: kimi-k2.6
28
+ state | turns:0 | ctx:on | tools:on | writes:ask | memory:new
29
+ commands| /clear reset | /exit quit
30
+ approve | y once | a all for current turn | enter no
31
+
32
+ anveesa[0]>
33
+ ```
34
+
35
+ Interactive mode keeps running after each answer. It also keeps the conversation
36
+ context for the same provider/model/system in the same working directory, even
37
+ after restarting Anveesa. Use `/clear` to reset that context and `/exit` to
38
+ return to the shell.
39
+
40
+ The prompt has full line editing, and your input history is remembered across
41
+ sessions (stored next to the config as `history`). The active conversation is
42
+ stored next to it as `session.json`. Use the up/down arrows to recall previous
43
+ prompts.
44
+
45
+ `ctx:on` means Anveesa sends lightweight terminal context with each request:
46
+ current directory, parent directory, git root/branch/status when available, and
47
+ a capped directory listing. This lets the model answer questions like "where are
48
+ you?" using the terminal workspace instead of guessing.
49
+
50
+ `tools:on` means OpenAI-compatible providers can ask Anveesa to inspect the
51
+ workspace: list directories, find files by name, search text, read capped file
52
+ snippets, and do a basic web lookup. The tools can inspect paths outside the
53
+ current project, but obvious secret files such as SSH keys and `.env` files are
54
+ blocked.
55
+
56
+ `writes:ask` covers the workspace-modifying tools — `create_dir`, `write_file`,
57
+ `edit_file`, and `run_command`. By default Anveesa asks for confirmation on the
58
+ terminal before each one:
59
+
60
+ ```text
61
+ allow run command `cargo test`? [y]es/[a]ll this turn/[N]o
62
+ ```
63
+
64
+ Answer `a` to approve the remaining write/run tools for the current assistant
65
+ turn, which is useful when scaffolding several files.
66
+
67
+ The indicator reflects the active policy: `writes:ask` (confirm each action,
68
+ the interactive default and the default for one-shot prompts typed directly in a
69
+ terminal), `writes:auto` (run without asking, enabled with `--yes`), or
70
+ `writes:off` (disabled for non-interactive stdin runs unless `--yes` is passed).
71
+
72
+ Responses stream token-by-token as the model generates them. While Anveesa waits
73
+ for the first token it shows a small status line such as:
74
+
75
+ ```text
76
+ - thinking... 2s
77
+ ```
78
+
79
+ When usage is reported by the provider, a token summary is printed to stderr
80
+ after the answer:
81
+
82
+ ```text
83
+ [tokens: 812 in / 144 out / 956 total]
84
+ ```
85
+
86
+ OpenAI-compatible providers can use up to 32 tool rounds per answer by default.
87
+ After that, Anveesa stops advertising tools and asks the model to produce a
88
+ final answer from the gathered results. Override the cap with
89
+ `ANVEESA_MAX_TOOL_ROUNDS`, up to 256.
90
+
91
+ Use GLM/Z.ai:
92
+
93
+ ```sh
94
+ export ZAI_API_KEY="..."
95
+ anveesa config set-provider glm
96
+ anveesa config set-model "glm-5.1"
97
+ anveesa "write a rust module outline"
98
+ ```
99
+
100
+ You can also use the default `ask` behavior:
101
+
102
+ ```sh
103
+ anveesa "write a git commit message"
104
+ ```
105
+
106
+ Pipe stdin into a prompt:
107
+
108
+ ```sh
109
+ git diff | anveesa ask --stdin "review this diff"
110
+ ```
111
+
112
+ Let the model make changes. In interactive mode and one-shot terminal prompts,
113
+ it asks before each write or command. Pass `--yes` (`-y`) to allow file writes
114
+ and command execution without prompting:
115
+
116
+ ```sh
117
+ anveesa --provider sumopod --yes "add a Default impl for the Config struct"
118
+ ```
119
+
120
+ Run through Claude Code if the `claude` CLI is installed:
121
+
122
+ ```sh
123
+ anveesa --provider claude-code "summarize this project"
124
+ ```
125
+
126
+ Run through Codex if the `codex` CLI is installed:
127
+
128
+ ```sh
129
+ anveesa --provider codex --model "gpt-5.1-codex" "review this repository"
130
+ ```
131
+
132
+ Run through GitHub Copilot CLI if the `copilot` CLI is installed:
133
+
134
+ ```sh
135
+ anveesa --provider copilot --model "gpt-5.1" "explain this function"
136
+ ```
137
+
138
+ Use Sumopod with its OpenAI-compatible API:
139
+
140
+ ```sh
141
+ export SUMOPOD_API_KEY="..."
142
+ anveesa --provider sumopod --model "your-sumopod-model" "explain this error"
143
+ ```
144
+
145
+ ## Built-in providers
146
+
147
+ OpenAI-compatible API providers:
148
+
149
+ - `openai`
150
+ - `sumopod`
151
+ - `openrouter`
152
+ - `glm`
153
+ - `glm-coding`
154
+ - `deepseek`
155
+ - `gemini`
156
+ - `github-models`
157
+ - `groq`
158
+ - `mistral`
159
+ - `xai`
160
+ - `together`
161
+ - `fireworks`
162
+ - `cerebras`
163
+ - `sambanova`
164
+ - `nvidia`
165
+ - `dashscope`
166
+ - `qwen`
167
+ - `huggingface`
168
+ - `vercel-ai-gateway`
169
+ - `perplexity`
170
+ - `ollama`
171
+ - `lm-studio`
172
+ - `vllm`
173
+ - `litellm`
174
+ - `localai`
175
+
176
+ Terminal command providers:
177
+
178
+ - `claude-code`
179
+ - `codex`
180
+ - `copilot`
181
+
182
+ Check the effective list any time:
183
+
184
+ ```sh
185
+ anveesa providers
186
+ ```
187
+
188
+ ## Config
189
+
190
+ Default path:
191
+
192
+ ```sh
193
+ anveesa config path
194
+ ```
195
+
196
+ The path can be overridden with `ANVEESA_CONFIG`.
197
+
198
+ Set defaults once:
199
+
200
+ ```sh
201
+ anveesa config set-provider sumopod
202
+ anveesa config set-model "kimi-k2.6"
203
+ ```
204
+
205
+ After that, just run:
206
+
207
+ ```sh
208
+ anveesa
209
+ ```
210
+
211
+ Example provider config:
212
+
213
+ ```toml
214
+ default_provider = "sumopod"
215
+
216
+ [providers.sumopod]
217
+ kind = "openai-compatible"
218
+ base_url = "https://ai.sumopod.com/v1"
219
+ api_key_env = "SUMOPOD_API_KEY"
220
+ default_model = "your-sumopod-model"
221
+
222
+ [providers.openrouter]
223
+ kind = "openai-compatible"
224
+ base_url = "https://openrouter.ai/api/v1"
225
+ api_key_env = "OPENROUTER_API_KEY"
226
+
227
+ [providers.glm]
228
+ kind = "openai-compatible"
229
+ base_url = "https://api.z.ai/api/paas/v4"
230
+ api_key_env = "ZAI_API_KEY"
231
+ default_model = "glm-5.1"
232
+
233
+ [providers.codex]
234
+ kind = "command"
235
+ command = "codex"
236
+ args = ["exec", "{model_args}", "{prompt}"]
237
+ model_args = ["--model", "{model}"]
238
+
239
+ [providers.claude-code]
240
+ kind = "command"
241
+ command = "claude"
242
+ args = ["-p", "{system_args}", "{model_args}", "{prompt}"]
243
+ model_args = ["--model", "{model}"]
244
+ system_args = ["--system-prompt", "{system}"]
245
+ ```
246
+
247
+ Command providers can use placeholders in args or env values:
248
+
249
+ - `{prompt}`
250
+ - `{model}`
251
+ - `{system}`
252
+ - `{model_args}`
253
+ - `{system_args}`
package/bin/anveesa.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execFile } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // Cari binary di beberapa kemungkinan lokasi
8
+ function findBinary() {
9
+ const candidates = [
10
+ // Installed via npm (global) - binary di samping script
11
+ path.join(__dirname, 'target', 'release', 'anveesa'),
12
+ path.join(__dirname, 'target', 'debug', 'anveesa'),
13
+ // Installed via npm local (node_modules/.bin)
14
+ path.join(__dirname, '..', '..', 'target', 'release', 'anveesa'),
15
+ path.join(__dirname, '..', '..', 'target', 'debug', 'anveesa'),
16
+ // Development mode (cargo build)
17
+ path.join(process.cwd(), 'target', 'release', 'anveesa'),
18
+ path.join(process.cwd(), 'target', 'debug', 'anveesa'),
19
+ ];
20
+
21
+ for (const p of candidates) {
22
+ if (fs.existsSync(p)) {
23
+ return p;
24
+ }
25
+ }
26
+
27
+ // Cek PATH environment
28
+ const paths = process.env.PATH.split(path.delimiter);
29
+ for (const p of paths) {
30
+ const full = path.join(p, 'anveesa');
31
+ if (fs.existsSync(full)) {
32
+ return full;
33
+ }
34
+ }
35
+
36
+ return null;
37
+ }
38
+
39
+ const binaryPath = findBinary();
40
+
41
+ if (!binaryPath) {
42
+ console.error('❌ anveesa binary not found.');
43
+ console.error('');
44
+ console.error('Build the Rust binary first:');
45
+ console.error(' cd ' + __dirname + ' && npm run build');
46
+ console.error(' # or: cargo build --release');
47
+ console.error('');
48
+ process.exit(1);
49
+ }
50
+
51
+ // Spawn the Rust binary
52
+ const args = process.argv.slice(2);
53
+ const options = {
54
+ cwd: process.cwd(),
55
+ stdio: ['inherit', 'inherit', 'inherit'],
56
+ env: { ...process.env },
57
+ };
58
+
59
+ try {
60
+ execFile(binaryPath, args, options);
61
+ } catch (error) {
62
+ console.error('Error running anveesa:', error.message);
63
+ process.exit(1);
64
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "anveesa",
3
+ "version": "0.1.0",
4
+ "description": "Anveesa terminal wrapper for AI providers",
5
+ "main": "bin/anveesa.js",
6
+ "bin": {
7
+ "anveesa": "bin/anveesa.js"
8
+ },
9
+ "scripts": {
10
+ "build": "cargo build --release",
11
+ "install": "node scripts/install.js",
12
+ "prepublishOnly": "npm run build",
13
+ "install:bin": "cargo build --release"
14
+ },
15
+ "files": [
16
+ "bin/",
17
+ "scripts/",
18
+ "Cargo.toml",
19
+ "Cargo.lock",
20
+ "src/",
21
+ "README.md"
22
+ ],
23
+ "keywords": ["ai", "cli", "terminal", "rust"],
24
+ "license": "MIT",
25
+ "engines": {
26
+ "node": ">=16"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/pandhuwibowo/anveesa-cli.git"
31
+ }
32
+ }
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Installation script for anveesa npm package
5
+ * This script checks if the Rust binary exists and provides instructions if not
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { execSync } = require('child_process');
11
+
12
+ function getBinaryPath() {
13
+ const pkgDir = __dirname;
14
+ const possiblePaths = [
15
+ path.join(pkgDir, '..', 'target', 'debug', 'anveesa'),
16
+ path.join(pkgDir, '..', 'target', 'release', 'anveesa'),
17
+ path.join(pkgDir, 'target', 'debug', 'anveesa'),
18
+ path.join(pkgDir, 'target', 'release', 'anveesa'),
19
+ ];
20
+
21
+ for (const p of possiblePaths) {
22
+ if (fs.existsSync(p)) {
23
+ return p;
24
+ }
25
+ }
26
+
27
+ return null;
28
+ }
29
+
30
+ function checkAndInstall() {
31
+ const binaryPath = getBinaryPath();
32
+
33
+ if (binaryPath) {
34
+ console.log('✓ Anveesa binary found at:', binaryPath);
35
+ console.log('✓ Installation complete!');
36
+ return true;
37
+ } else {
38
+ console.error('✗ Anveesa binary not found.');
39
+ console.error('');
40
+ console.error('Build the Rust binary first:');
41
+ console.error(' npm run install:bin');
42
+ console.error('');
43
+ return false;
44
+ }
45
+ }
46
+
47
+ // If run directly
48
+ if (require.main === module) {
49
+ checkAndInstall();
50
+ }
51
+
52
+ module.exports = {
53
+ getBinaryPath,
54
+ checkAndInstall
55
+ };
package/src/cli.rs ADDED
@@ -0,0 +1,94 @@
1
+ use clap::{Args, Parser, Subcommand};
2
+
3
+ #[derive(Debug, Parser)]
4
+ #[command(
5
+ name = "anveesa",
6
+ version,
7
+ about = "Terminal AI wrapper for multiple providers"
8
+ )]
9
+ pub struct Cli {
10
+ #[command(subcommand)]
11
+ pub command: Option<Command>,
12
+
13
+ #[command(flatten)]
14
+ pub ask_options: AskOptions,
15
+
16
+ #[arg(value_name = "PROMPT", trailing_var_arg = true)]
17
+ pub prompt: Vec<String>,
18
+ }
19
+
20
+ #[derive(Debug, Subcommand)]
21
+ pub enum Command {
22
+ /// Ask the configured AI provider.
23
+ Ask(AskArgs),
24
+ /// List available providers.
25
+ Providers,
26
+ /// Manage the Anveesa config file.
27
+ Config(ConfigArgs),
28
+ }
29
+
30
+ #[derive(Debug, Args)]
31
+ pub struct AskArgs {
32
+ #[command(flatten)]
33
+ pub options: AskOptions,
34
+
35
+ #[arg(value_name = "PROMPT", trailing_var_arg = true)]
36
+ pub prompt: Vec<String>,
37
+ }
38
+
39
+ #[derive(Debug, Args, Clone, Default)]
40
+ pub struct AskOptions {
41
+ /// Provider name from the config.
42
+ #[arg(short, long)]
43
+ pub provider: Option<String>,
44
+
45
+ /// Model name to send to the provider.
46
+ #[arg(short, long)]
47
+ pub model: Option<String>,
48
+
49
+ /// Optional system instruction.
50
+ #[arg(long)]
51
+ pub system: Option<String>,
52
+
53
+ /// Append stdin to the prompt.
54
+ #[arg(long)]
55
+ pub stdin: bool,
56
+
57
+ /// Auto-approve file writes and command execution without prompting.
58
+ #[arg(short = 'y', long)]
59
+ pub yes: bool,
60
+ }
61
+
62
+ #[derive(Debug, Args)]
63
+ pub struct ConfigArgs {
64
+ #[command(subcommand)]
65
+ pub command: ConfigCommand,
66
+ }
67
+
68
+ #[derive(Debug, Subcommand)]
69
+ pub enum ConfigCommand {
70
+ /// Create a starter config at the default path.
71
+ Init {
72
+ /// Overwrite an existing config file.
73
+ #[arg(long)]
74
+ force: bool,
75
+ },
76
+ /// Set the default model for a provider.
77
+ SetModel {
78
+ /// Provider name from the config. Defaults to default_provider.
79
+ #[arg(short, long)]
80
+ provider: Option<String>,
81
+
82
+ /// Model name to use by default.
83
+ model: String,
84
+ },
85
+ /// Set the default provider.
86
+ SetProvider {
87
+ /// Provider name from the config.
88
+ provider: String,
89
+ },
90
+ /// Print the config path.
91
+ Path,
92
+ /// Print the effective config.
93
+ Show,
94
+ }