skeptic-cli 0.5.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.
Files changed (41) hide show
  1. skeptic_cli-0.5.0/LICENSE +21 -0
  2. skeptic_cli-0.5.0/PKG-INFO +389 -0
  3. skeptic_cli-0.5.0/README.md +317 -0
  4. skeptic_cli-0.5.0/pyproject.toml +77 -0
  5. skeptic_cli-0.5.0/setup.cfg +4 -0
  6. skeptic_cli-0.5.0/src/skeptic/__init__.py +0 -0
  7. skeptic_cli-0.5.0/src/skeptic/adapters/__init__.py +0 -0
  8. skeptic_cli-0.5.0/src/skeptic/adapters/python/__init__.py +27 -0
  9. skeptic_cli-0.5.0/src/skeptic/adapters/python/bandit_adapter.py +61 -0
  10. skeptic_cli-0.5.0/src/skeptic/adapters/python/pip_audit_adapter.py +56 -0
  11. skeptic_cli-0.5.0/src/skeptic/adapters/python/pyright_adapter.py +45 -0
  12. skeptic_cli-0.5.0/src/skeptic/adapters/python/pytest_adapter.py +59 -0
  13. skeptic_cli-0.5.0/src/skeptic/adapters/python/ruff_adapter.py +44 -0
  14. skeptic_cli-0.5.0/src/skeptic/cli/__init__.py +0 -0
  15. skeptic_cli-0.5.0/src/skeptic/cli/main.py +337 -0
  16. skeptic_cli-0.5.0/src/skeptic/core/__init__.py +0 -0
  17. skeptic_cli-0.5.0/src/skeptic/core/complexity.py +110 -0
  18. skeptic_cli-0.5.0/src/skeptic/core/config.py +72 -0
  19. skeptic_cli-0.5.0/src/skeptic/core/gate.py +124 -0
  20. skeptic_cli-0.5.0/src/skeptic/core/git_utils.py +97 -0
  21. skeptic_cli-0.5.0/src/skeptic/core/graph.py +157 -0
  22. skeptic_cli-0.5.0/src/skeptic/core/models.py +69 -0
  23. skeptic_cli-0.5.0/src/skeptic/core/provenance.py +90 -0
  24. skeptic_cli-0.5.0/src/skeptic/core/risk_score.py +99 -0
  25. skeptic_cli-0.5.0/src/skeptic/core/runner.py +43 -0
  26. skeptic_cli-0.5.0/src/skeptic/core/solid.py +266 -0
  27. skeptic_cli-0.5.0/src/skeptic/mcp/__init__.py +0 -0
  28. skeptic_cli-0.5.0/src/skeptic/mcp/call_log.py +68 -0
  29. skeptic_cli-0.5.0/src/skeptic/mcp/server.py +101 -0
  30. skeptic_cli-0.5.0/src/skeptic/narration/__init__.py +3 -0
  31. skeptic_cli-0.5.0/src/skeptic/narration/gemini_narrator.py +92 -0
  32. skeptic_cli-0.5.0/src/skeptic/verifier/__init__.py +14 -0
  33. skeptic_cli-0.5.0/src/skeptic/verifier/attack_runner.py +144 -0
  34. skeptic_cli-0.5.0/src/skeptic/verifier/gemini_generator.py +93 -0
  35. skeptic_cli-0.5.0/src/skeptic/verifier/models.py +88 -0
  36. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/PKG-INFO +389 -0
  37. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/SOURCES.txt +39 -0
  38. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/dependency_links.txt +1 -0
  39. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/entry_points.txt +3 -0
  40. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/requires.txt +25 -0
  41. skeptic_cli-0.5.0/src/skeptic_cli.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hamza Shaikh
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,389 @@
1
+ Metadata-Version: 2.4
2
+ Name: skeptic-cli
3
+ Version: 0.5.0
4
+ Summary: Independent engineering quality gate for AI-generated and human-written Python code.
5
+ Author-email: Hamza Shaikh <hamzashaikhm123@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Hamza Shaikh
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/HamzaShaikh17/Skeptic
29
+ Project-URL: Repository, https://github.com/HamzaShaikh17/Skeptic
30
+ Project-URL: Issues, https://github.com/HamzaShaikh17/Skeptic/issues
31
+ Project-URL: Documentation, https://github.com/HamzaShaikh17/Skeptic/blob/master/docs/USAGE.md
32
+ Keywords: code-quality,static-analysis,security,ci-cd,ai-agents,mcp,linting,skeptic
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Environment :: Console
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Security
44
+ Classifier: Topic :: Software Development :: Quality Assurance
45
+ Classifier: Topic :: Software Development :: Testing
46
+ Requires-Python: >=3.10
47
+ Description-Content-Type: text/markdown
48
+ License-File: LICENSE
49
+ Requires-Dist: click>=8.1
50
+ Requires-Dist: pydantic>=2.6
51
+ Requires-Dist: rich>=13.7
52
+ Requires-Dist: ruff>=0.4
53
+ Requires-Dist: bandit>=1.7
54
+ Requires-Dist: pyright>=1.1
55
+ Requires-Dist: pytest>=8.0
56
+ Requires-Dist: pytest-cov>=5.0
57
+ Requires-Dist: pytest-json-report>=1.5
58
+ Requires-Dist: pip-audit>=2.7
59
+ Requires-Dist: pyyaml>=6.0
60
+ Requires-Dist: mcp>=1.2
61
+ Provides-Extra: dev
62
+ Requires-Dist: pytest>=8.0; extra == "dev"
63
+ Requires-Dist: anyio>=4.0; extra == "dev"
64
+ Provides-Extra: narration
65
+ Requires-Dist: google-genai>=1.0; extra == "narration"
66
+ Requires-Dist: python-dotenv>=1.0; extra == "narration"
67
+ Provides-Extra: verify
68
+ Requires-Dist: google-genai>=1.0; extra == "verify"
69
+ Requires-Dist: python-dotenv>=1.0; extra == "verify"
70
+ Requires-Dist: httpx>=0.27; extra == "verify"
71
+ Dynamic: license-file
72
+
73
+ # Skeptic
74
+
75
+ An independent engineering quality gate for AI-generated (and human-written) Python
76
+ code. Skeptic doesn't take a change's word for it — it orchestrates Ruff, Pyright,
77
+ Bandit, pytest/coverage, and pip-audit into one pass/fail verdict, with evidence
78
+ attached to every failure, so an AI coding agent (or a human) can be required to
79
+ satisfy it before a change counts as done.
80
+
81
+ ## Status
82
+
83
+ Phase 1 CLI MVP (`v0.3`), Phase 2's MCP server, and Phase 3 complete through
84
+ milestone 8 (LLM verifier/pricing/billing, milestone 9, not built). All five
85
+ deterministic adapters (Ruff, Pyright, Bandit, pytest, pip-audit) plus
86
+ structural SOLID checks, complexity, Change Risk Score, and AI provenance
87
+ tagging are wired and unit-tested against fixture repos; LLM-backed
88
+ narration and verification are live-tested against the real Gemini API. See
89
+ `Plan.md` for the full build plan and current milestone.
90
+
91
+ ## Install
92
+
93
+ ```bash
94
+ pip install -e .
95
+ ```
96
+
97
+ This installs `skeptic` as a console command, backed by Click. Verified against
98
+ a clean `python -m venv` + `pip install -e .` with no other setup. Not yet on
99
+ PyPI — install from a local clone until it is.
100
+
101
+ ## Prerequisites
102
+
103
+ **Install and activate the target repo's own dependencies before running `skeptic check`.**
104
+ `skeptic` shells out to `pyright` (and `pytest`) using whatever Python environment
105
+ is currently active — it does not install the target repo's dependencies for you.
106
+ If they aren't installed, Pyright can't resolve most imports and will report a
107
+ flood of `reportMissingImports` errors that have nothing to do with real type
108
+ safety, and `pytest` will fail to collect tests at all. Since the default gate
109
+ is zero-tolerance (`types_max_errors: 0`), this alone is enough to fail every
110
+ real repo. The fix: `cd` into the target repo, activate its venv (or otherwise
111
+ make sure its dependencies are installed in the active environment — e.g.
112
+ `pip install -r requirements.txt`, `uv sync`, `poetry install`), *then* run
113
+ `skeptic check`. Ruff, Bandit, and pip-audit don't need this — they analyze
114
+ source/dependency manifests directly rather than resolving imports.
115
+
116
+ ## Usage
117
+
118
+ ```bash
119
+ # Generate a starter config in your repo
120
+ skeptic init /path/to/your/repo
121
+
122
+ # Run the gate
123
+ skeptic check /path/to/your/repo
124
+
125
+ # Machine-readable output (for CI or an agent to parse)
126
+ skeptic check /path/to/your/repo --json
127
+ ```
128
+
129
+ ### Example
130
+
131
+ Given a repo with an unused import, a real type error, and a vulnerable pinned
132
+ dependency:
133
+
134
+ ```
135
+ $ skeptic check .
136
+
137
+ Engineering Gate: FAIL
138
+
139
+ +----------------------------------------------------------------------------+
140
+ | Rule | Status | Detail |
141
+ |-------------------+--------+-----------------------------------------------|
142
+ | lint | FAIL | 1 lint findings (max allowed: 0) |
143
+ | types | FAIL | 1 type errors (max allowed: 0) |
144
+ | security_critical | PASS | 0 critical security findings (max allowed: 0) |
145
+ | security_high | PASS | 0 high security findings (max allowed: 0) |
146
+ | coverage | PASS | 88.9% coverage (min required: 0.0%) |
147
+ | dependencies | FAIL | 12 critical/high CVEs (max allowed: 0) |
148
+ +----------------------------------------------------------------------------+
149
+
150
+ lint findings:
151
+ app.py:1 `os` imported but unused (F401)
152
+
153
+ types findings:
154
+ app.py:14 Argument of type "Literal['not a number']" cannot be assigned to
155
+ parameter "a" of type "int" in function "add" (reportArgumentType)
156
+ ...
157
+ ```
158
+
159
+ Exit code `1`. Fix the issues (or explicitly relax `skeptic.yaml`) and the same
160
+ command exits `0` with `Engineering Gate: PASS`.
161
+
162
+ ## Configuration
163
+
164
+ Edit `skeptic.yaml` in your repo root:
165
+
166
+ ```yaml
167
+ lint:
168
+ max_errors: 0
169
+ types:
170
+ max_errors: 0
171
+ security:
172
+ max_critical: 0
173
+ max_high: 0
174
+ coverage:
175
+ min_percent: 80
176
+ dependencies:
177
+ max_critical_cves: 0
178
+
179
+ # Optional, unset by default - see "Architecture findings" below. Neither
180
+ # blocks the gate until you uncomment it.
181
+ # architecture:
182
+ # max_findings: 0
183
+ # ai_review:
184
+ # max_risk_label: MEDIUM # LOW | MEDIUM | HIGH
185
+ ```
186
+
187
+ No `skeptic.yaml`? Defaults are strict (zero tolerance on everything) for
188
+ lint/types/security/dependencies — `architecture` and `ai_review` are the
189
+ two exceptions: they stay off until you explicitly configure them (see
190
+ below for why).
191
+
192
+ For the full walkthrough — reading output, `--json`/CI integration, what
193
+ each check actually does, troubleshooting — see [`docs/USAGE.md`](docs/USAGE.md).
194
+
195
+ ## Architecture findings (SOLID + complexity) + narration
196
+
197
+ `skeptic check` also runs four deterministic, LLM-free structural checks —
198
+ SRP, ISP, DIP (`tool="solid"`), and McCabe cyclomatic complexity
199
+ (`tool="complexity"`, functions over 10 flagged by default). They show up
200
+ in the table output and `--json` (`solid_findings`/`complexity_findings`)
201
+ either way, but whether they can **fail** the gate depends on `skeptic.yaml`:
202
+
203
+ ```
204
+ architecture findings (informational - not yet gated):
205
+ app/god_service.py:11 class 'GodService' touches 3 unrelated external
206
+ systems (database, email, http) via: ... (SRP)
207
+ ```
208
+
209
+ **Not gated by default, deliberately** — unlike lint/types/security/
210
+ dependencies, which default to zero-tolerance. These checks are new and
211
+ haven't been broadly triaged the way an established linter has, so turning
212
+ every existing repo's gate from PASS to FAIL the moment you upgrade would
213
+ be a surprising, unrequested breaking change. Opt in explicitly:
214
+
215
+ ```yaml
216
+ architecture:
217
+ max_findings: 0 # gates both solid and complexity findings together
218
+ ```
219
+
220
+ ### Change Risk Score
221
+
222
+ Every `skeptic check` run (CLI table, `--json`, and MCP output) includes a
223
+ composite LOW/MEDIUM/HIGH label — security + regression (test health) +
224
+ architecture + complexity + test confidence, each scored 0-100 and always
225
+ shown, not just the label:
226
+
227
+ ```
228
+ Change Risk Score: MEDIUM (38.2/100 - security=40.0, regression=0.0, architecture=45.0, complexity=10.0, test_confidence=100.0)
229
+ ```
230
+
231
+ The weights and LOW/MEDIUM/HIGH thresholds are a documented first-pass
232
+ heuristic (see `src/skeptic/core/risk_score.py`), not an empirically
233
+ calibrated model — treat the label as a prioritization signal, not a
234
+ certified verdict. Gate on it explicitly if you want it to block:
235
+
236
+ ```yaml
237
+ ai_review:
238
+ max_risk_label: MEDIUM # fails the gate if the label exceeds this
239
+ ```
240
+
241
+ ### AI provenance (`skeptic provenance`)
242
+
243
+ ```bash
244
+ skeptic provenance /path/to/your/repo --since-ref HEAD~20
245
+ ```
246
+
247
+ Estimates what share of recent commits landed while an AI agent was
248
+ actively using `skeptic-mcp` against this repo, correlating each commit's
249
+ timestamp against the local [MCP call log](#call-logging). **This is an
250
+ approximation, not a precise record**: `skeptic-mcp`'s tools are read-only
251
+ analysis, so the call log records when an agent called them, not which
252
+ lines it edited — a commit landing within `--window-minutes` (default 15)
253
+ of a logged call is labeled `ai_generated` (pure addition) or
254
+ `ai_modified` (touched existing lines); everything else is `human`. No
255
+ model identification, just the ratio. Requires no setup — with zero MCP
256
+ history for a repo, everything is reported as `human`.
257
+
258
+ ### Plain-language narration (optional, costs an API call)
259
+
260
+ ```bash
261
+ skeptic check /path/to/your/repo --narrate
262
+ ```
263
+
264
+ Sends each SOLID finding to Gemini for a short "why this matters + how to
265
+ fix it" explanation, printed under the finding. The LLM never originates a
266
+ finding or changes the verdict — it only narrates one a deterministic check
267
+ already produced, and if narration fails (no key, network error, rate
268
+ limit) `skeptic check` still runs and reports normally, just without the
269
+ narration text.
270
+
271
+ Requires `GEMINI_API_KEY`:
272
+
273
+ ```bash
274
+ pip install -e ".[narration]" # installs google-genai + python-dotenv
275
+ cp .env.example .env # then fill in GEMINI_API_KEY
276
+ ```
277
+
278
+ `.env` is loaded automatically (and is gitignored — never commit it). The
279
+ model is `gemini-3.5-flash` by default, overridable via `SKEPTIC_GEMINI_MODEL`
280
+ if it gets deprecated later — Gemini model availability shifted twice while
281
+ building this feature (see `src/skeptic/narration/gemini_narrator.py`), so
282
+ this is a real, not hypothetical, concern.
283
+
284
+ ## Adversarial verifier (`skeptic verify`)
285
+
286
+ Generates and runs attack test cases against a **running instance you
287
+ control** — boundary values, invalid input, injection, auth bypass, IDOR,
288
+ concurrency (race conditions), and failure-mode (timeout) probes — and
289
+ reports pass/fail per category with the exact request that triggered each
290
+ result.
291
+
292
+ ```bash
293
+ pip install -e ".[verify]" # installs google-genai + python-dotenv + httpx
294
+ # start your own app locally first, e.g.: uvicorn app.main:app --port 8000
295
+
296
+ skeptic verify /path/to/your/repo --target http://localhost:8000
297
+ ```
298
+
299
+ **Safety, by design, not as an afterthought:**
300
+ - **Read-only against your code.** It never writes to the repo path — only
301
+ generates requests and sends them to `--target`.
302
+ - **The LLM never executes anything.** Gemini returns structured data
303
+ (method/path/headers/body) via a JSON schema, never code — the only thing
304
+ that ever runs is an HTTP request Skeptic's own code sends. Real
305
+ arbitrary-code-execution risk was a deliberate design decision *not* to
306
+ take on for this milestone.
307
+ - **Refuses non-local targets by default.** `--target` must resolve to
308
+ localhost or a private address (`10.x`, `172.16–31.x`, `192.168.x`, link-
309
+ local) or the command exits immediately, before generating anything —
310
+ it's sending real injection/auth-bypass/IDOR payloads, so this shouldn't
311
+ be pointable at a service you don't own by accident. Pass
312
+ `--allow-external` if you're certain the target is yours.
313
+
314
+ `--diff-ref` (default `HEAD`) focuses attack-case generation on your
315
+ uncommitted changes if `path` is a git repo; falls back to general-purpose
316
+ REST-API cases otherwise (not a git repo, or the ref doesn't exist) — never
317
+ a hard failure. `passed: null` on a result means the heuristic genuinely
318
+ can't tell (e.g. every concurrent request to a mutating endpoint succeeding
319
+ identically — could be a race condition, could be a correctly-idempotent
320
+ endpoint) and a human should look at `detail`; it's never silently coerced
321
+ to a pass.
322
+
323
+ Uses the same `GEMINI_API_KEY`/`.env` as `--narrate` above.
324
+
325
+ ## MCP server (Claude Code / Cursor)
326
+
327
+ Skeptic's engine is also exposed as an MCP server, so an AI coding
328
+ agent can call it mid-task instead of you running `skeptic check` by hand.
329
+ Same engine, same adapters, same gate — the CLI and the MCP server are both
330
+ thin clients of `skeptic.core`.
331
+
332
+ ### Install
333
+
334
+ `pip install -e .` (see above) also installs the `skeptic-mcp` console
335
+ command, which starts the server over stdio.
336
+
337
+ ### Configure your project
338
+
339
+ Add this to your project's `.mcp.json` (Claude Code) or `.cursor/mcp.json`
340
+ (Cursor) — not Skeptic's own repo, *the repo you want the agent to check*:
341
+
342
+ ```json
343
+ {
344
+ "mcpServers": {
345
+ "skeptic": {
346
+ "command": "skeptic-mcp",
347
+ "args": []
348
+ }
349
+ }
350
+ }
351
+ ```
352
+
353
+ `skeptic-mcp` must resolve on `PATH` in whatever environment your editor
354
+ launches subprocesses from — same as any other locally-installed MCP server.
355
+ Skeptic's own repo ships this file too (dogfooding: Claude Code sessions
356
+ working on Skeptic itself get the tools automatically).
357
+
358
+ ### Tools exposed
359
+
360
+ | Tool | Signature | Returns |
361
+ | --- | --- | --- |
362
+ | `skeptic_check` | `(repo_path: str)` | Full pass/fail verdict + evidence for every failing rule + tool statuses. Equivalent to `skeptic check --json`. |
363
+ | `skeptic_get_findings` | `(repo_path: str, severity: str \| None)` | Every raw finding across all 5 tools, optionally filtered to one severity (`critical`/`high`/`medium`/`low`) — not limited to findings tied to a failing gate rule. |
364
+ | `skeptic_gate_status` | `(repo_path: str)` | Same gate evaluation as `skeptic_check`, without the findings payload — a cheap pass/fail poll. |
365
+
366
+ The same [Prerequisites](#prerequisites) caveat applies: the target repo's
367
+ own dependencies need to be installed/active in the environment the MCP
368
+ server runs in, or `types` findings will mostly be import-resolution noise.
369
+
370
+ ### Call logging
371
+
372
+ Every call to any of the three tools is appended to a local, per-repo,
373
+ append-only JSONL log at `~/.skeptic/mcp_logs/<repo-name>-<hash>.jsonl` —
374
+ timestamp, session id, tool name, args, and the full result. Nothing reads
375
+ this back today; it's the seed of future evidence/provenance work, logged
376
+ now because the cost of doing so later (once real usage has already
377
+ happened without a record of it) is much higher.
378
+
379
+ ## Architecture
380
+
381
+ See `Plan.md` and `src/skeptic/core/models.py` for the language-agnostic schema.
382
+ Python-specific tool wrappers live in `src/skeptic/adapters/python/` — adding a
383
+ new language later means adding a new adapter directory, not rewriting the core.
384
+
385
+ ## Development
386
+
387
+ ```bash
388
+ pytest tests/
389
+ ```