agentpack-cli 0.3.4__tar.gz → 0.3.5__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 (97) hide show
  1. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/PKG-INFO +79 -10
  2. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/README.md +78 -9
  3. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/pyproject.toml +1 -1
  4. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/__init__.py +1 -1
  5. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/application/pack_service.py +4 -14
  6. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/cli.py +4 -0
  7. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/doctor.py +16 -0
  8. agentpack_cli-0.3.5/src/agentpack/commands/guard.py +129 -0
  9. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/hook_cmd.py +77 -25
  10. agentpack_cli-0.3.5/src/agentpack/commands/migrate.py +182 -0
  11. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/pack.py +42 -0
  12. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/status.py +11 -17
  13. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/config.py +3 -0
  14. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/context_pack.py +9 -0
  15. agentpack_cli-0.3.5/src/agentpack/core/task_freshness.py +79 -0
  16. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/data/agentpack.md +5 -5
  17. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/antigravity.py +5 -4
  18. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/claude.py +15 -1
  19. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/codex.py +5 -4
  20. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/cursor.py +12 -10
  21. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/windsurf.py +6 -5
  22. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/agents.py +85 -9
  23. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/vscode_tasks.py +11 -2
  24. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/mcp_server.py +36 -29
  25. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/renderers/markdown.py +38 -0
  26. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/.gitignore +0 -0
  27. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/LICENSE +0 -0
  28. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/__init__.py +0 -0
  29. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/antigravity.py +0 -0
  30. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/base.py +0 -0
  31. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/claude.py +0 -0
  32. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/codex.py +0 -0
  33. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/cursor.py +0 -0
  34. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/detect.py +0 -0
  35. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/generic.py +0 -0
  36. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/adapters/windsurf.py +0 -0
  37. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/__init__.py +0 -0
  38. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/dependency_graph.py +0 -0
  39. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/go_imports.py +0 -0
  40. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/java_imports.py +0 -0
  41. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/js_ts_imports.py +0 -0
  42. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/monorepo.py +0 -0
  43. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/naming_signals.py +0 -0
  44. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/python_imports.py +0 -0
  45. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/ranking.py +0 -0
  46. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/repo_map.py +0 -0
  47. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/role_inference.py +0 -0
  48. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/rust_imports.py +0 -0
  49. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/symbols.py +0 -0
  50. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/task_classifier.py +0 -0
  51. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/analysis/tests.py +0 -0
  52. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/application/__init__.py +0 -0
  53. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/__init__.py +0 -0
  54. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/_shared.py +0 -0
  55. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/benchmark.py +0 -0
  56. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/claude_cmd.py +0 -0
  57. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/diff.py +0 -0
  58. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/explain.py +0 -0
  59. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/init.py +0 -0
  60. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/install.py +0 -0
  61. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/mcp_cmd.py +0 -0
  62. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/monitor.py +0 -0
  63. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/quickstart.py +0 -0
  64. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/repair.py +0 -0
  65. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/scan.py +0 -0
  66. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/stats.py +0 -0
  67. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/summarize.py +0 -0
  68. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/tune.py +0 -0
  69. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/commands/watch.py +0 -0
  70. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/__init__.py +0 -0
  71. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/bootstrap.py +0 -0
  72. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/cache.py +0 -0
  73. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/diff.py +0 -0
  74. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/git.py +0 -0
  75. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/git_hooks.py +0 -0
  76. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/global_install.py +0 -0
  77. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/ignore.py +0 -0
  78. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/merkle.py +0 -0
  79. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/models.py +0 -0
  80. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/redactor.py +0 -0
  81. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/scanner.py +0 -0
  82. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/snapshot.py +0 -0
  83. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/token_estimator.py +0 -0
  84. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/core/vscode_tasks.py +0 -0
  85. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/installers/__init__.py +0 -0
  86. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/__init__.py +0 -0
  87. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/git_hooks.py +0 -0
  88. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/global_install.py +0 -0
  89. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/integrations/platform.py +0 -0
  90. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/renderers/__init__.py +0 -0
  91. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/renderers/compact.py +0 -0
  92. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/renderers/receipts.py +0 -0
  93. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/session/__init__.py +0 -0
  94. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/session/state.py +0 -0
  95. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/summaries/__init__.py +0 -0
  96. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/summaries/base.py +0 -0
  97. {agentpack_cli-0.3.4 → agentpack_cli-0.3.5}/src/agentpack/summaries/offline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentpack-cli
3
- Version: 0.3.4
3
+ Version: 0.3.5
4
4
  Summary: Local context engine for AI coding agents that ranks relevant files and builds task-focused context packs.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
46
46
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
47
  [![CI](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg)](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
48
48
 
49
- > **Status: alpha (v0.3.4).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
49
+ > **Status: alpha (v0.3.5).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
50
50
  >
51
51
  > **Platform note:** macOS, Linux, and Windows are supported. Windows support targets PowerShell plus Git for Windows. `cmd.exe` and bare Git setups are not a supported path yet.
52
52
 
@@ -77,7 +77,7 @@ Use AgentPack when a repo is too large to paste and you want faster, more consis
77
77
  - **Budget-aware compression**: emits `full`, `diff`, `symbols`, `skeleton`, or `summary` views instead of all-or-nothing file dumps.
78
78
  - **Local code intelligence**: extracts roles, domains, entrypoints, definitions, dependencies, env reads, side effects, and external systems using static analysis.
79
79
  - **Semantic repo map**: adds a compact module-level map before file context so agents orient faster.
80
- - **Freshness and deltas**: records task source, git state, snapshot hashes, selected-file deltas, and stale-context warnings.
80
+ - **Freshness and deltas**: records task source, git state, snapshot hashes, selected-file deltas, stale-context warnings, MCP auto-refresh signals, and a machine-readable `agentpack:freshness` block in markdown fallback artifacts.
81
81
  - **Agent integrations**: installs Claude Code, Cursor, Windsurf, Codex, Antigravity, VS Code tasks, git hooks, and MCP configuration.
82
82
  - **Local and measurable**: no API calls for scan, summarize, rank, pack, stats, or benchmark; quality is measured with expected-file evals.
83
83
 
@@ -188,7 +188,7 @@ AgentPack is best treated as a **ranked starting map**. It should reduce repeate
188
188
  | Pack time | Seconds on a warm cache; first summarize pass is slower |
189
189
  | Recall | Expected files appear near the top; validate with `agentpack benchmark --misses` |
190
190
  | Precision | Good enough to reduce exploration; summaries and repo maps may still include noise |
191
- | Freshness | Stale packs are clearly marked by task, git, and snapshot checks |
191
+ | Freshness | Task or repo-stale MCP reads auto-refresh; static packs are clearly marked by task, git, and snapshot checks |
192
192
 
193
193
  Use real repo evals instead of trusting compression numbers:
194
194
 
@@ -246,12 +246,22 @@ This is the core reliability loop: pack, measure recall, inspect misses, then tu
246
246
  For MCP-capable agents, the preferred workflow is pull-based:
247
247
 
248
248
  1. Call `start_task(task)` when a new task begins. AgentPack writes `.agentpack/task.md`, packs context, and returns ranked markdown.
249
- 2. Call `get_context()` when you need the latest cached pack; it tells you if the pack is stale.
249
+ 2. Call `get_context()` when you need the latest pack. It blocks for one refresh if `.agentpack/task.md` or the repo snapshot changed since the last pack, and otherwise prepends a freshness header.
250
250
  3. Call `get_delta_context()` after edits or hook hints to see what changed without loading the full pack.
251
251
  4. Call `explain_file(path)` or `get_related_files(path)` when a file looks relevant or suspicious.
252
252
 
253
253
  The CLI remains the setup/debug/release path. MCP is the best interactive path because the agent can ask for only the context it needs instead of relying on one static startup blob.
254
254
 
255
+ Markdown context files are fallback artifacts for CI, logs, manual review, and non-MCP agents. Every rendered pack includes a machine-readable `agentpack:freshness` comment; agents should treat `active_context: mcp` as the preferred path and refresh before using markdown when `refresh_required: true`.
256
+
257
+ For non-MCP agents, use the executable guard before editing:
258
+
259
+ ```bash
260
+ agentpack guard --agent auto --repair-stale --refresh-context
261
+ ```
262
+
263
+ `guard` checks pack freshness, task freshness, repo snapshot freshness, and installed agent rules/hooks. With `--repair-stale --refresh-context`, it repairs stale AgentPack rule files and refreshes missing or stale context before returning success. `agentpack pack` also self-heals stale AgentPack rule blocks for the active agent, so older installs that still run `pack` get upgraded opportunistically.
264
+
255
265
  ## Before / After Agent Behavior
256
266
 
257
267
  Without AgentPack:
@@ -365,7 +375,7 @@ _*`--agent generic` outputs standard markdown. Claude adapter has richer instruc
365
375
 
366
376
  - AgentPack cannot intercept prompts inside IDEs — Cursor/Windsurf rely on rules being followed.
367
377
  - Claude wrapper (`agentpack claude`) is the most deterministic integration.
368
- - If the task changes drastically mid-session, context needs one refresh cycle.
378
+ - If the task changes drastically mid-session, Claude hooks update `.agentpack/task.md` and block once for fresh hints; plain repo edits still use background repack to keep prompts fast.
369
379
  - AgentPack-selected files are ranked starting points, not absolute truth.
370
380
 
371
381
  ---
@@ -733,6 +743,36 @@ agentpack repair --agent all # repair every supported integration
733
743
 
734
744
  ---
735
745
 
746
+ ### `agentpack guard`
747
+
748
+ Run the pre-edit safety gate an agent can execute instead of only reading instructions.
749
+
750
+ ```bash
751
+ agentpack guard # check current agent + context
752
+ agentpack guard --refresh-context # refresh stale/missing context
753
+ agentpack guard --agent codex --repair-stale # repair stale Codex rules/hooks
754
+ agentpack guard --agent auto --repair-stale --refresh-context
755
+ ```
756
+
757
+ This is the strongest non-native enforcement AgentPack can provide: tools that run commands get a failing exit code when context is unsafe, and an automatic repair/refresh path when allowed.
758
+
759
+ ---
760
+
761
+ ### `agentpack migrate`
762
+
763
+ Repair stale AgentPack integrations across existing repos after upgrading.
764
+
765
+ ```bash
766
+ agentpack migrate --path . --agent auto
767
+ agentpack migrate --path ~/src --discover --agent all
768
+ agentpack migrate --path ~/src --discover --agent codex --refresh-context
769
+ agentpack migrate --path ~/src --discover --dry-run
770
+ ```
771
+
772
+ Use this when older repos still have stale `.cursorrules`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.windsurfrules`, VS Code tasks, or hook files. `--discover` scans nested repo folders, `--dry-run` reports without writing, and `--refresh-context` regenerates packs after repair.
773
+
774
+ ---
775
+
736
776
  ### `agentpack summarize`
737
777
 
738
778
  Build or refresh the offline summary cache. **No API calls, ever.**
@@ -881,18 +921,38 @@ Register in Claude Code settings (`~/.claude/settings.json`):
881
921
  |---|---|
882
922
  | `start_task(task, mode, budget, max_tokens)` | Recommended MCP-first entry point. Writes `.agentpack/task.md`, generates a ranked pack, and returns packed markdown. |
883
923
  | `pack_context(task, mode, budget, max_tokens)` | Generate a ranked context pack. If `task` is provided, writes it to `.agentpack/task.md`; if omitted, reads `task.md` or infers from git. |
884
- | `get_context()` | Return the latest pre-built pack instantly (no repack). Prepends a freshness/staleness header so you know if it's stale. |
924
+ | `get_context()` | Return the latest pack. If `.agentpack/task.md` or the repo snapshot differs from the packed metadata, it auto-refreshes before returning; otherwise it prepends a freshness header. |
885
925
  | `refresh()` | Refresh using the current `task.md` or git-inferred task. |
886
926
  | `explain_file(path, task)` | Show score, inclusion mode, reasons, symbols, imports, and importers for one file. |
887
927
  | `get_related_files(path, depth)` | Return import-graph neighbours and related tests for a file. |
888
928
  | `get_delta_context(max_files)` | Return the latest selected-file delta plus top current selected files. Useful for cheap prompt-time refresh checks. |
889
929
  | `get_stats()` | Return latest pack stats, savings, selection quality, excluded files, and benchmark-style signals. |
890
930
 
891
- **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:
931
+ **Staleness detection:** `get_context()` compares the current task file, snapshot hash, and git state against the latest pack metadata. If `.agentpack/task.md` or the repo snapshot changed, it blocks for a fresh pack and prepends:
932
+
933
+ ```
934
+ > Context auto-refreshed because .agentpack/task.md differs from the packed task ...
892
935
  ```
893
- > **Stale context** — repo changed since last pack (generated: ...). Run pack_context() to refresh.
936
+
937
+ If auto-refresh fails, it falls back to the cached context with a loud stale warning and asks the agent to call `pack_context()` again.
938
+
939
+ Static markdown cannot refresh itself, so rendered packs include a machine-readable fallback header:
940
+
941
+ ```text
942
+ <!-- agentpack:freshness
943
+ {
944
+ "active_context": "mcp",
945
+ "fallback_context": "markdown",
946
+ "refresh_required": false,
947
+ "mcp_refresh_tool": "agentpack_get_context",
948
+ "cli_refresh_command": "agentpack pack --task auto",
949
+ "guard_command": "agentpack guard --agent auto --repair-stale --refresh-context"
950
+ }
951
+ -->
894
952
  ```
895
953
 
954
+ Claude prompt hooks also block once on clear task switches so first-turn hints are fresh. Non-MCP rule files and VS Code folder-open tasks use `agentpack guard --repair-stale --refresh-context` as the executable fallback. To prefer lower latency over first-turn freshness, set `blocking_task_refresh = false` under `[hooks]` in `.agentpack/config.toml`.
955
+
896
956
  **Smart truncation:** `start_task()` and `pack_context()` keep headers intact and trim file content blocks to fit the token budget, appending a note about how many files were omitted.
897
957
 
898
958
  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.
@@ -1444,6 +1504,13 @@ src/agentpack/
1444
1504
  vscode_tasks.py # install/remove .vscode/tasks.json entries
1445
1505
  global_install.py # global: git template hooks + shell rc hook
1446
1506
 
1507
+ ../native-integrations/ # tracked native-enforcement skeletons and blocked-status stubs
1508
+ status.json # machine-readable native host enforcement status
1509
+ cursor-extension/ # VS Code-style Cursor guard skeleton
1510
+ windsurf-extension/ # VS Code-style Windsurf guard skeleton
1511
+ claude-native/ # blocked native stub pending mandatory host API
1512
+ codex-native/ # blocked native stub pending mandatory host API
1513
+
1447
1514
  renderers/
1448
1515
  markdown.py # renders pre-redacted ContextPack to markdown, including freshness/map/delta
1449
1516
  compact.py # compact protocol format for session context files
@@ -1496,7 +1563,8 @@ src/agentpack/
1496
1563
  - **`integrations/` vs `core/`**: git hooks, shell rc patching, and VS Code tasks are infrastructure concerns — they live in `integrations/`, not `core/`. `core/` is pure domain logic.
1497
1564
  - **Adapters render; installers configure**: `adapters/` knows how to write a context file for an agent. `installers/` knows how to configure the agent's tool (CLAUDE.md, .cursorrules, settings.json). They are separate concerns and separate classes.
1498
1565
  - **Agent integration contract is shared**: `integrations/agents.py` defines install, audit, and repair behavior for Claude, Cursor, Windsurf, Codex, Antigravity, and Generic. `install`, `repair`, `doctor --agent all`, and release verification use the same contract.
1499
- - **MCP is the interactive path**: `start_task()` writes task state and returns a fresh pack, while `get_context()`, `get_delta_context()`, `explain_file()`, and `get_related_files()` let agents pull follow-up context on demand.
1566
+ - **MCP is the interactive path**: `start_task()` writes task state and returns a fresh pack, while `get_context()` auto-refreshes stale task or repo-snapshot context and `get_delta_context()`, `explain_file()`, and `get_related_files()` let agents pull follow-up context on demand.
1567
+ - **Native enforcement status is explicit**: `native-integrations/status.json` tracks host skeletons and blockers. Entries stay `guarded`, not `enforced`, until a host exposes mandatory pre-edit/pre-tool hooks that can block failed guard checks.
1500
1568
 
1501
1569
  ---
1502
1570
 
@@ -1521,6 +1589,7 @@ src/agentpack/
1521
1589
  - **Secret redaction**: covers AWS keys, GitHub tokens, OpenAI/Anthropic keys, JWTs, and private key blocks. Not a substitute for a dedicated secrets scanner on sensitive repos.
1522
1590
  - **Token estimates**: uses tiktoken `cl100k_base` — approximate, not exact for Claude's billing.
1523
1591
  - **Large repos (>5k files)**: global auto-bootstrap is skipped for repos over 5,000 files to avoid hangs. Run `agentpack init` explicitly in large codebases.
1592
+ - **Native hard enforcement**: tracked skeletons exist under `native-integrations/`, but all hosts remain `guarded` until their native APIs can guarantee mandatory pre-edit/pre-tool execution and block failed guard checks.
1524
1593
 
1525
1594
  ---
1526
1595
 
@@ -7,7 +7,7 @@
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![CI](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg)](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
9
9
 
10
- > **Status: alpha (v0.3.4).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
10
+ > **Status: alpha (v0.3.5).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
11
11
  >
12
12
  > **Platform note:** macOS, Linux, and Windows are supported. Windows support targets PowerShell plus Git for Windows. `cmd.exe` and bare Git setups are not a supported path yet.
13
13
 
@@ -38,7 +38,7 @@ Use AgentPack when a repo is too large to paste and you want faster, more consis
38
38
  - **Budget-aware compression**: emits `full`, `diff`, `symbols`, `skeleton`, or `summary` views instead of all-or-nothing file dumps.
39
39
  - **Local code intelligence**: extracts roles, domains, entrypoints, definitions, dependencies, env reads, side effects, and external systems using static analysis.
40
40
  - **Semantic repo map**: adds a compact module-level map before file context so agents orient faster.
41
- - **Freshness and deltas**: records task source, git state, snapshot hashes, selected-file deltas, and stale-context warnings.
41
+ - **Freshness and deltas**: records task source, git state, snapshot hashes, selected-file deltas, stale-context warnings, MCP auto-refresh signals, and a machine-readable `agentpack:freshness` block in markdown fallback artifacts.
42
42
  - **Agent integrations**: installs Claude Code, Cursor, Windsurf, Codex, Antigravity, VS Code tasks, git hooks, and MCP configuration.
43
43
  - **Local and measurable**: no API calls for scan, summarize, rank, pack, stats, or benchmark; quality is measured with expected-file evals.
44
44
 
@@ -149,7 +149,7 @@ AgentPack is best treated as a **ranked starting map**. It should reduce repeate
149
149
  | Pack time | Seconds on a warm cache; first summarize pass is slower |
150
150
  | Recall | Expected files appear near the top; validate with `agentpack benchmark --misses` |
151
151
  | Precision | Good enough to reduce exploration; summaries and repo maps may still include noise |
152
- | Freshness | Stale packs are clearly marked by task, git, and snapshot checks |
152
+ | Freshness | Task or repo-stale MCP reads auto-refresh; static packs are clearly marked by task, git, and snapshot checks |
153
153
 
154
154
  Use real repo evals instead of trusting compression numbers:
155
155
 
@@ -207,12 +207,22 @@ This is the core reliability loop: pack, measure recall, inspect misses, then tu
207
207
  For MCP-capable agents, the preferred workflow is pull-based:
208
208
 
209
209
  1. Call `start_task(task)` when a new task begins. AgentPack writes `.agentpack/task.md`, packs context, and returns ranked markdown.
210
- 2. Call `get_context()` when you need the latest cached pack; it tells you if the pack is stale.
210
+ 2. Call `get_context()` when you need the latest pack. It blocks for one refresh if `.agentpack/task.md` or the repo snapshot changed since the last pack, and otherwise prepends a freshness header.
211
211
  3. Call `get_delta_context()` after edits or hook hints to see what changed without loading the full pack.
212
212
  4. Call `explain_file(path)` or `get_related_files(path)` when a file looks relevant or suspicious.
213
213
 
214
214
  The CLI remains the setup/debug/release path. MCP is the best interactive path because the agent can ask for only the context it needs instead of relying on one static startup blob.
215
215
 
216
+ Markdown context files are fallback artifacts for CI, logs, manual review, and non-MCP agents. Every rendered pack includes a machine-readable `agentpack:freshness` comment; agents should treat `active_context: mcp` as the preferred path and refresh before using markdown when `refresh_required: true`.
217
+
218
+ For non-MCP agents, use the executable guard before editing:
219
+
220
+ ```bash
221
+ agentpack guard --agent auto --repair-stale --refresh-context
222
+ ```
223
+
224
+ `guard` checks pack freshness, task freshness, repo snapshot freshness, and installed agent rules/hooks. With `--repair-stale --refresh-context`, it repairs stale AgentPack rule files and refreshes missing or stale context before returning success. `agentpack pack` also self-heals stale AgentPack rule blocks for the active agent, so older installs that still run `pack` get upgraded opportunistically.
225
+
216
226
  ## Before / After Agent Behavior
217
227
 
218
228
  Without AgentPack:
@@ -326,7 +336,7 @@ _*`--agent generic` outputs standard markdown. Claude adapter has richer instruc
326
336
 
327
337
  - AgentPack cannot intercept prompts inside IDEs — Cursor/Windsurf rely on rules being followed.
328
338
  - Claude wrapper (`agentpack claude`) is the most deterministic integration.
329
- - If the task changes drastically mid-session, context needs one refresh cycle.
339
+ - If the task changes drastically mid-session, Claude hooks update `.agentpack/task.md` and block once for fresh hints; plain repo edits still use background repack to keep prompts fast.
330
340
  - AgentPack-selected files are ranked starting points, not absolute truth.
331
341
 
332
342
  ---
@@ -694,6 +704,36 @@ agentpack repair --agent all # repair every supported integration
694
704
 
695
705
  ---
696
706
 
707
+ ### `agentpack guard`
708
+
709
+ Run the pre-edit safety gate an agent can execute instead of only reading instructions.
710
+
711
+ ```bash
712
+ agentpack guard # check current agent + context
713
+ agentpack guard --refresh-context # refresh stale/missing context
714
+ agentpack guard --agent codex --repair-stale # repair stale Codex rules/hooks
715
+ agentpack guard --agent auto --repair-stale --refresh-context
716
+ ```
717
+
718
+ This is the strongest non-native enforcement AgentPack can provide: tools that run commands get a failing exit code when context is unsafe, and an automatic repair/refresh path when allowed.
719
+
720
+ ---
721
+
722
+ ### `agentpack migrate`
723
+
724
+ Repair stale AgentPack integrations across existing repos after upgrading.
725
+
726
+ ```bash
727
+ agentpack migrate --path . --agent auto
728
+ agentpack migrate --path ~/src --discover --agent all
729
+ agentpack migrate --path ~/src --discover --agent codex --refresh-context
730
+ agentpack migrate --path ~/src --discover --dry-run
731
+ ```
732
+
733
+ Use this when older repos still have stale `.cursorrules`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.windsurfrules`, VS Code tasks, or hook files. `--discover` scans nested repo folders, `--dry-run` reports without writing, and `--refresh-context` regenerates packs after repair.
734
+
735
+ ---
736
+
697
737
  ### `agentpack summarize`
698
738
 
699
739
  Build or refresh the offline summary cache. **No API calls, ever.**
@@ -842,18 +882,38 @@ Register in Claude Code settings (`~/.claude/settings.json`):
842
882
  |---|---|
843
883
  | `start_task(task, mode, budget, max_tokens)` | Recommended MCP-first entry point. Writes `.agentpack/task.md`, generates a ranked pack, and returns packed markdown. |
844
884
  | `pack_context(task, mode, budget, max_tokens)` | Generate a ranked context pack. If `task` is provided, writes it to `.agentpack/task.md`; if omitted, reads `task.md` or infers from git. |
845
- | `get_context()` | Return the latest pre-built pack instantly (no repack). Prepends a freshness/staleness header so you know if it's stale. |
885
+ | `get_context()` | Return the latest pack. If `.agentpack/task.md` or the repo snapshot differs from the packed metadata, it auto-refreshes before returning; otherwise it prepends a freshness header. |
846
886
  | `refresh()` | Refresh using the current `task.md` or git-inferred task. |
847
887
  | `explain_file(path, task)` | Show score, inclusion mode, reasons, symbols, imports, and importers for one file. |
848
888
  | `get_related_files(path, depth)` | Return import-graph neighbours and related tests for a file. |
849
889
  | `get_delta_context(max_files)` | Return the latest selected-file delta plus top current selected files. Useful for cheap prompt-time refresh checks. |
850
890
  | `get_stats()` | Return latest pack stats, savings, selection quality, excluded files, and benchmark-style signals. |
851
891
 
852
- **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:
892
+ **Staleness detection:** `get_context()` compares the current task file, snapshot hash, and git state against the latest pack metadata. If `.agentpack/task.md` or the repo snapshot changed, it blocks for a fresh pack and prepends:
893
+
894
+ ```
895
+ > Context auto-refreshed because .agentpack/task.md differs from the packed task ...
853
896
  ```
854
- > **Stale context** — repo changed since last pack (generated: ...). Run pack_context() to refresh.
897
+
898
+ If auto-refresh fails, it falls back to the cached context with a loud stale warning and asks the agent to call `pack_context()` again.
899
+
900
+ Static markdown cannot refresh itself, so rendered packs include a machine-readable fallback header:
901
+
902
+ ```text
903
+ <!-- agentpack:freshness
904
+ {
905
+ "active_context": "mcp",
906
+ "fallback_context": "markdown",
907
+ "refresh_required": false,
908
+ "mcp_refresh_tool": "agentpack_get_context",
909
+ "cli_refresh_command": "agentpack pack --task auto",
910
+ "guard_command": "agentpack guard --agent auto --repair-stale --refresh-context"
911
+ }
912
+ -->
855
913
  ```
856
914
 
915
+ Claude prompt hooks also block once on clear task switches so first-turn hints are fresh. Non-MCP rule files and VS Code folder-open tasks use `agentpack guard --repair-stale --refresh-context` as the executable fallback. To prefer lower latency over first-turn freshness, set `blocking_task_refresh = false` under `[hooks]` in `.agentpack/config.toml`.
916
+
857
917
  **Smart truncation:** `start_task()` and `pack_context()` keep headers intact and trim file content blocks to fit the token budget, appending a note about how many files were omitted.
858
918
 
859
919
  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.
@@ -1405,6 +1465,13 @@ src/agentpack/
1405
1465
  vscode_tasks.py # install/remove .vscode/tasks.json entries
1406
1466
  global_install.py # global: git template hooks + shell rc hook
1407
1467
 
1468
+ ../native-integrations/ # tracked native-enforcement skeletons and blocked-status stubs
1469
+ status.json # machine-readable native host enforcement status
1470
+ cursor-extension/ # VS Code-style Cursor guard skeleton
1471
+ windsurf-extension/ # VS Code-style Windsurf guard skeleton
1472
+ claude-native/ # blocked native stub pending mandatory host API
1473
+ codex-native/ # blocked native stub pending mandatory host API
1474
+
1408
1475
  renderers/
1409
1476
  markdown.py # renders pre-redacted ContextPack to markdown, including freshness/map/delta
1410
1477
  compact.py # compact protocol format for session context files
@@ -1457,7 +1524,8 @@ src/agentpack/
1457
1524
  - **`integrations/` vs `core/`**: git hooks, shell rc patching, and VS Code tasks are infrastructure concerns — they live in `integrations/`, not `core/`. `core/` is pure domain logic.
1458
1525
  - **Adapters render; installers configure**: `adapters/` knows how to write a context file for an agent. `installers/` knows how to configure the agent's tool (CLAUDE.md, .cursorrules, settings.json). They are separate concerns and separate classes.
1459
1526
  - **Agent integration contract is shared**: `integrations/agents.py` defines install, audit, and repair behavior for Claude, Cursor, Windsurf, Codex, Antigravity, and Generic. `install`, `repair`, `doctor --agent all`, and release verification use the same contract.
1460
- - **MCP is the interactive path**: `start_task()` writes task state and returns a fresh pack, while `get_context()`, `get_delta_context()`, `explain_file()`, and `get_related_files()` let agents pull follow-up context on demand.
1527
+ - **MCP is the interactive path**: `start_task()` writes task state and returns a fresh pack, while `get_context()` auto-refreshes stale task or repo-snapshot context and `get_delta_context()`, `explain_file()`, and `get_related_files()` let agents pull follow-up context on demand.
1528
+ - **Native enforcement status is explicit**: `native-integrations/status.json` tracks host skeletons and blockers. Entries stay `guarded`, not `enforced`, until a host exposes mandatory pre-edit/pre-tool hooks that can block failed guard checks.
1461
1529
 
1462
1530
  ---
1463
1531
 
@@ -1482,6 +1550,7 @@ src/agentpack/
1482
1550
  - **Secret redaction**: covers AWS keys, GitHub tokens, OpenAI/Anthropic keys, JWTs, and private key blocks. Not a substitute for a dedicated secrets scanner on sensitive repos.
1483
1551
  - **Token estimates**: uses tiktoken `cl100k_base` — approximate, not exact for Claude's billing.
1484
1552
  - **Large repos (>5k files)**: global auto-bootstrap is skipped for repos over 5,000 files to avoid hangs. Run `agentpack init` explicitly in large codebases.
1553
+ - **Native hard enforcement**: tracked skeletons exist under `native-integrations/`, but all hosts remain `guarded` until their native APIs can guarantee mandatory pre-edit/pre-tool execution and block failed guard checks.
1485
1554
 
1486
1555
  ---
1487
1556
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.3.4"
3
+ version = "0.3.5"
4
4
  description = "Local context engine for AI coding agents that ranks relevant files and builds task-focused context packs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """AgentPack — task-aware context packing for AI coding agents."""
2
2
 
3
- __version__ = "0.3.4"
3
+ __version__ = "0.3.5"
@@ -15,6 +15,7 @@ from agentpack.core.diff import diff_snapshots
15
15
  from agentpack.core import git
16
16
  from agentpack.core.context_pack import select_files, save_pack_metadata, load_pack_metadata
17
17
  from agentpack.core.models import ContextPack, DependencyGraph, FileInfo, ScanResult, SelectedFile, Receipt
18
+ from agentpack.core.task_freshness import read_task_md, task_metadata
18
19
  from agentpack.core.token_estimator import estimate_tokens
19
20
  from agentpack.renderers.markdown import render_generic
20
21
  from agentpack.analysis.ranking import (
@@ -851,19 +852,7 @@ def _change_source(root: Path, since: str | None, snapshot_changed: set[str], gi
851
852
 
852
853
 
853
854
  def _task_md_body(root: Path) -> str | None:
854
- task_md_path = root / ".agentpack" / "task.md"
855
- if not task_md_path.exists():
856
- return None
857
- try:
858
- content = task_md_path.read_text(encoding="utf-8").strip()
859
- except OSError:
860
- return None
861
- lines = [ln for ln in content.splitlines() if ln.strip() and not ln.startswith("#")]
862
- body = lines[0].strip() if lines else ""
863
- placeholder = "Write or update the current coding task here."
864
- if body and placeholder not in body:
865
- return body
866
- return None
855
+ return read_task_md(root)
867
856
 
868
857
 
869
858
  def _build_freshness_metadata(
@@ -885,6 +874,7 @@ def _build_freshness_metadata(
885
874
  "task_class_signals": plan.task_class_signals,
886
875
  "dirty_files_count": len(dirty),
887
876
  }
877
+ metadata.update(task_metadata(root, request.task))
888
878
  if plan.workspace:
889
879
  metadata["workspace"] = plan.workspace
890
880
  if plan.workspace_roots:
@@ -909,7 +899,7 @@ def _freshness_warnings(root: Path, request: PackRequest, freshness: dict[str, A
909
899
  task_md = freshness.get("task_md")
910
900
  if task_md and task_md != request.task:
911
901
  warnings.append(
912
- ".agentpack/task.md differs from the packed task; rerun with --task auto if task.md should win."
902
+ ".agentpack/task.md differs from the packed task; AgentPack-controlled context reads should auto-refresh, or run `agentpack pack --task auto`."
913
903
  )
914
904
  if freshness.get("changed_files_source") == "no live changes; ranking used task keywords and history":
915
905
  warnings.append("No live changed files were detected; treat selected files as keyword-based hints.")
@@ -7,10 +7,12 @@ from agentpack.commands import (
7
7
  diff,
8
8
  doctor,
9
9
  explain,
10
+ guard,
10
11
  hook_cmd,
11
12
  init,
12
13
  install,
13
14
  mcp_cmd,
15
+ migrate,
14
16
  monitor,
15
17
  pack,
16
18
  quickstart,
@@ -51,8 +53,10 @@ for mod in [
51
53
  pack,
52
54
  install,
53
55
  repair,
56
+ migrate,
54
57
  monitor,
55
58
  explain,
59
+ guard,
56
60
  doctor,
57
61
  tune,
58
62
  watch,
@@ -18,6 +18,7 @@ from agentpack.integrations.global_install import (
18
18
  )
19
19
  from agentpack.commands._shared import console, _root
20
20
  from agentpack.core.context_pack import load_pack_metadata
21
+ from agentpack.core.task_freshness import task_freshness
21
22
  from agentpack.integrations.agents import SUPPORTED_AGENTS, check_agent_integration, expand_agents
22
23
 
23
24
 
@@ -126,6 +127,14 @@ def register(app: typer.Typer) -> None:
126
127
  age = time.time() - context_path.stat().st_mtime
127
128
  age_str = f"{int(age // 3600)}h {int((age % 3600) // 60)}m" if age > 3600 else f"{int(age // 60)}m"
128
129
  console.print(f" [green]✓[/] context pack present (age: {age_str})")
130
+ task_state = task_freshness(root, load_pack_metadata(root))
131
+ if task_state.is_stale:
132
+ console.print(
133
+ " [yellow]![/] task context stale — "
134
+ f"packed: {task_state.packed_task}; current: {task_state.current_task}. "
135
+ "MCP get_context auto-refreshes this, or run: agentpack pack --task auto"
136
+ )
137
+ ok = False
129
138
  else:
130
139
  console.print(" [yellow]![/] No context pack yet — write .agentpack/task.md, then run: agentpack pack --task auto")
131
140
 
@@ -236,13 +245,20 @@ def register(app: typer.Typer) -> None:
236
245
  console.print(f" [dim]Auto-detected agent: {agents[0]}[/]")
237
246
  for selected in agents:
238
247
  console.print(f" [bold]{selected}[/]")
248
+ selected_ok = True
239
249
  for check in check_agent_integration(root, selected):
240
250
  if check.ok:
241
251
  console.print(f" [green]✓[/] {check.label}: {check.detail}")
242
252
  continue
243
253
  fix = f" — run: {check.fix}" if check.fix else ""
244
254
  console.print(f" [red]✗[/] {check.label}: {check.detail}{fix}")
255
+ selected_ok = False
245
256
  ok = False
257
+ if not selected_ok:
258
+ console.print(
259
+ f" [yellow]![/] executable guard repair: "
260
+ f"agentpack guard --agent {selected} --repair-stale --refresh-context"
261
+ )
246
262
 
247
263
  # --- Release hygiene ---
248
264
  console.print("\n[bold]Release hygiene[/]")
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from agentpack.commands._shared import console, _root, run_refresh
6
+ from agentpack.core.config import load_config
7
+ from agentpack.core.context_pack import load_pack_metadata
8
+ from agentpack.core.ignore import load_spec
9
+ from agentpack.core.scanner import scan
10
+ from agentpack.core.snapshot import build_snapshot
11
+ from agentpack.core.task_freshness import task_freshness
12
+ from agentpack.application.pack_service import AdapterRegistry
13
+ from agentpack.integrations.agents import (
14
+ SUPPORTED_AGENTS,
15
+ check_agent_integration,
16
+ expand_agents,
17
+ install_agent_integration,
18
+ )
19
+
20
+
21
+ def register(app: typer.Typer) -> None:
22
+ @app.command()
23
+ def guard(
24
+ agent: str = typer.Option(
25
+ "auto",
26
+ "--agent",
27
+ help=f"Agent integration to guard ({' | '.join(SUPPORTED_AGENTS)}).",
28
+ ),
29
+ repair_stale: bool = typer.Option(
30
+ False,
31
+ "--repair-stale",
32
+ help="Repair stale/missing AgentPack rule and hook files before returning.",
33
+ ),
34
+ refresh_context: bool = typer.Option(
35
+ False,
36
+ "--refresh-context",
37
+ help="Refresh the context pack when it is missing or stale.",
38
+ ),
39
+ mode: str = typer.Option("balanced", "--mode", help="Refresh mode (minimal|balanced|deep)."),
40
+ budget: int = typer.Option(0, "--budget", help="Refresh token budget (0 = config default)."),
41
+ ) -> None:
42
+ """Executable pre-edit gate for agents before they trust packed context."""
43
+ if agent not in SUPPORTED_AGENTS:
44
+ console.print(f"[yellow]Unknown agent: {agent}. Supported: {', '.join(SUPPORTED_AGENTS)}[/]")
45
+ raise typer.Exit(1)
46
+ if mode not in ("minimal", "balanced", "deep"):
47
+ console.print(f"[red]Invalid mode: {mode}. Use minimal|balanced|deep.[/]")
48
+ raise typer.Exit(1)
49
+
50
+ root = _root()
51
+ agents = expand_agents(agent, root)
52
+ ok = True
53
+
54
+ for selected in agents:
55
+ checks = check_agent_integration(root, selected)
56
+ failing = [check for check in checks if not check.ok]
57
+ if failing and repair_stale and selected != "generic":
58
+ _repair_agent(root, selected)
59
+ checks = check_agent_integration(root, selected)
60
+ failing = [check for check in checks if not check.ok]
61
+
62
+ if failing:
63
+ ok = False
64
+ console.print(f"[yellow]Agent integration needs repair: {selected}[/]")
65
+ for check in failing:
66
+ fix = f" Run: {check.fix}" if check.fix else ""
67
+ console.print(f" [yellow]![/] {check.label}: {check.detail}.{fix}")
68
+ else:
69
+ console.print(f"[green]✓[/] Agent integration current: {selected}")
70
+
71
+ context_ok, context_reason = _context_is_fresh(root)
72
+ if not context_ok and refresh_context:
73
+ selected_agent = agents[0] if agents else "generic"
74
+ console.print(f"[yellow]Refreshing context: {context_reason}[/]")
75
+ stats = run_refresh(root, selected_agent, mode, budget)
76
+ if stats is None:
77
+ ok = False
78
+ else:
79
+ context_ok, context_reason = _context_is_fresh(root)
80
+
81
+ if context_ok:
82
+ console.print("[green]✓[/] Context pack fresh")
83
+ else:
84
+ ok = False
85
+ console.print(f"[yellow]Context pack unsafe: {context_reason}[/]")
86
+ console.print(" Run: agentpack guard --repair-stale --refresh-context")
87
+
88
+ if not ok:
89
+ raise typer.Exit(1)
90
+
91
+
92
+ def _repair_agent(root, agent: str) -> None:
93
+ from agentpack.commands.install import _install_slash_command
94
+
95
+ console.print(f"[yellow]Repairing AgentPack integration: {agent}[/]")
96
+ install_agent_integration(
97
+ root,
98
+ agent,
99
+ global_install=False,
100
+ install_slash_command=_install_slash_command,
101
+ )
102
+
103
+
104
+ def _context_is_fresh(root) -> tuple[bool, str]:
105
+ meta = load_pack_metadata(root)
106
+ if not meta:
107
+ return False, "missing context pack metadata"
108
+
109
+ task_state = task_freshness(root, meta)
110
+ if task_state.is_stale:
111
+ return False, ".agentpack/task.md differs from packed task"
112
+
113
+ try:
114
+ cfg = load_config(root)
115
+ ignore_spec = load_spec(root / cfg.project.ignore_file)
116
+ scan_result = scan(
117
+ root,
118
+ ignore_spec,
119
+ cfg.context.max_file_tokens,
120
+ always_skip_paths=AdapterRegistry.generated_output_paths(root, cfg),
121
+ )
122
+ current = build_snapshot(scan_result.packable)
123
+ except Exception as exc:
124
+ return False, f"could not compute repo snapshot: {exc}"
125
+
126
+ if current["root_hash"] != meta.get("snapshot_root_hash"):
127
+ return False, "repo snapshot differs from packed snapshot"
128
+
129
+ return True, "fresh"