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.
- {libcontext-0.5.0 → libcontext-0.6.1}/CHANGELOG.md +10 -1
- {libcontext-0.5.0 → libcontext-0.6.1}/PKG-INFO +4 -4
- {libcontext-0.5.0 → libcontext-0.6.1}/README.md +3 -3
- {libcontext-0.5.0 → libcontext-0.6.1}/pyproject.toml +1 -1
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/collector.py +14 -2
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_collector.py +50 -10
- {libcontext-0.5.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/.github/workflows/ci.yml +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/.github/workflows/release.yml +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/.gitignore +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/CONTRIBUTING.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/DEPENDENCIES.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/LICENSE +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/SECURITY.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/001-progressive-disclosure-over-always-on-context.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/002-skill-plus-cli-as-primary-integration.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/004-ast-only-inspection.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/README.md +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/__init__.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/_envsetup.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/_security.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/cache.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/cli.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/config.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/diff.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/exceptions.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/inspector.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/mcp_server.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/models.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/py.typed +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/src/libcontext/renderer.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/__init__.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cache.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cli.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_cli_mcp_parity.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_config.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_diff.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_envsetup.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_inspector.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_mcp_server.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_models.py +0 -0
- {libcontext-0.5.0 → libcontext-0.6.1}/tests/test_renderer.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
687
|
-
|
|
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
|
-
|
|
696
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{libcontext-0.5.0 → libcontext-0.6.1}/docs/adr/001-progressive-disclosure-over-always-on-context.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|