ask2cmd 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.
- ask2cmd-0.1.0/LICENSE +21 -0
- ask2cmd-0.1.0/PKG-INFO +277 -0
- ask2cmd-0.1.0/README.md +254 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/PKG-INFO +277 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/SOURCES.txt +16 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/dependency_links.txt +1 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/entry_points.txt +2 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/requires.txt +3 -0
- ask2cmd-0.1.0/ask2cmd.egg-info/top_level.txt +1 -0
- ask2cmd-0.1.0/askcmd/__init__.py +3 -0
- ask2cmd-0.1.0/askcmd/ai.py +53 -0
- ask2cmd-0.1.0/askcmd/config.py +131 -0
- ask2cmd-0.1.0/askcmd/executor.py +21 -0
- ask2cmd-0.1.0/askcmd/main.py +76 -0
- ask2cmd-0.1.0/askcmd/prompts.py +25 -0
- ask2cmd-0.1.0/askcmd/utils.py +27 -0
- ask2cmd-0.1.0/pyproject.toml +35 -0
- ask2cmd-0.1.0/setup.cfg +4 -0
ask2cmd-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 askcmd contributors
|
|
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.
|
ask2cmd-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ask2cmd
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn natural language into terminal commands.
|
|
5
|
+
Author: askcmd contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dovzilla/askcmd
|
|
8
|
+
Project-URL: Repository, https://github.com/dovzilla/askcmd
|
|
9
|
+
Keywords: cli,ai,terminal,openai,natural-language
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: typer>=0.9.0
|
|
20
|
+
Requires-Dist: rich>=13.0.0
|
|
21
|
+
Requires-Dist: openai>=1.0.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<h1 align="center">askcmd</h1>
|
|
26
|
+
<p align="center"><strong>Stop Googling. Start asking.</strong></p>
|
|
27
|
+
<p align="center">Turn natural language into terminal commands — right from your shell.</p>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
|
|
32
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License"></a>
|
|
33
|
+
<a href="https://github.com/dovzilla/askcmd/stargazers"><img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="GitHub Stars"></a>
|
|
34
|
+
<a href="https://github.com/dovzilla/askcmd/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"></a>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Demo
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
$ ask "list all docker containers"
|
|
43
|
+
> docker ps -a
|
|
44
|
+
|
|
45
|
+
$ ask "kill process on port 3000"
|
|
46
|
+
> lsof -ti:3000 | xargs kill -9
|
|
47
|
+
|
|
48
|
+
$ ask "find large files in this folder"
|
|
49
|
+
> find . -type f -size +100M
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="demo.gif" alt="askcmd demo" width="600">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Why This Exists
|
|
59
|
+
|
|
60
|
+
You're a developer. You live in the terminal.
|
|
61
|
+
|
|
62
|
+
And yet — every single day — you open a browser to Google things like:
|
|
63
|
+
|
|
64
|
+
> *"how to find files larger than 100MB linux"*
|
|
65
|
+
> *"tar compress folder command"*
|
|
66
|
+
> *"kill process on port 3000 mac"*
|
|
67
|
+
|
|
68
|
+
You know the command *exists*. You just can't remember the exact flags.
|
|
69
|
+
|
|
70
|
+
**askcmd** fixes that. One tool. One command. No leaving the terminal.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
ask "undo last git commit but keep changes"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That's it.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Install
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install ask2cmd
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then run the setup wizard to pick your provider and enter your API key:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ask setup
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Choose from **OpenAI**, **DeepSeek**, or any OpenAI-compatible API:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
选择你的 AI 服务商:
|
|
96
|
+
|
|
97
|
+
1) OpenAI
|
|
98
|
+
2) DeepSeek
|
|
99
|
+
3) Custom (OpenAI-compatible)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Done. You're ready.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
This is where it gets fun.
|
|
109
|
+
|
|
110
|
+
#### Everyday
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
ask "show disk usage sorted by size"
|
|
114
|
+
ask "count lines of code in this project"
|
|
115
|
+
ask "show my public IP"
|
|
116
|
+
ask "what's using port 8080"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Git
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ask "undo last commit but keep changes"
|
|
123
|
+
ask "squash last 3 commits"
|
|
124
|
+
ask "show commits from last week by me"
|
|
125
|
+
ask "delete all merged branches"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Docker
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
ask "stop all running containers"
|
|
132
|
+
ask "remove all dangling images"
|
|
133
|
+
ask "show logs for container nginx"
|
|
134
|
+
ask "list all volumes not used by any container"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Files & Search
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
ask "find all TODO comments in python files"
|
|
141
|
+
ask "find files modified in the last 24 hours"
|
|
142
|
+
ask "replace foo with bar in all js files"
|
|
143
|
+
ask "find duplicate files in this folder"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### System
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
ask "kill process on port 3000"
|
|
150
|
+
ask "show top 10 processes by memory"
|
|
151
|
+
ask "how much RAM is free"
|
|
152
|
+
ask "list all open network connections"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Fun
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
ask "generate a random password 32 chars"
|
|
159
|
+
ask "download this youtube video as mp3" # you rebel
|
|
160
|
+
ask "what's the weather in Tokyo from the terminal"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every command is shown to you **before** it runs. You stay in control.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Features
|
|
168
|
+
|
|
169
|
+
| Feature | Description |
|
|
170
|
+
|---------|-------------|
|
|
171
|
+
| **Natural language** | Just describe what you want |
|
|
172
|
+
| **OS-aware** | Detects macOS / Linux / Windows and adapts commands |
|
|
173
|
+
| **Multi-provider** | OpenAI, DeepSeek, or any compatible API |
|
|
174
|
+
| **Safe by default** | Always asks for confirmation before running |
|
|
175
|
+
| `--explain` | Explains what the command does |
|
|
176
|
+
| `--dry-run` | Shows the command without executing |
|
|
177
|
+
| `--yes` | Skips confirmation (for the brave) |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Safety
|
|
182
|
+
|
|
183
|
+
**askcmd never runs anything without your permission.**
|
|
184
|
+
|
|
185
|
+
Default flow:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
$ ask "delete all node_modules recursively"
|
|
189
|
+
|
|
190
|
+
╭──────────────────────────────────────────╮
|
|
191
|
+
│ find . -name node_modules -type d │
|
|
192
|
+
│ -prune -exec rm -rf {} + │
|
|
193
|
+
╰──────────────────────────────────────────╯
|
|
194
|
+
|
|
195
|
+
Run this command? [y/N]:
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- **Default is N.** You must explicitly approve.
|
|
199
|
+
- Use `--dry-run` to preview without any execution.
|
|
200
|
+
- Use `--explain` to understand the command before running.
|
|
201
|
+
- No telemetry. No data collection. Your queries go to *your* API provider and nowhere else.
|
|
202
|
+
|
|
203
|
+
You are always in control.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## How It Works
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
You askcmd LLM API Terminal
|
|
211
|
+
│ │ │ │
|
|
212
|
+
│ "kill port 3000" │ │ │
|
|
213
|
+
│──────────────────>│ │ │
|
|
214
|
+
│ │ prompt + OS ctx │ │
|
|
215
|
+
│ │─────────────────>│ │
|
|
216
|
+
│ │ shell command │ │
|
|
217
|
+
│ │<─────────────────│ │
|
|
218
|
+
│ show command │ │ │
|
|
219
|
+
│<──────────────────│ │ │
|
|
220
|
+
│ confirm (y/N) │ │ │
|
|
221
|
+
│──────────────────>│ │ │
|
|
222
|
+
│ │ execute │ │
|
|
223
|
+
│ │─────────────────────────────────────>
|
|
224
|
+
│ │ │ output │
|
|
225
|
+
│<─────────────────────────────────────────────────────────
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
1. You describe what you want in plain English
|
|
229
|
+
2. `askcmd` builds a prompt with your OS and shell context
|
|
230
|
+
3. The LLM returns a single shell command (no markdown, no fluff)
|
|
231
|
+
4. You review, confirm, and it runs — or you walk away
|
|
232
|
+
|
|
233
|
+
No agents. No chains. No frameworks. Just one clean API call.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Roadmap
|
|
238
|
+
|
|
239
|
+
- [ ] Command history — recall and reuse past generations
|
|
240
|
+
- [ ] Shell aliases — auto-create aliases for commands you use often
|
|
241
|
+
- [ ] Offline mode — local models via Ollama
|
|
242
|
+
- [ ] Plugins — community-contributed prompt packs
|
|
243
|
+
- [ ] Clipboard mode — copy command instead of running
|
|
244
|
+
- [ ] Multi-command — generate pipelines and scripts
|
|
245
|
+
- [ ] i18n — prompts in any language
|
|
246
|
+
|
|
247
|
+
Want to build one of these? PRs are open.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Contributing
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
git clone https://github.com/dovzilla/askcmd.git
|
|
255
|
+
cd askcmd
|
|
256
|
+
pip install -e .
|
|
257
|
+
ask setup
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
That's the whole dev setup. Hack away.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT — do whatever you want with it.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
<p align="center">
|
|
271
|
+
<br>
|
|
272
|
+
If this saves you even one Google search, give it a ⭐
|
|
273
|
+
<br><br>
|
|
274
|
+
<a href="https://github.com/dovzilla/askcmd">
|
|
275
|
+
<img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="Star on GitHub">
|
|
276
|
+
</a>
|
|
277
|
+
</p>
|
ask2cmd-0.1.0/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">askcmd</h1>
|
|
3
|
+
<p align="center"><strong>Stop Googling. Start asking.</strong></p>
|
|
4
|
+
<p align="center">Turn natural language into terminal commands — right from your shell.</p>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License"></a>
|
|
10
|
+
<a href="https://github.com/dovzilla/askcmd/stargazers"><img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="GitHub Stars"></a>
|
|
11
|
+
<a href="https://github.com/dovzilla/askcmd/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Demo
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
$ ask "list all docker containers"
|
|
20
|
+
> docker ps -a
|
|
21
|
+
|
|
22
|
+
$ ask "kill process on port 3000"
|
|
23
|
+
> lsof -ti:3000 | xargs kill -9
|
|
24
|
+
|
|
25
|
+
$ ask "find large files in this folder"
|
|
26
|
+
> find . -type f -size +100M
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="demo.gif" alt="askcmd demo" width="600">
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Why This Exists
|
|
36
|
+
|
|
37
|
+
You're a developer. You live in the terminal.
|
|
38
|
+
|
|
39
|
+
And yet — every single day — you open a browser to Google things like:
|
|
40
|
+
|
|
41
|
+
> *"how to find files larger than 100MB linux"*
|
|
42
|
+
> *"tar compress folder command"*
|
|
43
|
+
> *"kill process on port 3000 mac"*
|
|
44
|
+
|
|
45
|
+
You know the command *exists*. You just can't remember the exact flags.
|
|
46
|
+
|
|
47
|
+
**askcmd** fixes that. One tool. One command. No leaving the terminal.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ask "undo last git commit but keep changes"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That's it.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install ask2cmd
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then run the setup wizard to pick your provider and enter your API key:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ask setup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Choose from **OpenAI**, **DeepSeek**, or any OpenAI-compatible API:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
选择你的 AI 服务商:
|
|
73
|
+
|
|
74
|
+
1) OpenAI
|
|
75
|
+
2) DeepSeek
|
|
76
|
+
3) Custom (OpenAI-compatible)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Done. You're ready.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Examples
|
|
84
|
+
|
|
85
|
+
This is where it gets fun.
|
|
86
|
+
|
|
87
|
+
#### Everyday
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
ask "show disk usage sorted by size"
|
|
91
|
+
ask "count lines of code in this project"
|
|
92
|
+
ask "show my public IP"
|
|
93
|
+
ask "what's using port 8080"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Git
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
ask "undo last commit but keep changes"
|
|
100
|
+
ask "squash last 3 commits"
|
|
101
|
+
ask "show commits from last week by me"
|
|
102
|
+
ask "delete all merged branches"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Docker
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ask "stop all running containers"
|
|
109
|
+
ask "remove all dangling images"
|
|
110
|
+
ask "show logs for container nginx"
|
|
111
|
+
ask "list all volumes not used by any container"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### Files & Search
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ask "find all TODO comments in python files"
|
|
118
|
+
ask "find files modified in the last 24 hours"
|
|
119
|
+
ask "replace foo with bar in all js files"
|
|
120
|
+
ask "find duplicate files in this folder"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### System
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ask "kill process on port 3000"
|
|
127
|
+
ask "show top 10 processes by memory"
|
|
128
|
+
ask "how much RAM is free"
|
|
129
|
+
ask "list all open network connections"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Fun
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
ask "generate a random password 32 chars"
|
|
136
|
+
ask "download this youtube video as mp3" # you rebel
|
|
137
|
+
ask "what's the weather in Tokyo from the terminal"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Every command is shown to you **before** it runs. You stay in control.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Features
|
|
145
|
+
|
|
146
|
+
| Feature | Description |
|
|
147
|
+
|---------|-------------|
|
|
148
|
+
| **Natural language** | Just describe what you want |
|
|
149
|
+
| **OS-aware** | Detects macOS / Linux / Windows and adapts commands |
|
|
150
|
+
| **Multi-provider** | OpenAI, DeepSeek, or any compatible API |
|
|
151
|
+
| **Safe by default** | Always asks for confirmation before running |
|
|
152
|
+
| `--explain` | Explains what the command does |
|
|
153
|
+
| `--dry-run` | Shows the command without executing |
|
|
154
|
+
| `--yes` | Skips confirmation (for the brave) |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Safety
|
|
159
|
+
|
|
160
|
+
**askcmd never runs anything without your permission.**
|
|
161
|
+
|
|
162
|
+
Default flow:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
$ ask "delete all node_modules recursively"
|
|
166
|
+
|
|
167
|
+
╭──────────────────────────────────────────╮
|
|
168
|
+
│ find . -name node_modules -type d │
|
|
169
|
+
│ -prune -exec rm -rf {} + │
|
|
170
|
+
╰──────────────────────────────────────────╯
|
|
171
|
+
|
|
172
|
+
Run this command? [y/N]:
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- **Default is N.** You must explicitly approve.
|
|
176
|
+
- Use `--dry-run` to preview without any execution.
|
|
177
|
+
- Use `--explain` to understand the command before running.
|
|
178
|
+
- No telemetry. No data collection. Your queries go to *your* API provider and nowhere else.
|
|
179
|
+
|
|
180
|
+
You are always in control.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## How It Works
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
You askcmd LLM API Terminal
|
|
188
|
+
│ │ │ │
|
|
189
|
+
│ "kill port 3000" │ │ │
|
|
190
|
+
│──────────────────>│ │ │
|
|
191
|
+
│ │ prompt + OS ctx │ │
|
|
192
|
+
│ │─────────────────>│ │
|
|
193
|
+
│ │ shell command │ │
|
|
194
|
+
│ │<─────────────────│ │
|
|
195
|
+
│ show command │ │ │
|
|
196
|
+
│<──────────────────│ │ │
|
|
197
|
+
│ confirm (y/N) │ │ │
|
|
198
|
+
│──────────────────>│ │ │
|
|
199
|
+
│ │ execute │ │
|
|
200
|
+
│ │─────────────────────────────────────>
|
|
201
|
+
│ │ │ output │
|
|
202
|
+
│<─────────────────────────────────────────────────────────
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
1. You describe what you want in plain English
|
|
206
|
+
2. `askcmd` builds a prompt with your OS and shell context
|
|
207
|
+
3. The LLM returns a single shell command (no markdown, no fluff)
|
|
208
|
+
4. You review, confirm, and it runs — or you walk away
|
|
209
|
+
|
|
210
|
+
No agents. No chains. No frameworks. Just one clean API call.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Roadmap
|
|
215
|
+
|
|
216
|
+
- [ ] Command history — recall and reuse past generations
|
|
217
|
+
- [ ] Shell aliases — auto-create aliases for commands you use often
|
|
218
|
+
- [ ] Offline mode — local models via Ollama
|
|
219
|
+
- [ ] Plugins — community-contributed prompt packs
|
|
220
|
+
- [ ] Clipboard mode — copy command instead of running
|
|
221
|
+
- [ ] Multi-command — generate pipelines and scripts
|
|
222
|
+
- [ ] i18n — prompts in any language
|
|
223
|
+
|
|
224
|
+
Want to build one of these? PRs are open.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
git clone https://github.com/dovzilla/askcmd.git
|
|
232
|
+
cd askcmd
|
|
233
|
+
pip install -e .
|
|
234
|
+
ask setup
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
That's the whole dev setup. Hack away.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT — do whatever you want with it.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
<p align="center">
|
|
248
|
+
<br>
|
|
249
|
+
If this saves you even one Google search, give it a ⭐
|
|
250
|
+
<br><br>
|
|
251
|
+
<a href="https://github.com/dovzilla/askcmd">
|
|
252
|
+
<img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="Star on GitHub">
|
|
253
|
+
</a>
|
|
254
|
+
</p>
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ask2cmd
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn natural language into terminal commands.
|
|
5
|
+
Author: askcmd contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dovzilla/askcmd
|
|
8
|
+
Project-URL: Repository, https://github.com/dovzilla/askcmd
|
|
9
|
+
Keywords: cli,ai,terminal,openai,natural-language
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: typer>=0.9.0
|
|
20
|
+
Requires-Dist: rich>=13.0.0
|
|
21
|
+
Requires-Dist: openai>=1.0.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<h1 align="center">askcmd</h1>
|
|
26
|
+
<p align="center"><strong>Stop Googling. Start asking.</strong></p>
|
|
27
|
+
<p align="center">Turn natural language into terminal commands — right from your shell.</p>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
|
|
32
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License"></a>
|
|
33
|
+
<a href="https://github.com/dovzilla/askcmd/stargazers"><img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="GitHub Stars"></a>
|
|
34
|
+
<a href="https://github.com/dovzilla/askcmd/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs Welcome"></a>
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Demo
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
$ ask "list all docker containers"
|
|
43
|
+
> docker ps -a
|
|
44
|
+
|
|
45
|
+
$ ask "kill process on port 3000"
|
|
46
|
+
> lsof -ti:3000 | xargs kill -9
|
|
47
|
+
|
|
48
|
+
$ ask "find large files in this folder"
|
|
49
|
+
> find . -type f -size +100M
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img src="demo.gif" alt="askcmd demo" width="600">
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Why This Exists
|
|
59
|
+
|
|
60
|
+
You're a developer. You live in the terminal.
|
|
61
|
+
|
|
62
|
+
And yet — every single day — you open a browser to Google things like:
|
|
63
|
+
|
|
64
|
+
> *"how to find files larger than 100MB linux"*
|
|
65
|
+
> *"tar compress folder command"*
|
|
66
|
+
> *"kill process on port 3000 mac"*
|
|
67
|
+
|
|
68
|
+
You know the command *exists*. You just can't remember the exact flags.
|
|
69
|
+
|
|
70
|
+
**askcmd** fixes that. One tool. One command. No leaving the terminal.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
ask "undo last git commit but keep changes"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That's it.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Install
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install ask2cmd
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then run the setup wizard to pick your provider and enter your API key:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ask setup
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Choose from **OpenAI**, **DeepSeek**, or any OpenAI-compatible API:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
选择你的 AI 服务商:
|
|
96
|
+
|
|
97
|
+
1) OpenAI
|
|
98
|
+
2) DeepSeek
|
|
99
|
+
3) Custom (OpenAI-compatible)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Done. You're ready.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
This is where it gets fun.
|
|
109
|
+
|
|
110
|
+
#### Everyday
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
ask "show disk usage sorted by size"
|
|
114
|
+
ask "count lines of code in this project"
|
|
115
|
+
ask "show my public IP"
|
|
116
|
+
ask "what's using port 8080"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Git
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ask "undo last commit but keep changes"
|
|
123
|
+
ask "squash last 3 commits"
|
|
124
|
+
ask "show commits from last week by me"
|
|
125
|
+
ask "delete all merged branches"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Docker
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
ask "stop all running containers"
|
|
132
|
+
ask "remove all dangling images"
|
|
133
|
+
ask "show logs for container nginx"
|
|
134
|
+
ask "list all volumes not used by any container"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Files & Search
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
ask "find all TODO comments in python files"
|
|
141
|
+
ask "find files modified in the last 24 hours"
|
|
142
|
+
ask "replace foo with bar in all js files"
|
|
143
|
+
ask "find duplicate files in this folder"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### System
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
ask "kill process on port 3000"
|
|
150
|
+
ask "show top 10 processes by memory"
|
|
151
|
+
ask "how much RAM is free"
|
|
152
|
+
ask "list all open network connections"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Fun
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
ask "generate a random password 32 chars"
|
|
159
|
+
ask "download this youtube video as mp3" # you rebel
|
|
160
|
+
ask "what's the weather in Tokyo from the terminal"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every command is shown to you **before** it runs. You stay in control.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Features
|
|
168
|
+
|
|
169
|
+
| Feature | Description |
|
|
170
|
+
|---------|-------------|
|
|
171
|
+
| **Natural language** | Just describe what you want |
|
|
172
|
+
| **OS-aware** | Detects macOS / Linux / Windows and adapts commands |
|
|
173
|
+
| **Multi-provider** | OpenAI, DeepSeek, or any compatible API |
|
|
174
|
+
| **Safe by default** | Always asks for confirmation before running |
|
|
175
|
+
| `--explain` | Explains what the command does |
|
|
176
|
+
| `--dry-run` | Shows the command without executing |
|
|
177
|
+
| `--yes` | Skips confirmation (for the brave) |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Safety
|
|
182
|
+
|
|
183
|
+
**askcmd never runs anything without your permission.**
|
|
184
|
+
|
|
185
|
+
Default flow:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
$ ask "delete all node_modules recursively"
|
|
189
|
+
|
|
190
|
+
╭──────────────────────────────────────────╮
|
|
191
|
+
│ find . -name node_modules -type d │
|
|
192
|
+
│ -prune -exec rm -rf {} + │
|
|
193
|
+
╰──────────────────────────────────────────╯
|
|
194
|
+
|
|
195
|
+
Run this command? [y/N]:
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- **Default is N.** You must explicitly approve.
|
|
199
|
+
- Use `--dry-run` to preview without any execution.
|
|
200
|
+
- Use `--explain` to understand the command before running.
|
|
201
|
+
- No telemetry. No data collection. Your queries go to *your* API provider and nowhere else.
|
|
202
|
+
|
|
203
|
+
You are always in control.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## How It Works
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
You askcmd LLM API Terminal
|
|
211
|
+
│ │ │ │
|
|
212
|
+
│ "kill port 3000" │ │ │
|
|
213
|
+
│──────────────────>│ │ │
|
|
214
|
+
│ │ prompt + OS ctx │ │
|
|
215
|
+
│ │─────────────────>│ │
|
|
216
|
+
│ │ shell command │ │
|
|
217
|
+
│ │<─────────────────│ │
|
|
218
|
+
│ show command │ │ │
|
|
219
|
+
│<──────────────────│ │ │
|
|
220
|
+
│ confirm (y/N) │ │ │
|
|
221
|
+
│──────────────────>│ │ │
|
|
222
|
+
│ │ execute │ │
|
|
223
|
+
│ │─────────────────────────────────────>
|
|
224
|
+
│ │ │ output │
|
|
225
|
+
│<─────────────────────────────────────────────────────────
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
1. You describe what you want in plain English
|
|
229
|
+
2. `askcmd` builds a prompt with your OS and shell context
|
|
230
|
+
3. The LLM returns a single shell command (no markdown, no fluff)
|
|
231
|
+
4. You review, confirm, and it runs — or you walk away
|
|
232
|
+
|
|
233
|
+
No agents. No chains. No frameworks. Just one clean API call.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Roadmap
|
|
238
|
+
|
|
239
|
+
- [ ] Command history — recall and reuse past generations
|
|
240
|
+
- [ ] Shell aliases — auto-create aliases for commands you use often
|
|
241
|
+
- [ ] Offline mode — local models via Ollama
|
|
242
|
+
- [ ] Plugins — community-contributed prompt packs
|
|
243
|
+
- [ ] Clipboard mode — copy command instead of running
|
|
244
|
+
- [ ] Multi-command — generate pipelines and scripts
|
|
245
|
+
- [ ] i18n — prompts in any language
|
|
246
|
+
|
|
247
|
+
Want to build one of these? PRs are open.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Contributing
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
git clone https://github.com/dovzilla/askcmd.git
|
|
255
|
+
cd askcmd
|
|
256
|
+
pip install -e .
|
|
257
|
+
ask setup
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
That's the whole dev setup. Hack away.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT — do whatever you want with it.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
<p align="center">
|
|
271
|
+
<br>
|
|
272
|
+
If this saves you even one Google search, give it a ⭐
|
|
273
|
+
<br><br>
|
|
274
|
+
<a href="https://github.com/dovzilla/askcmd">
|
|
275
|
+
<img src="https://img.shields.io/github/stars/dovzilla/askcmd?style=social" alt="Star on GitHub">
|
|
276
|
+
</a>
|
|
277
|
+
</p>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
ask2cmd.egg-info/PKG-INFO
|
|
5
|
+
ask2cmd.egg-info/SOURCES.txt
|
|
6
|
+
ask2cmd.egg-info/dependency_links.txt
|
|
7
|
+
ask2cmd.egg-info/entry_points.txt
|
|
8
|
+
ask2cmd.egg-info/requires.txt
|
|
9
|
+
ask2cmd.egg-info/top_level.txt
|
|
10
|
+
askcmd/__init__.py
|
|
11
|
+
askcmd/ai.py
|
|
12
|
+
askcmd/config.py
|
|
13
|
+
askcmd/executor.py
|
|
14
|
+
askcmd/main.py
|
|
15
|
+
askcmd/prompts.py
|
|
16
|
+
askcmd/utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
askcmd
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Thin wrapper around OpenAI-compatible chat completions APIs."""
|
|
2
|
+
|
|
3
|
+
from openai import OpenAI
|
|
4
|
+
|
|
5
|
+
from .config import get_config
|
|
6
|
+
from .prompts import EXPLAIN_PROMPT, SYSTEM_PROMPT
|
|
7
|
+
from .utils import get_os_info
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _client() -> tuple[OpenAI, str]:
|
|
11
|
+
"""Build an OpenAI client from the saved config and return (client, model)."""
|
|
12
|
+
cfg = get_config()
|
|
13
|
+
client = OpenAI(
|
|
14
|
+
api_key=cfg["api_key"],
|
|
15
|
+
base_url=cfg["base_url"],
|
|
16
|
+
)
|
|
17
|
+
return client, cfg["model"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_command(query: str) -> str:
|
|
21
|
+
"""Send a natural-language query and return a shell command string."""
|
|
22
|
+
client, model = _client()
|
|
23
|
+
os_info = get_os_info()
|
|
24
|
+
|
|
25
|
+
response = client.chat.completions.create(
|
|
26
|
+
model=model,
|
|
27
|
+
temperature=0,
|
|
28
|
+
max_tokens=256,
|
|
29
|
+
messages=[
|
|
30
|
+
{"role": "system", "content": SYSTEM_PROMPT.format(**os_info)},
|
|
31
|
+
{"role": "user", "content": query},
|
|
32
|
+
],
|
|
33
|
+
)
|
|
34
|
+
return response.choices[0].message.content.strip()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def explain_command(command: str) -> str:
|
|
38
|
+
"""Ask the model to explain a shell command in plain English."""
|
|
39
|
+
client, model = _client()
|
|
40
|
+
os_info = get_os_info()
|
|
41
|
+
|
|
42
|
+
response = client.chat.completions.create(
|
|
43
|
+
model=model,
|
|
44
|
+
temperature=0,
|
|
45
|
+
max_tokens=512,
|
|
46
|
+
messages=[
|
|
47
|
+
{
|
|
48
|
+
"role": "user",
|
|
49
|
+
"content": EXPLAIN_PROMPT.format(command=command, **os_info),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
return response.choices[0].message.content.strip()
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Configuration management — provider selection and API key storage."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.prompt import Prompt
|
|
10
|
+
|
|
11
|
+
CONFIG_DIR = Path.home() / ".askcmd"
|
|
12
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
13
|
+
|
|
14
|
+
PROVIDERS = {
|
|
15
|
+
"openai": {
|
|
16
|
+
"name": "OpenAI",
|
|
17
|
+
"base_url": "https://api.openai.com/v1",
|
|
18
|
+
"default_model": "gpt-4o-mini",
|
|
19
|
+
"key_hint": "sk-...",
|
|
20
|
+
},
|
|
21
|
+
"deepseek": {
|
|
22
|
+
"name": "DeepSeek",
|
|
23
|
+
"base_url": "https://api.deepseek.com",
|
|
24
|
+
"default_model": "deepseek-chat",
|
|
25
|
+
"key_hint": "sk-...",
|
|
26
|
+
},
|
|
27
|
+
"custom": {
|
|
28
|
+
"name": "Custom (OpenAI-compatible)",
|
|
29
|
+
"base_url": "",
|
|
30
|
+
"default_model": "",
|
|
31
|
+
"key_hint": "your-api-key",
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console = Console()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_config() -> dict:
|
|
39
|
+
"""Load config from disk, or return empty dict."""
|
|
40
|
+
if CONFIG_FILE.exists():
|
|
41
|
+
return json.loads(CONFIG_FILE.read_text())
|
|
42
|
+
return {}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def save_config(cfg: dict) -> None:
|
|
46
|
+
"""Persist config to ~/.askcmd/config.json."""
|
|
47
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
CONFIG_FILE.write_text(json.dumps(cfg, indent=2) + "\n")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_config() -> dict:
|
|
52
|
+
"""Return a validated config. Abort with helpful message if not set up."""
|
|
53
|
+
cfg = load_config()
|
|
54
|
+
if not cfg.get("api_key"):
|
|
55
|
+
console.print()
|
|
56
|
+
console.print(
|
|
57
|
+
Panel(
|
|
58
|
+
"[bold yellow]还没有配置 API Key[/bold yellow]\n"
|
|
59
|
+
"请先运行: [cyan bold]ask setup[/cyan bold]",
|
|
60
|
+
border_style="yellow",
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
raise SystemExit(1)
|
|
64
|
+
return cfg
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_setup() -> None:
|
|
68
|
+
"""Interactive setup wizard — choose provider, enter API key."""
|
|
69
|
+
console.print()
|
|
70
|
+
console.print(Panel("[bold]askcmd 配置向导[/bold]", border_style="blue"))
|
|
71
|
+
console.print()
|
|
72
|
+
|
|
73
|
+
# --- Provider selection ---
|
|
74
|
+
console.print("[bold]选择你的 AI 服务商:[/bold]\n")
|
|
75
|
+
choices = list(PROVIDERS.keys())
|
|
76
|
+
for i, key in enumerate(choices, 1):
|
|
77
|
+
p = PROVIDERS[key]
|
|
78
|
+
console.print(f" [cyan bold]{i}[/cyan bold]) {p['name']}")
|
|
79
|
+
console.print()
|
|
80
|
+
|
|
81
|
+
choice = Prompt.ask(
|
|
82
|
+
"[bold]请输入编号[/bold]",
|
|
83
|
+
choices=[str(i) for i in range(1, len(choices) + 1)],
|
|
84
|
+
default="1",
|
|
85
|
+
)
|
|
86
|
+
provider_key = choices[int(choice) - 1]
|
|
87
|
+
provider = PROVIDERS[provider_key]
|
|
88
|
+
|
|
89
|
+
# --- Base URL (custom only) ---
|
|
90
|
+
if provider_key == "custom":
|
|
91
|
+
base_url = Prompt.ask("\n[bold]输入 API Base URL[/bold]")
|
|
92
|
+
model = Prompt.ask("[bold]输入模型名称[/bold]")
|
|
93
|
+
else:
|
|
94
|
+
base_url = provider["base_url"]
|
|
95
|
+
model = provider["default_model"]
|
|
96
|
+
console.print(f"\n Provider : [green]{provider['name']}[/green]")
|
|
97
|
+
console.print(f" Base URL : [dim]{base_url}[/dim]")
|
|
98
|
+
console.print(f" Model : [dim]{model}[/dim]")
|
|
99
|
+
|
|
100
|
+
# --- API Key ---
|
|
101
|
+
console.print()
|
|
102
|
+
api_key = Prompt.ask(
|
|
103
|
+
f"[bold]输入你的 API Key[/bold] [dim]({provider['key_hint']})[/dim]",
|
|
104
|
+
password=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if not api_key.strip():
|
|
108
|
+
console.print("[red]API Key 不能为空,配置取消。[/red]")
|
|
109
|
+
raise SystemExit(1)
|
|
110
|
+
|
|
111
|
+
# --- Save ---
|
|
112
|
+
cfg = {
|
|
113
|
+
"provider": provider_key,
|
|
114
|
+
"provider_name": provider["name"],
|
|
115
|
+
"base_url": base_url,
|
|
116
|
+
"model": model,
|
|
117
|
+
"api_key": api_key.strip(),
|
|
118
|
+
}
|
|
119
|
+
save_config(cfg)
|
|
120
|
+
|
|
121
|
+
console.print()
|
|
122
|
+
console.print(
|
|
123
|
+
Panel(
|
|
124
|
+
f"[bold green]配置完成![/bold green]\n\n"
|
|
125
|
+
f" 服务商 : {cfg['provider_name']}\n"
|
|
126
|
+
f" 模型 : {cfg['model']}\n"
|
|
127
|
+
f" 配置文件 : [dim]{CONFIG_FILE}[/dim]\n\n"
|
|
128
|
+
f"现在可以使用了: [cyan bold]ask \"list files\"[/cyan bold]",
|
|
129
|
+
border_style="green",
|
|
130
|
+
)
|
|
131
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Safe subprocess execution."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def run_command(command: str) -> int:
|
|
8
|
+
"""Execute *command* in the user's shell and return the exit code."""
|
|
9
|
+
try:
|
|
10
|
+
result = subprocess.run(
|
|
11
|
+
command,
|
|
12
|
+
shell=True,
|
|
13
|
+
text=True,
|
|
14
|
+
)
|
|
15
|
+
return result.returncode
|
|
16
|
+
except KeyboardInterrupt:
|
|
17
|
+
print("\nAborted.")
|
|
18
|
+
return 130
|
|
19
|
+
except Exception as exc:
|
|
20
|
+
print(f"Execution error: {exc}", file=sys.stderr)
|
|
21
|
+
return 1
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""CLI entrypoint — the `ask` command."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.prompt import Confirm
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
from .config import run_setup
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
# Main app — single-command mode for the cleanest UX.
|
|
17
|
+
# `ask setup` is handled by intercepting argv before Typer runs.
|
|
18
|
+
app = typer.Typer(
|
|
19
|
+
name="ask",
|
|
20
|
+
add_completion=False,
|
|
21
|
+
no_args_is_help=True,
|
|
22
|
+
rich_markup_mode="rich",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def version_callback(value: bool) -> None:
|
|
27
|
+
if value:
|
|
28
|
+
console.print(f"[bold]askcmd[/bold] v{__version__}")
|
|
29
|
+
raise typer.Exit()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@app.command(
|
|
33
|
+
help="Turn natural language into terminal commands.",
|
|
34
|
+
epilog="Setup: [cyan]ask setup[/cyan] · Example: [cyan]ask 'list all docker containers'[/cyan]",
|
|
35
|
+
)
|
|
36
|
+
def main(
|
|
37
|
+
query: str = typer.Argument(..., help="What you want to do, in plain English."),
|
|
38
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation and execute immediately."),
|
|
39
|
+
explain: bool = typer.Option(False, "--explain", "-e", help="Explain the generated command."),
|
|
40
|
+
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Show the command but do not execute."),
|
|
41
|
+
version: Optional[bool] = typer.Option(None, "--version", "-v", callback=version_callback, is_eager=True, help="Show version and exit."),
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Generate and optionally run a shell command from a natural-language query."""
|
|
44
|
+
from .ai import explain_command, generate_command
|
|
45
|
+
from .executor import run_command
|
|
46
|
+
|
|
47
|
+
with console.status("[bold green]Thinking…[/bold green]", spinner="dots"):
|
|
48
|
+
cmd = generate_command(query)
|
|
49
|
+
|
|
50
|
+
console.print()
|
|
51
|
+
console.print(Panel(f"[bold green]{cmd}[/bold green]", title="[bold]💡 Command[/bold]", border_style="green"))
|
|
52
|
+
|
|
53
|
+
if explain:
|
|
54
|
+
with console.status("[bold cyan]Explaining…[/bold cyan]", spinner="dots"):
|
|
55
|
+
explanation = explain_command(cmd)
|
|
56
|
+
console.print()
|
|
57
|
+
console.print(Panel(explanation, title="[bold]📖 Explanation[/bold]", border_style="cyan"))
|
|
58
|
+
|
|
59
|
+
if dry_run:
|
|
60
|
+
raise typer.Exit()
|
|
61
|
+
|
|
62
|
+
if yes or Confirm.ask("\n[bold]Run this command?[/bold]", default=False):
|
|
63
|
+
console.print()
|
|
64
|
+
exit_code = run_command(cmd)
|
|
65
|
+
if exit_code != 0:
|
|
66
|
+
console.print(f"\n[yellow]⚠ Command exited with code {exit_code}[/yellow]")
|
|
67
|
+
else:
|
|
68
|
+
console.print("[dim]Cancelled.[/dim]")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def entry() -> None:
|
|
72
|
+
"""Real entrypoint: intercept `ask setup` before Typer runs."""
|
|
73
|
+
if len(sys.argv) >= 2 and sys.argv[1] == "setup":
|
|
74
|
+
run_setup()
|
|
75
|
+
else:
|
|
76
|
+
app()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Prompt templates for the OpenAI API."""
|
|
2
|
+
|
|
3
|
+
SYSTEM_PROMPT = """\
|
|
4
|
+
You are a terminal command generator.
|
|
5
|
+
You run on {os_name} ({os_version}).
|
|
6
|
+
The user's shell is {shell}.
|
|
7
|
+
|
|
8
|
+
Rules:
|
|
9
|
+
- Reply with ONLY the shell command. Nothing else.
|
|
10
|
+
- No markdown, no backticks, no explanation.
|
|
11
|
+
- One command (use && or | to chain if needed).
|
|
12
|
+
- Prefer safe, non-destructive commands when possible.
|
|
13
|
+
- If the request is ambiguous, pick the most common interpretation.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
EXPLAIN_PROMPT = """\
|
|
17
|
+
You are a terminal command explainer.
|
|
18
|
+
The user is running {os_name} ({os_version}) with {shell}.
|
|
19
|
+
|
|
20
|
+
Given the following shell command, explain what it does in plain English.
|
|
21
|
+
Be concise — use 2-4 short bullet points.
|
|
22
|
+
|
|
23
|
+
Command:
|
|
24
|
+
{command}
|
|
25
|
+
"""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Utility helpers — OS detection."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_os_info() -> dict[str, str]:
|
|
8
|
+
"""Return a dict with os_name, os_version, and shell."""
|
|
9
|
+
system = platform.system().lower()
|
|
10
|
+
version = platform.version()
|
|
11
|
+
|
|
12
|
+
if system == "darwin":
|
|
13
|
+
os_name = "macOS"
|
|
14
|
+
elif system == "linux":
|
|
15
|
+
os_name = "Linux"
|
|
16
|
+
elif system == "windows":
|
|
17
|
+
os_name = "Windows"
|
|
18
|
+
else:
|
|
19
|
+
os_name = system
|
|
20
|
+
|
|
21
|
+
shell = os.environ.get("SHELL", "unknown")
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
"os_name": os_name,
|
|
25
|
+
"os_version": version,
|
|
26
|
+
"shell": shell,
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ask2cmd"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Turn natural language into terminal commands."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "askcmd contributors"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["cli", "ai", "terminal", "openai", "natural-language"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Topic :: Utilities",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"typer>=0.9.0",
|
|
26
|
+
"rich>=13.0.0",
|
|
27
|
+
"openai>=1.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
ask = "askcmd.main:entry"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/dovzilla/askcmd"
|
|
35
|
+
Repository = "https://github.com/dovzilla/askcmd"
|
ask2cmd-0.1.0/setup.cfg
ADDED