cmdgen-ai-cli 0.1.0__py3-none-any.whl
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.
- cmdgen/__init__.py +5 -0
- cmdgen/__main__.py +4 -0
- cmdgen/config.py +43 -0
- cmdgen/context.py +32 -0
- cmdgen/executor.py +59 -0
- cmdgen/llm/__init__.py +3 -0
- cmdgen/llm/base.py +37 -0
- cmdgen/llm/gemini.py +66 -0
- cmdgen/main.py +104 -0
- cmdgen/update.py +63 -0
- cmdgen_ai_cli-0.1.0.dist-info/METADATA +66 -0
- cmdgen_ai_cli-0.1.0.dist-info/RECORD +14 -0
- cmdgen_ai_cli-0.1.0.dist-info/WHEEL +4 -0
- cmdgen_ai_cli-0.1.0.dist-info/entry_points.txt +3 -0
cmdgen/__init__.py
ADDED
cmdgen/__main__.py
ADDED
cmdgen/config.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/config.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This file is responsible for managing the user's settings. It defines what the
|
|
6
|
+
configuration looks like (using `pydantic`), and handles safely loading and
|
|
7
|
+
saving the API keys to a local file on the user's hard drive (`~/.config/cmdgen/config.json`).
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
CONFIG_DIR = Path.home() / ".config" / "cmdgen"
|
|
15
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
16
|
+
|
|
17
|
+
class AppConfig(BaseModel):
|
|
18
|
+
provider: str = Field(default="gemini", description="The LLM provider to use")
|
|
19
|
+
api_key: str = Field(default="", description="The API key for the provider")
|
|
20
|
+
|
|
21
|
+
def load_config() -> AppConfig:
|
|
22
|
+
"""Loads the configuration from disk, or returns default if not found."""
|
|
23
|
+
if not CONFIG_FILE.exists():
|
|
24
|
+
return AppConfig()
|
|
25
|
+
try:
|
|
26
|
+
with open(CONFIG_FILE, "r") as f:
|
|
27
|
+
data = json.load(f)
|
|
28
|
+
return AppConfig(**data)
|
|
29
|
+
except Exception:
|
|
30
|
+
# In case of corruption, return default
|
|
31
|
+
return AppConfig()
|
|
32
|
+
|
|
33
|
+
def save_config(config: AppConfig) -> None:
|
|
34
|
+
"""Saves the configuration to disk securely."""
|
|
35
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
with open(CONFIG_FILE, "w") as f:
|
|
37
|
+
json.dump(config.model_dump(), f, indent=4)
|
|
38
|
+
|
|
39
|
+
# Ensure strict permissions on Windows/Linux if possible
|
|
40
|
+
try:
|
|
41
|
+
os.chmod(CONFIG_FILE, 0o600)
|
|
42
|
+
except Exception:
|
|
43
|
+
pass # Permissions might act differently on Windows
|
cmdgen/context.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/context.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This file gathers information about the environment where the user is running the tool.
|
|
6
|
+
It detects the Operating System, the shell (e.g. bash, powershell), and the current
|
|
7
|
+
working directory so that the AI can generate commands tailored perfectly to their system.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import platform
|
|
11
|
+
|
|
12
|
+
def get_os_name() -> str:
|
|
13
|
+
"""Returns the name of the operating system."""
|
|
14
|
+
return platform.system() + " " + platform.release()
|
|
15
|
+
|
|
16
|
+
def get_shell_name() -> str:
|
|
17
|
+
"""Attempts to determine the current shell running the tool."""
|
|
18
|
+
# On Windows, COMSPEC is usually set to cmd.exe or powershell.exe
|
|
19
|
+
# On Unix, SHELL is usually set to /bin/bash, /bin/zsh, etc.
|
|
20
|
+
shell = os.environ.get("SHELL")
|
|
21
|
+
if shell:
|
|
22
|
+
return shell.split(os.sep)[-1]
|
|
23
|
+
|
|
24
|
+
comspec = os.environ.get("COMSPEC")
|
|
25
|
+
if comspec:
|
|
26
|
+
return comspec.split(os.sep)[-1]
|
|
27
|
+
|
|
28
|
+
return "unknown"
|
|
29
|
+
|
|
30
|
+
def get_cwd() -> str:
|
|
31
|
+
"""Returns the current working directory."""
|
|
32
|
+
return os.getcwd()
|
cmdgen/executor.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/executor.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This file handles the interactive prompt and execution of the final command.
|
|
6
|
+
It uses `prompt_toolkit` to allow the user to edit the generated command
|
|
7
|
+
before running it, and `subprocess` to actually run the command on the OS.
|
|
8
|
+
"""
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from prompt_toolkit import PromptSession
|
|
13
|
+
from prompt_toolkit.lexers import PygmentsLexer
|
|
14
|
+
from pygments.lexers.shell import BashLexer
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
DANGEROUS_KEYWORDS = [
|
|
19
|
+
"rm ", "del ", "format ", "drop ", "mkfs", "truncate"
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
def check_safety(command: str) -> None:
|
|
23
|
+
"""Checks if a command contains potentially dangerous keywords."""
|
|
24
|
+
cmd_lower = command.lower()
|
|
25
|
+
if any(keyword in cmd_lower for keyword in DANGEROUS_KEYWORDS):
|
|
26
|
+
console.print("[bold yellow][WARNING] This command may be destructive! Please review carefully.[/bold yellow]")
|
|
27
|
+
|
|
28
|
+
def execute_interactive(command: str) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Presents the generated command to the user for editing and executes it.
|
|
31
|
+
"""
|
|
32
|
+
check_safety(command)
|
|
33
|
+
|
|
34
|
+
session = PromptSession()
|
|
35
|
+
try:
|
|
36
|
+
# Prompt the user, pre-filled with the AI's command
|
|
37
|
+
final_command = session.prompt(
|
|
38
|
+
"> ",
|
|
39
|
+
default=command,
|
|
40
|
+
lexer=PygmentsLexer(BashLexer)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
final_command = final_command.strip()
|
|
44
|
+
if not final_command:
|
|
45
|
+
console.print("[dim]Command cancelled.[/dim]")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
# Execute natively
|
|
49
|
+
console.print()
|
|
50
|
+
subprocess.run(final_command, shell=True)
|
|
51
|
+
|
|
52
|
+
except KeyboardInterrupt:
|
|
53
|
+
# User pressed Ctrl+C
|
|
54
|
+
console.print("\n[dim]Command cancelled by user.[/dim]")
|
|
55
|
+
except EOFError:
|
|
56
|
+
# User pressed Ctrl+D
|
|
57
|
+
console.print("\n[dim]Command cancelled by user.[/dim]")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
console.print(f"[bold red]Failed to execute command:[/bold red] {e}")
|
cmdgen/llm/__init__.py
ADDED
cmdgen/llm/base.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/llm/base.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This file defines the `LLMProvider` abstract base class. It serves as a blueprint or
|
|
6
|
+
"contract". It guarantees that no matter what AI provider we add in the future (Gemini,
|
|
7
|
+
OpenAI, Claude, etc.), they will all have a `generate_command()` method with the same inputs.
|
|
8
|
+
"""
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Dict, Any
|
|
11
|
+
|
|
12
|
+
class LLMProvider(ABC):
|
|
13
|
+
"""Abstract base class for all LLM providers."""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def generate_command(
|
|
17
|
+
self,
|
|
18
|
+
query: str,
|
|
19
|
+
os_name: str,
|
|
20
|
+
shell_name: str,
|
|
21
|
+
cwd: str,
|
|
22
|
+
api_key: str
|
|
23
|
+
) -> tuple[str, str]:
|
|
24
|
+
"""
|
|
25
|
+
Generate a terminal command based on a natural language query.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
query: The user's natural language request.
|
|
29
|
+
os_name: The operating system (e.g., Windows, Linux).
|
|
30
|
+
shell_name: The shell being used (e.g., powershell, bash).
|
|
31
|
+
cwd: The current working directory.
|
|
32
|
+
api_key: The API key for the provider.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A tuple of (generated_command, explanation).
|
|
36
|
+
"""
|
|
37
|
+
pass
|
cmdgen/llm/gemini.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/llm/gemini.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This file contains the actual implementation for the Google Gemini AI. It takes the
|
|
6
|
+
user's query and their system context, constructs the hidden system prompt (with
|
|
7
|
+
safety filters), and calls the `google-genai` API to get the command back.
|
|
8
|
+
"""
|
|
9
|
+
from google import genai
|
|
10
|
+
from google.genai import types
|
|
11
|
+
|
|
12
|
+
from cmdgen.llm.base import LLMProvider
|
|
13
|
+
|
|
14
|
+
class GeminiProvider(LLMProvider):
|
|
15
|
+
"""Gemini implementation of the LLM Provider."""
|
|
16
|
+
|
|
17
|
+
def generate_command(
|
|
18
|
+
self,
|
|
19
|
+
query: str,
|
|
20
|
+
os_name: str,
|
|
21
|
+
shell_name: str,
|
|
22
|
+
cwd: str,
|
|
23
|
+
api_key: str
|
|
24
|
+
) -> tuple[str, str]:
|
|
25
|
+
|
|
26
|
+
# Initialize the new SDK client
|
|
27
|
+
client = genai.Client(api_key=api_key)
|
|
28
|
+
|
|
29
|
+
# Use gemini-2.5-flash, the fast model
|
|
30
|
+
model_name = 'gemini-2.5-flash'
|
|
31
|
+
|
|
32
|
+
system_prompt = (
|
|
33
|
+
"You are an expert systems administrator CLI assistant.\n"
|
|
34
|
+
"Your job is to translate the user's natural language request into a valid, safe terminal command.\n\n"
|
|
35
|
+
f"CONTEXT:\n"
|
|
36
|
+
f"- OS: {os_name}\n"
|
|
37
|
+
f"- Shell: {shell_name}\n"
|
|
38
|
+
f"- Current Working Directory: {cwd}\n\n"
|
|
39
|
+
"RULES:\n"
|
|
40
|
+
"1. Output ONLY the raw command on the first line.\n"
|
|
41
|
+
"2. Output a brief, 1-sentence explanation on the second line.\n"
|
|
42
|
+
"3. Do not use markdown blocks (```) or quotes around the command.\n"
|
|
43
|
+
"4. Ensure paths are compatible with the specified OS."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
prompt = f"{system_prompt}\n\nUSER REQUEST: {query}\n\nCOMMAND AND EXPLANATION:"
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
response = client.models.generate_content(
|
|
50
|
+
model=model_name,
|
|
51
|
+
contents=prompt,
|
|
52
|
+
config=types.GenerateContentConfig(
|
|
53
|
+
temperature=0.1 # Low temp for deterministic output
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
text = response.text.strip()
|
|
58
|
+
lines = text.split('\n', 1)
|
|
59
|
+
|
|
60
|
+
command = lines[0].strip()
|
|
61
|
+
explanation = lines[1].strip() if len(lines) > 1 else "No explanation provided."
|
|
62
|
+
|
|
63
|
+
return command, explanation
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
raise RuntimeError(f"Gemini API Error: {str(e)}")
|
cmdgen/main.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cmdgen/main.py
|
|
3
|
+
|
|
4
|
+
Purpose:
|
|
5
|
+
This is the primary entry point for the CLI application. It uses the `typer` library
|
|
6
|
+
to define all the commands the user can run (like `cmdgen "query"` and `cmdgen config`).
|
|
7
|
+
It connects the user's input to the configuration, context, and AI logic.
|
|
8
|
+
"""
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
14
|
+
from cmdgen.config import load_config, save_config
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(help="AI-powered terminal command generator.")
|
|
17
|
+
config_app = typer.Typer(help="Manage configuration (e.g. API keys).")
|
|
18
|
+
app.add_typer(config_app, name="config")
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
@app.command()
|
|
23
|
+
def generate(
|
|
24
|
+
query: list[str] = typer.Argument(..., help="The natural language query to translate into a command")
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Generate and execute a terminal command from natural language.
|
|
28
|
+
"""
|
|
29
|
+
query_str = " ".join(query)
|
|
30
|
+
config = load_config()
|
|
31
|
+
if not config.api_key:
|
|
32
|
+
console.print(Panel(
|
|
33
|
+
"[bold red]API Key Missing[/bold red]\n\n"
|
|
34
|
+
"To use cmdgen, you need to set up your free Gemini API key.\n"
|
|
35
|
+
"1. Get your key here: [link]https://aistudio.google.com/app/apikey[/link]\n"
|
|
36
|
+
"2. Set it by running: [bold cyan]cmdgen config set --provider gemini --api-key \"YOUR_KEY\"[/bold cyan]",
|
|
37
|
+
title="Setup Required", expand=False
|
|
38
|
+
))
|
|
39
|
+
raise typer.Exit(code=1)
|
|
40
|
+
|
|
41
|
+
# Check for new updates silently
|
|
42
|
+
from cmdgen.update import check_for_updates
|
|
43
|
+
check_for_updates()
|
|
44
|
+
|
|
45
|
+
with console.status(f"[bold green]Asking {config.provider}...", spinner="dots"):
|
|
46
|
+
from cmdgen.llm.gemini import GeminiProvider
|
|
47
|
+
from cmdgen.context import get_os_name, get_shell_name, get_cwd
|
|
48
|
+
|
|
49
|
+
provider = GeminiProvider() # We can make this dynamic later if we add OpenAI
|
|
50
|
+
try:
|
|
51
|
+
command, explanation = provider.generate_command(
|
|
52
|
+
query=query_str,
|
|
53
|
+
os_name=get_os_name(),
|
|
54
|
+
shell_name=get_shell_name(),
|
|
55
|
+
cwd=get_cwd(),
|
|
56
|
+
api_key=config.api_key
|
|
57
|
+
)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
console.print(f"[bold red]Error generating command:[/bold red] {e}")
|
|
60
|
+
raise typer.Exit(code=1)
|
|
61
|
+
|
|
62
|
+
console.print(f"\n[bold blue]Explanation:[/bold blue] {explanation}")
|
|
63
|
+
console.print("[dim]---[/dim]")
|
|
64
|
+
|
|
65
|
+
from cmdgen.executor import execute_interactive
|
|
66
|
+
execute_interactive(command)
|
|
67
|
+
|
|
68
|
+
@config_app.command("set")
|
|
69
|
+
def config_set(
|
|
70
|
+
provider: str = typer.Option("gemini", help="The LLM provider (e.g., gemini, openai)"),
|
|
71
|
+
api_key: str = typer.Option(..., help="The API key for the provider")
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Set the configuration values.
|
|
75
|
+
"""
|
|
76
|
+
config = load_config()
|
|
77
|
+
config.provider = provider
|
|
78
|
+
config.api_key = api_key
|
|
79
|
+
save_config(config)
|
|
80
|
+
console.print(f"[green]✔[/green] Configuration saved successfully! Provider set to [bold]{provider}[/bold].")
|
|
81
|
+
|
|
82
|
+
@config_app.command("view")
|
|
83
|
+
def config_view():
|
|
84
|
+
"""
|
|
85
|
+
View the current configuration safely.
|
|
86
|
+
"""
|
|
87
|
+
config = load_config()
|
|
88
|
+
masked_key = f"{config.api_key[:4]}...{config.api_key[-4:]}" if len(config.api_key) > 8 else "***"
|
|
89
|
+
|
|
90
|
+
console.print(Panel(
|
|
91
|
+
f"[bold]Provider:[/bold] {config.provider}\n"
|
|
92
|
+
f"[bold]API Key:[/bold] {masked_key}",
|
|
93
|
+
title="Current Configuration", expand=False
|
|
94
|
+
))
|
|
95
|
+
|
|
96
|
+
def cli_main():
|
|
97
|
+
import sys
|
|
98
|
+
# If the first argument is not a known subcommand or flag, default to "generate"
|
|
99
|
+
if len(sys.argv) > 1 and sys.argv[1] not in ["config", "generate", "--help", "-h", "--version"]:
|
|
100
|
+
sys.argv.insert(1, "generate")
|
|
101
|
+
app()
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
cli_main()
|
cmdgen/update.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Checks PyPI for a newer version of cmdgen and notifies the user.
|
|
3
|
+
Caches the check to avoid slowing down the CLI on every run.
|
|
4
|
+
"""
|
|
5
|
+
import urllib.request
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from cmdgen.config import CONFIG_DIR
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
# We cache the update check in the config directory
|
|
15
|
+
UPDATE_CACHE_FILE = CONFIG_DIR / "update_check.json"
|
|
16
|
+
CACHE_TTL = 86400 # 24 hours in seconds
|
|
17
|
+
PACKAGE_NAME = "cmdgen"
|
|
18
|
+
|
|
19
|
+
# Fallback version for development
|
|
20
|
+
def get_current_version() -> str:
|
|
21
|
+
try:
|
|
22
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
23
|
+
return version(PACKAGE_NAME)
|
|
24
|
+
except (ImportError, Exception):
|
|
25
|
+
return "0.1.0"
|
|
26
|
+
|
|
27
|
+
def check_for_updates() -> None:
|
|
28
|
+
"""Silently checks PyPI for updates, at most once per day."""
|
|
29
|
+
now = time.time()
|
|
30
|
+
|
|
31
|
+
# Check cache
|
|
32
|
+
if UPDATE_CACHE_FILE.exists():
|
|
33
|
+
try:
|
|
34
|
+
cache_data = json.loads(UPDATE_CACHE_FILE.read_text(encoding="utf-8"))
|
|
35
|
+
if now - cache_data.get("last_check", 0) < CACHE_TTL:
|
|
36
|
+
return # Skip check, too soon
|
|
37
|
+
except Exception:
|
|
38
|
+
pass # If cache is corrupted, ignore it
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Fetch latest version from PyPI
|
|
42
|
+
req = urllib.request.Request(
|
|
43
|
+
f"https://pypi.org/pypi/{PACKAGE_NAME}/json",
|
|
44
|
+
headers={"User-Agent": f"cmdgen/{get_current_version()}"}
|
|
45
|
+
)
|
|
46
|
+
with urllib.request.urlopen(req, timeout=1.5) as response:
|
|
47
|
+
data = json.loads(response.read().decode("utf-8"))
|
|
48
|
+
latest_version = data["info"]["version"]
|
|
49
|
+
|
|
50
|
+
current = get_current_version()
|
|
51
|
+
|
|
52
|
+
# Simple string comparison (this works well enough for simple semver like 0.1.0 vs 0.2.0)
|
|
53
|
+
# We don't want to add a `packaging` dependency just for this.
|
|
54
|
+
if latest_version and latest_version != current:
|
|
55
|
+
console.print(f"[bold yellow]💡 A new version of cmdgen ({latest_version}) is available! Run `pipx upgrade cmdgen` to update.[/bold yellow]")
|
|
56
|
+
|
|
57
|
+
# Update cache
|
|
58
|
+
UPDATE_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
UPDATE_CACHE_FILE.write_text(json.dumps({"last_check": now, "latest_version": latest_version}), encoding="utf-8")
|
|
60
|
+
|
|
61
|
+
except Exception:
|
|
62
|
+
# If offline or PyPI is down, silently fail. We don't want to bother the user.
|
|
63
|
+
pass
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cmdgen-ai-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI-powered CLI tool to translate natural language into terminal commands
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Requires-Dist: google-genai>=0.1.0
|
|
7
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
8
|
+
Requires-Dist: pydantic>=2.0.0
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
Requires-Dist: typer>=0.9.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: black>=23.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: flake8>=6.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# cmdgen 🪄
|
|
18
|
+
|
|
19
|
+
An AI-powered CLI tool that translates your natural language into native terminal commands and lets you review them before execution. Powered by Google's Gemini API.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Natural Language to CLI:** Just type what you want to do.
|
|
24
|
+
- **Context-Aware:** Knows your OS (Windows/Linux/macOS), Shell, and Working Directory for perfect commands.
|
|
25
|
+
- **Interactive Execution:** Uses `prompt_toolkit` to let you tweak the AI's generated command before running it.
|
|
26
|
+
- **Safety First:** Warns you with bold yellow text if the command looks destructive (e.g., `rm`, `del`, `format`).
|
|
27
|
+
- **Auto-Updater:** Silently checks for updates so you're always running the best version.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
The recommended way to install Python CLI tools globally is using `pipx`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pipx install cmdgen
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
*(Alternatively, you can `pip install cmdgen` in a virtual environment)*
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
1. **Get an API Key:** Get a free Gemini API key from [Google AI Studio](https://aistudio.google.com/app/apikey).
|
|
42
|
+
2. **Configure your Key:**
|
|
43
|
+
```bash
|
|
44
|
+
cmdgen config set --provider gemini --api-key "YOUR_API_KEY"
|
|
45
|
+
```
|
|
46
|
+
3. **Use the Tool:**
|
|
47
|
+
```bash
|
|
48
|
+
cmdgen "find all python files larger than 10MB in the current directory"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You will see an explanation of the command, and an interactive prompt where you can edit it or just press `Enter` to run it!
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
View your current configuration (safely masked):
|
|
56
|
+
```bash
|
|
57
|
+
cmdgen config view
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Contributing
|
|
61
|
+
1. Clone the repository
|
|
62
|
+
2. Install dependencies: `pip install -e .[dev]`
|
|
63
|
+
3. Run tests: `pytest`
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
cmdgen/__init__.py,sha256=wxV1u5iTty9coWhQsiy0VJwtoTDw8WIT-sFG_NZEq4U,113
|
|
2
|
+
cmdgen/__main__.py,sha256=P1upHqRqbajqAI8HNwvOfMmB1jB3Mw3yUYdExBqWvjc,66
|
|
3
|
+
cmdgen/config.py,sha256=HbREfGwP1ucmBFuGGlQtIa2F52W_noN8WJVM-_mIJ7E,1475
|
|
4
|
+
cmdgen/context.py,sha256=Vth2O-HBVWENt3nexJ5I7a3AsIP6hQEQBQ66-7_jvfo,1010
|
|
5
|
+
cmdgen/executor.py,sha256=5Yyxgas1DHgwkQE5aDBrLvIF2efjIGSU2EJe51eTQkc,1926
|
|
6
|
+
cmdgen/main.py,sha256=k1on3afYzdhlaGitd4voYTKHG3cT9CTfonB6efjRqxw,3674
|
|
7
|
+
cmdgen/update.py,sha256=PXN1OTpgL-7v6O5gHSDoY35EPd6jlb9TmyKiSBozTns,2374
|
|
8
|
+
cmdgen/llm/__init__.py,sha256=R-z_OJVCsRBWzQf3PNgM677ZzX-RtA0ikM-t8WgrdYY,37
|
|
9
|
+
cmdgen/llm/base.py,sha256=gm1QGmLqUt1zw7WUgSBmRFmeX3q663sOuR9XVsxAom8,1157
|
|
10
|
+
cmdgen/llm/gemini.py,sha256=aIyIBQoav_-48-ZPkldeV1ytiv8QLHVWndY6Ta01kpQ,2290
|
|
11
|
+
cmdgen_ai_cli-0.1.0.dist-info/METADATA,sha256=0y1JIjejXKrApfnAi27jJNlhSniveM7BV0VWsr9tEF4,2106
|
|
12
|
+
cmdgen_ai_cli-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
13
|
+
cmdgen_ai_cli-0.1.0.dist-info/entry_points.txt,sha256=Rv5gneL6LCvI_D9mX5oAM_1UCtpl2X-PIfiyUbtkLg0,74
|
|
14
|
+
cmdgen_ai_cli-0.1.0.dist-info/RECORD,,
|