pehloo-shell 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,8 @@
1
+ # Cline-pass judge model (OpenAI-compatible API) that grades semantic correctness.
2
+ CLINE_API_KEY=your_api_key_here
3
+ CLINE_BASE_URL=https://api.cline.bot/api/v1
4
+ CLINE_JUDGE_MODEL=cline-pass/deepseek-v4-flash
5
+
6
+ # The evaluated local ggufs are served by llama-server on :8080. Inspect's
7
+ # "llamacpp" provider still expects an API key; llama-server ignores it.
8
+ LLAMACPP_API_KEY=unused
@@ -0,0 +1,60 @@
1
+ name: publish
2
+
3
+ # Publishes to PyPI with trusted publishing (OIDC) — no API token is stored
4
+ # anywhere. The PyPI publisher form must match this repository row for row:
5
+ #
6
+ # PyPI Project Name: pehloo-shell
7
+ # Owner: ribhu97
8
+ # Repository name: pehloo-shell
9
+ # Workflow name: publish.yml
10
+ # Environment name: pypi
11
+ #
12
+ # If you leave "Environment name" blank on PyPI, delete `environment: pypi`
13
+ # below as well: the OIDC token carries the environment, and a mismatch is
14
+ # rejected. A release publishes whatever version `pyproject.toml` declares, so
15
+ # bump it first, then tag `v<version>`.
16
+
17
+ on:
18
+ release:
19
+ types: [published]
20
+ workflow_dispatch:
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ jobs:
26
+ build:
27
+ name: build sdist and wheel
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v7
31
+ - uses: astral-sh/setup-uv@v10
32
+ with:
33
+ python-version: "3.13"
34
+ - name: Build
35
+ run: uv build
36
+ - name: Check the artifact metadata before anything is uploaded
37
+ run: uv publish --dry-run dist/*
38
+ - uses: actions/upload-artifact@v7
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ publish:
44
+ name: publish to PyPI
45
+ needs: build
46
+ runs-on: ubuntu-latest
47
+ # Must match the Environment name configured on PyPI (see the header).
48
+ environment: pypi
49
+ permissions:
50
+ id-token: write # the OIDC token that trusted publishing exchanges for upload rights
51
+ steps:
52
+ - uses: actions/download-artifact@v8
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ - uses: astral-sh/setup-uv@v10
57
+ - name: Publish
58
+ # `--check-url` skips files PyPI already has, so re-running a release is
59
+ # a no-op instead of an error.
60
+ run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/ dist/*
@@ -0,0 +1,20 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .env
5
+ inspect-logs/
6
+ logs/
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .DS_Store
11
+
12
+ # Private for now — kept out of the public repo and the PyPI sdist.
13
+ benchmark/
14
+
15
+ # Editor/agent configuration.
16
+ .serena/
17
+ .claude/
18
+ .mcp.json
19
+ *.log
20
+ .ruff_cache/
@@ -0,0 +1,148 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for humans and AI agents working in this repository.
4
+
5
+ ## Project in one line
6
+
7
+ `pehloo-shell` (CLI: `pls`) turns a natural-language request into a shell command
8
+ via an OpenAI-compatible model server (local llama.cpp by default, or Cerebras,
9
+ Groq, OpenRouter). It prints the command, then asks whether to run, reject or
10
+ change it — nothing executes without an explicit `y`. From Pehloo
11
+ (<https://pehloo.xyz>).
12
+
13
+ ## Layout
14
+
15
+ - `src/pehloo_shell/cli.py` — the `pls` CLI: settings resolution, the model call,
16
+ and the run/reject/change confirmation loop.
17
+ - `src/pehloo_shell/providers.py` — the backend registry (local + hosted), URL
18
+ derivation, and `/models` discovery. Add a backend here and nowhere else.
19
+ - `src/pehloo_shell/onboarding.py` — the first-run setup wizard and the config
20
+ file writer. Also reachable with `pls --setup`.
21
+ - `install.sh` — the `curl … | sh` installer (uv → pipx → pip, user-level only).
22
+ - `tests/test_cli.py`, `tests/test_providers.py`, `tests/test_onboarding.py` —
23
+ unit tests (mock the HTTP layer and `input`).
24
+ - `benchmark/` is private and gitignored (kept out of the public repo and the
25
+ PyPI sdist "for now"); its tooling and run instructions live in
26
+ `benchmark/AGENTS.md`. The same applies to `.serena/`, `.claude/` and
27
+ `.mcp.json`.
28
+
29
+ ## Environment
30
+
31
+ - The project uses a `uv`-managed venv (`.venv`):
32
+
33
+ ```bash
34
+ uv venv .venv
35
+ uv sync # installs the `pls` CLI and its one dependency
36
+ ```
37
+
38
+ `uv.lock` is committed; keep it in sync when you touch `pyproject.toml`.
39
+ Benchmark tooling (`inspect-ai`, `openai`, `matplotlib`) is not part of the
40
+ package — install it as a local overlay when you work in `benchmark/`:
41
+ `uv pip install --python .venv/bin/python inspect-ai openai matplotlib`
42
+ (`uv sync` removes the overlay again).
43
+
44
+ - `.env` holds secrets and local defaults (cline-pass judge, local llama-server).
45
+ Never commit a real API key — `.env` is gitignored; put placeholders in
46
+ `.env.example` instead.
47
+
48
+ - End users install with `install.sh` (`sh install.sh` from a checkout, or
49
+ `curl -fsSL <url> | sh`): uv → pipx → pip, user-level only, no sudo. It
50
+ installs the `pehloo-shell` package from PyPI unless `--source` /
51
+ `$PEHLOO_SHELL_SOURCE` says otherwise.
52
+
53
+ ## Branding & voice
54
+
55
+ Public surfaces match <https://pehloo.xyz>:
56
+
57
+ - The ANSI-shadow `PEHLOO` wordmark (top of `README.md`, header of
58
+ `install.sh`) — the exact 6 lines are on the site's landing page.
59
+ - Status tags look like `[ PLS_INSTALL ]` and `[ COMING_SOON ]` — square
60
+ brackets, words_joined_by_underscores, sometimes boxed with `╔═╗`.
61
+ - Progress and pointer lines start with `▸` (the wizard, the installer).
62
+ - Terse terminal voice: "Natural language in, shell command out." — no
63
+ marketing, no filler. Fonts on the site are Share Tech Mono / Space Mono /
64
+ VT323; accent colour is brand-amber `#d4a017` on cream `#f5f2eb`.
65
+ - `pls` itself prints the command then `[y] run [n] reject [c] change >`.
66
+
67
+ ## How `pls` behaves (keep these contracts)
68
+
69
+ - **Nothing executes without an explicit `y`** at the `[y] run [n] reject
70
+ [c] change` prompt. Enter and `n` reject; unknown keys ask again.
71
+ - **Non-interactive runs never execute**: with stdin/stdout not a terminal,
72
+ `pls` prints the command and stops, so pipes and scripts stay safe.
73
+ - `y` propagates the command's exit status as `pls`'s own; Ctrl-C/Ctrl-D at a
74
+ prompt is 130; a missing request is 2.
75
+ - `c` sends the revision as a follow-up turn (`previous_command` + `change` in
76
+ `generate_command`), so revisions keep the original request as context.
77
+ - The setup wizard runs only when there is no config file and the session is
78
+ interactive, or when `--setup` is passed.
79
+ - New backends go in `providers.py`; nothing else should know provider URLs or
80
+ API-key environment variables.
81
+
82
+ ## Releasing (PyPI)
83
+
84
+ `install.sh` defaults to the `pehloo-shell` package on PyPI, so publishing is what
85
+ makes `pipx install pehloo-shell` work for everyone else.
86
+
87
+ Releases go out from `.github/workflows/publish.yml` using trusted publishing —
88
+ no API token is stored in the repo or in GitHub secrets. PyPI's publisher form
89
+ ("pending publisher" for a project that does not exist yet) must match that file
90
+ exactly:
91
+
92
+ | PyPI field | Value |
93
+ | --- | --- |
94
+ | PyPI Project Name | `pehloo-shell` |
95
+ | Owner | `ribhu97` |
96
+ | Repository name | `pehloo-shell` |
97
+ | Workflow name | `publish.yml` |
98
+ | Environment name | `pypi` — the workflow sets `environment: pypi`; leaving this blank on PyPI means deleting that line too |
99
+
100
+ Then: bump `version` in `pyproject.toml`, commit, tag `v<version>`, publish a
101
+ GitHub release. The workflow builds the artifacts, validates their metadata with
102
+ `uv publish --dry-run`, and uploads them with OIDC. `workflow_dispatch` re-runs it
103
+ on demand; `--check-url` makes that a no-op for files PyPI already has.
104
+
105
+ Publishing by hand, with a token, still works:
106
+
107
+ ```bash
108
+ uv build # dist/*.whl + dist/*.tar.gz
109
+ uv publish --dry-run dist/* # validates the metadata, stops at credentials
110
+
111
+ # optional rehearsal on TestPyPI, then install from there
112
+ uv publish --publish-url https://test.pypi.org/legacy/ --token "$TESTPYPI_TOKEN" dist/*
113
+ pipx install --index-url https://test.pypi.org/simple/ \
114
+ --pip-args="--extra-index-url https://pypi.org/simple/" pehloo-shell
115
+
116
+ uv publish --token "$PYPI_TOKEN" dist/* # the real thing
117
+ ```
118
+
119
+ - Credentials for the manual path come from `UV_PUBLISH_TOKEN` / `--token`, or a
120
+ token from <https://pypi.org/manage/account/token/>; never put one in the repo.
121
+ - Bump `version` in `pyproject.toml` for every upload — PyPI rejects a version it
122
+ already has, so a fix means a new version, not a re-upload.
123
+ - The sdist ships the package, tests and docs only; `.env`, `logs/`,
124
+ `benchmark/`, `.serena/`, `.claude/` and `.mcp.json` are excluded (see
125
+ `[tool.hatch.build.targets.sdist]` in `pyproject.toml`). After touching
126
+ packaging, re-check with `tar tzf dist/*.tar.gz` — the sdist must never contain
127
+ `.env`.
128
+ - Published installs are copies: `pipx install pehloo-shell` and
129
+ `uv tool install pehloo-shell` need `--force` (or `pipx upgrade` /
130
+ `uv tool upgrade`) to pick up a release.
131
+
132
+ ## Working agreements
133
+
134
+ 1. **Keep code human readable.**
135
+ - No unnecessary abstractions. Plain functions and simple data flow beat clever
136
+ design patterns.
137
+ - No god-functions. If a function does more than one clearly named thing, split it.
138
+ - Inline comments and docstrings explain *why*, not just *what*. A stranger should
139
+ be able to read the code top-to-bottom without spelunking.
140
+
141
+ ## Before committing
142
+
143
+ - Unit tests should pass: `uv run python -m unittest discover tests`
144
+ - CLI still runs: `uv run pls "list files by size"` (interactive), and
145
+ `echo "list files by size" | uv run pls` must print the command and run nothing.
146
+ - Installer still parses and installs: `sh -n install.sh`, then
147
+ `uv tool install --force .` (or `sh install.sh`).
148
+ - `.env` must not contain real secrets (only placeholders).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ribhu
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,126 @@
1
+ Metadata-Version: 2.5
2
+ Name: pehloo-shell
3
+ Version: 0.1.0
4
+ Summary: Natural language in, shell command out — review it, then press y to run. A Pehloo tool.
5
+ Project-URL: Homepage, https://pehloo.xyz
6
+ Author: Ribhu
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Classifier: Environment :: Console
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: python-dotenv>=1
14
+ Description-Content-Type: text/markdown
15
+
16
+ ```
17
+ ██████╗ ███████╗██╗ ██╗██╗ ██████╗ ██████╗
18
+ ██╔══██╗██╔════╝██║ ██║██║ ██╔═══██╗██╔═══██╗
19
+ ██████╔╝█████╗ ███████║██║ ██║ ██║██║ ██║
20
+ ██╔═══╝ ██╔══╝ ██╔══██║██║ ██║ ██║██║ ██║
21
+ ██║ ███████╗██║ ██║███████╗╚██████╔╝╚██████╔╝
22
+ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝
23
+ ```
24
+
25
+ # pls
26
+
27
+ **Natural language in, shell command out. Review it, then press `y`.**
28
+
29
+ `pls` is the Pehloo shell assistant — from [Pehloo](https://pehloo.xyz), AI
30
+ tools for builders who ship. It asks an OpenAI-compatible model to turn your
31
+ request into one shell command, shows it to you, and waits:
32
+
33
+ ```console
34
+ $ pls "list the 10 largest directories in the current folder"
35
+ du -h --max-depth=1 | sort -hr | head -n 10
36
+ [y] run [n] reject [c] change >
37
+ ```
38
+
39
+ - `y` runs the command in your shell; `pls` exits with its status.
40
+ - `n` or Enter rejects it — nothing runs.
41
+ - `c` asks the model to change it (e.g. type `sort by size instead`) and shows
42
+ the revision, with the previous command as context.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ curl -fsSL https://raw.githubusercontent.com/ribhu97/pehloo-shell/main/install.sh | sh
48
+ sh install.sh # from a checkout
49
+ ```
50
+
51
+ The installer works at user level — no sudo, no system Python — using uv, pipx
52
+ or pip, whichever you have, and prints the installed `pls` path plus the
53
+ `export PATH=…` line if you need one. It installs the `pehloo-shell` package
54
+ from PyPI, falling back to this repository while the package is unpublished.
55
+ Point it elsewhere with `--source` / `$PEHLOO_SHELL_SOURCE`:
56
+
57
+ ```bash
58
+ sh install.sh --source "git+https://github.com/ribhu97/pehloo-shell"
59
+ ```
60
+
61
+ Already have a Python tool runner? Skip the script:
62
+
63
+ ```bash
64
+ uv tool install pehloo-shell # or: pipx install pehloo-shell
65
+ ```
66
+
67
+ For development see [AGENTS.md](AGENTS.md) (`uv sync`, then `.venv/bin/pls`).
68
+ Note that `pipx install .` and `uv tool install .` copy the code — rerun them
69
+ after changing the CLI.
70
+
71
+ ## Setup
72
+
73
+ The first run with no config asks which backend to use:
74
+
75
+ ```console
76
+ ╔══════════════════════════════╗
77
+ ║ PLS_SETUP: MODEL_BACKEND ║
78
+ ╚══════════════════════════════╝
79
+ ▸ pehloo-shell · a Pehloo tool (pehloo.xyz)
80
+ ▸ your answers are saved to ~/.pehloo/shell-config.json (re-run any time with `pls --setup`)
81
+
82
+ Use a local model server? [Y/n]: y
83
+
84
+ Local server: llama.cpp, Ollama, LM Studio or vLLM — any OpenAI-compatible server.
85
+ Server URL [http://127.0.0.1:8080/v1]:
86
+ API key (leave blank if the server has none):
87
+ 1. LFM2.5-1.2B-Instruct-Q8_0.gguf (default)
88
+ 2. MiniCPM5-2B
89
+ Model number or name [1]:
90
+ ```
91
+
92
+ - **Local** (`y`): any OpenAI-compatible server — llama.cpp, Ollama, LM Studio,
93
+ vLLM. `pls` reads its `/v1/models` list so you can pick a model instead of
94
+ guessing a name.
95
+ - **Hosted** (`n`): Cerebras, Groq or OpenRouter. The wizard tells you where to
96
+ create a key and detects the provider's models with it. A key already in your
97
+ environment is used without being copied into the config file.
98
+
99
+ Re-run setup any time with `pls --setup`, or edit `~/.pehloo/shell-config.json`
100
+ by hand.
101
+
102
+ ## Non-interactive
103
+
104
+ ```bash
105
+ echo "show files changed today" | pls # prints the command, runs nothing
106
+ pls --markdown "show my current directory"
107
+ ```
108
+
109
+ When stdin is not a terminal — pipes, scripts, CI — `pls` prints the command and
110
+ stops. Nothing executes without an explicit `y` at a terminal.
111
+
112
+ ## Environment variables
113
+
114
+ Handy for CI, or to keep the API key out of the config file. They override the
115
+ config file; command-line flags override both.
116
+
117
+ | Variable | Meaning |
118
+ | --- | --- |
119
+ | `PEHLOO_SHELL_CONFIG` | where the config file lives (default `~/.pehloo/shell-config.json`) |
120
+ | `PEHLOO_SHELL_PROVIDER` | `llama.cpp`, `cerebras`, `groq` or `openrouter` |
121
+ | `PEHLOO_SHELL_API_KEY` | API key for any provider |
122
+ | `CEREBRAS_API_KEY`, `GROQ_API_KEY`, `OPENROUTER_API_KEY` | provider-specific keys |
123
+ | `LFM_SHELL_URL` | chat-completions URL of any OpenAI-compatible server |
124
+ | `LFM_SHELL_MODEL` | model name to send |
125
+
126
+ Read the command before you press `y`: model output can be destructive.
@@ -0,0 +1,111 @@
1
+ ```
2
+ ██████╗ ███████╗██╗ ██╗██╗ ██████╗ ██████╗
3
+ ██╔══██╗██╔════╝██║ ██║██║ ██╔═══██╗██╔═══██╗
4
+ ██████╔╝█████╗ ███████║██║ ██║ ██║██║ ██║
5
+ ██╔═══╝ ██╔══╝ ██╔══██║██║ ██║ ██║██║ ██║
6
+ ██║ ███████╗██║ ██║███████╗╚██████╔╝╚██████╔╝
7
+ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝
8
+ ```
9
+
10
+ # pls
11
+
12
+ **Natural language in, shell command out. Review it, then press `y`.**
13
+
14
+ `pls` is the Pehloo shell assistant — from [Pehloo](https://pehloo.xyz), AI
15
+ tools for builders who ship. It asks an OpenAI-compatible model to turn your
16
+ request into one shell command, shows it to you, and waits:
17
+
18
+ ```console
19
+ $ pls "list the 10 largest directories in the current folder"
20
+ du -h --max-depth=1 | sort -hr | head -n 10
21
+ [y] run [n] reject [c] change >
22
+ ```
23
+
24
+ - `y` runs the command in your shell; `pls` exits with its status.
25
+ - `n` or Enter rejects it — nothing runs.
26
+ - `c` asks the model to change it (e.g. type `sort by size instead`) and shows
27
+ the revision, with the previous command as context.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ curl -fsSL https://raw.githubusercontent.com/ribhu97/pehloo-shell/main/install.sh | sh
33
+ sh install.sh # from a checkout
34
+ ```
35
+
36
+ The installer works at user level — no sudo, no system Python — using uv, pipx
37
+ or pip, whichever you have, and prints the installed `pls` path plus the
38
+ `export PATH=…` line if you need one. It installs the `pehloo-shell` package
39
+ from PyPI, falling back to this repository while the package is unpublished.
40
+ Point it elsewhere with `--source` / `$PEHLOO_SHELL_SOURCE`:
41
+
42
+ ```bash
43
+ sh install.sh --source "git+https://github.com/ribhu97/pehloo-shell"
44
+ ```
45
+
46
+ Already have a Python tool runner? Skip the script:
47
+
48
+ ```bash
49
+ uv tool install pehloo-shell # or: pipx install pehloo-shell
50
+ ```
51
+
52
+ For development see [AGENTS.md](AGENTS.md) (`uv sync`, then `.venv/bin/pls`).
53
+ Note that `pipx install .` and `uv tool install .` copy the code — rerun them
54
+ after changing the CLI.
55
+
56
+ ## Setup
57
+
58
+ The first run with no config asks which backend to use:
59
+
60
+ ```console
61
+ ╔══════════════════════════════╗
62
+ ║ PLS_SETUP: MODEL_BACKEND ║
63
+ ╚══════════════════════════════╝
64
+ ▸ pehloo-shell · a Pehloo tool (pehloo.xyz)
65
+ ▸ your answers are saved to ~/.pehloo/shell-config.json (re-run any time with `pls --setup`)
66
+
67
+ Use a local model server? [Y/n]: y
68
+
69
+ Local server: llama.cpp, Ollama, LM Studio or vLLM — any OpenAI-compatible server.
70
+ Server URL [http://127.0.0.1:8080/v1]:
71
+ API key (leave blank if the server has none):
72
+ 1. LFM2.5-1.2B-Instruct-Q8_0.gguf (default)
73
+ 2. MiniCPM5-2B
74
+ Model number or name [1]:
75
+ ```
76
+
77
+ - **Local** (`y`): any OpenAI-compatible server — llama.cpp, Ollama, LM Studio,
78
+ vLLM. `pls` reads its `/v1/models` list so you can pick a model instead of
79
+ guessing a name.
80
+ - **Hosted** (`n`): Cerebras, Groq or OpenRouter. The wizard tells you where to
81
+ create a key and detects the provider's models with it. A key already in your
82
+ environment is used without being copied into the config file.
83
+
84
+ Re-run setup any time with `pls --setup`, or edit `~/.pehloo/shell-config.json`
85
+ by hand.
86
+
87
+ ## Non-interactive
88
+
89
+ ```bash
90
+ echo "show files changed today" | pls # prints the command, runs nothing
91
+ pls --markdown "show my current directory"
92
+ ```
93
+
94
+ When stdin is not a terminal — pipes, scripts, CI — `pls` prints the command and
95
+ stops. Nothing executes without an explicit `y` at a terminal.
96
+
97
+ ## Environment variables
98
+
99
+ Handy for CI, or to keep the API key out of the config file. They override the
100
+ config file; command-line flags override both.
101
+
102
+ | Variable | Meaning |
103
+ | --- | --- |
104
+ | `PEHLOO_SHELL_CONFIG` | where the config file lives (default `~/.pehloo/shell-config.json`) |
105
+ | `PEHLOO_SHELL_PROVIDER` | `llama.cpp`, `cerebras`, `groq` or `openrouter` |
106
+ | `PEHLOO_SHELL_API_KEY` | API key for any provider |
107
+ | `CEREBRAS_API_KEY`, `GROQ_API_KEY`, `OPENROUTER_API_KEY` | provider-specific keys |
108
+ | `LFM_SHELL_URL` | chat-completions URL of any OpenAI-compatible server |
109
+ | `LFM_SHELL_MODEL` | model name to send |
110
+
111
+ Read the command before you press `y`: model output can be destructive.
@@ -0,0 +1,175 @@
1
+ #!/bin/sh
2
+ # Install the `pls` CLI from pehloo-shell.
3
+ #
4
+ # curl -fsSL https://example.com/install.sh | sh
5
+ #
6
+ # Options:
7
+ # --source SPEC what to install (default: the `pehloo-shell` package)
8
+ # examples: --source .
9
+ # --source "git+https://github.com/you/pehloo-shell"
10
+ #
11
+ # Installs into your user account only: no sudo, no system Python, no files
12
+ # outside your home directory. Run it again to upgrade.
13
+ set -eu
14
+
15
+ SOURCE="${PEHLOO_SHELL_SOURCE:-pehloo-shell}"
16
+ # Fallback used when the PyPI package is not available (see the install step).
17
+ REPO_SOURCE="git+https://github.com/ribhu97/pehloo-shell"
18
+ MIN_PYTHON="3.10"
19
+
20
+ usage() {
21
+ cat <<'EOF'
22
+ Install the pls CLI.
23
+
24
+ Usage: install.sh [--source SPEC]
25
+
26
+ --source SPEC pip requirement to install (default: the pehloo-shell package
27
+ on PyPI, falling back to the GitHub repository when the
28
+ package is not published). Also read from $PEHLOO_SHELL_SOURCE.
29
+ --help show this message.
30
+
31
+ Installing from a checkout of this repository installs that checkout.
32
+ EOF
33
+ }
34
+
35
+ while [ $# -gt 0 ]; do
36
+ case "$1" in
37
+ --source|-s)
38
+ [ $# -ge 2 ] || { echo "install.sh: --source needs a value" >&2; exit 2; }
39
+ SOURCE="$2"
40
+ shift 2
41
+ ;;
42
+ --help|-h)
43
+ usage
44
+ exit 0
45
+ ;;
46
+ *)
47
+ echo "install.sh: unknown option '$1'" >&2
48
+ usage >&2
49
+ exit 2
50
+ ;;
51
+ esac
52
+ done
53
+
54
+ # --- Banner -----------------------------------------------------------------
55
+
56
+ cat <<'BANNER'
57
+
58
+ ██████╗ ███████╗██╗ ██╗██╗ ██████╗ ██████╗
59
+ ██╔══██╗██╔════╝██║ ██║██║ ██╔═══██╗██╔═══██╗
60
+ ██████╔╝█████╗ ███████║██║ ██║ ██║██║ ██║
61
+ ██╔═══╝ ██╔══╝ ██╔══██║██║ ██║ ██║██║ ██║
62
+ ██║ ███████╗██║ ██║███████╗╚██████╔╝╚██████╔╝
63
+ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝
64
+ [ PLS_INSTALL ] pehloo-shell · a Pehloo tool (pehloo.xyz)
65
+
66
+ BANNER
67
+
68
+ # Running inside the repository? Install that, so `sh install.sh` in a checkout
69
+ # does the obvious thing.
70
+ if [ "$SOURCE" = "pehloo-shell" ] && [ -f pyproject.toml ] && [ -f src/pehloo_shell/cli.py ]; then
71
+ SOURCE="."
72
+ echo "▸ installing from this checkout"
73
+ fi
74
+
75
+ # --- Python -----------------------------------------------------------------
76
+
77
+ PY=""
78
+ for candidate in python3 python; do
79
+ if command -v "$candidate" >/dev/null 2>&1; then
80
+ PY="$(command -v "$candidate")"
81
+ break
82
+ fi
83
+ done
84
+
85
+ if [ -z "$PY" ]; then
86
+ echo "install.sh: python3 not found." >&2
87
+ echo " macOS: brew install python" >&2
88
+ echo " Debian: sudo apt install python3 python3-pip" >&2
89
+ exit 1
90
+ fi
91
+
92
+ if ! "$PY" -c "import sys; raise SystemExit(0 if sys.version_info >= tuple(int(p) for p in '$MIN_PYTHON'.split('.')) else 1)"; then
93
+ echo "install.sh: python $MIN_PYTHON or newer is required, found $("$PY" -V 2>&1)." >&2
94
+ exit 1
95
+ fi
96
+
97
+ # --- Install ----------------------------------------------------------------
98
+
99
+ installer=""
100
+
101
+ # Install one pip requirement with whichever tool this machine has.
102
+ try_install() {
103
+ spec="$1"
104
+ if [ -z "$installer" ]; then
105
+ if command -v uv >/dev/null 2>&1; then
106
+ installer="uv"
107
+ elif command -v pipx >/dev/null 2>&1; then
108
+ installer="pipx"
109
+ else
110
+ installer="pip"
111
+ fi
112
+ fi
113
+ case "$installer" in
114
+ uv) echo "▸ uv tool install $spec"; uv tool install --force "$spec" ;;
115
+ pipx) echo "▸ pipx install $spec"; pipx install --force "$spec" ;;
116
+ *) echo "▸ $PY -m pip install --user --upgrade $spec"; "$PY" -m pip install --user --upgrade "$spec" ;;
117
+ esac
118
+ }
119
+
120
+ if ! try_install "$SOURCE"; then
121
+ if [ "$SOURCE" = "pehloo-shell" ]; then
122
+ # Nothing published on PyPI yet (or PyPI is down): install straight from
123
+ # the repository so the advertised `curl … | sh` always works.
124
+ echo "▸ pehloo-shell is not on PyPI yet — installing from $REPO_SOURCE"
125
+ if ! try_install "$REPO_SOURCE"; then
126
+ echo "install.sh: could not install $REPO_SOURCE." >&2
127
+ exit 1
128
+ fi
129
+ else
130
+ echo "install.sh: could not install $SOURCE." >&2
131
+ if [ "$installer" = "pip" ]; then
132
+ echo " This is often an externally-managed environment (PEP 668). Install uv or pipx and rerun:" >&2
133
+ echo " curl -LsSf https://astral.sh/uv/install.sh | sh" >&2
134
+ fi
135
+ exit 1
136
+ fi
137
+ fi
138
+
139
+ # --- Report -----------------------------------------------------------------
140
+
141
+ case "$installer" in
142
+ uv) bin_dir="${UV_TOOL_BIN_DIR:-$HOME/.local/bin}" ;;
143
+ pipx) bin_dir="${PIPX_BIN_DIR:-$HOME/.local/bin}" ;;
144
+ *) bin_dir="$("$PY" -c 'import sysconfig; print(sysconfig.get_path("scripts", scheme="posix_user"))')" ;;
145
+ esac
146
+ installed="$bin_dir/pls"
147
+
148
+ if [ ! -x "$installed" ]; then
149
+ echo "install.sh: $installer finished but $installed is missing." >&2
150
+ echo " Look for the tool in $bin_dir and report this at the project's issue tracker." >&2
151
+ exit 1
152
+ fi
153
+ echo "▸ installed: $installed"
154
+
155
+ if ! command -v pls >/dev/null 2>&1; then
156
+ echo "▸ $bin_dir is not on your PATH. Add it:"
157
+ echo " export PATH=\"$bin_dir:\$PATH\""
158
+ elif [ "$(command -v pls)" != "$installed" ]; then
159
+ # A different pls earlier on PATH would silently win over this install.
160
+ echo "▸ note: the 'pls' on your PATH is $(command -v pls), not this one."
161
+ fi
162
+
163
+ case "$installer" in
164
+ uv) echo "▸ upgrade: uv tool upgrade pehloo-shell" ;;
165
+ pipx) echo "▸ upgrade: pipx upgrade pehloo-shell" ;;
166
+ *) echo "▸ upgrade: $PY -m pip install --user --upgrade pehloo-shell" ;;
167
+ esac
168
+
169
+ cat <<'EOF'
170
+
171
+ ▸ next: pls "list files by size" — the first run sets up your model backend
172
+ ▸ local: llama.cpp, Ollama, LM Studio or vLLM (any OpenAI-compatible server)
173
+ ▸ hosted: Cerebras · Groq · OpenRouter
174
+ ▸ config: ~/.pehloo/shell-config.json · re-run setup with `pls --setup`
175
+ EOF