nlsh-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.9", "3.11", "3.12", "3.13"]
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0 # hatch-vcs derives the version from git history/tags
23
+
24
+ - name: Set up uv
25
+ uses: astral-sh/setup-uv@v6
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Install (locked)
30
+ run: uv sync --locked
31
+
32
+ - name: Lint
33
+ run: uv run ruff check .
34
+
35
+ - name: Test
36
+ run: uv run pytest -q
37
+
38
+ lock:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ with:
43
+ fetch-depth: 0
44
+ - uses: astral-sh/setup-uv@v6
45
+ - name: Verify lockfile is up to date
46
+ run: uv lock --check
@@ -0,0 +1,39 @@
1
+ name: Publish
2
+
3
+ # Push a tag like v0.1.0 to build and publish that version to PyPI.
4
+ # The version is derived from the tag by hatch-vcs — no manual version bump.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0 # full history so hatch-vcs sees the tag
16
+ - uses: astral-sh/setup-uv@v6
17
+ - name: Build sdist and wheel
18
+ run: uv build
19
+ - uses: actions/upload-artifact@v4
20
+ with:
21
+ name: dist
22
+ path: dist/
23
+
24
+ publish:
25
+ needs: build
26
+ runs-on: ubuntu-latest
27
+ # Configure a "pypi" environment on the repo and register this workflow as a
28
+ # PyPI Trusted Publisher — no API token needed.
29
+ environment: pypi
30
+ permissions:
31
+ id-token: write
32
+ steps:
33
+ - uses: actions/download-artifact@v4
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+ - uses: astral-sh/setup-uv@v6
38
+ - name: Publish to PyPI (trusted publishing)
39
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,26 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Test / tooling caches
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+
12
+ # Version file generated by hatch-vcs at build time
13
+ nlsh/_version.py
14
+
15
+ # Virtual env (recreate with `uv sync`)
16
+ .venv/
17
+
18
+ # uv's managed Python downloads (not committed)
19
+ .uv/
20
+
21
+ # Local config / secrets
22
+ .env
23
+ config.toml
24
+
25
+ # OS
26
+ .DS_Store
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: nlsh-cli
3
+ Version: 0.1.0
4
+ Summary: Turn natural language into shell commands via the DeepSeek API, with a confirm-before-run step.
5
+ Project-URL: Homepage, https://github.com/decajoin/nlsh
6
+ Project-URL: Repository, https://github.com/decajoin/nlsh
7
+ Author: Yiqi Yang
8
+ License: MIT
9
+ Keywords: cli,deepseek,llm,natural-language,shell,terminal
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Utilities
15
+ Requires-Python: >=3.9
16
+ Requires-Dist: httpx>=0.27
17
+ Requires-Dist: rich>=13
18
+ Requires-Dist: typer>=0.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # nlsh
22
+
23
+ Describe what you want to do in plain language; `nlsh` asks the DeepSeek API for
24
+ the matching shell command, shows it with a short explanation, and waits for your
25
+ confirmation before running it.
26
+
27
+ ```
28
+ $ nlsh find the 5 largest files under this directory
29
+ ╭─ proposed command ───────────────────────────────────────╮
30
+ │ $ du -ah . | sort -rh | head -n 5 │
31
+ │ │
32
+ │ Lists files by size and shows the five largest. │
33
+ ╰───────────────────────────────────────────────────────────╯
34
+ Run it? [y/n/e] (n):
35
+ ```
36
+
37
+ `y` runs it, `n` cancels, `e` opens the command in your `$EDITOR` first.
38
+
39
+ ## Install
40
+
41
+ Once published to PyPI, install it as a standalone tool (the package is
42
+ `nlsh-cli`; the command it installs is `nlsh`):
43
+
44
+ ```sh
45
+ uv tool install nlsh-cli # or: pipx install nlsh-cli
46
+ ```
47
+
48
+ ### From source
49
+
50
+ One command sets up the virtual environment and all dependencies from the
51
+ lockfile:
52
+
53
+ ```sh
54
+ uv sync
55
+ ```
56
+
57
+ This creates `.venv/` and installs `nlsh`. Run it with `uv run nlsh ...`, or
58
+ activate the env first (`source .venv/bin/activate`) and just call `nlsh`.
59
+
60
+ Plain pip (no uv) works too:
61
+
62
+ ```sh
63
+ python -m venv .venv && . .venv/bin/activate
64
+ pip install -r requirements.txt
65
+ pip install -e .
66
+ ```
67
+
68
+ ## Configure
69
+
70
+ Easiest — store the key interactively (written to `~/.config/nlsh/config.toml`,
71
+ mode 0600):
72
+
73
+ ```sh
74
+ nlsh config set-key
75
+ nlsh config set-model deepseek-v4-pro # optional, change default model
76
+ nlsh config show # show resolved config (key masked)
77
+ ```
78
+
79
+ Or via environment variable:
80
+
81
+ ```sh
82
+ export DEEPSEEK_API_KEY=sk-...
83
+ ```
84
+
85
+ or edit `~/.config/nlsh/config.toml` directly:
86
+
87
+ ```toml
88
+ api_key = "sk-..."
89
+ model = "deepseek-v4-flash" # optional (also: deepseek-v4-pro)
90
+ base_url = "https://api.deepseek.com" # optional
91
+ ```
92
+
93
+ Resolution order for each setting is: environment variable → config file → default.
94
+
95
+ ## Usage
96
+
97
+ ```sh
98
+ nlsh <natural language request>
99
+
100
+ -y, --yes run without confirmation (dangerous commands still prompt)
101
+ -n, --dry-run only print the command, never run it
102
+ --pro use the stronger model (deepseek-v4-pro) for this request
103
+ -m, --model ID override the model id for this request
104
+ --version show version
105
+ ```
106
+
107
+ At the confirmation prompt: `y` runs, `n` cancels, `e` edits first. Commands
108
+ that match risky patterns (`rm -rf`, `dd of=/dev/…`, `mkfs`, fork bombs,
109
+ `curl … | sh`, `sudo`, …) are flagged in red and require typing the full word
110
+ `yes` before they run — even under `--yes`. Multi-stage commands (pipes, `&&`)
111
+ are shown with a per-step breakdown.
112
+
113
+ ## Development
114
+
115
+ ```sh
116
+ uv sync # installs deps + dev tools (pytest, ruff)
117
+ uv run pytest -q # run the test suite
118
+ uv run ruff check . # lint
119
+ ```
120
+
121
+ CI (GitHub Actions) runs ruff + pytest across Python 3.9–3.13 and checks the
122
+ lockfile on every push and PR.
123
+
124
+ ### Releasing
125
+
126
+ The version is derived from the git tag by `hatch-vcs`, so a release is just a
127
+ tag:
128
+
129
+ ```sh
130
+ git tag v0.1.0
131
+ git push origin v0.1.0
132
+ ```
133
+
134
+ The `Publish` workflow builds the sdist/wheel and uploads them to PyPI via
135
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (configure a
136
+ `pypi` environment on the repo and register the workflow as a trusted publisher
137
+ — no API token stored).
138
+
139
+ ## How it works
140
+
141
+ `nlsh` sends your request plus context (OS, shell, current directory) to the
142
+ DeepSeek chat API constrained to JSON output, parses `{command, explanation}`,
143
+ and runs the chosen command through your `$SHELL` so aliases and the working
144
+ directory behave as expected.
@@ -0,0 +1,124 @@
1
+ # nlsh
2
+
3
+ Describe what you want to do in plain language; `nlsh` asks the DeepSeek API for
4
+ the matching shell command, shows it with a short explanation, and waits for your
5
+ confirmation before running it.
6
+
7
+ ```
8
+ $ nlsh find the 5 largest files under this directory
9
+ ╭─ proposed command ───────────────────────────────────────╮
10
+ │ $ du -ah . | sort -rh | head -n 5 │
11
+ │ │
12
+ │ Lists files by size and shows the five largest. │
13
+ ╰───────────────────────────────────────────────────────────╯
14
+ Run it? [y/n/e] (n):
15
+ ```
16
+
17
+ `y` runs it, `n` cancels, `e` opens the command in your `$EDITOR` first.
18
+
19
+ ## Install
20
+
21
+ Once published to PyPI, install it as a standalone tool (the package is
22
+ `nlsh-cli`; the command it installs is `nlsh`):
23
+
24
+ ```sh
25
+ uv tool install nlsh-cli # or: pipx install nlsh-cli
26
+ ```
27
+
28
+ ### From source
29
+
30
+ One command sets up the virtual environment and all dependencies from the
31
+ lockfile:
32
+
33
+ ```sh
34
+ uv sync
35
+ ```
36
+
37
+ This creates `.venv/` and installs `nlsh`. Run it with `uv run nlsh ...`, or
38
+ activate the env first (`source .venv/bin/activate`) and just call `nlsh`.
39
+
40
+ Plain pip (no uv) works too:
41
+
42
+ ```sh
43
+ python -m venv .venv && . .venv/bin/activate
44
+ pip install -r requirements.txt
45
+ pip install -e .
46
+ ```
47
+
48
+ ## Configure
49
+
50
+ Easiest — store the key interactively (written to `~/.config/nlsh/config.toml`,
51
+ mode 0600):
52
+
53
+ ```sh
54
+ nlsh config set-key
55
+ nlsh config set-model deepseek-v4-pro # optional, change default model
56
+ nlsh config show # show resolved config (key masked)
57
+ ```
58
+
59
+ Or via environment variable:
60
+
61
+ ```sh
62
+ export DEEPSEEK_API_KEY=sk-...
63
+ ```
64
+
65
+ or edit `~/.config/nlsh/config.toml` directly:
66
+
67
+ ```toml
68
+ api_key = "sk-..."
69
+ model = "deepseek-v4-flash" # optional (also: deepseek-v4-pro)
70
+ base_url = "https://api.deepseek.com" # optional
71
+ ```
72
+
73
+ Resolution order for each setting is: environment variable → config file → default.
74
+
75
+ ## Usage
76
+
77
+ ```sh
78
+ nlsh <natural language request>
79
+
80
+ -y, --yes run without confirmation (dangerous commands still prompt)
81
+ -n, --dry-run only print the command, never run it
82
+ --pro use the stronger model (deepseek-v4-pro) for this request
83
+ -m, --model ID override the model id for this request
84
+ --version show version
85
+ ```
86
+
87
+ At the confirmation prompt: `y` runs, `n` cancels, `e` edits first. Commands
88
+ that match risky patterns (`rm -rf`, `dd of=/dev/…`, `mkfs`, fork bombs,
89
+ `curl … | sh`, `sudo`, …) are flagged in red and require typing the full word
90
+ `yes` before they run — even under `--yes`. Multi-stage commands (pipes, `&&`)
91
+ are shown with a per-step breakdown.
92
+
93
+ ## Development
94
+
95
+ ```sh
96
+ uv sync # installs deps + dev tools (pytest, ruff)
97
+ uv run pytest -q # run the test suite
98
+ uv run ruff check . # lint
99
+ ```
100
+
101
+ CI (GitHub Actions) runs ruff + pytest across Python 3.9–3.13 and checks the
102
+ lockfile on every push and PR.
103
+
104
+ ### Releasing
105
+
106
+ The version is derived from the git tag by `hatch-vcs`, so a release is just a
107
+ tag:
108
+
109
+ ```sh
110
+ git tag v0.1.0
111
+ git push origin v0.1.0
112
+ ```
113
+
114
+ The `Publish` workflow builds the sdist/wheel and uploads them to PyPI via
115
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (configure a
116
+ `pypi` environment on the repo and register the workflow as a trusted publisher
117
+ — no API token stored).
118
+
119
+ ## How it works
120
+
121
+ `nlsh` sends your request plus context (OS, shell, current directory) to the
122
+ DeepSeek chat API constrained to JSON output, parses `{command, explanation}`,
123
+ and runs the chosen command through your `$SHELL` so aliases and the working
124
+ directory behave as expected.
@@ -0,0 +1,8 @@
1
+ """nlsh — natural language to shell command CLI."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("nlsh-cli") # PyPI distribution name (command is `nlsh`)
7
+ except PackageNotFoundError: # not installed (e.g. running from a source tree)
8
+ __version__ = "0+unknown"
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None