gpt-command 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.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpt-command
3
+ Version: 0.1.0
4
+ Summary: Ask GPT for Ubuntu/macOS/Linux shell commands from natural language
5
+ Author: Your Name
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: openai>=2.0.0
9
+ Requires-Dist: tqdm>=4.66.0
10
+
11
+ # gpt-command (gptc)
12
+
13
+ > Turn natural language into ready-to-use terminal commands.
14
+
15
+ `gptc` is a lightweight CLI tool that converts plain English (or any language) into executable shell commands for macOS and Linux.
16
+
17
+ ---
18
+
19
+ ## 🚀 Installation
20
+
21
+ ```bash
22
+ pip install gpt-command
23
+ ```
24
+
25
+ ---
26
+
27
+ 🔑 Setup API Key
28
+
29
+ Run once to store your OpenAI API key securely:
30
+
31
+ ```bash
32
+ gptc-key
33
+ ```
34
+ Check status:
35
+
36
+ ```bash
37
+ gptc-key --status
38
+ ```
39
+ Set default model (optional):
40
+
41
+ ```bash
42
+ gptc-key --model gpt-4.1
43
+ ```
44
+
45
+ ---
46
+
47
+ 💡 Usage
48
+ ```bash
49
+ gptc <your question>
50
+ ```
51
+ Example
52
+ ```bash
53
+ gptc find and delete all txt files in current directory recursively
54
+ ```
55
+ Output:
56
+ ```bash
57
+ find . -type f -name "*.txt" -delete
58
+ ```
59
+ Then it will automatically prefill your terminal input:
60
+
61
+ ```bash
62
+ ~/current/path$ find . -type f -name "*.txt" -delete
63
+ ```
64
+ ⚠️ The command is NOT executed automatically.
65
+
66
+ ---
67
+
68
+ ⚙️ Options
69
+
70
+ 📋 Copy to clipboard
71
+
72
+ ```bash
73
+ gptc --copy compress current folder into tar.gz
74
+ ```
75
+ 📖 Show explanation
76
+
77
+ ```bash
78
+ gptc --explain find process using port 8000
79
+ ```
80
+ ▶️ Execute command (with confirmation)
81
+
82
+ ```bash
83
+ gptc --run check disk usage
84
+ ```
85
+ 🧠 Show history
86
+
87
+ ```bash
88
+ gptc --history
89
+ ```
90
+ 🤖 Specify model
91
+
92
+ ```bash
93
+ gptc --model gpt-4.1 list all running processes
94
+ ```
95
+
96
+ ---
97
+
98
+ 🔐 Security
99
+
100
+ - API key is stored locally at:
101
+
102
+ ```bash
103
+ ~/.config/gptc/config.json
104
+ ```
105
+ File permissions are restricted to the user (600)
106
+ Dangerous commands are automatically detected and blocked from execution
107
+
108
+ ---
109
+
110
+ ⚠️ Disclaimer
111
+ - Always review generated commands before running them
112
+ - Some commands may modify or delete system data
113
+ - Use with caution, especially with elevated privileges (sudo)
114
+
115
+ ---
116
+
117
+ 🖥️ Supported Platforms
118
+
119
+ - macOS
120
+ - Linux (Ubuntu, etc.)
121
+
122
+ ---
123
+
124
+ ✨ Features
125
+ - Natural language → shell command
126
+ - Auto-prefilled terminal input (no copy-paste needed)
127
+ - Optional execution with confirmation
128
+ - Clipboard copy support
129
+ - Command explanation
130
+ - History tracking
131
+ - Local API key management (no environment variable required)
132
+
133
+ ---
134
+
135
+ 📌 Summary
136
+
137
+ > Stop Googling terminal commands. Just ask.
138
+
139
+ ---
140
+
141
+ 📄 License
142
+
143
+ MIT License
@@ -0,0 +1,133 @@
1
+ # gpt-command (gptc)
2
+
3
+ > Turn natural language into ready-to-use terminal commands.
4
+
5
+ `gptc` is a lightweight CLI tool that converts plain English (or any language) into executable shell commands for macOS and Linux.
6
+
7
+ ---
8
+
9
+ ## 🚀 Installation
10
+
11
+ ```bash
12
+ pip install gpt-command
13
+ ```
14
+
15
+ ---
16
+
17
+ 🔑 Setup API Key
18
+
19
+ Run once to store your OpenAI API key securely:
20
+
21
+ ```bash
22
+ gptc-key
23
+ ```
24
+ Check status:
25
+
26
+ ```bash
27
+ gptc-key --status
28
+ ```
29
+ Set default model (optional):
30
+
31
+ ```bash
32
+ gptc-key --model gpt-4.1
33
+ ```
34
+
35
+ ---
36
+
37
+ 💡 Usage
38
+ ```bash
39
+ gptc <your question>
40
+ ```
41
+ Example
42
+ ```bash
43
+ gptc find and delete all txt files in current directory recursively
44
+ ```
45
+ Output:
46
+ ```bash
47
+ find . -type f -name "*.txt" -delete
48
+ ```
49
+ Then it will automatically prefill your terminal input:
50
+
51
+ ```bash
52
+ ~/current/path$ find . -type f -name "*.txt" -delete
53
+ ```
54
+ ⚠️ The command is NOT executed automatically.
55
+
56
+ ---
57
+
58
+ ⚙️ Options
59
+
60
+ 📋 Copy to clipboard
61
+
62
+ ```bash
63
+ gptc --copy compress current folder into tar.gz
64
+ ```
65
+ 📖 Show explanation
66
+
67
+ ```bash
68
+ gptc --explain find process using port 8000
69
+ ```
70
+ ▶️ Execute command (with confirmation)
71
+
72
+ ```bash
73
+ gptc --run check disk usage
74
+ ```
75
+ 🧠 Show history
76
+
77
+ ```bash
78
+ gptc --history
79
+ ```
80
+ 🤖 Specify model
81
+
82
+ ```bash
83
+ gptc --model gpt-4.1 list all running processes
84
+ ```
85
+
86
+ ---
87
+
88
+ 🔐 Security
89
+
90
+ - API key is stored locally at:
91
+
92
+ ```bash
93
+ ~/.config/gptc/config.json
94
+ ```
95
+ File permissions are restricted to the user (600)
96
+ Dangerous commands are automatically detected and blocked from execution
97
+
98
+ ---
99
+
100
+ ⚠️ Disclaimer
101
+ - Always review generated commands before running them
102
+ - Some commands may modify or delete system data
103
+ - Use with caution, especially with elevated privileges (sudo)
104
+
105
+ ---
106
+
107
+ 🖥️ Supported Platforms
108
+
109
+ - macOS
110
+ - Linux (Ubuntu, etc.)
111
+
112
+ ---
113
+
114
+ ✨ Features
115
+ - Natural language → shell command
116
+ - Auto-prefilled terminal input (no copy-paste needed)
117
+ - Optional execution with confirmation
118
+ - Clipboard copy support
119
+ - Command explanation
120
+ - History tracking
121
+ - Local API key management (no environment variable required)
122
+
123
+ ---
124
+
125
+ 📌 Summary
126
+
127
+ > Stop Googling terminal commands. Just ask.
128
+
129
+ ---
130
+
131
+ 📄 License
132
+
133
+ MIT License
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gpt-command"
7
+ version = "0.1.0"
8
+ description = "Ask GPT for Ubuntu/macOS/Linux shell commands from natural language"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Your Name" }
13
+ ]
14
+ dependencies = [
15
+ "openai>=2.0.0",
16
+ "tqdm>=4.66.0"
17
+ ]
18
+
19
+ [project.scripts]
20
+ gptc = "gpt_command.cli:main"
21
+ gptc-key = "gpt_command.key_manager:main"
22
+
23
+ [tool.setuptools]
24
+ package-dir = {"" = "src"}
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,226 @@
1
+ import argparse
2
+ import sys
3
+
4
+ from openai import OpenAI
5
+ from tqdm import tqdm
6
+
7
+ from .config import get_api_key, get_default_model
8
+ from .history import print_history, save_history_item
9
+ from .utils import (
10
+ copy_to_clipboard,
11
+ extract_command,
12
+ is_dangerous_command,
13
+ prefilled_input,
14
+ run_shell_command,
15
+ yes_no,
16
+ )
17
+
18
+ SYSTEM_PROMPT = """You are a Linux/macOS terminal command generator.
19
+
20
+ Follow these rules exactly:
21
+ 1. Output only one executable shell command on a single line.
22
+ 2. Do not output explanations, code fences, quotes, or extra text.
23
+ 3. Prefer commands that work in bash/zsh environments.
24
+ 4. If the user's request is risky or destructive, suggest the safest practical command possible.
25
+ 5. If multiple steps are needed, combine them into a single one-liner using && when appropriate.
26
+ 6. Return only the command string.
27
+ """
28
+
29
+ EXPLAIN_PROMPT = """You are a Linux/macOS terminal command explainer.
30
+ Explain the given command in up to 3 short lines:
31
+ 1. What it does
32
+ 2. What the important options mean
33
+ 3. Any caution the user should know
34
+ """
35
+
36
+
37
+ def ask_model_for_command(question: str, model: str) -> str:
38
+ api_key = get_api_key()
39
+ if not api_key:
40
+ raise RuntimeError(
41
+ "No OpenAI API key found. Run `gptc-key` first to store your API key."
42
+ )
43
+
44
+ client = OpenAI(api_key=api_key)
45
+
46
+ with tqdm(total=3, desc="GPT processing", ncols=88) as pbar:
47
+ pbar.set_description("Preparing request")
48
+ pbar.update(1)
49
+
50
+ response = client.responses.create(
51
+ model=model,
52
+ instructions=SYSTEM_PROMPT,
53
+ input=f"Convert this natural language request into a single shell command:\n{question}",
54
+ )
55
+
56
+ pbar.set_description("Receiving response")
57
+ pbar.update(1)
58
+
59
+ text = getattr(response, "output_text", "") or ""
60
+ command = extract_command(text)
61
+
62
+ pbar.set_description("Finalizing result")
63
+ pbar.update(1)
64
+
65
+ if not command:
66
+ raise RuntimeError("Failed to extract a command from the model response.")
67
+
68
+ return command
69
+
70
+
71
+ def ask_model_for_explanation(command: str, model: str) -> str:
72
+ api_key = get_api_key()
73
+ if not api_key:
74
+ raise RuntimeError(
75
+ "No OpenAI API key found. Run `gptc-key` first to store your API key."
76
+ )
77
+
78
+ client = OpenAI(api_key=api_key)
79
+
80
+ with tqdm(total=2, desc="Generating explanation", ncols=88) as pbar:
81
+ pbar.set_description("Sending request")
82
+ pbar.update(1)
83
+
84
+ response = client.responses.create(
85
+ model=model,
86
+ instructions=EXPLAIN_PROMPT,
87
+ input=command,
88
+ )
89
+
90
+ pbar.set_description("Receiving explanation")
91
+ pbar.update(1)
92
+
93
+ text = getattr(response, "output_text", "") or ""
94
+ return text.strip()
95
+
96
+
97
+ def build_parser() -> argparse.ArgumentParser:
98
+ parser = argparse.ArgumentParser(
99
+ prog="gptc",
100
+ description="Convert natural language into terminal commands for macOS and Linux.",
101
+ )
102
+ parser.add_argument("question", nargs="*", help="Your natural language request.")
103
+ parser.add_argument("--copy", action="store_true", help="Copy the result to the clipboard.")
104
+ parser.add_argument("--explain", action="store_true", help="Show a short explanation of the command.")
105
+ parser.add_argument("--run", action="store_true", help="Run the generated command after confirmation.")
106
+ parser.add_argument("--model", type=str, default=None, help="Specify the model to use.")
107
+ parser.add_argument("--history", action="store_true", help="Show recent command history.")
108
+ return parser
109
+
110
+
111
+ def main() -> None:
112
+ parser = build_parser()
113
+ args = parser.parse_args()
114
+
115
+ if args.history:
116
+ print_history(limit=10)
117
+ return
118
+
119
+ if not args.question:
120
+ parser.print_help()
121
+ sys.exit(1)
122
+
123
+ question = " ".join(args.question).strip()
124
+ model = args.model or get_default_model()
125
+
126
+ try:
127
+ command = ask_model_for_command(question, model=model)
128
+ except KeyboardInterrupt:
129
+ print("\nInterrupted.")
130
+ sys.exit(130)
131
+ except Exception as e:
132
+ print(f"Error: {e}", file=sys.stderr)
133
+ sys.exit(1)
134
+
135
+ print(command)
136
+
137
+ explained = False
138
+ if args.explain:
139
+ try:
140
+ explanation = ask_model_for_explanation(command, model=model)
141
+ if explanation:
142
+ print("\n[Explanation]")
143
+ print(explanation)
144
+ explained = True
145
+ except Exception as e:
146
+ print(f"\nExplanation failed: {e}")
147
+
148
+ copied = False
149
+ if args.copy:
150
+ copied = copy_to_clipboard(command)
151
+ if copied:
152
+ print("\nCopied to clipboard.")
153
+ else:
154
+ print("\nFailed to copy to clipboard. Check pbcopy, xclip, or wl-copy.")
155
+
156
+ if is_dangerous_command(command):
157
+ print(
158
+ "\n[Warning] A potentially dangerous command pattern was detected. "
159
+ "Auto-run and auto-prefill have been blocked."
160
+ )
161
+ save_history_item(
162
+ question=question,
163
+ command=command,
164
+ executed=False,
165
+ copied=copied,
166
+ explained=explained,
167
+ )
168
+ sys.exit(2)
169
+
170
+ if args.run:
171
+ print()
172
+ if yes_no("Do you want to run this command?", default="n"):
173
+ code = run_shell_command(command)
174
+ print(f"\nExit code: {code}")
175
+ save_history_item(
176
+ question=question,
177
+ command=command,
178
+ executed=True,
179
+ copied=copied,
180
+ explained=explained,
181
+ )
182
+ sys.exit(code)
183
+ else:
184
+ print("Execution cancelled.")
185
+ save_history_item(
186
+ question=question,
187
+ command=command,
188
+ executed=False,
189
+ copied=copied,
190
+ explained=explained,
191
+ )
192
+ sys.exit(0)
193
+
194
+ try:
195
+ print()
196
+ entered = prefilled_input(command)
197
+ if entered.strip():
198
+ print("Input prefilled successfully.")
199
+ except KeyboardInterrupt:
200
+ print("\nCancelled.")
201
+ save_history_item(
202
+ question=question,
203
+ command=command,
204
+ executed=False,
205
+ copied=copied,
206
+ explained=explained,
207
+ )
208
+ sys.exit(130)
209
+ except EOFError:
210
+ print()
211
+ save_history_item(
212
+ question=question,
213
+ command=command,
214
+ executed=False,
215
+ copied=copied,
216
+ explained=explained,
217
+ )
218
+ sys.exit(0)
219
+
220
+ save_history_item(
221
+ question=question,
222
+ command=command,
223
+ executed=False,
224
+ copied=copied,
225
+ explained=explained,
226
+ )
@@ -0,0 +1,76 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Any, Dict, Optional
5
+
6
+ APP_NAME = "gptc"
7
+ CONFIG_DIR = Path.home() / ".config" / APP_NAME
8
+ CONFIG_FILE = CONFIG_DIR / "config.json"
9
+ HISTORY_FILE = CONFIG_DIR / "history.json"
10
+
11
+
12
+ def ensure_config_dir() -> None:
13
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
14
+
15
+
16
+ def load_json_file(path: Path, default: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
17
+ if default is None:
18
+ default = {}
19
+
20
+ if not path.exists():
21
+ return default
22
+
23
+ try:
24
+ with open(path, "r", encoding="utf-8") as f:
25
+ return json.load(f)
26
+ except Exception:
27
+ return default
28
+
29
+
30
+ def save_json_file(path: Path, data: Dict[str, Any]) -> None:
31
+ ensure_config_dir()
32
+ with open(path, "w", encoding="utf-8") as f:
33
+ json.dump(data, f, ensure_ascii=False, indent=2)
34
+ os.chmod(path, 0o600)
35
+
36
+
37
+ def load_config() -> Dict[str, Any]:
38
+ return load_json_file(CONFIG_FILE, default={})
39
+
40
+
41
+ def save_config(data: Dict[str, Any]) -> None:
42
+ save_json_file(CONFIG_FILE, data)
43
+
44
+
45
+ def get_api_key() -> Optional[str]:
46
+ # 1순위: 환경변수
47
+ env_key = os.environ.get("OPENAI_API_KEY")
48
+ if env_key:
49
+ return env_key.strip()
50
+
51
+ # 2순위: 로컬 설정 파일
52
+ config = load_config()
53
+ key = config.get("OPENAI_API_KEY")
54
+ if isinstance(key, str) and key.strip():
55
+ return key.strip()
56
+
57
+ return None
58
+
59
+
60
+ def get_default_model() -> str:
61
+ env_model = os.environ.get("OPENAI_MODEL")
62
+ if env_model and env_model.strip():
63
+ return env_model.strip()
64
+
65
+ config = load_config()
66
+ model = config.get("DEFAULT_MODEL")
67
+ if isinstance(model, str) and model.strip():
68
+ return model.strip()
69
+
70
+ return "gpt-4.1"
71
+
72
+
73
+ def set_default_model(model: str) -> None:
74
+ config = load_config()
75
+ config["DEFAULT_MODEL"] = model
76
+ save_config(config)
@@ -0,0 +1,53 @@
1
+ from datetime import datetime
2
+ from typing import Any, Dict, List
3
+
4
+ from .config import HISTORY_FILE, load_json_file, save_json_file
5
+
6
+ MAX_HISTORY = 50
7
+
8
+
9
+ def load_history() -> List[Dict[str, Any]]:
10
+ data = load_json_file(HISTORY_FILE, default={"items": []})
11
+ items = data.get("items", [])
12
+ if isinstance(items, list):
13
+ return items
14
+ return []
15
+
16
+
17
+ def save_history_item(
18
+ question: str,
19
+ command: str,
20
+ executed: bool = False,
21
+ copied: bool = False,
22
+ explained: bool = False,
23
+ ) -> None:
24
+ items = load_history()
25
+ items.insert(
26
+ 0,
27
+ {
28
+ "timestamp": datetime.now().isoformat(timespec="seconds"),
29
+ "question": question,
30
+ "command": command,
31
+ "executed": executed,
32
+ "copied": copied,
33
+ "explained": explained,
34
+ },
35
+ )
36
+ items = items[:MAX_HISTORY]
37
+ save_json_file(HISTORY_FILE, {"items": items})
38
+
39
+
40
+ def print_history(limit: int = 10) -> None:
41
+ items = load_history()[:limit]
42
+ if not items:
43
+ print("히스토리가 없습니다.")
44
+ return
45
+
46
+ for i, item in enumerate(items, start=1):
47
+ ts = item.get("timestamp", "-")
48
+ q = item.get("question", "")
49
+ cmd = item.get("command", "")
50
+ print(f"[{i}] {ts}")
51
+ print(f" 질문: {q}")
52
+ print(f" 명령: {cmd}")
53
+ print()
@@ -0,0 +1,80 @@
1
+ import argparse
2
+ import getpass
3
+
4
+ from .config import CONFIG_FILE, get_api_key, load_config, save_config, set_default_model
5
+
6
+
7
+ def set_api_key() -> None:
8
+ print("Enter your OpenAI API key. Input will be hidden.")
9
+ key = getpass.getpass("API Key: ").strip()
10
+
11
+ if not key:
12
+ print("No key was entered.")
13
+ return
14
+
15
+ config = load_config()
16
+ config["OPENAI_API_KEY"] = key
17
+ save_config(config)
18
+ print(f"API key saved: {CONFIG_FILE}")
19
+
20
+
21
+ def delete_api_key() -> None:
22
+ config = load_config()
23
+ if "OPENAI_API_KEY" in config:
24
+ del config["OPENAI_API_KEY"]
25
+ save_config(config)
26
+ print("Stored API key deleted.")
27
+ else:
28
+ print("No stored API key found.")
29
+
30
+
31
+ def show_status() -> None:
32
+ key = get_api_key()
33
+ if key:
34
+ masked = key[:7] + "*" * max(0, len(key) - 11) + key[-4:]
35
+ print("API key is configured.")
36
+ print(f"Key preview: {masked}")
37
+ else:
38
+ print("API key is not configured.")
39
+
40
+ config = load_config()
41
+ model = config.get("DEFAULT_MODEL", "gpt-4.1")
42
+ print(f"Default model: {model}")
43
+
44
+
45
+ def set_model(model: str) -> None:
46
+ set_default_model(model)
47
+ print(f"Default model saved: {model}")
48
+
49
+
50
+ def main() -> None:
51
+ parser = argparse.ArgumentParser(
52
+ prog="gptc-key",
53
+ description="Manage gptc API key and default model settings.",
54
+ )
55
+ parser.add_argument("--set", action="store_true", help="Set the API key.")
56
+ parser.add_argument("--delete", action="store_true", help="Delete the stored API key.")
57
+ parser.add_argument("--status", action="store_true", help="Show current configuration status.")
58
+ parser.add_argument("--model", type=str, help="Set the default model.")
59
+
60
+ args = parser.parse_args()
61
+ acted = False
62
+
63
+ if args.set:
64
+ set_api_key()
65
+ acted = True
66
+
67
+ if args.delete:
68
+ delete_api_key()
69
+ acted = True
70
+
71
+ if args.status:
72
+ show_status()
73
+ acted = True
74
+
75
+ if args.model:
76
+ set_model(args.model)
77
+ acted = True
78
+
79
+ if not acted:
80
+ set_api_key()
@@ -0,0 +1,117 @@
1
+ import os
2
+ import platform
3
+ import readline
4
+ import shutil
5
+ import subprocess
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+
10
+ DANGEROUS_PATTERNS = [
11
+ "rm -rf /",
12
+ "rm -rf ~",
13
+ "mkfs",
14
+ "dd if=",
15
+ ":(){ :|:& };:",
16
+ "shutdown -h now",
17
+ "reboot",
18
+ "poweroff",
19
+ "halt",
20
+ "chmod -R 777 /",
21
+ "chown -R",
22
+ "> /dev/sda",
23
+ "> /dev/nvme",
24
+ ]
25
+
26
+
27
+ def is_dangerous_command(cmd: str) -> bool:
28
+ lowered = cmd.lower()
29
+ return any(p.lower() in lowered for p in DANGEROUS_PATTERNS)
30
+
31
+
32
+ def build_prompt() -> str:
33
+ cwd = Path.cwd()
34
+ home = Path.home()
35
+
36
+ try:
37
+ cwd_str = str(cwd)
38
+ home_str = str(home)
39
+ if cwd_str.startswith(home_str):
40
+ cwd_display = cwd_str.replace(home_str, "~", 1)
41
+ else:
42
+ cwd_display = cwd_str
43
+ except Exception:
44
+ cwd_display = str(cwd)
45
+
46
+ return f"{cwd_display}$ "
47
+
48
+
49
+ def prefilled_input(prefill: str, prompt: Optional[str] = None) -> str:
50
+ if prompt is None:
51
+ prompt = build_prompt()
52
+
53
+ def hook() -> None:
54
+ readline.insert_text(prefill)
55
+ readline.redisplay()
56
+
57
+ old_hook = readline.get_startup_hook()
58
+ try:
59
+ readline.set_startup_hook(hook)
60
+ return input(prompt)
61
+ finally:
62
+ readline.set_startup_hook(old_hook)
63
+
64
+
65
+ def extract_command(text: str) -> str:
66
+ cmd = text.strip()
67
+
68
+ if cmd.startswith("```"):
69
+ lines = [line for line in cmd.splitlines() if not line.strip().startswith("```")]
70
+ cmd = "\n".join(lines).strip()
71
+
72
+ if "\n" in cmd:
73
+ cmd = cmd.splitlines()[0].strip()
74
+
75
+ cmd = cmd.strip().strip("`").strip()
76
+ cmd = cmd.strip('"').strip("'").strip()
77
+
78
+ return cmd
79
+
80
+
81
+ def copy_to_clipboard(text: str) -> bool:
82
+ system = platform.system()
83
+
84
+ try:
85
+ if system == "Darwin" and shutil.which("pbcopy"):
86
+ subprocess.run(["pbcopy"], input=text, text=True, check=True)
87
+ return True
88
+
89
+ if shutil.which("wl-copy"):
90
+ subprocess.run(["wl-copy"], input=text, text=True, check=True)
91
+ return True
92
+
93
+ if shutil.which("xclip"):
94
+ subprocess.run(["xclip", "-selection", "clipboard"], input=text, text=True, check=True)
95
+ return True
96
+
97
+ return False
98
+ except Exception:
99
+ return False
100
+
101
+
102
+ def run_shell_command(cmd: str) -> int:
103
+ try:
104
+ completed = subprocess.run(cmd, shell=True)
105
+ return completed.returncode
106
+ except KeyboardInterrupt:
107
+ return 130
108
+
109
+
110
+ def yes_no(question: str, default: str = "n") -> bool:
111
+ default = default.lower().strip()
112
+ prompt = " [Y/n] " if default == "y" else " [y/N] "
113
+
114
+ answer = input(question + prompt).strip().lower()
115
+ if not answer:
116
+ return default == "y"
117
+ return answer in {"y", "yes"}
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpt-command
3
+ Version: 0.1.0
4
+ Summary: Ask GPT for Ubuntu/macOS/Linux shell commands from natural language
5
+ Author: Your Name
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: openai>=2.0.0
9
+ Requires-Dist: tqdm>=4.66.0
10
+
11
+ # gpt-command (gptc)
12
+
13
+ > Turn natural language into ready-to-use terminal commands.
14
+
15
+ `gptc` is a lightweight CLI tool that converts plain English (or any language) into executable shell commands for macOS and Linux.
16
+
17
+ ---
18
+
19
+ ## 🚀 Installation
20
+
21
+ ```bash
22
+ pip install gpt-command
23
+ ```
24
+
25
+ ---
26
+
27
+ 🔑 Setup API Key
28
+
29
+ Run once to store your OpenAI API key securely:
30
+
31
+ ```bash
32
+ gptc-key
33
+ ```
34
+ Check status:
35
+
36
+ ```bash
37
+ gptc-key --status
38
+ ```
39
+ Set default model (optional):
40
+
41
+ ```bash
42
+ gptc-key --model gpt-4.1
43
+ ```
44
+
45
+ ---
46
+
47
+ 💡 Usage
48
+ ```bash
49
+ gptc <your question>
50
+ ```
51
+ Example
52
+ ```bash
53
+ gptc find and delete all txt files in current directory recursively
54
+ ```
55
+ Output:
56
+ ```bash
57
+ find . -type f -name "*.txt" -delete
58
+ ```
59
+ Then it will automatically prefill your terminal input:
60
+
61
+ ```bash
62
+ ~/current/path$ find . -type f -name "*.txt" -delete
63
+ ```
64
+ ⚠️ The command is NOT executed automatically.
65
+
66
+ ---
67
+
68
+ ⚙️ Options
69
+
70
+ 📋 Copy to clipboard
71
+
72
+ ```bash
73
+ gptc --copy compress current folder into tar.gz
74
+ ```
75
+ 📖 Show explanation
76
+
77
+ ```bash
78
+ gptc --explain find process using port 8000
79
+ ```
80
+ ▶️ Execute command (with confirmation)
81
+
82
+ ```bash
83
+ gptc --run check disk usage
84
+ ```
85
+ 🧠 Show history
86
+
87
+ ```bash
88
+ gptc --history
89
+ ```
90
+ 🤖 Specify model
91
+
92
+ ```bash
93
+ gptc --model gpt-4.1 list all running processes
94
+ ```
95
+
96
+ ---
97
+
98
+ 🔐 Security
99
+
100
+ - API key is stored locally at:
101
+
102
+ ```bash
103
+ ~/.config/gptc/config.json
104
+ ```
105
+ File permissions are restricted to the user (600)
106
+ Dangerous commands are automatically detected and blocked from execution
107
+
108
+ ---
109
+
110
+ ⚠️ Disclaimer
111
+ - Always review generated commands before running them
112
+ - Some commands may modify or delete system data
113
+ - Use with caution, especially with elevated privileges (sudo)
114
+
115
+ ---
116
+
117
+ 🖥️ Supported Platforms
118
+
119
+ - macOS
120
+ - Linux (Ubuntu, etc.)
121
+
122
+ ---
123
+
124
+ ✨ Features
125
+ - Natural language → shell command
126
+ - Auto-prefilled terminal input (no copy-paste needed)
127
+ - Optional execution with confirmation
128
+ - Clipboard copy support
129
+ - Command explanation
130
+ - History tracking
131
+ - Local API key management (no environment variable required)
132
+
133
+ ---
134
+
135
+ 📌 Summary
136
+
137
+ > Stop Googling terminal commands. Just ask.
138
+
139
+ ---
140
+
141
+ 📄 License
142
+
143
+ MIT License
@@ -0,0 +1,14 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/gpt_command/__init__.py
4
+ src/gpt_command/cli.py
5
+ src/gpt_command/config.py
6
+ src/gpt_command/history.py
7
+ src/gpt_command/key_manager.py
8
+ src/gpt_command/utils.py
9
+ src/gpt_command.egg-info/PKG-INFO
10
+ src/gpt_command.egg-info/SOURCES.txt
11
+ src/gpt_command.egg-info/dependency_links.txt
12
+ src/gpt_command.egg-info/entry_points.txt
13
+ src/gpt_command.egg-info/requires.txt
14
+ src/gpt_command.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ gptc = gpt_command.cli:main
3
+ gptc-key = gpt_command.key_manager:main
@@ -0,0 +1,2 @@
1
+ openai>=2.0.0
2
+ tqdm>=4.66.0
@@ -0,0 +1 @@
1
+ gpt_command