copa-cli 0.6.0__tar.gz → 0.7.1__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.
- {copa_cli-0.6.0 → copa_cli-0.7.1}/PKG-INFO +102 -11
- {copa_cli-0.6.0 → copa_cli-0.7.1}/README.md +101 -10
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/cli.py +189 -3
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/config.py +6 -4
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/copa.zsh +45 -28
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/PKG-INFO +102 -11
- {copa_cli-0.6.0 → copa_cli-0.7.1}/pyproject.toml +1 -1
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_modal.py +29 -26
- {copa_cli-0.6.0 → copa_cli-0.7.1}/LICENSE +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/__init__.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/__main__.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/cli_common.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/cli_internal.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/cli_llm.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/cli_share.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/db.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/evolve.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/fzf.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/history.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/llm.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/mcp_server.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/models.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/scanner.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/scoring.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa/sharing.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/SOURCES.txt +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/dependency_links.txt +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/entry_points.txt +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/requires.txt +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/copa_cli.egg-info/top_level.txt +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/setup.cfg +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_cli_and_sharing.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_db.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_fzf.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_models.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_polish.py +0 -0
- {copa_cli-0.6.0 → copa_cli-0.7.1}/tests/test_scanner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: copa-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Command Palette — smart command tracking, ranking, and sharing for your shell
|
|
5
5
|
Author: Mark Stanford
|
|
6
6
|
License-Expression: MIT
|
|
@@ -46,7 +46,9 @@ Copa tracks the commands you run, ranks them by frequency and recency, and gives
|
|
|
46
46
|
- **LLM descriptions** — `copa fix --auto` uses Claude or ollama to generate descriptions for undescribed commands
|
|
47
47
|
- **Script protocol** — `#@ Description:` / `#@ Usage:` / `#@ Purpose:` / `#@ Flag:` headers in your scripts are auto-detected by `copa scan` across all `$PATH` directories
|
|
48
48
|
- **Flag documentation** — document command flags with descriptions; flags are searchable, visible in the preview pane, and preserved in `.copa` exports
|
|
49
|
+
- **Inline suggestions** — ghost text appears as you type; Tab accepts or opens a completion menu with the suggestion highlighted
|
|
49
50
|
- **Groups & Ctrl+G** — organize commands by project, device, or workflow; assign groups inline from the fzf palette with Ctrl+G
|
|
51
|
+
- **Bulk operations** — Ctrl+B enters select mode for batch group assignment, batch deletion, or batch LLM description
|
|
50
52
|
- **Sharing & `copa create`** — export/import command sets as `.copa` JSON files; `copa create` scaffolds a `.copa` file from an existing group
|
|
51
53
|
- **Set filtering** — scope list, search, and fzf to a specific shared set with `--set`
|
|
52
54
|
- **MCP server** — expose your commands to Claude Code (or any MCP client)
|
|
@@ -85,24 +87,47 @@ pip install -e .
|
|
|
85
87
|
pip install copa-cli[ollama]
|
|
86
88
|
```
|
|
87
89
|
|
|
88
|
-
###
|
|
90
|
+
### Setup
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
Run the interactive setup wizard:
|
|
91
93
|
|
|
92
94
|
```bash
|
|
93
|
-
|
|
95
|
+
copa setup
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
This walks you through everything:
|
|
99
|
+
1. Checks that **fzf** is installed (tells you how to install if missing)
|
|
100
|
+
2. Creates the Copa database (`~/.copa/copa.db`)
|
|
101
|
+
3. Adds shell integration to your `~/.zshrc` (prompts for confirmation)
|
|
102
|
+
4. Optionally imports your zsh history
|
|
103
|
+
|
|
104
|
+
Then activate Copa in your current terminal:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
source ~/.zshrc
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### What shell integration does
|
|
111
|
+
|
|
112
|
+
The `eval "$(copa init zsh)"` line added to your `.zshrc` does three things:
|
|
97
113
|
|
|
98
114
|
1. **Records every command you run** — a `precmd` hook silently calls `copa _record` in the background after each command, building up frequency and recency data with zero latency impact.
|
|
99
115
|
2. **Replaces Ctrl+R** — the default zsh reverse-history-search is replaced with Copa's fzf-powered command palette (see below).
|
|
100
116
|
3. **Supplements tab completion** — Copa registers as a completer so that any command gets completion candidates from your Copa database. The behavior is configurable (`fallback`, `hybrid`, `always`, or `never`) — see [Tab Completion](#tab-completion).
|
|
101
117
|
|
|
102
|
-
|
|
118
|
+
### Manual setup
|
|
119
|
+
|
|
120
|
+
If you prefer to set up manually instead of using `copa setup`:
|
|
103
121
|
|
|
104
122
|
```bash
|
|
105
|
-
|
|
123
|
+
# Add shell integration to ~/.zshrc
|
|
124
|
+
echo 'eval "$(copa init zsh)"' >> ~/.zshrc
|
|
125
|
+
|
|
126
|
+
# Activate in current terminal
|
|
127
|
+
source ~/.zshrc
|
|
128
|
+
|
|
129
|
+
# Import your history (optional)
|
|
130
|
+
copa sync
|
|
106
131
|
```
|
|
107
132
|
|
|
108
133
|
## Ctrl+R — fzf Command Palette
|
|
@@ -151,11 +176,40 @@ While the fzf palette is open, these keys are available:
|
|
|
151
176
|
| **Ctrl+N** | Cycle group | Cycles through groups: (all) → group1 → group2 → ... → (all) |
|
|
152
177
|
| **Ctrl+D** | Describe | Generate/edit a description using LLM (with tty-aware input) |
|
|
153
178
|
| **Ctrl+F** | Edit flags | Add flag documentation to the highlighted command |
|
|
179
|
+
| **Ctrl+B** | Select mode | Enter multi-select for bulk operations (see below) |
|
|
154
180
|
| **Ctrl+H** | Toggle header | Show/hide the key hints for more screen space |
|
|
155
181
|
| **ESC** | Cancel/back | In scope/group mode: returns to command list. Otherwise: closes fzf |
|
|
156
182
|
|
|
157
183
|
Keybindings are configurable via `~/.copa/config.toml`. See [Configuration](#configuration).
|
|
158
184
|
|
|
185
|
+
### Select mode (bulk operations)
|
|
186
|
+
|
|
187
|
+
Press **Ctrl+B** from the Ctrl+R palette to enter **select mode**. This opens a new fzf view with multi-select enabled:
|
|
188
|
+
|
|
189
|
+
- **Tab** toggles selection on individual commands
|
|
190
|
+
- **Ctrl+R** cycles modes (all → frequent → recent) just like the main palette
|
|
191
|
+
- **Enter** confirms your selection and shows the batch action menu
|
|
192
|
+
- **ESC** cancels and returns to your prompt
|
|
193
|
+
|
|
194
|
+
After selecting commands, Copa shows a batch action menu:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
Selected 5 command(s).
|
|
198
|
+
g = assign group
|
|
199
|
+
d = delete
|
|
200
|
+
a = auto-describe (LLM)
|
|
201
|
+
q = cancel
|
|
202
|
+
Action:
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Action | What it does |
|
|
206
|
+
|--------|-------------|
|
|
207
|
+
| **g** | Assign all selected commands to a group (or clear their group) |
|
|
208
|
+
| **d** | Delete all selected commands (with confirmation) |
|
|
209
|
+
| **a** | Auto-generate descriptions for all selected commands using your configured LLM backend |
|
|
210
|
+
|
|
211
|
+
This is useful for organizing large command sets — select 20 undescribed commands and batch-describe them, or move a set of related commands into a group in one step.
|
|
212
|
+
|
|
159
213
|
### Preview pane
|
|
160
214
|
|
|
161
215
|
The right side shows a detail card for the highlighted command: full description, usage, purpose, flag documentation, score breakdown, frequency, last used, source, group, shared set, and tags.
|
|
@@ -231,8 +285,8 @@ $ git pu█sh origin main ← grey ghost text
|
|
|
231
285
|
|-----|-------------------|---------------|
|
|
232
286
|
| Type chars | Insert char, re-fetch suggestion | Insert char, fetch suggestion |
|
|
233
287
|
| Backspace | Delete char, **latch** (suppress suggestions) | Delete char normally |
|
|
234
|
-
| **Tab** | `tab_accept=1`: accept full suggestion. `tab_accept=2` (default):
|
|
235
|
-
| **Down** |
|
|
288
|
+
| **Tab** | `tab_accept=1`: accept full suggestion. `tab_accept=2` (default): open completion menu with suggestion highlighted at top, native completions below | If latched: unlatch + re-fetch suggestion. Else: normal tab completion |
|
|
289
|
+
| **Down** | Open completion menu with suggestion highlighted at top | History navigation |
|
|
236
290
|
| **Right arrow** | Accept one word, re-fetch | Move cursor right |
|
|
237
291
|
| Enter | Clear suggestion, execute | Execute |
|
|
238
292
|
| Esc | Dismiss suggestion | Normal Esc |
|
|
@@ -241,9 +295,29 @@ $ git pu█sh origin main ← grey ghost text
|
|
|
241
295
|
|
|
242
296
|
### Tab accept mode
|
|
243
297
|
|
|
244
|
-
|
|
298
|
+
Copa supports two Tab behaviors when a suggestion is showing:
|
|
245
299
|
|
|
246
|
-
|
|
300
|
+
**Menu select** (`tab_accept = 2`, default):
|
|
301
|
+
1. Press **Tab** — ghost text clears, a completion menu opens with the Copa suggestion highlighted at the top, alongside native completions below
|
|
302
|
+
2. Press **Tab** or **Space** — accepts the highlighted item
|
|
303
|
+
3. Press **Escape** — cancels the menu, restores your original text
|
|
304
|
+
4. Use **arrow keys** to navigate if you want a different completion
|
|
305
|
+
|
|
306
|
+
This gives you a chance to see alternatives before committing. The Copa suggestion is always the first item in the menu.
|
|
307
|
+
|
|
308
|
+
**Inline accept** (`tab_accept = 1`):
|
|
309
|
+
- Press **Tab** — the suggestion is accepted directly into your command line. One keystroke, done.
|
|
310
|
+
|
|
311
|
+
### Completion menu navigation
|
|
312
|
+
|
|
313
|
+
When the completion menu is open (from Tab in `tab_accept = 2` mode or from normal tab completion):
|
|
314
|
+
|
|
315
|
+
| Key | Action |
|
|
316
|
+
|-----|--------|
|
|
317
|
+
| **Tab** | Accept the highlighted completion |
|
|
318
|
+
| **Space** | Accept the highlighted completion |
|
|
319
|
+
| **Escape** | Cancel — dismiss menu, restore original text |
|
|
320
|
+
| **Arrow keys** | Navigate between completions |
|
|
247
321
|
|
|
248
322
|
### Backspace latch
|
|
249
323
|
|
|
@@ -259,8 +333,11 @@ Press **Tab** to unlatch and re-enable suggestions. The next new prompt (Enter)
|
|
|
259
333
|
enabled = true # set to false to disable inline suggestions
|
|
260
334
|
min_length = 2 # minimum characters before querying (default: 2)
|
|
261
335
|
tab_accept = 2 # 1 = Tab accepts directly, 2 = Tab opens menu first (default)
|
|
336
|
+
color = 242 # ghost text color (256-color palette, default: 242 mid-grey)
|
|
262
337
|
```
|
|
263
338
|
|
|
339
|
+
The `color` value is a [256-color palette](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) number. Some useful values: `240` (darker grey), `242` (default mid-grey), `245` (lighter grey), `8` (bright black), `244` (light grey).
|
|
340
|
+
|
|
264
341
|
Inline suggestions are enabled by default. Set `enabled = false` to disable them entirely (zero performance overhead when disabled).
|
|
265
342
|
|
|
266
343
|
## Quick Start
|
|
@@ -438,10 +515,20 @@ Copa ships with ready-to-use `.copa` files in the [`examples/`](examples/) direc
|
|
|
438
515
|
| [`git.copa`](examples/git.copa) | Essential Git commands |
|
|
439
516
|
| [`docker.copa`](examples/docker.copa) | Docker container management |
|
|
440
517
|
| [`python-dev.copa`](examples/python-dev.copa) | Python development workflow |
|
|
518
|
+
| [`npm.copa`](examples/npm.copa) | Node.js package management |
|
|
441
519
|
| [`network.copa`](examples/network.copa) | Network diagnostics |
|
|
520
|
+
| [`curl-http.copa`](examples/curl-http.copa) | HTTP requests and API testing |
|
|
521
|
+
| [`ssh-remote.copa`](examples/ssh-remote.copa) | SSH, SCP, and remote server management |
|
|
442
522
|
| [`adb.copa`](examples/adb.copa) | Android Debug Bridge |
|
|
523
|
+
| [`aws.copa`](examples/aws.copa) | AWS CLI cloud infrastructure |
|
|
524
|
+
| [`terraform.copa`](examples/terraform.copa) | Terraform infrastructure-as-code |
|
|
443
525
|
| [`k8s.copa`](examples/k8s.copa) | Kubernetes cluster management |
|
|
526
|
+
| [`systemd.copa`](examples/systemd.copa) | Linux service management |
|
|
444
527
|
| [`sysadmin.copa`](examples/sysadmin.copa) | System administration |
|
|
528
|
+
| [`process-monitoring.copa`](examples/process-monitoring.copa) | Process management and debugging |
|
|
529
|
+
| [`disk-files.copa`](examples/disk-files.copa) | Disk usage and file management |
|
|
530
|
+
| [`tmux.copa`](examples/tmux.copa) | Terminal multiplexer sessions |
|
|
531
|
+
| [`homebrew.copa`](examples/homebrew.copa) | Homebrew package manager (macOS) |
|
|
445
532
|
|
|
446
533
|
Load any of them:
|
|
447
534
|
|
|
@@ -560,6 +647,7 @@ group = "ctrl-g" # assign group (inline modal)
|
|
|
560
647
|
flags = "ctrl-f" # edit flags
|
|
561
648
|
filter_group = "ctrl-s" # scope by group (inline modal)
|
|
562
649
|
cycle_group = "ctrl-n" # cycle through groups
|
|
650
|
+
select = "ctrl-b" # enter select mode (bulk operations)
|
|
563
651
|
toggle_header = "ctrl-h" # show/hide key hints
|
|
564
652
|
|
|
565
653
|
# Tab completion behavior
|
|
@@ -572,6 +660,7 @@ branding = true # show "Copa history" group header
|
|
|
572
660
|
enabled = true # set to false to disable
|
|
573
661
|
min_length = 2 # minimum chars before querying
|
|
574
662
|
tab_accept = 2 # 1 = accept directly, 2 = open menu first
|
|
663
|
+
color = 242 # ghost text color (256-color palette)
|
|
575
664
|
|
|
576
665
|
# Composition key behavior (continue vs close)
|
|
577
666
|
# "continue" keys re-open fzf so you can chain another command
|
|
@@ -607,6 +696,7 @@ continue = []
|
|
|
607
696
|
|
|
608
697
|
| Command | Purpose |
|
|
609
698
|
|---------|---------|
|
|
699
|
+
| `copa setup` | Interactive setup wizard |
|
|
610
700
|
| `copa add "cmd" -d "desc" -g group -f "flag: desc"` | Save a command (with optional flags) |
|
|
611
701
|
| `copa edit ID [-d desc] [-g group] [-f flags] [--pin]` | Edit a command's metadata |
|
|
612
702
|
| `copa remove ID` | Remove a command |
|
|
@@ -628,6 +718,7 @@ continue = []
|
|
|
628
718
|
| `copa share list` | List shared sets |
|
|
629
719
|
| `copa share sync DIR` | Sync .copa files from dir |
|
|
630
720
|
| `copa import FILE [-g group]` | Import commands from markdown |
|
|
721
|
+
| `copa reset` | Wipe database and start fresh (keeps config) |
|
|
631
722
|
| `copa uninstall` | Remove Copa data and show cleanup steps |
|
|
632
723
|
|
|
633
724
|
## How Scoring Works
|
|
@@ -16,7 +16,9 @@ Copa tracks the commands you run, ranks them by frequency and recency, and gives
|
|
|
16
16
|
- **LLM descriptions** — `copa fix --auto` uses Claude or ollama to generate descriptions for undescribed commands
|
|
17
17
|
- **Script protocol** — `#@ Description:` / `#@ Usage:` / `#@ Purpose:` / `#@ Flag:` headers in your scripts are auto-detected by `copa scan` across all `$PATH` directories
|
|
18
18
|
- **Flag documentation** — document command flags with descriptions; flags are searchable, visible in the preview pane, and preserved in `.copa` exports
|
|
19
|
+
- **Inline suggestions** — ghost text appears as you type; Tab accepts or opens a completion menu with the suggestion highlighted
|
|
19
20
|
- **Groups & Ctrl+G** — organize commands by project, device, or workflow; assign groups inline from the fzf palette with Ctrl+G
|
|
21
|
+
- **Bulk operations** — Ctrl+B enters select mode for batch group assignment, batch deletion, or batch LLM description
|
|
20
22
|
- **Sharing & `copa create`** — export/import command sets as `.copa` JSON files; `copa create` scaffolds a `.copa` file from an existing group
|
|
21
23
|
- **Set filtering** — scope list, search, and fzf to a specific shared set with `--set`
|
|
22
24
|
- **MCP server** — expose your commands to Claude Code (or any MCP client)
|
|
@@ -55,24 +57,47 @@ pip install -e .
|
|
|
55
57
|
pip install copa-cli[ollama]
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
###
|
|
60
|
+
### Setup
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
Run the interactive setup wizard:
|
|
61
63
|
|
|
62
64
|
```bash
|
|
63
|
-
|
|
65
|
+
copa setup
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
This walks you through everything:
|
|
69
|
+
1. Checks that **fzf** is installed (tells you how to install if missing)
|
|
70
|
+
2. Creates the Copa database (`~/.copa/copa.db`)
|
|
71
|
+
3. Adds shell integration to your `~/.zshrc` (prompts for confirmation)
|
|
72
|
+
4. Optionally imports your zsh history
|
|
73
|
+
|
|
74
|
+
Then activate Copa in your current terminal:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
source ~/.zshrc
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### What shell integration does
|
|
81
|
+
|
|
82
|
+
The `eval "$(copa init zsh)"` line added to your `.zshrc` does three things:
|
|
67
83
|
|
|
68
84
|
1. **Records every command you run** — a `precmd` hook silently calls `copa _record` in the background after each command, building up frequency and recency data with zero latency impact.
|
|
69
85
|
2. **Replaces Ctrl+R** — the default zsh reverse-history-search is replaced with Copa's fzf-powered command palette (see below).
|
|
70
86
|
3. **Supplements tab completion** — Copa registers as a completer so that any command gets completion candidates from your Copa database. The behavior is configurable (`fallback`, `hybrid`, `always`, or `never`) — see [Tab Completion](#tab-completion).
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
### Manual setup
|
|
89
|
+
|
|
90
|
+
If you prefer to set up manually instead of using `copa setup`:
|
|
73
91
|
|
|
74
92
|
```bash
|
|
75
|
-
|
|
93
|
+
# Add shell integration to ~/.zshrc
|
|
94
|
+
echo 'eval "$(copa init zsh)"' >> ~/.zshrc
|
|
95
|
+
|
|
96
|
+
# Activate in current terminal
|
|
97
|
+
source ~/.zshrc
|
|
98
|
+
|
|
99
|
+
# Import your history (optional)
|
|
100
|
+
copa sync
|
|
76
101
|
```
|
|
77
102
|
|
|
78
103
|
## Ctrl+R — fzf Command Palette
|
|
@@ -121,11 +146,40 @@ While the fzf palette is open, these keys are available:
|
|
|
121
146
|
| **Ctrl+N** | Cycle group | Cycles through groups: (all) → group1 → group2 → ... → (all) |
|
|
122
147
|
| **Ctrl+D** | Describe | Generate/edit a description using LLM (with tty-aware input) |
|
|
123
148
|
| **Ctrl+F** | Edit flags | Add flag documentation to the highlighted command |
|
|
149
|
+
| **Ctrl+B** | Select mode | Enter multi-select for bulk operations (see below) |
|
|
124
150
|
| **Ctrl+H** | Toggle header | Show/hide the key hints for more screen space |
|
|
125
151
|
| **ESC** | Cancel/back | In scope/group mode: returns to command list. Otherwise: closes fzf |
|
|
126
152
|
|
|
127
153
|
Keybindings are configurable via `~/.copa/config.toml`. See [Configuration](#configuration).
|
|
128
154
|
|
|
155
|
+
### Select mode (bulk operations)
|
|
156
|
+
|
|
157
|
+
Press **Ctrl+B** from the Ctrl+R palette to enter **select mode**. This opens a new fzf view with multi-select enabled:
|
|
158
|
+
|
|
159
|
+
- **Tab** toggles selection on individual commands
|
|
160
|
+
- **Ctrl+R** cycles modes (all → frequent → recent) just like the main palette
|
|
161
|
+
- **Enter** confirms your selection and shows the batch action menu
|
|
162
|
+
- **ESC** cancels and returns to your prompt
|
|
163
|
+
|
|
164
|
+
After selecting commands, Copa shows a batch action menu:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
Selected 5 command(s).
|
|
168
|
+
g = assign group
|
|
169
|
+
d = delete
|
|
170
|
+
a = auto-describe (LLM)
|
|
171
|
+
q = cancel
|
|
172
|
+
Action:
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
| Action | What it does |
|
|
176
|
+
|--------|-------------|
|
|
177
|
+
| **g** | Assign all selected commands to a group (or clear their group) |
|
|
178
|
+
| **d** | Delete all selected commands (with confirmation) |
|
|
179
|
+
| **a** | Auto-generate descriptions for all selected commands using your configured LLM backend |
|
|
180
|
+
|
|
181
|
+
This is useful for organizing large command sets — select 20 undescribed commands and batch-describe them, or move a set of related commands into a group in one step.
|
|
182
|
+
|
|
129
183
|
### Preview pane
|
|
130
184
|
|
|
131
185
|
The right side shows a detail card for the highlighted command: full description, usage, purpose, flag documentation, score breakdown, frequency, last used, source, group, shared set, and tags.
|
|
@@ -201,8 +255,8 @@ $ git pu█sh origin main ← grey ghost text
|
|
|
201
255
|
|-----|-------------------|---------------|
|
|
202
256
|
| Type chars | Insert char, re-fetch suggestion | Insert char, fetch suggestion |
|
|
203
257
|
| Backspace | Delete char, **latch** (suppress suggestions) | Delete char normally |
|
|
204
|
-
| **Tab** | `tab_accept=1`: accept full suggestion. `tab_accept=2` (default):
|
|
205
|
-
| **Down** |
|
|
258
|
+
| **Tab** | `tab_accept=1`: accept full suggestion. `tab_accept=2` (default): open completion menu with suggestion highlighted at top, native completions below | If latched: unlatch + re-fetch suggestion. Else: normal tab completion |
|
|
259
|
+
| **Down** | Open completion menu with suggestion highlighted at top | History navigation |
|
|
206
260
|
| **Right arrow** | Accept one word, re-fetch | Move cursor right |
|
|
207
261
|
| Enter | Clear suggestion, execute | Execute |
|
|
208
262
|
| Esc | Dismiss suggestion | Normal Esc |
|
|
@@ -211,9 +265,29 @@ $ git pu█sh origin main ← grey ghost text
|
|
|
211
265
|
|
|
212
266
|
### Tab accept mode
|
|
213
267
|
|
|
214
|
-
|
|
268
|
+
Copa supports two Tab behaviors when a suggestion is showing:
|
|
215
269
|
|
|
216
|
-
|
|
270
|
+
**Menu select** (`tab_accept = 2`, default):
|
|
271
|
+
1. Press **Tab** — ghost text clears, a completion menu opens with the Copa suggestion highlighted at the top, alongside native completions below
|
|
272
|
+
2. Press **Tab** or **Space** — accepts the highlighted item
|
|
273
|
+
3. Press **Escape** — cancels the menu, restores your original text
|
|
274
|
+
4. Use **arrow keys** to navigate if you want a different completion
|
|
275
|
+
|
|
276
|
+
This gives you a chance to see alternatives before committing. The Copa suggestion is always the first item in the menu.
|
|
277
|
+
|
|
278
|
+
**Inline accept** (`tab_accept = 1`):
|
|
279
|
+
- Press **Tab** — the suggestion is accepted directly into your command line. One keystroke, done.
|
|
280
|
+
|
|
281
|
+
### Completion menu navigation
|
|
282
|
+
|
|
283
|
+
When the completion menu is open (from Tab in `tab_accept = 2` mode or from normal tab completion):
|
|
284
|
+
|
|
285
|
+
| Key | Action |
|
|
286
|
+
|-----|--------|
|
|
287
|
+
| **Tab** | Accept the highlighted completion |
|
|
288
|
+
| **Space** | Accept the highlighted completion |
|
|
289
|
+
| **Escape** | Cancel — dismiss menu, restore original text |
|
|
290
|
+
| **Arrow keys** | Navigate between completions |
|
|
217
291
|
|
|
218
292
|
### Backspace latch
|
|
219
293
|
|
|
@@ -229,8 +303,11 @@ Press **Tab** to unlatch and re-enable suggestions. The next new prompt (Enter)
|
|
|
229
303
|
enabled = true # set to false to disable inline suggestions
|
|
230
304
|
min_length = 2 # minimum characters before querying (default: 2)
|
|
231
305
|
tab_accept = 2 # 1 = Tab accepts directly, 2 = Tab opens menu first (default)
|
|
306
|
+
color = 242 # ghost text color (256-color palette, default: 242 mid-grey)
|
|
232
307
|
```
|
|
233
308
|
|
|
309
|
+
The `color` value is a [256-color palette](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) number. Some useful values: `240` (darker grey), `242` (default mid-grey), `245` (lighter grey), `8` (bright black), `244` (light grey).
|
|
310
|
+
|
|
234
311
|
Inline suggestions are enabled by default. Set `enabled = false` to disable them entirely (zero performance overhead when disabled).
|
|
235
312
|
|
|
236
313
|
## Quick Start
|
|
@@ -408,10 +485,20 @@ Copa ships with ready-to-use `.copa` files in the [`examples/`](examples/) direc
|
|
|
408
485
|
| [`git.copa`](examples/git.copa) | Essential Git commands |
|
|
409
486
|
| [`docker.copa`](examples/docker.copa) | Docker container management |
|
|
410
487
|
| [`python-dev.copa`](examples/python-dev.copa) | Python development workflow |
|
|
488
|
+
| [`npm.copa`](examples/npm.copa) | Node.js package management |
|
|
411
489
|
| [`network.copa`](examples/network.copa) | Network diagnostics |
|
|
490
|
+
| [`curl-http.copa`](examples/curl-http.copa) | HTTP requests and API testing |
|
|
491
|
+
| [`ssh-remote.copa`](examples/ssh-remote.copa) | SSH, SCP, and remote server management |
|
|
412
492
|
| [`adb.copa`](examples/adb.copa) | Android Debug Bridge |
|
|
493
|
+
| [`aws.copa`](examples/aws.copa) | AWS CLI cloud infrastructure |
|
|
494
|
+
| [`terraform.copa`](examples/terraform.copa) | Terraform infrastructure-as-code |
|
|
413
495
|
| [`k8s.copa`](examples/k8s.copa) | Kubernetes cluster management |
|
|
496
|
+
| [`systemd.copa`](examples/systemd.copa) | Linux service management |
|
|
414
497
|
| [`sysadmin.copa`](examples/sysadmin.copa) | System administration |
|
|
498
|
+
| [`process-monitoring.copa`](examples/process-monitoring.copa) | Process management and debugging |
|
|
499
|
+
| [`disk-files.copa`](examples/disk-files.copa) | Disk usage and file management |
|
|
500
|
+
| [`tmux.copa`](examples/tmux.copa) | Terminal multiplexer sessions |
|
|
501
|
+
| [`homebrew.copa`](examples/homebrew.copa) | Homebrew package manager (macOS) |
|
|
415
502
|
|
|
416
503
|
Load any of them:
|
|
417
504
|
|
|
@@ -530,6 +617,7 @@ group = "ctrl-g" # assign group (inline modal)
|
|
|
530
617
|
flags = "ctrl-f" # edit flags
|
|
531
618
|
filter_group = "ctrl-s" # scope by group (inline modal)
|
|
532
619
|
cycle_group = "ctrl-n" # cycle through groups
|
|
620
|
+
select = "ctrl-b" # enter select mode (bulk operations)
|
|
533
621
|
toggle_header = "ctrl-h" # show/hide key hints
|
|
534
622
|
|
|
535
623
|
# Tab completion behavior
|
|
@@ -542,6 +630,7 @@ branding = true # show "Copa history" group header
|
|
|
542
630
|
enabled = true # set to false to disable
|
|
543
631
|
min_length = 2 # minimum chars before querying
|
|
544
632
|
tab_accept = 2 # 1 = accept directly, 2 = open menu first
|
|
633
|
+
color = 242 # ghost text color (256-color palette)
|
|
545
634
|
|
|
546
635
|
# Composition key behavior (continue vs close)
|
|
547
636
|
# "continue" keys re-open fzf so you can chain another command
|
|
@@ -577,6 +666,7 @@ continue = []
|
|
|
577
666
|
|
|
578
667
|
| Command | Purpose |
|
|
579
668
|
|---------|---------|
|
|
669
|
+
| `copa setup` | Interactive setup wizard |
|
|
580
670
|
| `copa add "cmd" -d "desc" -g group -f "flag: desc"` | Save a command (with optional flags) |
|
|
581
671
|
| `copa edit ID [-d desc] [-g group] [-f flags] [--pin]` | Edit a command's metadata |
|
|
582
672
|
| `copa remove ID` | Remove a command |
|
|
@@ -598,6 +688,7 @@ continue = []
|
|
|
598
688
|
| `copa share list` | List shared sets |
|
|
599
689
|
| `copa share sync DIR` | Sync .copa files from dir |
|
|
600
690
|
| `copa import FILE [-g group]` | Import commands from markdown |
|
|
691
|
+
| `copa reset` | Wipe database and start fresh (keeps config) |
|
|
601
692
|
| `copa uninstall` | Remove Copa data and show cleanup steps |
|
|
602
693
|
|
|
603
694
|
## How Scoring Works
|
|
@@ -40,11 +40,30 @@ def _cmd_to_json(cmd: Command) -> dict:
|
|
|
40
40
|
# --- Main group ---
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
@click.group()
|
|
43
|
+
@click.group(invoke_without_command=True)
|
|
44
44
|
@click.version_option(package_name="copa-cli")
|
|
45
|
-
|
|
45
|
+
@click.pass_context
|
|
46
|
+
def cli(ctx):
|
|
46
47
|
"""Copa — Command Palette. Smart command tracking, ranking, and sharing."""
|
|
47
|
-
|
|
48
|
+
if ctx.invoked_subcommand is None:
|
|
49
|
+
# Show help, then a setup hint if needed
|
|
50
|
+
click.echo(ctx.get_help())
|
|
51
|
+
_maybe_show_setup_hint()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _maybe_show_setup_hint():
|
|
55
|
+
"""Show a setup hint if Copa is not fully configured."""
|
|
56
|
+
db_path = Path.home() / ".copa" / "copa.db"
|
|
57
|
+
zshrc = Path.home() / ".zshrc"
|
|
58
|
+
has_db = db_path.is_file()
|
|
59
|
+
has_shell = zshrc.is_file() and "copa init zsh" in zshrc.read_text()
|
|
60
|
+
has_fzf = shutil.which("fzf") is not None
|
|
61
|
+
|
|
62
|
+
if not has_db or not has_shell or not has_fzf:
|
|
63
|
+
click.echo()
|
|
64
|
+
hint = click.style(" Tip: ", fg="cyan", bold=True)
|
|
65
|
+
hint += "run " + click.style("copa setup", bold=True) + " to get started"
|
|
66
|
+
click.echo(hint)
|
|
48
67
|
|
|
49
68
|
|
|
50
69
|
# --- init ---
|
|
@@ -60,6 +79,173 @@ def init(shell: str):
|
|
|
60
79
|
click.echo(zsh_file.read_text())
|
|
61
80
|
|
|
62
81
|
|
|
82
|
+
# --- setup ---
|
|
83
|
+
|
|
84
|
+
_SHELL_INTEGRATION_LINE = 'eval "$(copa init zsh)"'
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _write_config_tab_accept(tab_accept: int) -> None:
|
|
88
|
+
"""Write or update tab_accept in ~/.copa/config.toml."""
|
|
89
|
+
config_path = Path.home() / ".copa" / "config.toml"
|
|
90
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
if config_path.is_file():
|
|
93
|
+
content = config_path.read_text()
|
|
94
|
+
else:
|
|
95
|
+
content = ""
|
|
96
|
+
|
|
97
|
+
import re
|
|
98
|
+
|
|
99
|
+
if re.search(r"^\[suggest\]", content, re.MULTILINE):
|
|
100
|
+
# [suggest] section exists — update or add tab_accept
|
|
101
|
+
if re.search(r"^tab_accept\s*=", content, re.MULTILINE):
|
|
102
|
+
content = re.sub(
|
|
103
|
+
r"^tab_accept\s*=\s*\d+",
|
|
104
|
+
f"tab_accept = {tab_accept}",
|
|
105
|
+
content,
|
|
106
|
+
flags=re.MULTILINE,
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
content = re.sub(
|
|
110
|
+
r"(\[suggest\]\n)",
|
|
111
|
+
f"\\1tab_accept = {tab_accept}\n",
|
|
112
|
+
content,
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
# No [suggest] section — append it
|
|
116
|
+
if content and not content.endswith("\n"):
|
|
117
|
+
content += "\n"
|
|
118
|
+
content += f"\n[suggest]\ntab_accept = {tab_accept}\n"
|
|
119
|
+
|
|
120
|
+
config_path.write_text(content)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@cli.command()
|
|
124
|
+
def setup():
|
|
125
|
+
"""Interactive setup wizard — checks prerequisites and configures Copa."""
|
|
126
|
+
ok = click.style("OK", fg="green")
|
|
127
|
+
fixed = click.style("FIXED", fg="green")
|
|
128
|
+
skip = click.style("SKIP", fg="yellow")
|
|
129
|
+
fail = click.style("!!", fg="yellow")
|
|
130
|
+
|
|
131
|
+
click.echo(click.style("Copa Setup", bold=True))
|
|
132
|
+
click.echo()
|
|
133
|
+
|
|
134
|
+
# 1. Check fzf
|
|
135
|
+
if shutil.which("fzf"):
|
|
136
|
+
click.echo(f" [{ok}] fzf is installed")
|
|
137
|
+
else:
|
|
138
|
+
click.echo(f" [{fail}] fzf is not installed")
|
|
139
|
+
click.echo(" Copa's Ctrl+R palette requires fzf.")
|
|
140
|
+
click.echo(" Install: " + click.style("brew install fzf", bold=True) + " (macOS)")
|
|
141
|
+
click.echo(" " + click.style("sudo apt install fzf", bold=True) + " (Linux)")
|
|
142
|
+
click.echo()
|
|
143
|
+
|
|
144
|
+
# 2. Initialize database
|
|
145
|
+
db_path = Path.home() / ".copa" / "copa.db"
|
|
146
|
+
if db_path.is_file():
|
|
147
|
+
click.echo(f" [{ok}] Database exists ({db_path})")
|
|
148
|
+
else:
|
|
149
|
+
click.echo(f" [{fixed}] Database created ({db_path})")
|
|
150
|
+
# Always call get_db to ensure db + tables exist
|
|
151
|
+
from .cli_common import get_db
|
|
152
|
+
|
|
153
|
+
get_db()
|
|
154
|
+
|
|
155
|
+
# 3. Shell integration
|
|
156
|
+
zshrc = Path.home() / ".zshrc"
|
|
157
|
+
if zshrc.is_file() and _SHELL_INTEGRATION_LINE in zshrc.read_text():
|
|
158
|
+
click.echo(f" [{ok}] Shell integration in ~/.zshrc")
|
|
159
|
+
else:
|
|
160
|
+
click.echo(f" [{fail}] Shell integration not found in ~/.zshrc")
|
|
161
|
+
if click.confirm(" Add it now?", default=True):
|
|
162
|
+
with open(zshrc, "a") as f:
|
|
163
|
+
f.write(f"\n# Copa — Command Palette\n{_SHELL_INTEGRATION_LINE}\n")
|
|
164
|
+
click.echo(f" [{fixed}] Added to ~/.zshrc")
|
|
165
|
+
else:
|
|
166
|
+
click.echo(f" [{skip}] Skipped — add manually:")
|
|
167
|
+
click.echo(f" {_SHELL_INTEGRATION_LINE}")
|
|
168
|
+
|
|
169
|
+
# 4. Tab completion style
|
|
170
|
+
click.echo()
|
|
171
|
+
click.echo(" Copa shows inline suggestions as you type (ghost text).")
|
|
172
|
+
click.echo(" How should " + click.style("Tab", bold=True) + " handle suggestions?")
|
|
173
|
+
click.echo()
|
|
174
|
+
click.echo(" " + click.style("1", bold=True) + " " + click.style("Inline accept", fg="cyan"))
|
|
175
|
+
click.echo(" Tab accepts the suggestion directly into your command line.")
|
|
176
|
+
click.echo()
|
|
177
|
+
opt2 = click.style("Menu select", fg="cyan") + click.style(" (default)", dim=True)
|
|
178
|
+
click.echo(" " + click.style("2", bold=True) + " " + opt2)
|
|
179
|
+
click.echo(" Tab opens a completion menu with the suggestion highlighted.")
|
|
180
|
+
click.echo(" Tab again accepts it. See alternatives before committing.")
|
|
181
|
+
click.echo()
|
|
182
|
+
tab_choice = click.prompt(
|
|
183
|
+
" Choose tab style",
|
|
184
|
+
type=click.Choice(["1", "2"]),
|
|
185
|
+
default="2",
|
|
186
|
+
show_choices=False,
|
|
187
|
+
)
|
|
188
|
+
tab_accept = int(tab_choice)
|
|
189
|
+
_write_config_tab_accept(tab_accept)
|
|
190
|
+
label = "Inline accept" if tab_accept == 1 else "Menu select"
|
|
191
|
+
click.echo(f" [{fixed}] Tab style: {label}")
|
|
192
|
+
|
|
193
|
+
# 5. Sync history
|
|
194
|
+
click.echo()
|
|
195
|
+
if click.confirm(" Import commands from your zsh history?", default=True):
|
|
196
|
+
from .history import sync_history
|
|
197
|
+
|
|
198
|
+
db = get_db()
|
|
199
|
+
added = sync_history(db)
|
|
200
|
+
click.echo(f" [{fixed}] Synced {added} commands from history")
|
|
201
|
+
else:
|
|
202
|
+
click.echo(f" [{skip}] Skipped — run " + click.style("copa sync", bold=True) + " later")
|
|
203
|
+
|
|
204
|
+
# 6. Done
|
|
205
|
+
click.echo()
|
|
206
|
+
click.echo(click.style(" Setup complete!", fg="green", bold=True))
|
|
207
|
+
click.echo()
|
|
208
|
+
click.echo(" Next steps:")
|
|
209
|
+
click.echo(" " + click.style("source ~/.zshrc", bold=True) + " Activate Copa in this terminal")
|
|
210
|
+
click.echo(" " + click.style("copa doctor", bold=True) + " Verify everything is working")
|
|
211
|
+
click.echo(" Press " + click.style("Ctrl+R", bold=True) + " Open the command palette")
|
|
212
|
+
click.echo(" Type a command + " + click.style("Tab", bold=True) + " See inline suggestions")
|
|
213
|
+
click.echo()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# --- reset ---
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@cli.command()
|
|
220
|
+
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation prompt.")
|
|
221
|
+
def reset(yes: bool):
|
|
222
|
+
"""Wipe the command database and start fresh. Keeps your config."""
|
|
223
|
+
db_path = Path.home() / ".copa" / "copa.db"
|
|
224
|
+
|
|
225
|
+
if not db_path.is_file():
|
|
226
|
+
click.echo("No database found — nothing to reset.")
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
from .cli_common import get_db
|
|
230
|
+
|
|
231
|
+
db = get_db()
|
|
232
|
+
stats = db.get_stats()
|
|
233
|
+
click.echo(f"Database: {db_path}")
|
|
234
|
+
cmds, grps, sets = stats["total_commands"], stats["total_groups"], stats["shared_sets"]
|
|
235
|
+
click.echo(f" {cmds} commands, {grps} groups, {sets} shared sets")
|
|
236
|
+
click.echo()
|
|
237
|
+
|
|
238
|
+
if not yes:
|
|
239
|
+
click.confirm(click.style("Delete all commands and start fresh?", fg="red"), abort=True)
|
|
240
|
+
|
|
241
|
+
db.conn.close()
|
|
242
|
+
db_path.unlink()
|
|
243
|
+
# Re-create empty database
|
|
244
|
+
get_db()
|
|
245
|
+
click.echo(click.style("Database reset.", fg="green") + " Config preserved.")
|
|
246
|
+
click.echo("Run " + click.style("copa sync", bold=True) + " to re-import your shell history.")
|
|
247
|
+
|
|
248
|
+
|
|
63
249
|
# --- uninstall ---
|
|
64
250
|
|
|
65
251
|
|
|
@@ -89,6 +89,7 @@ def load_config(path: Path | None = None) -> dict:
|
|
|
89
89
|
config["_suggest_enabled"] = True # default: inline suggestions on
|
|
90
90
|
config["_suggest_min_length"] = 2 # minimum chars before querying
|
|
91
91
|
config["_suggest_tab_accept"] = 2 # 1=direct accept, 2=open menu first
|
|
92
|
+
config["_suggest_color"] = "242" # ghost text color (256-color palette)
|
|
92
93
|
|
|
93
94
|
if path is None:
|
|
94
95
|
path = Path.home() / ".copa" / "config.toml"
|
|
@@ -129,10 +130,7 @@ def load_config(path: Path | None = None) -> dict:
|
|
|
129
130
|
continue_list = composition_section.get("continue")
|
|
130
131
|
if isinstance(continue_list, list):
|
|
131
132
|
# Only keep valid composition action names (those that have suffixes)
|
|
132
|
-
config["_continue_actions"] = {
|
|
133
|
-
name for name in continue_list
|
|
134
|
-
if isinstance(name, str) and name in SUFFIXES
|
|
135
|
-
}
|
|
133
|
+
config["_continue_actions"] = {name for name in continue_list if isinstance(name, str) and name in SUFFIXES}
|
|
136
134
|
|
|
137
135
|
# [suggest] section — inline suggestion settings
|
|
138
136
|
suggest_section = data.get("suggest")
|
|
@@ -146,6 +144,9 @@ def load_config(path: Path | None = None) -> dict:
|
|
|
146
144
|
tab_accept = suggest_section.get("tab_accept")
|
|
147
145
|
if isinstance(tab_accept, int) and tab_accept in (1, 2):
|
|
148
146
|
config["_suggest_tab_accept"] = tab_accept
|
|
147
|
+
color = suggest_section.get("color")
|
|
148
|
+
if isinstance(color, (int, str)):
|
|
149
|
+
config["_suggest_color"] = str(color)
|
|
149
150
|
|
|
150
151
|
keys_section = data.get("keys")
|
|
151
152
|
if not isinstance(keys_section, dict):
|
|
@@ -249,6 +250,7 @@ def emit_zsh_config(config: dict[str, str]) -> str:
|
|
|
249
250
|
lines.append(f"_COPA_SUGGEST_ENABLED='{'true' if suggest_enabled else 'false'}'")
|
|
250
251
|
lines.append(f"_COPA_SUGGEST_MIN_LENGTH='{config.get('_suggest_min_length', 2)}'")
|
|
251
252
|
lines.append(f"_COPA_SUGGEST_TAB_ACCEPT='{config.get('_suggest_tab_accept', 2)}'")
|
|
253
|
+
lines.append(f"_COPA_SUGGEST_COLOR='{config.get('_suggest_color', '242')}'")
|
|
252
254
|
|
|
253
255
|
# Split suffixes into close (fzf exits) and continue (fzf re-opens)
|
|
254
256
|
lines.append("typeset -gA _COPA_CLOSE_SUFFIXES")
|