lanthei-cli 0.1.0__tar.gz → 0.2.0__tar.gz

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.
@@ -1,6 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lanthei-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A simple CLI that chats with Gemini using your own API key
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: openai
7
+ Requires-Dist: rich
@@ -0,0 +1,28 @@
1
+ # Lanthei CLI
2
+
3
+ A simple command-line tool that lets you chat with Google's Gemini AI directly from your terminal — using your own free API key.
4
+
5
+ ## Install
6
+
7
+ pip install lanthei-cli
8
+
9
+ ## Usage
10
+
11
+ Ask a question directly:
12
+
13
+ lanthei "explain what a python list comprehension is"
14
+
15
+ Or run it with no arguments for an interactive prompt:
16
+
17
+ lanthei
18
+
19
+ The first time you run it, you'll be asked to paste your Gemini API key (get one free at https://aistudio.google.com). It's saved locally at `~/.lanthei_key.txt` and only used on your machine — never sent anywhere except directly to Google's API.
20
+
21
+ ## Requirements
22
+
23
+ - Python 3.9+
24
+ - A free Gemini API key from Google AI Studio
25
+
26
+ ## License
27
+
28
+ MIT
@@ -0,0 +1,154 @@
1
+ # This script provides a command-line interface (CLI) to interact with the Google Gemini AI
2
+ # model through the OpenAI API facade. It allows users to:
3
+ # - Chat with Gemini (default command or `chat`)
4
+ # - Explain the contents of a specified code file (`explain`)
5
+ # - Fix or modify a specified code file based on an instruction (`fix`), with diff review and Git commit integration.
6
+ # It stores the Gemini API key locally for convenience.
7
+
8
+ import os
9
+ import sys
10
+ import argparse
11
+ import difflib
12
+ from pathlib import Path
13
+ from openai import OpenAI
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+ from rich.syntax import Syntax
17
+
18
+ console = Console()
19
+ CONFIG_PATH = Path.home() / ".lanthei_key.txt"
20
+
21
+
22
+ def get_api_key():
23
+ if CONFIG_PATH.exists():
24
+ return CONFIG_PATH.read_text().strip()
25
+ key = input("Enter your Gemini API key (get one free at aistudio.google.com): ").strip()
26
+ CONFIG_PATH.write_text(key)
27
+ console.print(f"[green]Saved.[/green] (stored locally at {CONFIG_PATH})")
28
+ return key
29
+
30
+
31
+ def get_client():
32
+ api_key = get_api_key()
33
+ return OpenAI(
34
+ api_key=api_key,
35
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
36
+ )
37
+
38
+
39
+ def ask_gemini(client, prompt, model="gemini-2.5-flash"):
40
+ response = client.chat.completions.create(
41
+ model=model,
42
+ messages=[{"role": "user", "content": prompt}],
43
+ )
44
+ return response.choices[0].message.content
45
+
46
+
47
+ def cmd_chat(args):
48
+ client = get_client()
49
+ question = " ".join(args.question) if args.question else input("Ask me anything: ")
50
+ try:
51
+ console.print(ask_gemini(client, question))
52
+ except Exception as e:
53
+ console.print(f"[red]Error: {e}[/red]")
54
+
55
+
56
+ def cmd_explain(args):
57
+ filepath = Path(args.file)
58
+ if not filepath.exists():
59
+ console.print(f"[red]File not found: {filepath}[/red]")
60
+ return
61
+
62
+ code = filepath.read_text()
63
+ client = get_client()
64
+ prompt = f"Explain what this code does, in plain language:\n\n{code}"
65
+ try:
66
+ console.print(ask_gemini(client, prompt))
67
+ except Exception as e:
68
+ console.print(f"[red]Error: {e}[/red]")
69
+
70
+
71
+ def cmd_fix(args):
72
+ filepath = Path(args.file)
73
+ if not filepath.exists():
74
+ console.print(f"[red]File not found: {filepath}[/red]")
75
+ return
76
+
77
+ original_code = filepath.read_text()
78
+ instruction = " ".join(args.instruction)
79
+ client = get_client()
80
+
81
+ prompt = (
82
+ f"Here is a Python file:\n\n{original_code}\n\n"
83
+ f"Task: {instruction}\n\n"
84
+ "Return ONLY the complete corrected file content, with no explanation, "
85
+ "no markdown code fences, just the raw file contents."
86
+ )
87
+
88
+ console.print("[yellow]Asking Gemini for changes...[/yellow]")
89
+ try:
90
+ new_code = ask_gemini(client, prompt)
91
+ except Exception as e:
92
+ console.print(f"[red]Error: {e}[/red]")
93
+ return
94
+
95
+ new_code = new_code.strip()
96
+ if new_code.startswith("```"):
97
+ lines = new_code.split("\n")
98
+ new_code = "\n".join(lines[1:-1]) if lines[-1].strip() == "```" else "\n".join(lines[1:])
99
+
100
+ diff = list(difflib.unified_diff(
101
+ original_code.splitlines(keepends=True),
102
+ new_code.splitlines(keepends=True),
103
+ fromfile="before",
104
+ tofile="after",
105
+ ))
106
+
107
+ if not diff:
108
+ console.print("[yellow]No changes suggested.[/yellow]")
109
+ return
110
+
111
+ diff_text = "".join(diff)
112
+ console.print(Panel(Syntax(diff_text, "diff", theme="ansi_dark"), title="Proposed Changes"))
113
+
114
+ console.print("\n[bold green][A][/bold green] Apply & Commit [bold red][R][/bold red] Reject")
115
+ choice = input("Choose: ").strip().lower()
116
+
117
+ if choice == "a":
118
+ filepath.write_text(new_code)
119
+ os.system(f'git add "{filepath}"')
120
+ os.system(f'git commit -m "AI edit: {instruction}"')
121
+ console.print(f"[green]Applied and committed changes to {filepath}[/green]")
122
+ else:
123
+ console.print("[yellow]Rejected. No changes made.[/yellow]")
124
+
125
+
126
+ def main():
127
+ known_commands = {"explain", "fix", "chat"}
128
+
129
+ if len(sys.argv) > 1 and sys.argv[1] in known_commands:
130
+ parser = argparse.ArgumentParser(prog="lanthei")
131
+ subparsers = parser.add_subparsers(dest="command")
132
+
133
+ explain_parser = subparsers.add_parser("explain")
134
+ explain_parser.add_argument("file")
135
+ explain_parser.set_defaults(func=cmd_explain)
136
+
137
+ fix_parser = subparsers.add_parser("fix")
138
+ fix_parser.add_argument("file")
139
+ fix_parser.add_argument("instruction", nargs="+")
140
+ fix_parser.set_defaults(func=cmd_fix)
141
+
142
+ chat_parser = subparsers.add_parser("chat")
143
+ chat_parser.add_argument("question", nargs="*")
144
+ chat_parser.set_defaults(func=cmd_chat)
145
+
146
+ args = parser.parse_args()
147
+ args.func(args)
148
+ else:
149
+ chat_args = argparse.Namespace(question=sys.argv[1:])
150
+ cmd_chat(chat_args)
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "lanthei-cli"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "A simple CLI that chats with Gemini using your own API key"
5
5
  requires-python = ">=3.9"
6
- dependencies = ["openai"]
6
+ dependencies = ["openai", "rich"]
7
7
 
8
8
  [project.scripts]
9
9
  lanthei = "lanthei.cli:main"
@@ -1,38 +0,0 @@
1
- import os
2
- import sys
3
- from pathlib import Path
4
- from openai import OpenAI
5
-
6
- CONFIG_PATH = Path.home() / ".lanthei_key.txt"
7
-
8
- def get_api_key():
9
- if CONFIG_PATH.exists():
10
- return CONFIG_PATH.read_text().strip()
11
- key = input("Enter your Gemini API key (get one free at aistudio.google.com): ").strip()
12
- CONFIG_PATH.write_text(key)
13
- print(f"Saved. (stored locally at {CONFIG_PATH})")
14
- return key
15
-
16
- def main():
17
- api_key = get_api_key()
18
- client = OpenAI(
19
- api_key=api_key,
20
- base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
21
- )
22
-
23
- if len(sys.argv) > 1:
24
- question = " ".join(sys.argv[1:])
25
- else:
26
- question = input("Ask me anything: ")
27
-
28
- try:
29
- response = client.chat.completions.create(
30
- model="gemini-2.5-flash",
31
- messages=[{"role": "user", "content": question}],
32
- )
33
- print(response.choices[0].message.content)
34
- except Exception as e:
35
- print(f"Error: {e}")
36
-
37
- if __name__ == "__main__":
38
- main()