refactor-dependencies
Refactor AICP's Python dependencies — audit `pyproject.toml` / `requirements.txt`, prune unused, tighten/loosen pins, replace transitive direct-dependents, isolate security-flagged packages, separate
SKILL.md.claude/skills/refactor-dependencies/SKILL.md
GitHubrefactor-dependencies
Audit and reshape AICP's Python dependency graph — what's pinned, what's
unused, what's vulnerable, what's transitive-but-should-be-direct, what
belongs in dev-only vs runtime. Produces a cleaner, smaller, more
defensible pyproject.toml and lock file.
Trigger phrases (when to load this skill)
Load when the conversation matches any of:
- Direct verb: operator says "audit dependencies", "prune unused packages", "tighten pins", "loosen these constraints", "split dev vs runtime deps"
- Security: operator says "package X has a CVE", "rotate dependency", "vulnerability scan flagged Y"
- Footprint: operator wants to reduce install size or import time — inspect what's actually used vs declared
- Discovery: operator notices an import path they don't recognize — trace which dependency owns it
- Pre-fleet rollout: before AICP runs on N fleet machines, audit dependencies for stability + security
Do NOT load when:
- The concern is package-level CODE refactoring (load
refactor-architecture) - The concern is a NEW dependency being added for a feature (load
feature-implement— adding deps is part of implementation, not housekeeping) - The concern is upgrading deps for a new feature requirement (load
evolve-integrate— adding capability is different from cleaning graph)
Operations
Operation 1 — Audit declared vs actually-used dependencies
When: pre-cleanup discovery — see what's in pyproject.toml/requirements
that isn't actually imported anywhere in aicp/.
Process:
- Read declared deps:
pyproject.toml(project.dependencies + project.optional-dependencies) - Find imports across
aicp/andtests/:Grep -E "^(from|import) [a-z_][a-z0-9_]*" --type py - Map each import to its owning package (e.g.,
httpx→httpx,from rich.table import Table→rich) - Compare: declared but not imported = candidates for removal
- Imported but not declared = HIDDEN transitive deps (someone else pulled them in); these need to become direct or be replaced
Quality bar: a "candidate for removal" needs verification — sometimes
a dep is loaded dynamically (e.g., importlib), or used only in a
try: import X fallback path. Verify before removing.
Operation 2 — Prune unused dependencies
When: Operation 1 produced a verified list of unused packages.
Process:
- Remove from
pyproject.tomlproject.dependencies - If using a lock file (e.g.,
uv.lockorpoetry.lock), regenerate - Run
pytest— must pass with zero changes (if a test fails, the package was actually used; restore) - Run
aicp --checkto verify CLI still loads - Document the removals (commit message + entry in
wiki/decisions/00_inbox/if removing a "load-bearing" looking dep)
Quality bar: NEVER prune without running pytest. The verification gate catches dynamically-imported deps that grep-based audits miss.
Operation 3 — Tighten or loosen version pins
When: pin policy needs adjustment — too loose causes incompatible upgrades, too tight blocks security patches.
Process:
- Identify the pin policy intent: production (tight) vs library (loose)
- AICP is a product (per CLAUDE.md
## Identity ProfileType=product), so pins should be RELATIVELY tight (e.g.,>=X.Y.Z,<X.(Y+1)or~=X.Ypatch-level) for runtime deps; dev deps can be looser - Edit
pyproject.tomlper package - Regenerate lock file
- Run pytest +
aicp --checkto verify the new pins resolve
Quality bar: NEVER pin to exact versions (e.g., ==X.Y.Z) for runtime
deps unless investigating a specific bug. Exact pins block transitive
security upgrades.
Operation 4 — Separate dev vs test vs runtime groups
When: dev-only or test-only deps are leaking into the runtime install.
Process:
- Inspect current
pyproject.tomlstructure — look forproject.optional-dependenciesgroups (typical:dev,test,lint) - Re-categorize each declared dep:
- Runtime (imported in
aicp/, not intests/): stays inproject.dependencies - Test-only (imported only in
tests/): move to[project.optional-dependencies] test - Dev-only (used by tooling, not imported): move to
[project.optional-dependencies] dev
- Runtime (imported in
- Update install instructions (Makefile, README, CI config) to use
pip install -e ".[dev,test]"for dev environments - Verify CI still passes (CI install command may need update)
Quality bar: a fresh pip install aicp (no extras) should produce a
working AICP CLI. If something breaks, a runtime dep was misclassified
as dev/test.
Operation 5 — Address a security-flagged dependency
When: a CVE or security advisory implicates a dep.
Process:
- Identify the affected version range
- Check for an upgrade:
pip index versions <package>— is there a patched version compatible with the existing constraint? - If yes: bump pin in
pyproject.toml, regenerate lock, run pytest - If no patched version exists yet: assess workarounds — disable the feature using the dep, or temporarily replace with a fork/alternative
- Document the decision in
wiki/decisions/00_inbox/<cve-id>-response.md(per Knowledge Evolution Standards — even short security responses benefit from a decision record)
Quality bar: NEVER ignore a CVE. If no patch exists, document the risk acceptance + monitoring plan.
Gotchas
Detection: agent uses Bash
pip install -Xdirectly without updating pyproject.toml. Rule: dependency changes go throughpyproject.tomlfirst, thenpip install -e .to reflect. Reasoning: ad-hoc pip installs don't survive environment recreation and produce silent drift between dev environments and CI.Detection: agent removes a dep without running pytest. Rule: every removal runs through pytest before declaring done. Reasoning: dynamically-imported deps (
importlib,try: import Xfallbacks) won't show inGrepaudits. The test suite catches them if test coverage is reasonable.Detection: agent pins all deps to exact versions. Rule: use range pins (
~=X.Y,>=X,<X+1) for runtime; only exact pin when investigating a specific bug. Reasoning: exact pins block transitive security upgrades and produce unfixable supply-chain risk.Detection: agent moves a runtime dep to the
[dev]group "to clean up". Rule: only move to dev IF the dep is genuinely not imported inaicp/. Verify withGrepfirst. Reasoning: misclassifying a runtime dep as dev breakspip install aicp(no extras) for end users — they get an ImportError they can't diagnose.Detection: agent skips lock file regeneration after pyproject.toml change. Rule: if a lock file exists (uv.lock, poetry.lock, requirements.lock), regenerate after every dep change. Reasoning: lock files are the reproducibility floor; stale locks produce divergence between developers and CI.
Reference exemplars
pyproject.toml— AICP's declared dependencies + extras groupsaicp/backends/localai.py— useshttpx(runtime dep), motivates HTTP client choiceaicp/cli/main.py— usesrichheavily for Tables/Console (runtime)tests/conftest.py— usespytestfixtures (test-only)wiki/decisions/01_drafts/localai-over-ollama-vllm-for-multi-model-orchestration.md— example of a dependency-shaping decision documented
Domain context
AICP runs as a Python 3.11+ product on Linux/WSL2. Dependencies
include: httpx (HTTP client), rich (CLI styling), pyyaml (config
parsing), mcp (MCP server framework), chromem or similar (vector
store via LocalAI), pytest + ruff (dev/test). The pyproject.toml is
the source of truth; CI installs via pip install -e ".[dev,test]".
Related skills
| Skill | When to use |
|---|---|
refactor-architecture |
When the dep structure reflects a package-level architecture issue |
infra-security |
When the audit is part of a broader security posture review |
quality-debt |
When dep cleanup is part of debt inventory |
feature-implement |
When ADDING a dep for a new feature (different from cleaning) |
evolve-integrate |
When adding a dep to integrate a NEW capability |