agentpack-cli 0.1.8__tar.gz → 0.1.9__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.
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/PKG-INFO +50 -19
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/README.md +49 -13
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/pyproject.toml +4 -8
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/__init__.py +1 -1
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/application/pack_service.py +1 -2
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/benchmark.py +0 -1
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/explain.py +0 -2
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/pack.py +2 -5
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/session.py +0 -1
- agentpack_cli-0.1.9/src/agentpack/commands/summarize.py +51 -0
- agentpack_cli-0.1.9/src/agentpack/mcp_server.py +195 -0
- agentpack_cli-0.1.9/src/agentpack/summaries/base.py +93 -0
- agentpack_cli-0.1.9/src/agentpack/summaries/offline.py +148 -0
- agentpack_cli-0.1.8/src/agentpack/commands/summarize.py +0 -64
- agentpack_cli-0.1.8/src/agentpack/mcp_server.py +0 -119
- agentpack_cli-0.1.8/src/agentpack/summaries/base.py +0 -42
- agentpack_cli-0.1.8/src/agentpack/summaries/llm.py +0 -100
- agentpack_cli-0.1.8/src/agentpack/summaries/offline.py +0 -97
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/.gitignore +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/LICENSE +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/antigravity.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/base.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/claude.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/codex.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/cursor.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/detect.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/generic.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/adapters/windsurf.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/dependency_graph.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/go_imports.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/java_imports.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/js_ts_imports.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/python_imports.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/ranking.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/rust_imports.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/symbols.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/analysis/tests.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/application/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/cli.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/_shared.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/claude_cmd.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/diff.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/doctor.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/init.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/install.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/mcp_cmd.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/monitor.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/scan.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/stats.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/status.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/commands/watch.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/bootstrap.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/cache.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/config.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/context_pack.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/diff.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/git.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/git_hooks.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/global_install.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/ignore.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/merkle.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/models.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/redactor.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/scanner.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/snapshot.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/token_estimator.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/core/vscode_tasks.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/data/agentpack.md +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/antigravity.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/claude.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/codex.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/cursor.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/installers/windsurf.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/integrations/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/integrations/git_hooks.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/integrations/global_install.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/integrations/vscode_tasks.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/renderers/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/renderers/compact.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/renderers/markdown.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/renderers/receipts.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/session/__init__.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/session/state.py +0 -0
- {agentpack_cli-0.1.8 → agentpack_cli-0.1.9}/src/agentpack/summaries/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentpack-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Token-aware context packing for AI coding agents — Claude, Cursor, Windsurf, and Codex
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -23,18 +23,13 @@ Requires-Dist: tomli-w>=1.0.0
|
|
|
23
23
|
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
24
24
|
Requires-Dist: typer>=0.12.0
|
|
25
25
|
Provides-Extra: all
|
|
26
|
-
Requires-Dist: anthropic>=0.39.0; extra == 'all'
|
|
27
26
|
Requires-Dist: mcp>=1.0.0; extra == 'all'
|
|
28
|
-
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
29
27
|
Requires-Dist: watchdog>=4.0.0; extra == 'all'
|
|
30
28
|
Provides-Extra: dev
|
|
31
29
|
Requires-Dist: mypy; extra == 'dev'
|
|
32
30
|
Requires-Dist: pytest; extra == 'dev'
|
|
33
31
|
Requires-Dist: ruff; extra == 'dev'
|
|
34
32
|
Requires-Dist: tomli>=2.0.0; (python_version < '3.11') and extra == 'dev'
|
|
35
|
-
Provides-Extra: llm
|
|
36
|
-
Requires-Dist: anthropic>=0.39.0; extra == 'llm'
|
|
37
|
-
Requires-Dist: openai>=1.0.0; extra == 'llm'
|
|
38
33
|
Provides-Extra: mcp
|
|
39
34
|
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
40
35
|
Provides-Extra: watch
|
|
@@ -48,7 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
48
43
|
[](https://opensource.org/licenses/MIT)
|
|
49
44
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
50
45
|
|
|
51
|
-
> **Status: alpha (v0.1.
|
|
46
|
+
> **Status: alpha (v0.1.9).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
|
|
52
47
|
>
|
|
53
48
|
> **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
|
|
54
49
|
|
|
@@ -598,14 +593,14 @@ All installs are idempotent — safe to re-run, merge with existing config, neve
|
|
|
598
593
|
|
|
599
594
|
### `agentpack summarize`
|
|
600
595
|
|
|
601
|
-
Build or refresh the offline summary cache. **No API calls.**
|
|
596
|
+
Build or refresh the offline summary cache. **No API calls, ever.**
|
|
602
597
|
|
|
603
598
|
```bash
|
|
604
599
|
agentpack summarize # build summaries for all files not yet cached
|
|
605
600
|
agentpack summarize --refresh # force rebuild all
|
|
606
601
|
```
|
|
607
602
|
|
|
608
|
-
Run
|
|
603
|
+
Summaries are built with parallel AST/regex analysis — no network, no tokens spent. Run once after `init`. After that, pack automatically rebuilds summaries only for changed files (hash-keyed cache).
|
|
609
604
|
|
|
610
605
|
---
|
|
611
606
|
|
|
@@ -635,6 +630,7 @@ Options:
|
|
|
635
630
|
| `--since` | — | Only include files changed since this git ref |
|
|
636
631
|
| `--session` | off | Re-pack on every file change (watch mode) |
|
|
637
632
|
| `--refresh` | off | Force rebuild summaries before packing |
|
|
633
|
+
| `--budget` | 25000 | Token budget override |
|
|
638
634
|
|
|
639
635
|
**Budget modes:**
|
|
640
636
|
|
|
@@ -699,6 +695,47 @@ Requires an initialized project (`agentpack init`). Refreshes context, prints th
|
|
|
699
695
|
|
|
700
696
|
---
|
|
701
697
|
|
|
698
|
+
### `agentpack mcp`
|
|
699
|
+
|
|
700
|
+
Run AgentPack as an MCP server — exposes context packing as tools that Claude Code (and any MCP-compatible agent) can call directly.
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
pip install "agentpack-cli[mcp]"
|
|
704
|
+
agentpack mcp
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Register in Claude Code settings (`~/.claude/settings.json`):
|
|
708
|
+
|
|
709
|
+
```json
|
|
710
|
+
{
|
|
711
|
+
"mcpServers": {
|
|
712
|
+
"agentpack": {
|
|
713
|
+
"command": "agentpack",
|
|
714
|
+
"args": ["mcp"]
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
**Tools exposed:**
|
|
721
|
+
|
|
722
|
+
| Tool | Description |
|
|
723
|
+
|---|---|
|
|
724
|
+
| `pack_context(task, mode, budget, max_tokens)` | Generate a ranked context pack for a task. Returns packed markdown, truncated to `max_tokens` (default 20,000). |
|
|
725
|
+
| `get_context()` | Return the latest pre-built pack instantly (no repack). Prepends a freshness/staleness header so you know if it's stale. |
|
|
726
|
+
| `refresh()` | Refresh using the current `task.md` or git-inferred task. |
|
|
727
|
+
|
|
728
|
+
**Staleness detection:** `get_context()` compares the snapshot hash from when the pack was built against the current repo snapshot. If files changed since last pack, it prepends:
|
|
729
|
+
```
|
|
730
|
+
> **Stale context** — repo changed since last pack (generated: ...). Run pack_context() to refresh.
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
**Smart truncation:** `pack_context()` keeps headers intact and trims file content blocks to fit the token budget, appending a note about how many files were omitted.
|
|
734
|
+
|
|
735
|
+
Zero API calls — all analysis is offline. Summary cache keyed by file hash: cold run parallelises AST parsing across CPU cores; warm cache hits are instant.
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
702
739
|
### `agentpack explain`
|
|
703
740
|
|
|
704
741
|
Debug file selection — show which files would be selected, why, and what was excluded — without writing a context pack.
|
|
@@ -920,10 +957,6 @@ include_tests = true
|
|
|
920
957
|
include_configs = true
|
|
921
958
|
include_receipts = true
|
|
922
959
|
|
|
923
|
-
[summary]
|
|
924
|
-
provider = "offline"
|
|
925
|
-
schema_version = 1
|
|
926
|
-
|
|
927
960
|
[agents.claude]
|
|
928
961
|
output = ".agentpack/context.claude.md"
|
|
929
962
|
patch_claude_md = true
|
|
@@ -1026,7 +1059,6 @@ Works like `.gitignore`. Default rules exclude:
|
|
|
1026
1059
|
│ miss → build from AST/regex, cache it │
|
|
1027
1060
|
│ │
|
|
1028
1061
|
│ offline ── AST / regex extract │
|
|
1029
|
-
│ claude ── Haiku API (optional) │
|
|
1030
1062
|
└────────────────────┬────────────────────┘
|
|
1031
1063
|
│
|
|
1032
1064
|
┌────────────────────▼────────────────────┐
|
|
@@ -1127,8 +1159,7 @@ src/agentpack/
|
|
|
1127
1159
|
|
|
1128
1160
|
summaries/
|
|
1129
1161
|
offline.py # zero-API: AST/regex → imports, symbols, summary
|
|
1130
|
-
|
|
1131
|
-
base.py # cache-or-build orchestration
|
|
1162
|
+
base.py # cache-or-build orchestration (parallel, ThreadPool+ProcessPool)
|
|
1132
1163
|
|
|
1133
1164
|
adapters/ # context rendering only — no installation logic
|
|
1134
1165
|
base.py # abstract BaseAdapter (output_path + render + write)
|
|
@@ -1365,7 +1396,7 @@ config_file = 60 # was 25 — configs always matter here
|
|
|
1365
1396
|
|
|
1366
1397
|
## Principles
|
|
1367
1398
|
|
|
1368
|
-
- **Local-first**: `init`, `scan`, `diff`, `pack`, `stats`, `summarize` make zero API calls
|
|
1399
|
+
- **Local-first**: `init`, `scan`, `diff`, `pack`, `stats`, `summarize` make zero API calls — ever. No optional LLM paths, no per-file costs.
|
|
1369
1400
|
- **Non-destructive**: never overwrites user files; config patching only touches agentpack-managed blocks
|
|
1370
1401
|
- **Agent-neutral**: architecture is generic; Claude Code is the primary target (deepest integration); Cursor, Windsurf, Codex, and Antigravity are supported but less battle-tested
|
|
1371
1402
|
- **No daemons**: file watching is opt-in via `agentpack watch`; git hooks run in the background and are opt-in via `install`
|
|
@@ -1387,9 +1418,9 @@ config_file = 60 # was 25 — configs always matter here
|
|
|
1387
1418
|
## Optional dependencies
|
|
1388
1419
|
|
|
1389
1420
|
```bash
|
|
1390
|
-
pip install "agentpack-cli[llm]" # anthropic — LLM summaries via Claude Haiku
|
|
1391
1421
|
pip install "agentpack-cli[watch]" # watchdog — faster file watching for agentpack watch
|
|
1392
|
-
pip install "agentpack-cli[
|
|
1422
|
+
pip install "agentpack-cli[mcp]" # mcp — expose agentpack as MCP server tools
|
|
1423
|
+
pip install "agentpack-cli[all]" # watch + mcp
|
|
1393
1424
|
```
|
|
1394
1425
|
|
|
1395
1426
|
---
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
|
-
> **Status: alpha (v0.1.
|
|
8
|
+
> **Status: alpha (v0.1.9).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
|
|
9
9
|
>
|
|
10
10
|
> **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
|
|
11
11
|
|
|
@@ -555,14 +555,14 @@ All installs are idempotent — safe to re-run, merge with existing config, neve
|
|
|
555
555
|
|
|
556
556
|
### `agentpack summarize`
|
|
557
557
|
|
|
558
|
-
Build or refresh the offline summary cache. **No API calls.**
|
|
558
|
+
Build or refresh the offline summary cache. **No API calls, ever.**
|
|
559
559
|
|
|
560
560
|
```bash
|
|
561
561
|
agentpack summarize # build summaries for all files not yet cached
|
|
562
562
|
agentpack summarize --refresh # force rebuild all
|
|
563
563
|
```
|
|
564
564
|
|
|
565
|
-
Run
|
|
565
|
+
Summaries are built with parallel AST/regex analysis — no network, no tokens spent. Run once after `init`. After that, pack automatically rebuilds summaries only for changed files (hash-keyed cache).
|
|
566
566
|
|
|
567
567
|
---
|
|
568
568
|
|
|
@@ -592,6 +592,7 @@ Options:
|
|
|
592
592
|
| `--since` | — | Only include files changed since this git ref |
|
|
593
593
|
| `--session` | off | Re-pack on every file change (watch mode) |
|
|
594
594
|
| `--refresh` | off | Force rebuild summaries before packing |
|
|
595
|
+
| `--budget` | 25000 | Token budget override |
|
|
595
596
|
|
|
596
597
|
**Budget modes:**
|
|
597
598
|
|
|
@@ -656,6 +657,47 @@ Requires an initialized project (`agentpack init`). Refreshes context, prints th
|
|
|
656
657
|
|
|
657
658
|
---
|
|
658
659
|
|
|
660
|
+
### `agentpack mcp`
|
|
661
|
+
|
|
662
|
+
Run AgentPack as an MCP server — exposes context packing as tools that Claude Code (and any MCP-compatible agent) can call directly.
|
|
663
|
+
|
|
664
|
+
```bash
|
|
665
|
+
pip install "agentpack-cli[mcp]"
|
|
666
|
+
agentpack mcp
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Register in Claude Code settings (`~/.claude/settings.json`):
|
|
670
|
+
|
|
671
|
+
```json
|
|
672
|
+
{
|
|
673
|
+
"mcpServers": {
|
|
674
|
+
"agentpack": {
|
|
675
|
+
"command": "agentpack",
|
|
676
|
+
"args": ["mcp"]
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Tools exposed:**
|
|
683
|
+
|
|
684
|
+
| Tool | Description |
|
|
685
|
+
|---|---|
|
|
686
|
+
| `pack_context(task, mode, budget, max_tokens)` | Generate a ranked context pack for a task. Returns packed markdown, truncated to `max_tokens` (default 20,000). |
|
|
687
|
+
| `get_context()` | Return the latest pre-built pack instantly (no repack). Prepends a freshness/staleness header so you know if it's stale. |
|
|
688
|
+
| `refresh()` | Refresh using the current `task.md` or git-inferred task. |
|
|
689
|
+
|
|
690
|
+
**Staleness detection:** `get_context()` compares the snapshot hash from when the pack was built against the current repo snapshot. If files changed since last pack, it prepends:
|
|
691
|
+
```
|
|
692
|
+
> **Stale context** — repo changed since last pack (generated: ...). Run pack_context() to refresh.
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**Smart truncation:** `pack_context()` keeps headers intact and trims file content blocks to fit the token budget, appending a note about how many files were omitted.
|
|
696
|
+
|
|
697
|
+
Zero API calls — all analysis is offline. Summary cache keyed by file hash: cold run parallelises AST parsing across CPU cores; warm cache hits are instant.
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
659
701
|
### `agentpack explain`
|
|
660
702
|
|
|
661
703
|
Debug file selection — show which files would be selected, why, and what was excluded — without writing a context pack.
|
|
@@ -877,10 +919,6 @@ include_tests = true
|
|
|
877
919
|
include_configs = true
|
|
878
920
|
include_receipts = true
|
|
879
921
|
|
|
880
|
-
[summary]
|
|
881
|
-
provider = "offline"
|
|
882
|
-
schema_version = 1
|
|
883
|
-
|
|
884
922
|
[agents.claude]
|
|
885
923
|
output = ".agentpack/context.claude.md"
|
|
886
924
|
patch_claude_md = true
|
|
@@ -983,7 +1021,6 @@ Works like `.gitignore`. Default rules exclude:
|
|
|
983
1021
|
│ miss → build from AST/regex, cache it │
|
|
984
1022
|
│ │
|
|
985
1023
|
│ offline ── AST / regex extract │
|
|
986
|
-
│ claude ── Haiku API (optional) │
|
|
987
1024
|
└────────────────────┬────────────────────┘
|
|
988
1025
|
│
|
|
989
1026
|
┌────────────────────▼────────────────────┐
|
|
@@ -1084,8 +1121,7 @@ src/agentpack/
|
|
|
1084
1121
|
|
|
1085
1122
|
summaries/
|
|
1086
1123
|
offline.py # zero-API: AST/regex → imports, symbols, summary
|
|
1087
|
-
|
|
1088
|
-
base.py # cache-or-build orchestration
|
|
1124
|
+
base.py # cache-or-build orchestration (parallel, ThreadPool+ProcessPool)
|
|
1089
1125
|
|
|
1090
1126
|
adapters/ # context rendering only — no installation logic
|
|
1091
1127
|
base.py # abstract BaseAdapter (output_path + render + write)
|
|
@@ -1322,7 +1358,7 @@ config_file = 60 # was 25 — configs always matter here
|
|
|
1322
1358
|
|
|
1323
1359
|
## Principles
|
|
1324
1360
|
|
|
1325
|
-
- **Local-first**: `init`, `scan`, `diff`, `pack`, `stats`, `summarize` make zero API calls
|
|
1361
|
+
- **Local-first**: `init`, `scan`, `diff`, `pack`, `stats`, `summarize` make zero API calls — ever. No optional LLM paths, no per-file costs.
|
|
1326
1362
|
- **Non-destructive**: never overwrites user files; config patching only touches agentpack-managed blocks
|
|
1327
1363
|
- **Agent-neutral**: architecture is generic; Claude Code is the primary target (deepest integration); Cursor, Windsurf, Codex, and Antigravity are supported but less battle-tested
|
|
1328
1364
|
- **No daemons**: file watching is opt-in via `agentpack watch`; git hooks run in the background and are opt-in via `install`
|
|
@@ -1344,9 +1380,9 @@ config_file = 60 # was 25 — configs always matter here
|
|
|
1344
1380
|
## Optional dependencies
|
|
1345
1381
|
|
|
1346
1382
|
```bash
|
|
1347
|
-
pip install "agentpack-cli[llm]" # anthropic — LLM summaries via Claude Haiku
|
|
1348
1383
|
pip install "agentpack-cli[watch]" # watchdog — faster file watching for agentpack watch
|
|
1349
|
-
pip install "agentpack-cli[
|
|
1384
|
+
pip install "agentpack-cli[mcp]" # mcp — expose agentpack as MCP server tools
|
|
1385
|
+
pip install "agentpack-cli[all]" # watch + mcp
|
|
1350
1386
|
```
|
|
1351
1387
|
|
|
1352
1388
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentpack-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.9"
|
|
4
4
|
description = "Token-aware context packing for AI coding agents — Claude, Cursor, Windsurf, and Codex"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -31,11 +31,6 @@ dependencies = [
|
|
|
31
31
|
agentpack = "agentpack.cli:app"
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
|
-
llm = [
|
|
35
|
-
"anthropic>=0.39.0",
|
|
36
|
-
"openai>=1.0.0"
|
|
37
|
-
]
|
|
38
|
-
|
|
39
34
|
watch = [
|
|
40
35
|
"watchdog>=4.0.0"
|
|
41
36
|
]
|
|
@@ -45,8 +40,6 @@ mcp = [
|
|
|
45
40
|
]
|
|
46
41
|
|
|
47
42
|
all = [
|
|
48
|
-
"anthropic>=0.39.0",
|
|
49
|
-
"openai>=1.0.0",
|
|
50
43
|
"watchdog>=4.0.0",
|
|
51
44
|
"mcp>=1.0.0"
|
|
52
45
|
]
|
|
@@ -60,6 +53,9 @@ dev = [
|
|
|
60
53
|
|
|
61
54
|
[tool.pytest.ini_options]
|
|
62
55
|
pythonpath = ["src"]
|
|
56
|
+
markers = [
|
|
57
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
58
|
+
]
|
|
63
59
|
|
|
64
60
|
[build-system]
|
|
65
61
|
requires = ["hatchling"]
|
|
@@ -31,7 +31,6 @@ class PackRequest:
|
|
|
31
31
|
budget: int
|
|
32
32
|
since: str | None
|
|
33
33
|
refresh: bool
|
|
34
|
-
summary_provider: str
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
@dataclass
|
|
@@ -176,7 +175,7 @@ class PackPlanner:
|
|
|
176
175
|
packable = scan_result.packable
|
|
177
176
|
|
|
178
177
|
t0 = time.perf_counter()
|
|
179
|
-
summaries_objs = build_all_summaries(packable, root
|
|
178
|
+
summaries_objs = build_all_summaries(packable, root)
|
|
180
179
|
summaries = {p: s.model_dump() for p, s in summaries_objs.items()}
|
|
181
180
|
phase_times["summarize"] = time.perf_counter() - t0
|
|
182
181
|
|
|
@@ -118,7 +118,6 @@ def register(app: typer.Typer) -> None:
|
|
|
118
118
|
mode: str = typer.Option("balanced", "--mode", help="Budget mode (minimal|balanced|deep)."),
|
|
119
119
|
budget: int = typer.Option(0, "--budget", help="Token budget (0 = use config default)."),
|
|
120
120
|
since: Optional[str] = typer.Option(None, "--since", help="Git ref to compare against (e.g. HEAD~1, main)."),
|
|
121
|
-
summary_provider: str = typer.Option("offline", "--summary-provider", help="Summary provider (offline|claude)."),
|
|
122
121
|
file: Optional[str] = typer.Option(None, "--file", help="Show detailed score breakdown for a specific file."),
|
|
123
122
|
omitted: bool = typer.Option(False, "--omitted", is_flag=True, help="Show top-10 excluded files and why."),
|
|
124
123
|
) -> None:
|
|
@@ -138,7 +137,6 @@ def register(app: typer.Typer) -> None:
|
|
|
138
137
|
budget=budget,
|
|
139
138
|
since=since,
|
|
140
139
|
refresh=False,
|
|
141
|
-
summary_provider=summary_provider,
|
|
142
140
|
)
|
|
143
141
|
|
|
144
142
|
with console.status("[bold]Planning..."):
|
|
@@ -25,7 +25,6 @@ def register(app: typer.Typer) -> None:
|
|
|
25
25
|
budget: int = typer.Option(0, "--budget", help="Token budget (0 = use config default)."),
|
|
26
26
|
since: Optional[str] = typer.Option(None, "--since", help="Git ref to compare against (e.g. HEAD~1, main)."),
|
|
27
27
|
refresh: bool = typer.Option(False, "--refresh", help="Rebuild summaries before packing."),
|
|
28
|
-
summary_provider: str = typer.Option("offline", "--summary-provider", help="Summary provider (offline|claude)."),
|
|
29
28
|
watch: bool = typer.Option(False, "--watch", help="Watch for file changes and re-pack automatically."),
|
|
30
29
|
session: bool = typer.Option(False, "--session", help="Keep re-packing on changes for the whole session (alias for --watch)."),
|
|
31
30
|
) -> None:
|
|
@@ -39,7 +38,7 @@ def register(app: typer.Typer) -> None:
|
|
|
39
38
|
|
|
40
39
|
if watch or session:
|
|
41
40
|
_pack_watch(agent=resolved_agent, task=resolved_task, mode=mode, budget=budget,
|
|
42
|
-
since=since
|
|
41
|
+
since=since)
|
|
43
42
|
return
|
|
44
43
|
|
|
45
44
|
result = PackService().run(PackRequest(
|
|
@@ -50,7 +49,6 @@ def register(app: typer.Typer) -> None:
|
|
|
50
49
|
budget=budget,
|
|
51
50
|
since=since,
|
|
52
51
|
refresh=refresh,
|
|
53
|
-
summary_provider=summary_provider,
|
|
54
52
|
))
|
|
55
53
|
_print_pack_summary(result)
|
|
56
54
|
|
|
@@ -159,7 +157,6 @@ def _pack_watch(
|
|
|
159
157
|
mode: str,
|
|
160
158
|
budget: int,
|
|
161
159
|
since: str | None,
|
|
162
|
-
summary_provider: str,
|
|
163
160
|
) -> None:
|
|
164
161
|
try:
|
|
165
162
|
from watchdog.observers import Observer
|
|
@@ -176,7 +173,7 @@ def _pack_watch(
|
|
|
176
173
|
def _run_pack() -> None:
|
|
177
174
|
result = PackService().run(PackRequest(
|
|
178
175
|
root=root, agent=agent, task=task, mode=mode, budget=budget,
|
|
179
|
-
since=since, refresh=False,
|
|
176
|
+
since=since, refresh=False,
|
|
180
177
|
))
|
|
181
178
|
_print_pack_summary(result)
|
|
182
179
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from agentpack.core.config import load_config
|
|
6
|
+
from agentpack.core.ignore import load_spec
|
|
7
|
+
from agentpack.core.scanner import scan
|
|
8
|
+
from agentpack.summaries.base import get_or_build_summary
|
|
9
|
+
from agentpack.commands._shared import console, _root
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register(app: typer.Typer) -> None:
|
|
13
|
+
@app.command()
|
|
14
|
+
def summarize(
|
|
15
|
+
refresh: bool = typer.Option(False, "--refresh", help="Force rebuild all summaries."),
|
|
16
|
+
) -> None:
|
|
17
|
+
"""Build or refresh offline summary cache (no API calls)."""
|
|
18
|
+
root = _root()
|
|
19
|
+
cfg = load_config(root)
|
|
20
|
+
ignore_spec = load_spec(root / cfg.project.ignore_file)
|
|
21
|
+
|
|
22
|
+
console.print("[bold]Building offline summaries...[/]")
|
|
23
|
+
|
|
24
|
+
scan_result = scan(root, ignore_spec, cfg.context.max_file_tokens)
|
|
25
|
+
active = scan_result.packable
|
|
26
|
+
|
|
27
|
+
if refresh:
|
|
28
|
+
from agentpack.core import cache as summary_cache
|
|
29
|
+
for fi in active:
|
|
30
|
+
if fi.hash:
|
|
31
|
+
cache_path = (
|
|
32
|
+
root / ".agentpack" / "cache" /
|
|
33
|
+
f"{summary_cache._cache_key(fi.path, fi.hash, 'offline', 1)}.json"
|
|
34
|
+
)
|
|
35
|
+
cache_path.unlink(missing_ok=True)
|
|
36
|
+
|
|
37
|
+
built = 0
|
|
38
|
+
errors = 0
|
|
39
|
+
for fi in active:
|
|
40
|
+
try:
|
|
41
|
+
get_or_build_summary(fi, root)
|
|
42
|
+
built += 1
|
|
43
|
+
except Exception as e:
|
|
44
|
+
console.print(f"[yellow]Warning:[/] {fi.path}: {e}")
|
|
45
|
+
errors += 1
|
|
46
|
+
|
|
47
|
+
console.print(f"[green]Done.[/] Built/refreshed {built} summaries.", end="")
|
|
48
|
+
if errors:
|
|
49
|
+
console.print(f" [yellow]{errors} errors.[/]")
|
|
50
|
+
else:
|
|
51
|
+
console.print()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""AgentPack MCP server — exposes context packing as MCP tools.
|
|
2
|
+
|
|
3
|
+
Start with:
|
|
4
|
+
agentpack mcp
|
|
5
|
+
|
|
6
|
+
Or register in Claude Code settings:
|
|
7
|
+
{
|
|
8
|
+
"mcpServers": {
|
|
9
|
+
"agentpack": {
|
|
10
|
+
"command": "agentpack",
|
|
11
|
+
"args": ["mcp"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Tools exposed:
|
|
17
|
+
pack_context — generate/refresh a context pack for a task
|
|
18
|
+
get_context — read the latest context pack (no repack)
|
|
19
|
+
refresh — refresh using the current task.md
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from agentpack.core.token_estimator import estimate_tokens
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _repo_root() -> Path:
|
|
31
|
+
"""Walk up from cwd until .agentpack/ found; fall back to cwd."""
|
|
32
|
+
cwd = Path.cwd()
|
|
33
|
+
for parent in [cwd, *cwd.parents]:
|
|
34
|
+
if (parent / ".agentpack").exists():
|
|
35
|
+
return parent
|
|
36
|
+
return cwd
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _truncate_to_budget(text: str, max_tokens: int = 20000) -> str:
|
|
40
|
+
"""Truncate packed context to fit within max_tokens (estimated via tiktoken, falls back to len//4)."""
|
|
41
|
+
if estimate_tokens(text) <= max_tokens:
|
|
42
|
+
return text
|
|
43
|
+
|
|
44
|
+
split_marker = "\n## File Context"
|
|
45
|
+
marker_pos = text.find(split_marker)
|
|
46
|
+
if marker_pos == -1:
|
|
47
|
+
budget_chars = max_tokens * 4
|
|
48
|
+
truncated = text[:budget_chars]
|
|
49
|
+
omit_files = max(1, (len(text) - budget_chars) // 2000)
|
|
50
|
+
return truncated + f"\n\n> [Truncated: {omit_files} files omitted to fit context window. Use get_context() to read full pack or narrow the task.]"
|
|
51
|
+
|
|
52
|
+
header = text[:marker_pos]
|
|
53
|
+
file_section = text[marker_pos:]
|
|
54
|
+
|
|
55
|
+
if estimate_tokens(header) >= max_tokens:
|
|
56
|
+
return header + "\n\n> [Truncated: file context omitted to fit context window. Use get_context() to read full pack or narrow the task.]"
|
|
57
|
+
|
|
58
|
+
blocks = file_section.split("\n### ")
|
|
59
|
+
# blocks[0] is the "## File Context" heading; blocks[1:] are individual files
|
|
60
|
+
accumulated = blocks[0]
|
|
61
|
+
total_files = len(blocks) - 1
|
|
62
|
+
kept_files = 0
|
|
63
|
+
for block in blocks[1:]:
|
|
64
|
+
candidate = accumulated + "\n### " + block
|
|
65
|
+
if estimate_tokens(header + candidate) > max_tokens:
|
|
66
|
+
break
|
|
67
|
+
accumulated = candidate
|
|
68
|
+
kept_files += 1
|
|
69
|
+
|
|
70
|
+
omitted = total_files - kept_files
|
|
71
|
+
if omitted > 0:
|
|
72
|
+
return header + accumulated + f"\n\n> [Truncated: {omitted} files omitted to fit context window. Use get_context() to read full pack or narrow the task.]"
|
|
73
|
+
return header + accumulated
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_context_impl(root: Path) -> str:
|
|
77
|
+
"""Read the latest pre-built context pack from root, with staleness header."""
|
|
78
|
+
pack_path = None
|
|
79
|
+
for candidate in (
|
|
80
|
+
root / ".agentpack" / "context.claude.md",
|
|
81
|
+
root / ".agentpack" / "context.md",
|
|
82
|
+
):
|
|
83
|
+
if candidate.exists():
|
|
84
|
+
pack_path = candidate
|
|
85
|
+
break
|
|
86
|
+
if pack_path is None:
|
|
87
|
+
return ""
|
|
88
|
+
|
|
89
|
+
content = pack_path.read_text(encoding="utf-8")
|
|
90
|
+
|
|
91
|
+
metadata_path = root / ".agentpack" / "pack_metadata.json"
|
|
92
|
+
snapshot_path = root / ".agentpack" / "snapshots" / "latest.json"
|
|
93
|
+
|
|
94
|
+
metadata = None
|
|
95
|
+
if metadata_path.exists():
|
|
96
|
+
try:
|
|
97
|
+
metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
|
|
98
|
+
except Exception:
|
|
99
|
+
metadata = None
|
|
100
|
+
|
|
101
|
+
snapshot = None
|
|
102
|
+
if snapshot_path.exists():
|
|
103
|
+
try:
|
|
104
|
+
snapshot = json.loads(snapshot_path.read_text(encoding="utf-8"))
|
|
105
|
+
except Exception:
|
|
106
|
+
snapshot = None
|
|
107
|
+
|
|
108
|
+
generated_at = metadata.get("generated_at", "unknown") if metadata else "unknown"
|
|
109
|
+
token_estimate = metadata.get("token_estimate", 0) if metadata else 0
|
|
110
|
+
|
|
111
|
+
if metadata is None or snapshot is None or metadata.get("snapshot_root_hash") != snapshot.get("root_hash"):
|
|
112
|
+
header = f"> **Stale context** — repo changed since last pack (generated: {generated_at}). Run pack_context() to refresh.\n\n"
|
|
113
|
+
else:
|
|
114
|
+
header = f"> Context is fresh (generated: {generated_at}, {token_estimate:,} tokens).\n\n"
|
|
115
|
+
|
|
116
|
+
return header + content
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def serve() -> None:
|
|
120
|
+
try:
|
|
121
|
+
from mcp.server.fastmcp import FastMCP
|
|
122
|
+
except ImportError:
|
|
123
|
+
print(
|
|
124
|
+
"mcp package required for MCP server. "
|
|
125
|
+
"Install: pip install 'agentpack-cli[mcp]'",
|
|
126
|
+
file=sys.stderr,
|
|
127
|
+
)
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
|
|
130
|
+
mcp = FastMCP("agentpack")
|
|
131
|
+
|
|
132
|
+
@mcp.tool()
|
|
133
|
+
def pack_context(task: str, mode: str = "balanced", budget: int = 0, max_tokens: int = 20000) -> str:
|
|
134
|
+
"""Generate a ranked context pack for the given task.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
task: Describe what you're working on (e.g. "fix auth token refresh").
|
|
138
|
+
mode: minimal | balanced (default) | deep
|
|
139
|
+
budget: Token budget, 0 = config default (usually 25000).
|
|
140
|
+
max_tokens: Maximum tokens to return (default 20000). Increase for deep context.
|
|
141
|
+
|
|
142
|
+
Returns the packed context as a markdown string.
|
|
143
|
+
"""
|
|
144
|
+
from agentpack.application.pack_service import PackService, PackRequest
|
|
145
|
+
from agentpack.adapters.detect import detect_agent
|
|
146
|
+
from agentpack.renderers.markdown import render_claude
|
|
147
|
+
|
|
148
|
+
root = _repo_root()
|
|
149
|
+
agent = detect_agent(root)
|
|
150
|
+
result = PackService().run(PackRequest(
|
|
151
|
+
root=root,
|
|
152
|
+
agent=agent,
|
|
153
|
+
task=task,
|
|
154
|
+
mode=mode,
|
|
155
|
+
budget=budget,
|
|
156
|
+
since=None,
|
|
157
|
+
refresh=False,
|
|
158
|
+
))
|
|
159
|
+
return _truncate_to_budget(render_claude(result.pack), max_tokens)
|
|
160
|
+
|
|
161
|
+
@mcp.tool()
|
|
162
|
+
def get_context() -> str:
|
|
163
|
+
"""Return the latest pre-built context pack without repacking.
|
|
164
|
+
|
|
165
|
+
Fast — just reads the cached file. Use pack_context() to regenerate.
|
|
166
|
+
Returns empty string if no pack exists yet.
|
|
167
|
+
"""
|
|
168
|
+
return _get_context_impl(_repo_root())
|
|
169
|
+
|
|
170
|
+
@mcp.tool()
|
|
171
|
+
def refresh() -> str:
|
|
172
|
+
"""Refresh context using the current task.md (or git-inferred task).
|
|
173
|
+
|
|
174
|
+
Equivalent to running `agentpack session refresh`.
|
|
175
|
+
Returns summary of what was packed.
|
|
176
|
+
"""
|
|
177
|
+
from agentpack.commands.session import _run_refresh
|
|
178
|
+
from agentpack.session.state import load_session
|
|
179
|
+
from agentpack.adapters.detect import detect_agent
|
|
180
|
+
|
|
181
|
+
root = _repo_root()
|
|
182
|
+
state = load_session(root)
|
|
183
|
+
agent = state.agent if state else detect_agent(root)
|
|
184
|
+
mode = state.mode if state else "balanced"
|
|
185
|
+
|
|
186
|
+
result = _run_refresh(root, agent, mode, 0)
|
|
187
|
+
if result is None:
|
|
188
|
+
return "Refresh failed."
|
|
189
|
+
return (
|
|
190
|
+
f"Refreshed: {result['files']} files, "
|
|
191
|
+
f"{result['tokens']:,} tokens, "
|
|
192
|
+
f"{result['saving']:.1f}% saving"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
mcp.run()
|