libcontext 0.5.0__tar.gz → 0.6.1__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 (45) hide show
  1. {libcontext-0.5.0 → libcontext-0.6.1}/CHANGELOG.md +10 -1
  2. {libcontext-0.5.0 → libcontext-0.6.1}/PKG-INFO +4 -4
  3. {libcontext-0.5.0 → libcontext-0.6.1}/README.md +3 -3
  4. {libcontext-0.5.0 → libcontext-0.6.1}/pyproject.toml +1 -1
  5. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/collector.py +14 -2
  6. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_collector.py +50 -10
  7. {libcontext-0.5.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  8. {libcontext-0.5.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  9. {libcontext-0.5.0 → libcontext-0.6.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  10. {libcontext-0.5.0 → libcontext-0.6.1}/.github/workflows/ci.yml +0 -0
  11. {libcontext-0.5.0 → libcontext-0.6.1}/.github/workflows/release.yml +0 -0
  12. {libcontext-0.5.0 → libcontext-0.6.1}/.gitignore +0 -0
  13. {libcontext-0.5.0 → libcontext-0.6.1}/CONTRIBUTING.md +0 -0
  14. {libcontext-0.5.0 → libcontext-0.6.1}/DEPENDENCIES.md +0 -0
  15. {libcontext-0.5.0 → libcontext-0.6.1}/LICENSE +0 -0
  16. {libcontext-0.5.0 → libcontext-0.6.1}/SECURITY.md +0 -0
  17. {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/001-progressive-disclosure-over-always-on-context.md +0 -0
  18. {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/002-skill-plus-cli-as-primary-integration.md +0 -0
  19. {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/004-ast-only-inspection.md +0 -0
  20. {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/README.md +0 -0
  21. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/__init__.py +0 -0
  22. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/_envsetup.py +0 -0
  23. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/_security.py +0 -0
  24. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/cache.py +0 -0
  25. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/cli.py +0 -0
  26. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/config.py +0 -0
  27. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/diff.py +0 -0
  28. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/exceptions.py +0 -0
  29. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/inspector.py +0 -0
  30. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/mcp_server.py +0 -0
  31. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/models.py +0 -0
  32. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/py.typed +0 -0
  33. {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/renderer.py +0 -0
  34. {libcontext-0.5.0 → libcontext-0.6.1}/tests/__init__.py +0 -0
  35. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cache.py +0 -0
  36. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cli.py +0 -0
  37. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cli_mcp_parity.py +0 -0
  38. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_config.py +0 -0
  39. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_diff.py +0 -0
  40. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_envsetup.py +0 -0
  41. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_inspector.py +0 -0
  42. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_mcp_server.py +0 -0
  43. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_models.py +0 -0
  44. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_renderer.py +0 -0
  45. {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_security.py +0 -0
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.1] - 2026-03-25
11
+
12
+ ### Fixed
13
+
14
+ - **ImportError crash with `packages_distributions()`**: wrapped call in try/except to handle broken metadata or mixed Python installations (e.g. `_csv` ImportError on Python 3.13).
15
+ - **Python 3.9 compatibility**: fixed parenthesized context managers and `create=True` for `mock.patch`.
16
+
10
17
  ## [0.5.0] - 2026-03-23
11
18
 
12
19
  ### Added
@@ -101,7 +108,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
101
108
  - Free-form `extra_context` field for library authors.
102
109
  - Python API for programmatic usage (`collect_package`, `render_package`).
103
110
 
104
- [Unreleased]: https://github.com/Syclaw/libcontext/compare/v0.5.0...HEAD
111
+ [Unreleased]: https://github.com/Syclaw/libcontext/compare/v0.6.1...HEAD
112
+ [0.6.1]: https://github.com/Syclaw/libcontext/compare/v0.6.0...v0.6.1
113
+ [0.6.0]: https://github.com/Syclaw/libcontext/compare/v0.5.0...v0.6.0
105
114
  [0.5.0]: https://github.com/Syclaw/libcontext/compare/v0.4.0...v0.5.0
106
115
  [0.4.0]: https://github.com/Syclaw/libcontext/compare/v0.3.0...v0.4.0
107
116
  [0.3.0]: https://github.com/Syclaw/libcontext/compare/v0.2.0...v0.3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libcontext
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Summary: Generate optimized LLM context from Python library APIs — CLI, skill, and MCP server
5
5
  Project-URL: Homepage, https://github.com/Syclaw/libcontext
6
6
  Project-URL: Repository, https://github.com/Syclaw/libcontext
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  > Context-efficient API references for LLM toolchains — structured, on-demand, not always-on.
41
41
 
42
- **libcontext** inspects any installed Python package via static AST analysis (no code execution) and generates compact Markdown API references. It integrates with Claude Code (via a `/lib` skill) and VS Code Copilot (via an MCP server) to provide **progressive disclosure** — only loading API context when you actually need it, avoiding context window pollution.
42
+ **libcontext** inspects any installed Python package via static AST analysis (no code execution) and generates compact Markdown API references. It integrates with Claude Code, GitHub Copilot (via a `/lib` skill), and VS Code / Cursor (via an MCP server) to provide **progressive disclosure** — only loading API context when you actually need it, avoiding context window pollution.
43
43
 
44
44
  ## Why This Exists
45
45
 
@@ -107,7 +107,7 @@ libctx inspect requests libctx inspect requests libctx inspect requests
107
107
  (no signatures) for one module across all modules
108
108
  ```
109
109
 
110
- The `/lib` skill (Claude Code) and MCP server (VS Code / Cursor) automate this workflow — the AI assistant decides what to inspect based on the task at hand.
110
+ The `/lib` skill (Claude Code, GitHub Copilot) and MCP server (Claude Code, VS Code, Cursor) automate this workflow — the AI assistant decides what to inspect based on the task at hand.
111
111
 
112
112
  ### Direct CLI Usage
113
113
 
@@ -332,7 +332,7 @@ All async operations use httpx internally.
332
332
  | `diff.py` | API diff between two package versions with breaking change detection |
333
333
  | `cache.py` | Persistent disk cache with mtime/file-count invalidation and LRU eviction |
334
334
  | `cli.py` | CLI entry point — `inspect`, `install`, `diff`, and `cache` subcommands |
335
- | `mcp_server.py` | MCP server for VS Code / Cursor integration (optional) |
335
+ | `mcp_server.py` | MCP server for Claude Code / VS Code / Cursor integration (optional) |
336
336
 
337
337
  ## Development
338
338
 
@@ -10,7 +10,7 @@
10
10
 
11
11
  > Context-efficient API references for LLM toolchains — structured, on-demand, not always-on.
12
12
 
13
- **libcontext** inspects any installed Python package via static AST analysis (no code execution) and generates compact Markdown API references. It integrates with Claude Code (via a `/lib` skill) and VS Code Copilot (via an MCP server) to provide **progressive disclosure** — only loading API context when you actually need it, avoiding context window pollution.
13
+ **libcontext** inspects any installed Python package via static AST analysis (no code execution) and generates compact Markdown API references. It integrates with Claude Code, GitHub Copilot (via a `/lib` skill), and VS Code / Cursor (via an MCP server) to provide **progressive disclosure** — only loading API context when you actually need it, avoiding context window pollution.
14
14
 
15
15
  ## Why This Exists
16
16
 
@@ -78,7 +78,7 @@ libctx inspect requests libctx inspect requests libctx inspect requests
78
78
  (no signatures) for one module across all modules
79
79
  ```
80
80
 
81
- The `/lib` skill (Claude Code) and MCP server (VS Code / Cursor) automate this workflow — the AI assistant decides what to inspect based on the task at hand.
81
+ The `/lib` skill (Claude Code, GitHub Copilot) and MCP server (Claude Code, VS Code, Cursor) automate this workflow — the AI assistant decides what to inspect based on the task at hand.
82
82
 
83
83
  ### Direct CLI Usage
84
84
 
@@ -303,7 +303,7 @@ All async operations use httpx internally.
303
303
  | `diff.py` | API diff between two package versions with breaking change detection |
304
304
  | `cache.py` | Persistent disk cache with mtime/file-count invalidation and LRU eviction |
305
305
  | `cli.py` | CLI entry point — `inspect`, `install`, `diff`, and `cache` subcommands |
306
- | `mcp_server.py` | MCP server for VS Code / Cursor integration (optional) |
306
+ | `mcp_server.py` | MCP server for Claude Code / VS Code / Cursor integration (optional) |
307
307
 
308
308
  ## Development
309
309
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "libcontext"
7
- version = "0.5.0"
7
+ version = "0.6.1"
8
8
  description = "Generate optimized LLM context from Python library APIs — CLI, skill, and MCP server"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -47,8 +47,20 @@ def _get_installed_package_names() -> list[str]:
47
47
  names: set[str] = set()
48
48
 
49
49
  if sys.version_info >= (3, 11):
50
- for import_name in importlib.metadata.packages_distributions():
51
- names.add(import_name)
50
+ try:
51
+ for import_name in importlib.metadata.packages_distributions():
52
+ names.add(import_name)
53
+ except (ImportError, Exception):
54
+ # packages_distributions() may fail when the environment
55
+ # contains distributions with broken metadata or when a
56
+ # mixed Python installation causes an ImportError inside
57
+ # stdlib modules (e.g. csv). Fall through to the
58
+ # distributions()-based collection below which is more
59
+ # resilient.
60
+ logger.debug(
61
+ "packages_distributions() failed; falling back to distributions() only",
62
+ exc_info=True,
63
+ )
52
64
 
53
65
  seen_distributions: set[str] = set()
54
66
  for dist in importlib.metadata.distributions():
@@ -676,26 +676,66 @@ def test_suggest_similar_packages_uses_top_level_names() -> None:
676
676
  assert "PIL" in suggestions
677
677
 
678
678
 
679
+ _PKGS_DISTS_PATH = "libcontext.collector.importlib.metadata.packages_distributions"
680
+
681
+
682
+ def test_get_installed_package_names_handles_import_error() -> None:
683
+ """packages_distributions() raising ImportError is caught gracefully."""
684
+ dists = [
685
+ _make_mock_dist("requests"),
686
+ ]
687
+ err = ImportError("cannot import name '__version__' from '_csv'")
688
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
689
+ with patch(_DISTS_PATH, return_value=dists):
690
+ names = _get_installed_package_names()
691
+
692
+ # Should still collect names from distributions() fallback
693
+ assert "requests" in names
694
+
695
+
696
+ def test_get_installed_package_names_handles_generic_exception() -> None:
697
+ """packages_distributions() raising any Exception is caught gracefully."""
698
+ dists = [
699
+ _make_mock_dist("flask"),
700
+ ]
701
+ err = RuntimeError("broken metadata")
702
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
703
+ with patch(_DISTS_PATH, return_value=dists):
704
+ names = _get_installed_package_names()
705
+
706
+ assert "flask" in names
707
+
708
+
709
+ def test_suggest_similar_packages_resilient_to_import_error() -> None:
710
+ """suggest_similar_packages works even if packages_distributions() fails."""
711
+ dists = [
712
+ _make_mock_dist("requests"),
713
+ _make_mock_dist("flask"),
714
+ ]
715
+ err = ImportError("csv import broken")
716
+ with patch(_PKGS_DISTS_PATH, side_effect=err, create=True): # noqa: SIM117
717
+ with patch(_DISTS_PATH, return_value=dists):
718
+ suggestions = suggest_similar_packages("reqeusts")
719
+
720
+ assert "requests" in suggestions
721
+
722
+
679
723
  def test_collect_package_error_includes_suggestions() -> None:
680
724
  """PackageNotFoundError from collect_package includes suggestions."""
681
725
  dists = [
682
726
  _make_mock_dist("click"),
683
727
  _make_mock_dist("flask"),
684
728
  ]
685
- with (
686
- patch(_DISTS_PATH, return_value=dists),
687
- pytest.raises(PackageNotFoundError, match="Did you mean"),
688
- ):
689
- collect_package("clck")
729
+ with patch(_DISTS_PATH, return_value=dists): # noqa: SIM117
730
+ with pytest.raises(PackageNotFoundError, match="Did you mean"):
731
+ collect_package("clck")
690
732
 
691
733
 
692
734
  def test_collect_package_error_no_suggestions() -> None:
693
735
  """PackageNotFoundError without suggestions shows install hint."""
694
- with (
695
- patch(_DISTS_PATH, return_value=[]),
696
- pytest.raises(PackageNotFoundError, match="Make sure it is installed"),
697
- ):
698
- collect_package("totally_nonexistent_pkg_xyz_999")
736
+ with patch(_DISTS_PATH, return_value=[]): # noqa: SIM117
737
+ with pytest.raises(PackageNotFoundError, match="Make sure it is installed"):
738
+ collect_package("totally_nonexistent_pkg_xyz_999")
699
739
 
700
740
 
701
741
  # ---------------------------------------------------------------------------
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes