--- name: sync-deps description: 'Synchronises all dependency files from the actual imports in the codebase. Use when asked to "update requirements.txt", "sync dependencies", "update pyproject.toml dependencies", "fix requirements", "update deps", "sync deps", "update dependency files", or "update renv/environment.yml". Scans all Python source files for third-party imports, resolves PyPI package names, fetches installed versions, then writes requirements.txt and updates pyproject.toml [project.dependencies] — keeping every dep file in sync with a single source of truth.' --- # Sync Dependencies Derives the project's dependency list from **actual imports in source files** — one scan, one canonical list, propagated to every dep file. No manual bookkeeping; no drift between `requirements.txt` and `pyproject.toml`. ## When to Use This Skill - User says "update requirements.txt", "sync deps", "fix requirements", "update deps" - User says "update pyproject.toml dependencies", "sync dependency files" - After adding or removing a package to the project - Before a release or a code review --- ## Single Source of Truth ``` source files (.py, .ipynb) │ ▼ sync_deps.py ← ONE scan, ONE canonical list │ ├──▶ requirements.txt ├──▶ pyproject.toml [project.dependencies] ├──▶ environment.yml (if present) └──▶ setup.cfg (if present) ``` Everything is derived; never edit dep files by hand. --- ## Step 1 — Run the scanner Run the bundled script from the **repo root** (not from inside the skill folder): ```bash python .claude/skills/sync-deps/scripts/sync_deps.py ``` The script outputs one `package>=X.Y` line per third-party dependency, sorted alphabetically, to **stdout**. Capture it: ```bash python .claude/skills/sync-deps/scripts/sync_deps.py > /tmp/deps_raw.txt ``` Inspect the output before writing anything. If a package is missing or wrong, update `IMPORT_TO_PACKAGE` in the script — that is the only place to change. --- ## Step 2 — Write `requirements.txt` Replace the full content of `requirements.txt` at the repo root with: ``` # Runtime dependencies — generated by sync-deps, do not edit by hand. # Regenerate with: python .claude/skills/sync-deps/scripts/sync_deps.py # ``` Rules: - Keep the header comment exactly as shown. - Packages are sorted case-insensitively. - Dev-only packages (`pytest`, `mypy`, `ruff`, `pytest-cov`) must **not** appear here — they belong in `pyproject.toml [project.optional-dependencies].dev` only. --- ## Step 3 — Update `pyproject.toml [project.dependencies]` Read `pyproject.toml`. Replace the `dependencies = [...]` list under `[project]` with the same package list from Step 1, formatted as TOML strings: ```toml dependencies = [ "package-a>=X.Y", "package-b>=X.Y", ] ``` Rules: - Preserve all other sections unchanged (surgical edit only). - Dev packages stay in `[project.optional-dependencies].dev` — do not move them. - If `[project.optional-dependencies].dev` is missing, add it with at minimum: `["pytest>=8", "pytest-cov", "mypy", "ruff"]`. --- ## Step 4 — Update optional dep files (if present) Scan the repo root for these files and update them if found: ### `environment.yml` (conda) Update the `dependencies:` list. Preserve `- pip:` sub-list structure. Example shape: ```yaml dependencies: - python>=3.9 - pip: - package-a>=X.Y - package-b>=X.Y ``` ### `setup.cfg` If `install_requires` exists under `[options]`, replace it with the new list: ```ini [options] install_requires = package-a>=X.Y package-b>=X.Y ``` ### `Pipfile` If present, update the `[packages]` section. Do not modify `[dev-packages]`. --- ## Step 5 — Verify and report After all edits, print a summary: ``` sync-deps complete ───────────────────────────────────────── Source files scanned : .py files, .ipynb files Third-party imports : ───────────────────────────────────────── Updated: requirements.txt — packages pyproject.toml — [project.dependencies] ( entries) environment.yml — (skipped — not found) setup.cfg — (skipped — not found) ───────────────────────────────────────── Packages: ``` --- ## Import-to-Package Name Map The script maintains `IMPORT_TO_PACKAGE` — a dict mapping Python import names to their canonical PyPI package names. This is the **only** place to fix name mismatches. Never hardcode a mapping elsewhere. | Import name | PyPI package | |---|---| | `sklearn` | `scikit-learn` | | `PIL` | `Pillow` | | `cv2` | `opencv-python` | | `yaml` | `PyYAML` | | `bs4` | `beautifulsoup4` | | `dotenv` | `python-dotenv` | | `faiss` | `faiss-cpu` | | `rank_bm25` | `rank-bm25` | | `langchain_classic` | `langchain-classic` | | `langchain_core` | `langchain-core` | | `langchain_community` | `langchain-community` | | `langchain_openai` | `langchain-openai` | To add a new mapping: edit `IMPORT_TO_PACKAGE` in `scripts/sync_deps.py`. --- ## Exclusion Rules These are **never** added to `requirements.txt`: - Standard library modules (`os`, `sys`, `json`, …) - The project's own package (auto-detected from `pyproject.toml [project].name`) - Dev-only packages: `pytest`, `pytest-cov`, `mypy`, `ruff`, `black`, `isort` - Private names (starting with `_`) - Modules in excluded directories: `venv/`, `.venv/`, `build/`, `dist/`, `.eggs/` --- ## Troubleshooting | Problem | Cause | Fix | |---|---|---| | Package missing from output | Import name not in `IMPORT_TO_PACKAGE` and not installed | Add mapping to `IMPORT_TO_PACKAGE` or install the package | | Wrong version shown | Package not installed in active venv | Activate the correct venv before running | | `sys.stdlib_module_names` not available | Python < 3.10 | Script falls back to a static stdlib set; update Python if possible | | Package appears but shouldn't | Import from a test fixture or example that shadows a real name | Add it to `DEV_ONLY_PACKAGES` in the script | | `pyproject.toml` parse error | Non-standard TOML formatting | Read the file carefully and apply a surgical edit |