toni-cli 0.1.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.
toni_cli-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MatrixDynamo
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.
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.2
2
+ Name: toni-cli
3
+ Version: 0.1.0
4
+ Summary: Terminal Operation Natural Instruction - AI-powered terminal assistant
5
+ Author-email: Dakai Ou <oudakai@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/dakai/toni
8
+ Project-URL: Bug Tracker, https://github.com/dakai/toni/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: openai>=1.0.0
16
+ Requires-Dist: google-generativeai>=0.1.0
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "toni-cli"
7
+ version = "0.1.0"
8
+ description = "Terminal Operation Natural Instruction - AI-powered terminal assistant"
9
+ readme = "README.md"
10
+ authors = [{name = "Dakai Ou", email = "oudakai@gmail.com"}]
11
+ license = {text = "MIT"}
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ requires-python = ">=3.7"
18
+ dependencies = [
19
+ "openai>=1.0.0",
20
+ "google-generativeai>=0.1.0",
21
+ ]
22
+
23
+ [project.urls]
24
+ "Homepage" = "https://github.com/dakai/toni"
25
+ "Bug Tracker" = "https://github.com/dakai/toni/issues"
26
+
27
+ [project.scripts]
28
+ toni = "toni.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python
2
+ from setuptools import setup
3
+
4
+ if __name__ == "__main__":
5
+ setup()
@@ -0,0 +1,3 @@
1
+ """TONI - Terminal Operation Natural Instruction."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,89 @@
1
+ import argparse
2
+ import os
3
+ import json
4
+
5
+ from toni.core import (
6
+ get_system_info,
7
+ get_gemini_response,
8
+ get_open_ai_response,
9
+ command_exists,
10
+ execute_command,
11
+ )
12
+
13
+
14
+ def main():
15
+ try:
16
+ parser = argparse.ArgumentParser(
17
+ description="TONI: Terminal Operation Natural Instruction"
18
+ )
19
+ parser.add_argument("query", nargs="+", help="Your natural language query")
20
+ args = parser.parse_args()
21
+
22
+ # Remove trailing question mark if present
23
+ query = " ".join(args.query).rstrip("?")
24
+
25
+ system_info = get_system_info()
26
+ print(f"Detected system: {system_info}")
27
+
28
+ google_api_key = os.environ.get("GOOGLEAI_API_KEY")
29
+ openai_api_key = os.environ.get("OPENAI_API_KEY")
30
+ response = None
31
+
32
+ # Try Gemini first, fall back to OpenAI
33
+ if google_api_key:
34
+ response = get_gemini_response(google_api_key, query, system_info)
35
+
36
+ if response is None and openai_api_key:
37
+ response = get_open_ai_response(openai_api_key, query, system_info)
38
+
39
+ if response is None:
40
+ print(
41
+ "Please set the GOOGLEAI_API_KEY or OPENAI_API_KEY environment variable."
42
+ )
43
+ return
44
+
45
+ try:
46
+ data = json.loads(response)
47
+ except Exception as e:
48
+ print(f"An error occurred while parsing the response: {e}")
49
+ print(f"Raw response: {response}")
50
+ return
51
+
52
+ if data.get("exec") == False:
53
+ print(data.get("exp"))
54
+ return
55
+
56
+ cmd = data.get("cmd")
57
+
58
+ # Check if the command exists
59
+ if cmd and not command_exists(cmd):
60
+ print(
61
+ f"Warning: The command '{cmd.split()[0]}' doesn't appear to be installed on your system."
62
+ )
63
+ print(f"Suggested command: {cmd}")
64
+ print(f"Explanation: {data.get('exp')}")
65
+ print("Please verify that this command will work on your system.")
66
+ else:
67
+ print(f"Suggested command: {cmd}")
68
+ print(f"Explanation: {data.get('exp')}")
69
+
70
+ try:
71
+ confirmation = input("Do you want to execute this command? (y/n): ").lower()
72
+ if confirmation == "y" or confirmation == "":
73
+ execute_command(cmd)
74
+ else:
75
+ print("Command execution cancelled.")
76
+ except KeyboardInterrupt:
77
+ print("\nOperation cancelled by user.")
78
+ return
79
+
80
+ except KeyboardInterrupt:
81
+ print("\nOperation cancelled by user.")
82
+ return
83
+
84
+
85
+ if __name__ == "__main__":
86
+ try:
87
+ main()
88
+ except KeyboardInterrupt:
89
+ print("\nOperation cancelled by user.")
@@ -0,0 +1,161 @@
1
+ from openai import OpenAI
2
+ import os
3
+ import subprocess
4
+ import time
5
+ from google import genai
6
+ import platform
7
+ import shutil
8
+
9
+ system_message = """Your are a powerful terminal assistant generating a JSON containing a command line for my input.
10
+ You will always reply using the following json structure: {{"cmd":"the command", "exp": "some explanation", "exec": true}}.
11
+ Your answer will always only contain the json structure, never add any advice or supplementary detail or information,
12
+ even if I asked the same question before.
13
+ The field cmd will contain a single line command (don't use new lines, use separators like && and ; instead).
14
+ The field exp will contain an short explanation of the command if you managed to generate an executable command, otherwise it will contain the reason of your failure.
15
+ The field exec will contain true if you managed to generate an executable command, false otherwise.
16
+
17
+ The host system is using {system_info}. Please ensure commands are compatible with this environment.
18
+
19
+ Examples:
20
+ Me: list all files in my home dir
21
+ You: {{"cmd":"ls ~", "exp": "list all files in your home dir", "exec": true}}
22
+ Me: list all pods of all namespaces
23
+ You: {{"cmd":"kubectl get pods --all-namespaces", "exp": "list pods form all k8s namespaces", "exec": true}}
24
+ Me: how are you ?
25
+ You: {{"cmd":"", "exp": "I'm good thanks but I cannot generate a command for this.", "exec": false}}"""
26
+
27
+
28
+ def get_system_info():
29
+ system = platform.system()
30
+ if system == "Linux":
31
+ try:
32
+ distro = (
33
+ subprocess.check_output("cat /etc/os-release | grep -w ID", shell=True)
34
+ .decode()
35
+ .strip()
36
+ .split("=")[1]
37
+ .strip('"')
38
+ )
39
+ return f"Linux ({distro})"
40
+ except:
41
+ return "Linux"
42
+ elif system == "Darwin":
43
+ return "macOS"
44
+ elif system == "Windows":
45
+ return "Windows"
46
+ else:
47
+ return system
48
+
49
+
50
+ def get_gemini_response(api_key, prompt, system_info):
51
+ try:
52
+ client = genai.Client(api_key=api_key)
53
+
54
+ formatted_system_message = system_message.format(system_info=system_info)
55
+
56
+ # Create the generation config with system instructions
57
+ # generation_config = {
58
+ # "temperature": 0.2,
59
+ # "top_p": 0.95,
60
+ # "top_k": 0,
61
+ # "max_output_tokens": 1024,
62
+ # }
63
+
64
+ # The new Gemini API doesn't always handle system messages properly
65
+ # Let's combine the system message with the user prompt
66
+ combined_prompt = f"{formatted_system_message}\n\nUser request: {prompt}"
67
+
68
+ response = client.models.generate_content(
69
+ model="gemini-2.0-flash",
70
+ contents=[{"parts": [{"text": combined_prompt}]}],
71
+ )
72
+
73
+ # Extract just the JSON part from the response
74
+ response_text = response.text
75
+ # Find JSON between curly braces if there's extra text
76
+ import re
77
+
78
+ if response_text:
79
+ json_match = re.search(r"(\{.*?\})", response_text, re.DOTALL)
80
+ if json_match:
81
+ return json_match.group(1)
82
+ return response_text
83
+
84
+ except Exception as e:
85
+ print(f"An error occurred with Gemini: {e}")
86
+ return None
87
+
88
+
89
+ def get_open_ai_response(api_key, prompt, system_info):
90
+ try:
91
+ client = OpenAI(api_key=api_key)
92
+
93
+ formatted_system_message = system_message.format(system_info=system_info)
94
+
95
+ chat_completion = client.chat.completions.create(
96
+ messages=[
97
+ {"role": "system", "content": formatted_system_message},
98
+ {"role": "user", "content": prompt},
99
+ ],
100
+ model="gpt-4o-mini",
101
+ temperature=0.2,
102
+ )
103
+
104
+ response = chat_completion.choices[0].message.content
105
+
106
+ # Extract just the JSON part from the response
107
+ import re
108
+
109
+ if response:
110
+ json_match = re.search(r"(\{.*?\})", response, re.DOTALL)
111
+ if json_match:
112
+ return json_match.group(1)
113
+ return response
114
+
115
+ except Exception as e:
116
+ print(f"An error occurred with OpenAI: {e}")
117
+ return None
118
+
119
+
120
+ def write_to_zsh_history(command):
121
+ try:
122
+ current_time = int(time.time()) # Get current Unix timestamp
123
+ timestamped_command = (
124
+ f": {current_time}:0;{command}" # Assuming duration of 0 for now
125
+ )
126
+ with open("/home/dakai/.zsh_history", "a") as f:
127
+ f.write(timestamped_command + "\n")
128
+ except Exception as e:
129
+ print(f"An error occurred while writing to .zsh_history: {e}")
130
+
131
+
132
+ def reload_zsh_history():
133
+ try:
134
+ os.system("source ~/.zshrc")
135
+ result = subprocess.run(
136
+ "source ~/.zshrc", shell=True, check=True, text=True, capture_output=True
137
+ )
138
+ print(result.stdout)
139
+ except Exception as e:
140
+ print(f"An error occurred while reloading .zshrc: {e}")
141
+
142
+
143
+ def execute_command(command):
144
+ try:
145
+ result = subprocess.run(
146
+ command, shell=True, check=True, text=True, capture_output=True
147
+ )
148
+ print("Command output:")
149
+ print(result.stdout)
150
+ write_to_zsh_history(command)
151
+ # reload_zsh_history()
152
+ except subprocess.CalledProcessError as e:
153
+ print(f"An error occurred while executing the command: {e}")
154
+ print("Error output:")
155
+ print(e.stderr)
156
+
157
+
158
+ def command_exists(command):
159
+ # Extract the base command (before any options or arguments)
160
+ base_command = command.split()[0]
161
+ return shutil.which(base_command) is not None
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.2
2
+ Name: toni-cli
3
+ Version: 0.1.0
4
+ Summary: Terminal Operation Natural Instruction - AI-powered terminal assistant
5
+ Author-email: Dakai Ou <oudakai@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/dakai/toni
8
+ Project-URL: Bug Tracker, https://github.com/dakai/toni/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: openai>=1.0.0
16
+ Requires-Dist: google-generativeai>=0.1.0
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ pyproject.toml
3
+ setup.py
4
+ src/toni/__init__.py
5
+ src/toni/cli.py
6
+ src/toni/core.py
7
+ src/toni_cli.egg-info/PKG-INFO
8
+ src/toni_cli.egg-info/SOURCES.txt
9
+ src/toni_cli.egg-info/dependency_links.txt
10
+ src/toni_cli.egg-info/entry_points.txt
11
+ src/toni_cli.egg-info/requires.txt
12
+ src/toni_cli.egg-info/top_level.txt
13
+ tests/test_toni.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ toni = toni.cli:main
@@ -0,0 +1,2 @@
1
+ openai>=1.0.0
2
+ google-generativeai>=0.1.0
@@ -0,0 +1,28 @@
1
+ """Tests for the TONI package."""
2
+
3
+ import unittest
4
+ from unittest.mock import patch, MagicMock
5
+ from toni.core import get_system_info, command_exists
6
+
7
+
8
+ class TestToni(unittest.TestCase):
9
+ """Test cases for TONI functionality."""
10
+
11
+ def test_get_system_info(self):
12
+ """Test that system info is returned."""
13
+ result = get_system_info()
14
+ self.assertIsInstance(result, str)
15
+ self.assertTrue(len(result) > 0)
16
+
17
+ @patch("toni.core.shutil.which")
18
+ def test_command_exists(self, mock_which):
19
+ """Test command_exists function."""
20
+ mock_which.return_value = "/usr/bin/ls"
21
+ self.assertTrue(command_exists("ls"))
22
+
23
+ mock_which.return_value = None
24
+ self.assertFalse(command_exists("nonexistentcommand"))
25
+
26
+
27
+ if __name__ == "__main__":
28
+ unittest.main()