since-cutoff 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.
Files changed (38) hide show
  1. since_cutoff-0.1.0/.gitignore +15 -0
  2. since_cutoff-0.1.0/CHANGELOG.md +20 -0
  3. since_cutoff-0.1.0/LICENSE +21 -0
  4. since_cutoff-0.1.0/PKG-INFO +263 -0
  5. since_cutoff-0.1.0/README.md +233 -0
  6. since_cutoff-0.1.0/pyproject.toml +90 -0
  7. since_cutoff-0.1.0/src/since_cutoff/__init__.py +3 -0
  8. since_cutoff-0.1.0/src/since_cutoff/__main__.py +3 -0
  9. since_cutoff-0.1.0/src/since_cutoff/apidiff.py +875 -0
  10. since_cutoff-0.1.0/src/since_cutoff/cache.py +78 -0
  11. since_cutoff-0.1.0/src/since_cutoff/checker.py +562 -0
  12. since_cutoff-0.1.0/src/since_cutoff/cli.py +496 -0
  13. since_cutoff-0.1.0/src/since_cutoff/data/models_snapshot.json +3484 -0
  14. since_cutoff-0.1.0/src/since_cutoff/engine.py +1009 -0
  15. since_cutoff-0.1.0/src/since_cutoff/errors.py +27 -0
  16. since_cutoff-0.1.0/src/since_cutoff/models.py +206 -0
  17. since_cutoff-0.1.0/src/since_cutoff/net.py +97 -0
  18. since_cutoff-0.1.0/src/since_cutoff/notes.py +273 -0
  19. since_cutoff-0.1.0/src/since_cutoff/project.py +551 -0
  20. since_cutoff-0.1.0/src/since_cutoff/prompts.py +259 -0
  21. since_cutoff-0.1.0/src/since_cutoff/providers/__init__.py +87 -0
  22. since_cutoff-0.1.0/src/since_cutoff/providers/base.py +28 -0
  23. since_cutoff-0.1.0/src/since_cutoff/providers/claude_code.py +163 -0
  24. since_cutoff-0.1.0/src/since_cutoff/providers/http_api.py +111 -0
  25. since_cutoff-0.1.0/src/since_cutoff/pypi.py +451 -0
  26. since_cutoff-0.1.0/src/since_cutoff/report.py +446 -0
  27. since_cutoff-0.1.0/src/since_cutoff/selection.py +74 -0
  28. since_cutoff-0.1.0/src/since_cutoff/stats.py +25 -0
  29. since_cutoff-0.1.0/tests/__init__.py +0 -0
  30. since_cutoff-0.1.0/tests/conftest.py +271 -0
  31. since_cutoff-0.1.0/tests/test_apidiff.py +124 -0
  32. since_cutoff-0.1.0/tests/test_checker.py +111 -0
  33. since_cutoff-0.1.0/tests/test_engine.py +144 -0
  34. since_cutoff-0.1.0/tests/test_models.py +104 -0
  35. since_cutoff-0.1.0/tests/test_project.py +207 -0
  36. since_cutoff-0.1.0/tests/test_pypi_providers_cli.py +279 -0
  37. since_cutoff-0.1.0/tests/test_review_fixes.py +633 -0
  38. since_cutoff-0.1.0/tests/test_units.py +208 -0
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ .env
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
14
+ .since-cutoff/
15
+ study/
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-26)
4
+
5
+ First release.
6
+
7
+ - `since-cutoff scan`: find the dependency versions a model could have seen at its training
8
+ cutoff and statically diff their public API against the versions your project uses.
9
+ - `since-cutoff run`: probe the model on the most relevant breaking changes, score its code with
10
+ basedpyright against both versions (stale / wrong / deprecated / correct), write verified
11
+ AGENTS.md notes and measure their effect on held-out tasks.
12
+ - Lockfiles: uv.lock, poetry.lock, pdm.lock, pylock.toml, Pipfile.lock; requirements files;
13
+ `.venv` metadata; pyproject.toml.
14
+ - Providers: Claude Code CLI, Anthropic API, OpenAI, OpenRouter, DeepSeek, Ollama and any
15
+ OpenAI-compatible endpoint.
16
+ - Claude Code plugin with a `since-cutoff` skill.
17
+ - `--effort` (default `low`) for Claude Code calls, and only API-knowledge errors (unknown names,
18
+ imports and parameters, missing arguments, arity) count as stale or wrong; type-strictness
19
+ complaints do not.
20
+ - A reproducible example project in `examples/agent-app` and a first real run in the README.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohammad Hijjawi
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,263 @@
1
+ Metadata-Version: 2.5
2
+ Name: since-cutoff
3
+ Version: 0.1.0
4
+ Summary: Find which of your exact dependency versions your coding agent writes wrong, and fix it with a small, verified AGENTS.md note.
5
+ Project-URL: Homepage, https://github.com/MohammadHijjawi97/since-cutoff
6
+ Project-URL: Issues, https://github.com/MohammadHijjawi97/since-cutoff/issues
7
+ Project-URL: Changelog, https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CHANGELOG.md
8
+ Author-email: Mohammad Hijjawi <mohammad.hijjawi1997@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents-md,api-changes,claude-code,codex,coding-agents,knowledge-cutoff,llm,static-analysis
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: basedpyright>=1.20
25
+ Requires-Dist: griffe>=1.14
26
+ Requires-Dist: packaging>=23.0
27
+ Requires-Dist: rich>=13.7
28
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # since-cutoff
32
+
33
+ English | [简体中文](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/README.zh-CN.md)
34
+
35
+ **Your coding agent learned your libraries before they changed.**
36
+ since-cutoff finds exactly which APIs of *your* dependency versions it gets wrong, and fixes
37
+ them with a small AGENTS.md note that is checked by a type checker, not by another LLM.
38
+
39
+ [![CI](https://github.com/MohammadHijjawi97/since-cutoff/actions/workflows/ci.yml/badge.svg)](https://github.com/MohammadHijjawi97/since-cutoff/actions/workflows/ci.yml)
40
+ [![PyPI](https://img.shields.io/pypi/v/since-cutoff)](https://pypi.org/project/since-cutoff/)
41
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
42
+ ![Status: beta](https://img.shields.io/badge/status-beta-orange)
43
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/LICENSE)
44
+
45
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/run.svg" alt="since-cutoff run: Claude Haiku 4.5 on a real project" width="860"></p>
46
+
47
+ ## The problem
48
+
49
+ Every model has a training cutoff. Your lockfile does not. A few real examples for a model
50
+ with a July 2025 cutoff (Claude Sonnet 4.5) and current releases, found by `since-cutoff scan`:
51
+
52
+ | library | version the model saw | your version | what breaks |
53
+ |---|---|---|---|
54
+ | anthropic | 0.60.0 | 1.8.0 | `messages.create(temperature=..., top_p=..., top_k=...)` no longer accepted |
55
+ | huggingface-hub | 0.34.3 | 2.0.0 | `hf_hub_download(resume_download=..., force_filename=..., local_dir_use_symlinks=...)` removed |
56
+ | langchain-core | 0.3.72 | 1.6.5 | `retriever.get_relevant_documents()`, `llm.predict()` removed |
57
+ | openai | 1.98.0 | 3.19.2 | 26 breaking changes, 6 new deprecations |
58
+
59
+ For that sample project, 7 of 9 dependencies had changed their public API after the cutoff
60
+ (the static diff flags 483 changes; many are internals, which the task writer skips). An agent that learned the old API writes code that fails at
61
+ import or call time, or, worse, still runs because the old path is only deprecated.
62
+
63
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/scan.svg" alt="since-cutoff scan output" width="820"></p>
64
+
65
+ Documentation tools paste whole docs into the context and hope. since-cutoff **measures**
66
+ which of those changes your model actually gets wrong, writes **only** the notes that are
67
+ needed, and **proves** on held-out tasks that the notes fix them.
68
+
69
+ ## Features
70
+
71
+ - **`scan`**: for every dependency, the version your model saw at its training cutoff vs. the one
72
+ you pin, and a static diff of what broke in between (no model calls, no API key).
73
+ - **`run`**: probes the model with short tasks that need the changed APIs and scores its code with
74
+ a type checker against *both* versions: stale, wrong, deprecated or correct.
75
+ - **Verified fixes**: one-line AGENTS.md / CLAUDE.md notes, kept only if their example
76
+ type-checks against your exact version, and re-tested on held-out tasks.
77
+ - **Works where you are**: Claude Code plugin and skill, or any of Anthropic, OpenAI, OpenRouter,
78
+ DeepSeek, Ollama and OpenAI-compatible servers.
79
+ - **Every lockfile**: uv, Poetry, PDM, pylock, Pipenv, requirements files, or a `.venv`.
80
+ - **Safe and reproducible**: never runs package or model-written code; everything is cached;
81
+ full JSON and Markdown reports.
82
+
83
+ ## Quick start
84
+
85
+ ```bash
86
+ # list API changes since your model's cutoff (fast, no model calls)
87
+ uvx since-cutoff scan
88
+
89
+ # probe the model, write verified notes, and apply them to AGENTS.md
90
+ uvx since-cutoff run --apply
91
+ ```
92
+
93
+ Or install it with `pipx install since-cutoff` (or `pip install since-cutoff`) and run
94
+ `since-cutoff`. Run it from your project root (anything with `uv.lock`, `poetry.lock`,
95
+ `pdm.lock`, `pylock.toml`, `Pipfile.lock`, `requirements*.txt`, `pyproject.toml` or a `.venv`).
96
+
97
+ ### In Claude Code
98
+
99
+ ```text
100
+ /plugin marketplace add MohammadHijjawi97/since-cutoff
101
+ /plugin install since-cutoff@since-cutoff
102
+ ```
103
+
104
+ Then ask Claude to "check which of our dependencies you are out of date on", or run
105
+ `/since-cutoff:since-cutoff`. The skill runs the CLI; the measuring itself is done by a fresh,
106
+ tool-less copy of the model, so the agent cannot grade itself.
107
+
108
+ ## A real run
109
+
110
+ Two Claude models on the 9-dependency sample project in
111
+ [`examples/agent-app`](https://github.com/MohammadHijjawi97/since-cutoff/tree/main/examples/agent-app),
112
+ with Claude Opus 4.6 writing the tasks and notes:
113
+
114
+ | | Claude Haiku 4.5 | Claude Opus 4.6 |
115
+ |---|---|---|
116
+ | training cutoff | Feb 2025 | May 2025 |
117
+ | API changes probed | 20 | 16 |
118
+ | **stale** / wrong / deprecated / correct | **5** / 1 / 2 / 12 | **7** / 0 / 3 / 6 |
119
+ | libraries with stale use | 3 of 5 probed | 2 of 4 probed |
120
+ | notes written (type-checker verified) | 8 (7), about 391 tokens | 10 (7), about 437 tokens |
121
+ | **held-out correct, without -> with notes** | **14% -> 57%** (14 pairs) | **5% -> 65%** (20 pairs) |
122
+ | previously-correct APIs after notes | 6/6 still correct | 6/6 still correct |
123
+
124
+ The stronger model is not safer: Opus 4.6 confidently wrote APIs that were removed after its
125
+ cutoff, including `anthropic.HUMAN_PROMPT` with `client.completions`. Stale code from both runs,
126
+ each valid for the version the model learned and broken for the pinned one:
127
+ `messages.create(temperature=...)` (anthropic 1.8), `hf_hub_download(resume_download=...)`,
128
+ `local_dir_use_symlinks=...`, `force_filename=...` and `proxies=...` (huggingface-hub 2.0), and
129
+ `client.beta.vector_stores` (openai 3.x).
130
+
131
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/run-opus.svg" alt="since-cutoff run: Claude Opus 4.6" width="860"></p>
132
+
133
+ The notes it wrote (excerpt, verbatim):
134
+
135
+ ```markdown
136
+ <!-- since-cutoff:start -->
137
+ ## Library changes after the model's training cutoff
138
+
139
+ **anthropic 1.8.0**
140
+ - `temperature=...` was removed from `messages.create()` in anthropic 1.8.0. Omit the `temperature` parameter entirely; there is no replacement.
141
+
142
+ **huggingface-hub 2.0.0**
143
+ - `hf_hub_download(..., resume_download=True)`: The `resume_download` parameter was removed in huggingface-hub 2.0.0. Omit it; downloads resume automatically.
144
+
145
+ **openai 3.19.2**
146
+ - `client.beta.vector_stores` is removed in openai 3.19.2. Use `client.vector_stores` instead.
147
+ <!-- since-cutoff:end -->
148
+ ```
149
+
150
+ Small samples, two models, one project: treat it as a demonstration, not a benchmark. The full
151
+ report (every task, answer and type-checker error) is what `since-cutoff run` writes to
152
+ `.since-cutoff/report.md`. To reproduce: `cd examples/agent-app && since-cutoff run --model
153
+ claude-code:claude-haiku-4-5 --task-model claude-code:claude-opus-4-6`.
154
+
155
+ ## Models
156
+
157
+ | `--model` | uses | needs |
158
+ |---|---|---|
159
+ | `claude-code` (default) | your Claude Code login (subscription or key), current model | the `claude` CLI |
160
+ | `claude-code:sonnet`, `claude-code:claude-haiku-4-5` | a specific Claude model | the `claude` CLI |
161
+ | `anthropic:<model>` | Anthropic API | `ANTHROPIC_API_KEY` |
162
+ | `openai:<model>` | OpenAI API | `OPENAI_API_KEY` |
163
+ | `openrouter:<vendor/model>` | OpenRouter | `OPENROUTER_API_KEY` |
164
+ | `deepseek:<model>` | DeepSeek API | `DEEPSEEK_API_KEY` |
165
+ | `ollama:<model>` | local Ollama | Ollama running |
166
+ | `openai-compatible:<model>` | any OpenAI-compatible server | `--base-url`, optional `OPENAI_API_KEY` |
167
+
168
+ Training cutoffs come from [models.dev](https://models.dev) (a snapshot is bundled for
169
+ offline use). `since-cutoff models sonnet` lists them; `--cutoff 2025-07` overrides.
170
+
171
+ ## How it works
172
+
173
+ ```mermaid
174
+ flowchart LR
175
+ L[lockfile] --> V[version at the model's cutoff<br/>vs your version]
176
+ V --> D[static API diff<br/>griffe]
177
+ D --> T[short tasks that need<br/>the changed API]
178
+ T --> M[model answers<br/>no tools, no docs]
179
+ M --> C[basedpyright against<br/>BOTH versions]
180
+ C --> N[notes, verified<br/>by the type checker]
181
+ N --> H[held-out tasks<br/>with vs without notes]
182
+ ```
183
+
184
+ | outcome | meaning |
185
+ |---|---|
186
+ | **stale** | the code is valid for the version the model knew and invalid for yours, and the error involves an API that changed |
187
+ | **wrong** | invalid for your version, but not explained by a change (hallucinated or misused API) |
188
+ | **deprecated** | valid, but uses an API marked `@deprecated` in your version |
189
+ | **correct** | valid for your version and actually uses the changed API |
190
+ | untouched / off-task / invalid / error | not counted in any rate, and always reported |
191
+
192
+ Everything is scored by a type checker against the exact package versions, each in an isolated
193
+ environment with that package's own runtime dependencies. No LLM judges anything, and every
194
+ number traces back to `results.json`. Details: [docs/how-it-works.md](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/docs/how-it-works.md).
195
+
196
+ ## What it runs, sends and fetches
197
+
198
+ - **Fetches** package metadata and wheels from PyPI and model cutoffs from models.dev (a snapshot
199
+ is bundled for offline use).
200
+ - **Sends** prompts only to the model provider you choose (`run` only; `scan` sends nothing).
201
+ Prompts contain package names, versions, public signatures and docstrings of the changed APIs,
202
+ the generated tasks and, for notes, the model's own answer. Never your source code.
203
+ - **Runs** basedpyright locally on the model's answers. It never executes them.
204
+ - **Writes** `.since-cutoff/` in your project, its cache (`since-cutoff cache path`) and, with
205
+ `--apply`, one marked block in `AGENTS.md`/`CLAUDE.md`. No telemetry.
206
+
207
+ ## Safe by design
208
+
209
+ - **Never executes code.** Package code is read statically (griffe with inspection off; only
210
+ `.py`/`.pyi` files are extracted, with path and size checks). Model-written code is only
211
+ type-checked.
212
+ - **Writes almost nothing.** Only `.since-cutoff/` (which ignores itself in git) and, with
213
+ `--apply`, one marked block in `AGENTS.md`/`CLAUDE.md`. Everything else in that file is
214
+ left byte-for-byte unchanged.
215
+ - **Stays on PyPI.** Git, path, workspace and private-index dependencies are never looked up on
216
+ public PyPI by name.
217
+ - **Local and cached.** No telemetry. PyPI data, diffs, tasks and answers are cached, so
218
+ re-runs are free and reproducible (`--fresh` asks the model again).
219
+
220
+ ## Use in CI
221
+
222
+ ```bash
223
+ since-cutoff run --quick --fail-on-stale --json > since-cutoff.json
224
+ ```
225
+
226
+ Exit codes: `0` ok, `1` error (including "no model answer could be scored"), `2` usage error,
227
+ `3` stale API use found with `--fail-on-stale`.
228
+
229
+ ## Limitations
230
+
231
+ - Python only for now. TypeScript (`.d.ts` diffs, `tsc`) is next.
232
+ - A type checker sees wrong names, wrong parameters and PEP 702 deprecations. It cannot see
233
+ behaviour changes behind an unchanged signature, or deprecations that only warn at run time.
234
+ - Probes cover a ranked **sample** of the breaking changes (symbols your code already uses
235
+ first), not all of them.
236
+ - "The version the model saw" is the newest release on or before the cutoff date. Models know
237
+ recent releases less well, so real staleness can start earlier.
238
+ - Held-out tasks are paraphrases of the same change: they show that a note fixes *that* change,
239
+ not that the model got better in general.
240
+
241
+ ## Related work
242
+
243
+ - [Context7](https://github.com/upstash/context7) and similar tools retrieve current docs at
244
+ answer time. since-cutoff is complementary: it measures what is actually wrong and keeps a
245
+ small, verified note in the repo.
246
+ - [cutoff](https://github.com/sandeepsirodia/cutoff) probes a library you maintain;
247
+ [postcut](https://github.com/justi/postcut) pastes changelogs since the cutoff.
248
+ - Built on [griffe](https://mkdocstrings.github.io/griffe/),
249
+ [basedpyright](https://github.com/DetachHead/basedpyright), [models.dev](https://models.dev)
250
+ and [rich](https://github.com/Textualize/rich).
251
+
252
+ ## Contributing
253
+
254
+ Issues and pull requests are welcome; see [CONTRIBUTING.md](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CONTRIBUTING.md). The offline test
255
+ suite runs the whole pipeline with a toy library and a scripted model, so no API key is needed.
256
+
257
+ ## Citation
258
+
259
+ If you use since-cutoff in research, please cite it (see [`CITATION.cff`](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CITATION.cff)).
260
+
261
+ ## License
262
+
263
+ MIT © [Mohammad Hijjawi](https://github.com/MohammadHijjawi97)
@@ -0,0 +1,233 @@
1
+ # since-cutoff
2
+
3
+ English | [简体中文](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/README.zh-CN.md)
4
+
5
+ **Your coding agent learned your libraries before they changed.**
6
+ since-cutoff finds exactly which APIs of *your* dependency versions it gets wrong, and fixes
7
+ them with a small AGENTS.md note that is checked by a type checker, not by another LLM.
8
+
9
+ [![CI](https://github.com/MohammadHijjawi97/since-cutoff/actions/workflows/ci.yml/badge.svg)](https://github.com/MohammadHijjawi97/since-cutoff/actions/workflows/ci.yml)
10
+ [![PyPI](https://img.shields.io/pypi/v/since-cutoff)](https://pypi.org/project/since-cutoff/)
11
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
12
+ ![Status: beta](https://img.shields.io/badge/status-beta-orange)
13
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/LICENSE)
14
+
15
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/run.svg" alt="since-cutoff run: Claude Haiku 4.5 on a real project" width="860"></p>
16
+
17
+ ## The problem
18
+
19
+ Every model has a training cutoff. Your lockfile does not. A few real examples for a model
20
+ with a July 2025 cutoff (Claude Sonnet 4.5) and current releases, found by `since-cutoff scan`:
21
+
22
+ | library | version the model saw | your version | what breaks |
23
+ |---|---|---|---|
24
+ | anthropic | 0.60.0 | 1.8.0 | `messages.create(temperature=..., top_p=..., top_k=...)` no longer accepted |
25
+ | huggingface-hub | 0.34.3 | 2.0.0 | `hf_hub_download(resume_download=..., force_filename=..., local_dir_use_symlinks=...)` removed |
26
+ | langchain-core | 0.3.72 | 1.6.5 | `retriever.get_relevant_documents()`, `llm.predict()` removed |
27
+ | openai | 1.98.0 | 3.19.2 | 26 breaking changes, 6 new deprecations |
28
+
29
+ For that sample project, 7 of 9 dependencies had changed their public API after the cutoff
30
+ (the static diff flags 483 changes; many are internals, which the task writer skips). An agent that learned the old API writes code that fails at
31
+ import or call time, or, worse, still runs because the old path is only deprecated.
32
+
33
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/scan.svg" alt="since-cutoff scan output" width="820"></p>
34
+
35
+ Documentation tools paste whole docs into the context and hope. since-cutoff **measures**
36
+ which of those changes your model actually gets wrong, writes **only** the notes that are
37
+ needed, and **proves** on held-out tasks that the notes fix them.
38
+
39
+ ## Features
40
+
41
+ - **`scan`**: for every dependency, the version your model saw at its training cutoff vs. the one
42
+ you pin, and a static diff of what broke in between (no model calls, no API key).
43
+ - **`run`**: probes the model with short tasks that need the changed APIs and scores its code with
44
+ a type checker against *both* versions: stale, wrong, deprecated or correct.
45
+ - **Verified fixes**: one-line AGENTS.md / CLAUDE.md notes, kept only if their example
46
+ type-checks against your exact version, and re-tested on held-out tasks.
47
+ - **Works where you are**: Claude Code plugin and skill, or any of Anthropic, OpenAI, OpenRouter,
48
+ DeepSeek, Ollama and OpenAI-compatible servers.
49
+ - **Every lockfile**: uv, Poetry, PDM, pylock, Pipenv, requirements files, or a `.venv`.
50
+ - **Safe and reproducible**: never runs package or model-written code; everything is cached;
51
+ full JSON and Markdown reports.
52
+
53
+ ## Quick start
54
+
55
+ ```bash
56
+ # list API changes since your model's cutoff (fast, no model calls)
57
+ uvx since-cutoff scan
58
+
59
+ # probe the model, write verified notes, and apply them to AGENTS.md
60
+ uvx since-cutoff run --apply
61
+ ```
62
+
63
+ Or install it with `pipx install since-cutoff` (or `pip install since-cutoff`) and run
64
+ `since-cutoff`. Run it from your project root (anything with `uv.lock`, `poetry.lock`,
65
+ `pdm.lock`, `pylock.toml`, `Pipfile.lock`, `requirements*.txt`, `pyproject.toml` or a `.venv`).
66
+
67
+ ### In Claude Code
68
+
69
+ ```text
70
+ /plugin marketplace add MohammadHijjawi97/since-cutoff
71
+ /plugin install since-cutoff@since-cutoff
72
+ ```
73
+
74
+ Then ask Claude to "check which of our dependencies you are out of date on", or run
75
+ `/since-cutoff:since-cutoff`. The skill runs the CLI; the measuring itself is done by a fresh,
76
+ tool-less copy of the model, so the agent cannot grade itself.
77
+
78
+ ## A real run
79
+
80
+ Two Claude models on the 9-dependency sample project in
81
+ [`examples/agent-app`](https://github.com/MohammadHijjawi97/since-cutoff/tree/main/examples/agent-app),
82
+ with Claude Opus 4.6 writing the tasks and notes:
83
+
84
+ | | Claude Haiku 4.5 | Claude Opus 4.6 |
85
+ |---|---|---|
86
+ | training cutoff | Feb 2025 | May 2025 |
87
+ | API changes probed | 20 | 16 |
88
+ | **stale** / wrong / deprecated / correct | **5** / 1 / 2 / 12 | **7** / 0 / 3 / 6 |
89
+ | libraries with stale use | 3 of 5 probed | 2 of 4 probed |
90
+ | notes written (type-checker verified) | 8 (7), about 391 tokens | 10 (7), about 437 tokens |
91
+ | **held-out correct, without -> with notes** | **14% -> 57%** (14 pairs) | **5% -> 65%** (20 pairs) |
92
+ | previously-correct APIs after notes | 6/6 still correct | 6/6 still correct |
93
+
94
+ The stronger model is not safer: Opus 4.6 confidently wrote APIs that were removed after its
95
+ cutoff, including `anthropic.HUMAN_PROMPT` with `client.completions`. Stale code from both runs,
96
+ each valid for the version the model learned and broken for the pinned one:
97
+ `messages.create(temperature=...)` (anthropic 1.8), `hf_hub_download(resume_download=...)`,
98
+ `local_dir_use_symlinks=...`, `force_filename=...` and `proxies=...` (huggingface-hub 2.0), and
99
+ `client.beta.vector_stores` (openai 3.x).
100
+
101
+ <p align="center"><img src="https://raw.githubusercontent.com/MohammadHijjawi97/since-cutoff/main/docs/img/run-opus.svg" alt="since-cutoff run: Claude Opus 4.6" width="860"></p>
102
+
103
+ The notes it wrote (excerpt, verbatim):
104
+
105
+ ```markdown
106
+ <!-- since-cutoff:start -->
107
+ ## Library changes after the model's training cutoff
108
+
109
+ **anthropic 1.8.0**
110
+ - `temperature=...` was removed from `messages.create()` in anthropic 1.8.0. Omit the `temperature` parameter entirely; there is no replacement.
111
+
112
+ **huggingface-hub 2.0.0**
113
+ - `hf_hub_download(..., resume_download=True)`: The `resume_download` parameter was removed in huggingface-hub 2.0.0. Omit it; downloads resume automatically.
114
+
115
+ **openai 3.19.2**
116
+ - `client.beta.vector_stores` is removed in openai 3.19.2. Use `client.vector_stores` instead.
117
+ <!-- since-cutoff:end -->
118
+ ```
119
+
120
+ Small samples, two models, one project: treat it as a demonstration, not a benchmark. The full
121
+ report (every task, answer and type-checker error) is what `since-cutoff run` writes to
122
+ `.since-cutoff/report.md`. To reproduce: `cd examples/agent-app && since-cutoff run --model
123
+ claude-code:claude-haiku-4-5 --task-model claude-code:claude-opus-4-6`.
124
+
125
+ ## Models
126
+
127
+ | `--model` | uses | needs |
128
+ |---|---|---|
129
+ | `claude-code` (default) | your Claude Code login (subscription or key), current model | the `claude` CLI |
130
+ | `claude-code:sonnet`, `claude-code:claude-haiku-4-5` | a specific Claude model | the `claude` CLI |
131
+ | `anthropic:<model>` | Anthropic API | `ANTHROPIC_API_KEY` |
132
+ | `openai:<model>` | OpenAI API | `OPENAI_API_KEY` |
133
+ | `openrouter:<vendor/model>` | OpenRouter | `OPENROUTER_API_KEY` |
134
+ | `deepseek:<model>` | DeepSeek API | `DEEPSEEK_API_KEY` |
135
+ | `ollama:<model>` | local Ollama | Ollama running |
136
+ | `openai-compatible:<model>` | any OpenAI-compatible server | `--base-url`, optional `OPENAI_API_KEY` |
137
+
138
+ Training cutoffs come from [models.dev](https://models.dev) (a snapshot is bundled for
139
+ offline use). `since-cutoff models sonnet` lists them; `--cutoff 2025-07` overrides.
140
+
141
+ ## How it works
142
+
143
+ ```mermaid
144
+ flowchart LR
145
+ L[lockfile] --> V[version at the model's cutoff<br/>vs your version]
146
+ V --> D[static API diff<br/>griffe]
147
+ D --> T[short tasks that need<br/>the changed API]
148
+ T --> M[model answers<br/>no tools, no docs]
149
+ M --> C[basedpyright against<br/>BOTH versions]
150
+ C --> N[notes, verified<br/>by the type checker]
151
+ N --> H[held-out tasks<br/>with vs without notes]
152
+ ```
153
+
154
+ | outcome | meaning |
155
+ |---|---|
156
+ | **stale** | the code is valid for the version the model knew and invalid for yours, and the error involves an API that changed |
157
+ | **wrong** | invalid for your version, but not explained by a change (hallucinated or misused API) |
158
+ | **deprecated** | valid, but uses an API marked `@deprecated` in your version |
159
+ | **correct** | valid for your version and actually uses the changed API |
160
+ | untouched / off-task / invalid / error | not counted in any rate, and always reported |
161
+
162
+ Everything is scored by a type checker against the exact package versions, each in an isolated
163
+ environment with that package's own runtime dependencies. No LLM judges anything, and every
164
+ number traces back to `results.json`. Details: [docs/how-it-works.md](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/docs/how-it-works.md).
165
+
166
+ ## What it runs, sends and fetches
167
+
168
+ - **Fetches** package metadata and wheels from PyPI and model cutoffs from models.dev (a snapshot
169
+ is bundled for offline use).
170
+ - **Sends** prompts only to the model provider you choose (`run` only; `scan` sends nothing).
171
+ Prompts contain package names, versions, public signatures and docstrings of the changed APIs,
172
+ the generated tasks and, for notes, the model's own answer. Never your source code.
173
+ - **Runs** basedpyright locally on the model's answers. It never executes them.
174
+ - **Writes** `.since-cutoff/` in your project, its cache (`since-cutoff cache path`) and, with
175
+ `--apply`, one marked block in `AGENTS.md`/`CLAUDE.md`. No telemetry.
176
+
177
+ ## Safe by design
178
+
179
+ - **Never executes code.** Package code is read statically (griffe with inspection off; only
180
+ `.py`/`.pyi` files are extracted, with path and size checks). Model-written code is only
181
+ type-checked.
182
+ - **Writes almost nothing.** Only `.since-cutoff/` (which ignores itself in git) and, with
183
+ `--apply`, one marked block in `AGENTS.md`/`CLAUDE.md`. Everything else in that file is
184
+ left byte-for-byte unchanged.
185
+ - **Stays on PyPI.** Git, path, workspace and private-index dependencies are never looked up on
186
+ public PyPI by name.
187
+ - **Local and cached.** No telemetry. PyPI data, diffs, tasks and answers are cached, so
188
+ re-runs are free and reproducible (`--fresh` asks the model again).
189
+
190
+ ## Use in CI
191
+
192
+ ```bash
193
+ since-cutoff run --quick --fail-on-stale --json > since-cutoff.json
194
+ ```
195
+
196
+ Exit codes: `0` ok, `1` error (including "no model answer could be scored"), `2` usage error,
197
+ `3` stale API use found with `--fail-on-stale`.
198
+
199
+ ## Limitations
200
+
201
+ - Python only for now. TypeScript (`.d.ts` diffs, `tsc`) is next.
202
+ - A type checker sees wrong names, wrong parameters and PEP 702 deprecations. It cannot see
203
+ behaviour changes behind an unchanged signature, or deprecations that only warn at run time.
204
+ - Probes cover a ranked **sample** of the breaking changes (symbols your code already uses
205
+ first), not all of them.
206
+ - "The version the model saw" is the newest release on or before the cutoff date. Models know
207
+ recent releases less well, so real staleness can start earlier.
208
+ - Held-out tasks are paraphrases of the same change: they show that a note fixes *that* change,
209
+ not that the model got better in general.
210
+
211
+ ## Related work
212
+
213
+ - [Context7](https://github.com/upstash/context7) and similar tools retrieve current docs at
214
+ answer time. since-cutoff is complementary: it measures what is actually wrong and keeps a
215
+ small, verified note in the repo.
216
+ - [cutoff](https://github.com/sandeepsirodia/cutoff) probes a library you maintain;
217
+ [postcut](https://github.com/justi/postcut) pastes changelogs since the cutoff.
218
+ - Built on [griffe](https://mkdocstrings.github.io/griffe/),
219
+ [basedpyright](https://github.com/DetachHead/basedpyright), [models.dev](https://models.dev)
220
+ and [rich](https://github.com/Textualize/rich).
221
+
222
+ ## Contributing
223
+
224
+ Issues and pull requests are welcome; see [CONTRIBUTING.md](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CONTRIBUTING.md). The offline test
225
+ suite runs the whole pipeline with a toy library and a scripted model, so no API key is needed.
226
+
227
+ ## Citation
228
+
229
+ If you use since-cutoff in research, please cite it (see [`CITATION.cff`](https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CITATION.cff)).
230
+
231
+ ## License
232
+
233
+ MIT © [Mohammad Hijjawi](https://github.com/MohammadHijjawi97)
@@ -0,0 +1,90 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "since-cutoff"
7
+ dynamic = ["version"]
8
+ description = "Find which of your exact dependency versions your coding agent writes wrong, and fix it with a small, verified AGENTS.md note."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "Mohammad Hijjawi", email = "mohammad.hijjawi1997@gmail.com" }]
14
+ keywords = [
15
+ "llm",
16
+ "coding-agents",
17
+ "claude-code",
18
+ "codex",
19
+ "agents-md",
20
+ "knowledge-cutoff",
21
+ "api-changes",
22
+ "static-analysis",
23
+ ]
24
+ classifiers = [
25
+ "Development Status :: 4 - Beta",
26
+ "Environment :: Console",
27
+ "Intended Audience :: Developers",
28
+ "Operating System :: OS Independent",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
34
+ "Topic :: Software Development :: Quality Assurance",
35
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
36
+ ]
37
+ dependencies = [
38
+ "basedpyright>=1.20",
39
+ "griffe>=1.14",
40
+ "packaging>=23.0",
41
+ "rich>=13.7",
42
+ "tomli>=2.0; python_version < '3.11'",
43
+ ]
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/MohammadHijjawi97/since-cutoff"
47
+ Issues = "https://github.com/MohammadHijjawi97/since-cutoff/issues"
48
+ Changelog = "https://github.com/MohammadHijjawi97/since-cutoff/blob/main/CHANGELOG.md"
49
+
50
+ [project.scripts]
51
+ since-cutoff = "since_cutoff.cli:main"
52
+
53
+ [dependency-groups]
54
+ dev = ["pytest>=8", "pytest-cov>=5", "ruff>=0.6", "mypy>=1.11"]
55
+
56
+ [tool.hatch.version]
57
+ path = "src/since_cutoff/__init__.py"
58
+
59
+ [tool.hatch.build.targets.wheel]
60
+ packages = ["src/since_cutoff"]
61
+
62
+ [tool.hatch.build.targets.sdist]
63
+ include = ["src", "tests", "README.md", "LICENSE", "CHANGELOG.md"]
64
+
65
+ [tool.pytest.ini_options]
66
+ testpaths = ["tests"]
67
+ addopts = "-ra --strict-markers"
68
+ markers = [
69
+ "network: needs internet access (PyPI, models.dev)",
70
+ "pyright: runs the basedpyright type checker",
71
+ ]
72
+
73
+ [tool.ruff]
74
+ line-length = 100
75
+ target-version = "py310"
76
+ src = ["src", "tests"]
77
+
78
+ [tool.ruff.lint]
79
+ select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "PTH", "C4"]
80
+ ignore = ["E501", "RUF001", "RUF002", "RUF003"]
81
+
82
+ [tool.mypy]
83
+ python_version = "3.10"
84
+ strict = true
85
+ warn_unused_ignores = true
86
+ files = ["src/since_cutoff"]
87
+
88
+ [[tool.mypy.overrides]]
89
+ module = ["griffe.*", "tomli", "basedpyright"]
90
+ ignore_missing_imports = true
@@ -0,0 +1,3 @@
1
+ """since-cutoff: measure and fix what your coding agent gets wrong about your exact dependencies."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from since_cutoff.cli import main
2
+
3
+ raise SystemExit(main())