comma-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,27 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: install
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install -e '.[dev]'
24
+ - name: lint
25
+ run: ruff check .
26
+ - name: test
27
+ run: pytest -q
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ venv/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .uv/
11
+ *.swp
12
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.5
2
+ Name: comma-command
3
+ Version: 0.1.0
4
+ Summary: Run any configured command from any shell as `name, args` — register once, invoke everywhere.
5
+ Project-URL: Homepage, https://github.com/by3hak/comma-command
6
+ Project-URL: Repository, https://github.com/by3hak/comma-command
7
+ Project-URL: Issues, https://github.com/by3hak/comma-command/issues
8
+ Author-email: hak <by3hak@users.noreply.github.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: alias,cli,dispatch,multicall,shell,terminal
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS :: MacOS X
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: System :: Shells
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.6; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # comma-command
33
+
34
+ Run any command you have installed from any shell, as if it were a shell builtin:
35
+
36
+ ```console
37
+ $ gus, hello world
38
+ # → too talk gus -- hello world
39
+ ```
40
+
41
+ Register an entry once. From then on, `NAME, any words` is the whole interface — no
42
+ aliases to source, no shell hook, no TUI.
43
+
44
+ ## Install
45
+
46
+ ```console
47
+ $ uv tool install comma-command # or: pipx install comma-command
48
+ $ comma init --write # puts the shim directory on PATH
49
+ $ exec $SHELL # or open a new terminal
50
+ ```
51
+
52
+ ## Quickstart
53
+
54
+ ```console
55
+ $ comma add gus -- too talk gus --
56
+ added gus, → too talk gus --
57
+
58
+ $ comma add pyi -- uv run ipython --
59
+ added pyi, → uv run ipython --
60
+
61
+ $ gus, hello world # runs: too talk gus -- hello world
62
+ $ pyi, --pdb script.py # runs: uv run ipython -- --pdb script.py
63
+ $ comma ls
64
+ gus, too talk gus --
65
+ pyi, uv run ipython --
66
+ ```
67
+
68
+ `comma add` does two things: writes the entry to `~/.config/comma/config.toml` and
69
+ creates an executable named exactly `gus,` in `~/.local/share/comma/bin`.
70
+
71
+ ## How it works
72
+
73
+ The shim is a multicall dispatcher — the same trick `busybox` and `git` use for
74
+ subcommands, applied to names:
75
+
76
+ 1. `gus,` is a symlink to the `comma` executable.
77
+ 2. `comma` looks at `argv[0]`, sees a trailing comma, strips it, and finds `gus` in
78
+ the config.
79
+ 3. It calls `os.execvpe("too", ["too", "talk", "gus", "--", "hello", "world"])`.
80
+
81
+ Because the final step *replaces* the process instead of spawning a child, the
82
+ target command inherits the terminal, stdin, signals and job control directly, and
83
+ its exit status reaches your shell unchanged. `comma,` adds no wrapping layer.
84
+
85
+ Why not the alternatives:
86
+
87
+ - **Shell aliases/functions.** `gus,` is not a portable function name in bash or
88
+ fish, and functions only exist in interactive shells you have sourced.
89
+ - **`command_not_found_handle` hooks.** Bash-only, collides with distro handlers
90
+ (Nix, Debian's package suggestion hook), and silently does nothing in
91
+ non-interactive shells, `sh -c`, cron, or most editors' task runners.
92
+ - **Shims on `PATH`.** Work everywhere a real executable works, including
93
+ `xargs`, `find -exec`, CI scripts, Makefiles and shell completion.
94
+
95
+ ## Configuration
96
+
97
+ `~/.config/comma/config.toml` — managed by `comma`, but hand-editable:
98
+
99
+ ```toml
100
+ [commands.gus]
101
+ cmd = ["too", "talk", "gus", "--"]
102
+
103
+ # shorthand is fine too
104
+ [commands.pyi]
105
+ cmd = "uv run ipython --"
106
+ ```
107
+
108
+ Arguments are appended verbatim, so `--` at the end of `cmd` is the usual way to
109
+ keep flags from being swallowed by the target's own option parser.
110
+
111
+ ## Commands
112
+
113
+ | Command | What it does |
114
+ | --- | --- |
115
+ | `comma add NAME -- CMD...` | register a name and create its `NAME,` shim |
116
+ | `comma rm NAME` | unregister a name and remove its shim |
117
+ | `comma ls [--json]` | list registered names |
118
+ | `comma run NAME [--] [ARG...]` | run a name without going through the shim |
119
+ | `comma which NAME` | show the shim path and the command it runs |
120
+ | `comma init [--write] [--shell S]` | print or install PATH + completion setup |
121
+ | `comma doctor` | check config, PATH, shims and targets |
122
+ | `comma completion bash\|zsh\|fish` | print a completion script |
123
+
124
+ Names must be shell-safe words: letters, digits, `.`, `_`, `+`, `-`. The comma is
125
+ added for you, so `comma add gus, -- ...` is rejected rather than silently creating
126
+ `gus,,`.
127
+
128
+ ## Limits
129
+
130
+ - POSIX first. Windows needs a `.cmd` shim layer, which is not in 0.1.
131
+ - No per-command `env`/`cwd` yet; use `cmd = ["sh", "-c", "..."]` as an escape hatch.
132
+ - The `comma-command` crate on crates.io is a name reservation for a future native
133
+ binary; the Python implementation is the one to install today.
134
+
135
+ ## Development
136
+
137
+ ```console
138
+ $ uv venv && uv pip install -e '.[dev]'
139
+ $ uv run pytest
140
+ $ uv run ruff check .
141
+ ```
142
+
143
+ MIT licensed.
@@ -0,0 +1,112 @@
1
+ # comma-command
2
+
3
+ Run any command you have installed from any shell, as if it were a shell builtin:
4
+
5
+ ```console
6
+ $ gus, hello world
7
+ # → too talk gus -- hello world
8
+ ```
9
+
10
+ Register an entry once. From then on, `NAME, any words` is the whole interface — no
11
+ aliases to source, no shell hook, no TUI.
12
+
13
+ ## Install
14
+
15
+ ```console
16
+ $ uv tool install comma-command # or: pipx install comma-command
17
+ $ comma init --write # puts the shim directory on PATH
18
+ $ exec $SHELL # or open a new terminal
19
+ ```
20
+
21
+ ## Quickstart
22
+
23
+ ```console
24
+ $ comma add gus -- too talk gus --
25
+ added gus, → too talk gus --
26
+
27
+ $ comma add pyi -- uv run ipython --
28
+ added pyi, → uv run ipython --
29
+
30
+ $ gus, hello world # runs: too talk gus -- hello world
31
+ $ pyi, --pdb script.py # runs: uv run ipython -- --pdb script.py
32
+ $ comma ls
33
+ gus, too talk gus --
34
+ pyi, uv run ipython --
35
+ ```
36
+
37
+ `comma add` does two things: writes the entry to `~/.config/comma/config.toml` and
38
+ creates an executable named exactly `gus,` in `~/.local/share/comma/bin`.
39
+
40
+ ## How it works
41
+
42
+ The shim is a multicall dispatcher — the same trick `busybox` and `git` use for
43
+ subcommands, applied to names:
44
+
45
+ 1. `gus,` is a symlink to the `comma` executable.
46
+ 2. `comma` looks at `argv[0]`, sees a trailing comma, strips it, and finds `gus` in
47
+ the config.
48
+ 3. It calls `os.execvpe("too", ["too", "talk", "gus", "--", "hello", "world"])`.
49
+
50
+ Because the final step *replaces* the process instead of spawning a child, the
51
+ target command inherits the terminal, stdin, signals and job control directly, and
52
+ its exit status reaches your shell unchanged. `comma,` adds no wrapping layer.
53
+
54
+ Why not the alternatives:
55
+
56
+ - **Shell aliases/functions.** `gus,` is not a portable function name in bash or
57
+ fish, and functions only exist in interactive shells you have sourced.
58
+ - **`command_not_found_handle` hooks.** Bash-only, collides with distro handlers
59
+ (Nix, Debian's package suggestion hook), and silently does nothing in
60
+ non-interactive shells, `sh -c`, cron, or most editors' task runners.
61
+ - **Shims on `PATH`.** Work everywhere a real executable works, including
62
+ `xargs`, `find -exec`, CI scripts, Makefiles and shell completion.
63
+
64
+ ## Configuration
65
+
66
+ `~/.config/comma/config.toml` — managed by `comma`, but hand-editable:
67
+
68
+ ```toml
69
+ [commands.gus]
70
+ cmd = ["too", "talk", "gus", "--"]
71
+
72
+ # shorthand is fine too
73
+ [commands.pyi]
74
+ cmd = "uv run ipython --"
75
+ ```
76
+
77
+ Arguments are appended verbatim, so `--` at the end of `cmd` is the usual way to
78
+ keep flags from being swallowed by the target's own option parser.
79
+
80
+ ## Commands
81
+
82
+ | Command | What it does |
83
+ | --- | --- |
84
+ | `comma add NAME -- CMD...` | register a name and create its `NAME,` shim |
85
+ | `comma rm NAME` | unregister a name and remove its shim |
86
+ | `comma ls [--json]` | list registered names |
87
+ | `comma run NAME [--] [ARG...]` | run a name without going through the shim |
88
+ | `comma which NAME` | show the shim path and the command it runs |
89
+ | `comma init [--write] [--shell S]` | print or install PATH + completion setup |
90
+ | `comma doctor` | check config, PATH, shims and targets |
91
+ | `comma completion bash\|zsh\|fish` | print a completion script |
92
+
93
+ Names must be shell-safe words: letters, digits, `.`, `_`, `+`, `-`. The comma is
94
+ added for you, so `comma add gus, -- ...` is rejected rather than silently creating
95
+ `gus,,`.
96
+
97
+ ## Limits
98
+
99
+ - POSIX first. Windows needs a `.cmd` shim layer, which is not in 0.1.
100
+ - No per-command `env`/`cwd` yet; use `cmd = ["sh", "-c", "..."]` as an escape hatch.
101
+ - The `comma-command` crate on crates.io is a name reservation for a future native
102
+ binary; the Python implementation is the one to install today.
103
+
104
+ ## Development
105
+
106
+ ```console
107
+ $ uv venv && uv pip install -e '.[dev]'
108
+ $ uv run pytest
109
+ $ uv run ruff check .
110
+ ```
111
+
112
+ MIT licensed.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "comma-command"
7
+ version = "0.1.0"
8
+ description = "Run any configured command from any shell as `name, args` — register once, invoke everywhere."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "hak", email = "by3hak@users.noreply.github.com" }]
13
+ keywords = ["cli", "shell", "alias", "dispatch", "multicall", "terminal"]
14
+ dependencies = ['tomli>=2.0; python_version < "3.11"']
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: MacOS :: MacOS X",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: System :: Shells",
28
+ "Topic :: Utilities",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/by3hak/comma-command"
33
+ Repository = "https://github.com/by3hak/comma-command"
34
+ Issues = "https://github.com/by3hak/comma-command/issues"
35
+
36
+ [project.scripts]
37
+ comma = "comma_command.cli:main"
38
+
39
+ [project.optional-dependencies]
40
+ dev = ["pytest>=8.0", "ruff>=0.6"]
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/comma_command"]
44
+
45
+ [tool.ruff]
46
+ line-length = 100
47
+ src = ["src", "tests"]
48
+ target-version = "py310"
49
+
50
+ [tool.ruff.lint]
51
+ select = ["E", "F", "I", "UP", "B"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ """comma-command — run any configured command as ``name, args``."""
2
+
3
+ __version__ = "0.1.0"
4
+ __all__ = ["__version__"]
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())