sift-cli 1.0.1__tar.gz → 1.0.2__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.
- {sift_cli-1.0.1 → sift_cli-1.0.2}/PKG-INFO +6 -2
- {sift_cli-1.0.1 → sift_cli-1.0.2}/README.md +5 -1
- {sift_cli-1.0.1 → sift_cli-1.0.2}/pyproject.toml +4 -1
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/__init__.py +1 -1
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/cli.py +26 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/mutations.py +7 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_package.py +18 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_server.py +43 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/.gitattributes +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/.github/workflows/ci.yml +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/.github/workflows/release.yml +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/.gitignore +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/LICENSE +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/00-PLAN.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/01-YAKALAMA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/02-MODEL-KOPRUSU.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/03-DAMITMA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/04-GUVENLIK-AGI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/05-DIL-BAGIMSIZLIGI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/06-DOSYA-TASLAGI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/07-MCP-SUNUCUSU.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/08-BUTCE-VE-OLCUM.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/09-ARKA-PLAN-KOMUTLARI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/10-GIZLILIK.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/11-YAYIN.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/12-CAGIRANIN-SOZ-HAKKI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/13-DOSYA-DAMITMA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/14-ARAYAN-PEEK.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/15-COKLU-IS.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/16-HAFIZA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/17-YOGUN-ARACLAR.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/18-KAPSAM.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/19-KALINTI-MUTASYON.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/20-KAYIT-BIRIMI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/21-SAKLAMA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/22-YANIT-ONBELLEGI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/23-OLCUM-BIRIMI.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/24-ANAHTAR.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/25-TEKLIF.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/26-BEKLEMEK.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/27-WINDOWS.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/notlar/28-KURULUM.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/answers.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/background.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/capture.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/digest.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/distill.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/fallback.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/hook.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/lines.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/many.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/memory.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/model.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/outline.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/peek.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/privacy.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/records.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/server.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/store.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/tools.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/view.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/src/sift/watch.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/tanitim/00-PLAN.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/tanitim/ARASTIRMA.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/tanitim/T1-SURUM-1-0-1.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/budget.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/conftest.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/README.md +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/ansi-progress.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/ansi-progress.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cargo-de.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cargo-de.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cmake-vi.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cmake-vi.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cobol-mainframe.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/cobol-mainframe.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/docker-ar.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/docker-ar.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/dotnet-fr.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/dotnet-fr.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/ffmpeg-fa.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/ffmpeg-fa.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gcc-ru.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gcc-ru.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/git-quiet.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/git-quiet.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gotest-zh.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gotest-zh.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gradle-ko.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/gradle-ko.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/journalctl-it.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/journalctl-it.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/kubectl-hi.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/kubectl-hi.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/latex-pl.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/latex-pl.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/maven-tr.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/maven-tr.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/mix-he.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/mix-he.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/psql-es.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/psql-es.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/pytest-en.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/pytest-en.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/rspec-id.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/rspec-id.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/terraform-pt.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/terraform-pt.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/vite-ja.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/vite-ja.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/zig-sw.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus/zig-sw.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/clojure-rapor.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/clojure-rapor.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/cpp-baslik.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/cpp-baslik.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/elixir-onbellek.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/elixir-onbellek.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/go-kuyruk.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/go-kuyruk.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/haskell-ayristirici.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/haskell-ayristirici.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/hcl-altyapi.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/hcl-altyapi.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/java-depo.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/java-depo.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/makefile-yapi.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/makefile-yapi.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/python-akis.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/python-akis.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/qqzz-uydurma.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/qqzz-uydurma.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/ruby-fatura.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/ruby-fatura.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/rust-matris.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/rust-matris.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/shell-dagitim.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/shell-dagitim.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/sql-sema.json +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus-kaynak/sql-sema.txt +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/korpus_reader.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/languages.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/outlines.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_answers.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_background.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_capture.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_cli.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_digest.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_distill.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_fallback.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_gc.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_hook.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_hook_setup.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_kaynak.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_korpus.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_lines.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_many.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_model.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_outline.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_privacy.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_records.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_setup.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/test_tokens.py +0 -0
- {sift_cli-1.0.1 → sift_cli-1.0.2}/test/wire.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sift-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Runs your command, then gives the model only the lines that matter.
|
|
5
5
|
Project-URL: Homepage, https://github.com/slymnysr/sift
|
|
6
6
|
Author: slmnys
|
|
@@ -182,6 +182,7 @@ sift outline PATH what a file declares, without its bodies
|
|
|
182
182
|
sift digest PATH... what is in files somebody else produced
|
|
183
183
|
sift peek HANDLE|PATH [FIRST] [LAST]
|
|
184
184
|
sift hook answer one shell-command event on stdin
|
|
185
|
+
sift mcp speak the protocol on stdin, for a client
|
|
185
186
|
sift tools which dense tools this machine has
|
|
186
187
|
sift tool NAME [ARGS...] run one of them, distilled
|
|
187
188
|
sift memory [TERM] what has been run here before, and how it went
|
|
@@ -260,7 +261,10 @@ Without a key the server starts and every tool that would need a model declines
|
|
|
260
261
|
with an explanation, so the agent falls back to its own shell rather than being
|
|
261
262
|
handed a worse answer it cannot tell apart from a good one.
|
|
262
263
|
|
|
263
|
-
Any client that speaks stdio will do
|
|
264
|
+
Any client that speaks stdio will do. The command is `sift-mcp`, and `sift mcp`
|
|
265
|
+
starts the same server — one word for a client that would rather run the package
|
|
266
|
+
by its own name (`uvx --from "sift-cli[mcp]" sift-cli mcp`, which is what the MCP
|
|
267
|
+
registry entry says). It offers
|
|
264
268
|
`run`, `follow`, `outline`, `digest`, `digest_many`, `tool` and `peek`. `list`
|
|
265
269
|
and `stats` are deliberately not offered: they would hand a model every command lately run on this machine,
|
|
266
270
|
including the ones it never asked about, and the person at a terminal already
|
|
@@ -162,6 +162,7 @@ sift outline PATH what a file declares, without its bodies
|
|
|
162
162
|
sift digest PATH... what is in files somebody else produced
|
|
163
163
|
sift peek HANDLE|PATH [FIRST] [LAST]
|
|
164
164
|
sift hook answer one shell-command event on stdin
|
|
165
|
+
sift mcp speak the protocol on stdin, for a client
|
|
165
166
|
sift tools which dense tools this machine has
|
|
166
167
|
sift tool NAME [ARGS...] run one of them, distilled
|
|
167
168
|
sift memory [TERM] what has been run here before, and how it went
|
|
@@ -240,7 +241,10 @@ Without a key the server starts and every tool that would need a model declines
|
|
|
240
241
|
with an explanation, so the agent falls back to its own shell rather than being
|
|
241
242
|
handed a worse answer it cannot tell apart from a good one.
|
|
242
243
|
|
|
243
|
-
Any client that speaks stdio will do
|
|
244
|
+
Any client that speaks stdio will do. The command is `sift-mcp`, and `sift mcp`
|
|
245
|
+
starts the same server — one word for a client that would rather run the package
|
|
246
|
+
by its own name (`uvx --from "sift-cli[mcp]" sift-cli mcp`, which is what the MCP
|
|
247
|
+
registry entry says). It offers
|
|
244
248
|
`run`, `follow`, `outline`, `digest`, `digest_many`, `tool` and `peek`. `list`
|
|
245
249
|
and `stats` are deliberately not offered: they would hand a model every command lately run on this machine,
|
|
246
250
|
including the ones it never asked about, and the person at a terminal already
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sift-cli"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Runs your command, then gives the model only the lines that matter."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -30,6 +30,9 @@ mcp = ["mcp>=2"]
|
|
|
30
30
|
[project.scripts]
|
|
31
31
|
sift = "sift.cli:main"
|
|
32
32
|
sift-mcp = "sift.server:main"
|
|
33
|
+
# The package name, spelled as a command, because that is the only name a
|
|
34
|
+
# registry entry can carry: `uvx <package>` is how a client starts one.
|
|
35
|
+
sift-cli = "sift.cli:main"
|
|
33
36
|
|
|
34
37
|
[project.urls]
|
|
35
38
|
Homepage = "https://github.com/slymnysr/sift"
|
|
@@ -73,6 +73,7 @@ USAGE = """sift -- run a command, keep every byte, show the lines that matter
|
|
|
73
73
|
sift hook answer one shell-command event on stdin
|
|
74
74
|
sift hook --install [--yes] route the client's own shell here too
|
|
75
75
|
sift hook --uninstall and take it back out
|
|
76
|
+
sift mcp speak the protocol on stdin, for a client
|
|
76
77
|
sift tools which dense tools this machine has
|
|
77
78
|
sift tool NAME [ARGS...] run one of them, distilled
|
|
78
79
|
sift memory [TERM] [--here]
|
|
@@ -161,6 +162,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
161
162
|
return _list(rest)
|
|
162
163
|
if word == "hook":
|
|
163
164
|
return _hook(rest)
|
|
165
|
+
if word == "mcp":
|
|
166
|
+
return _mcp(rest)
|
|
164
167
|
if word == "tools":
|
|
165
168
|
return _tools()
|
|
166
169
|
if word == "tool":
|
|
@@ -573,6 +576,29 @@ def _hook(args: list[str]) -> int:
|
|
|
573
576
|
return 0
|
|
574
577
|
|
|
575
578
|
|
|
579
|
+
def _mcp(args: list[str]) -> int:
|
|
580
|
+
"""Speak the protocol on stdin -- the same server `sift-mcp` starts.
|
|
581
|
+
|
|
582
|
+
Two names for one server looks like waste until you try to write down how a
|
|
583
|
+
stranger's client should start it. What a registry entry can name is the
|
|
584
|
+
package, and the package is `sift-cli`; what `uvx` runs is a command of that
|
|
585
|
+
same name. So `uvx --from "sift-cli[mcp]" sift-cli mcp` is a line anyone can
|
|
586
|
+
build from the entry alone, and it needs this word to exist.
|
|
587
|
+
|
|
588
|
+
The import is here rather than at the top because the whole point of the
|
|
589
|
+
extra is that the command line works without it. Somebody who never
|
|
590
|
+
installed `mcp` gets the sentence `sift.server` raises, not a traceback.
|
|
591
|
+
"""
|
|
592
|
+
if args:
|
|
593
|
+
print(f"sift: mcp takes no arguments\n\n{USAGE}", file=sys.stderr)
|
|
594
|
+
return 2
|
|
595
|
+
|
|
596
|
+
from sift.server import main as serve
|
|
597
|
+
|
|
598
|
+
serve()
|
|
599
|
+
return 0
|
|
600
|
+
|
|
601
|
+
|
|
576
602
|
def _tools() -> int:
|
|
577
603
|
"""What each of them replaces, and whether this machine has it.
|
|
578
604
|
|
|
@@ -1255,6 +1255,13 @@ MUTATIONS = [
|
|
|
1255
1255
|
" if not wanted():\n return PASS",
|
|
1256
1256
|
" if False:\n return PASS",
|
|
1257
1257
|
),
|
|
1258
|
+
# -- T1/T2: the word a registry entry can spell ----------------------------
|
|
1259
|
+
Mutation(
|
|
1260
|
+
"cli.py",
|
|
1261
|
+
"the server can be started by the package's own name",
|
|
1262
|
+
' if word == "mcp":\n return _mcp(rest)\n',
|
|
1263
|
+
"",
|
|
1264
|
+
),
|
|
1258
1265
|
]
|
|
1259
1266
|
|
|
1260
1267
|
|
|
@@ -23,6 +23,24 @@ def test_the_published_version_is_the_one_the_package_reports():
|
|
|
23
23
|
assert veri["project"]["version"] == sift.__version__
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def test_a_command_is_named_after_the_package_itself():
|
|
27
|
+
"""`uvx sift-cli` has to start something.
|
|
28
|
+
|
|
29
|
+
What an MCP registry entry can name is the package, and what `uvx` runs is a
|
|
30
|
+
command of that same name -- so a client building a command from the entry
|
|
31
|
+
alone types `sift-cli`, and until this existed it got "an executable named
|
|
32
|
+
`sift-cli` is not provided by package `sift-cli`". The names differ because
|
|
33
|
+
`sift` was taken on PyPI, which is nobody's fault and still the user's
|
|
34
|
+
problem.
|
|
35
|
+
"""
|
|
36
|
+
veri = tomllib.loads((KOK / "pyproject.toml").read_text(encoding="utf-8"))
|
|
37
|
+
scripts = veri["project"]["scripts"]
|
|
38
|
+
ad = veri["project"]["name"]
|
|
39
|
+
|
|
40
|
+
assert ad in scripts, f"{ad} adinda bir komut yok; uvx {ad} calismaz"
|
|
41
|
+
assert scripts[ad] == scripts["sift"], "paketin adi komut satirini baslatmali"
|
|
42
|
+
|
|
43
|
+
|
|
26
44
|
def test_notes_and_tests_live_in_their_own_folders():
|
|
27
45
|
"""The layout the project promised: notes in notlar/, tests in test/.
|
|
28
46
|
|
|
@@ -268,6 +268,49 @@ def test_and_the_test_above_would_have_noticed():
|
|
|
268
268
|
assert "`sift`) is already installed" in finished.stderr
|
|
269
269
|
|
|
270
270
|
|
|
271
|
+
# -- the word a registry entry can spell -------------------------------------
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def test_the_subcommand_starts_the_same_server(monkeypatch):
|
|
275
|
+
"""`sift mcp` and `sift-mcp` are one server under two names.
|
|
276
|
+
|
|
277
|
+
The second name exists because an MCP registry entry can only name the
|
|
278
|
+
package, and a client turns that name into `uvx --from "sift-cli[mcp]"
|
|
279
|
+
sift-cli mcp`. A word that a stranger's client types has to reach the same
|
|
280
|
+
place the documented command does, or the entry is worse than no entry.
|
|
281
|
+
"""
|
|
282
|
+
from sift import cli
|
|
283
|
+
|
|
284
|
+
started = []
|
|
285
|
+
monkeypatch.setattr(s, "main", lambda: started.append(True))
|
|
286
|
+
|
|
287
|
+
assert cli.main(["mcp"]) == 0
|
|
288
|
+
assert started == [True], "alt komut sunucuyu baslatmadi"
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def test_the_subcommand_takes_nothing_and_says_so(capsys):
|
|
292
|
+
"""Silence would be worse: a flag that was ignored looks like a flag that worked."""
|
|
293
|
+
from sift import cli
|
|
294
|
+
|
|
295
|
+
assert cli.main(["mcp", "--port", "8080"]) == 2
|
|
296
|
+
assert "mcp takes no arguments" in capsys.readouterr().err
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_the_subcommand_says_the_same_sentence_when_the_extra_is_missing():
|
|
300
|
+
"""The registry command minus the extra is an ordinary thing to type.
|
|
301
|
+
|
|
302
|
+
`uvx sift-cli mcp` -- without `--from "sift-cli[mcp]"` -- installs the
|
|
303
|
+
command line and nothing else, and this is the path a client takes when it
|
|
304
|
+
drops the runtime arguments. What comes back has to be the sentence, not a
|
|
305
|
+
stack: the import lives inside the subcommand for exactly this reason.
|
|
306
|
+
"""
|
|
307
|
+
finished = _without_mcp("import sift.cli\nsift.cli.main(['mcp'])\n")
|
|
308
|
+
|
|
309
|
+
assert finished.returncode != 0
|
|
310
|
+
assert 'uv tool install "sift-cli[mcp]"' in finished.stderr
|
|
311
|
+
assert "Traceback" not in finished.stderr
|
|
312
|
+
|
|
313
|
+
|
|
271
314
|
# -- over the real transport -------------------------------------------------
|
|
272
315
|
|
|
273
316
|
SCRIPT = shutil.which("sift-mcp")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|