agentpack-cli 0.3.25__tar.gz → 0.3.28__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 (164) hide show
  1. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/PKG-INFO +2 -1
  2. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/README.md +1 -0
  3. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/pyproject.toml +1 -1
  4. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/__init__.py +1 -1
  5. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/application/pack_service.py +7 -0
  6. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/benchmark.py +20 -0
  7. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/doctor.py +38 -5
  8. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/next_cmd.py +4 -3
  9. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/pack.py +3 -2
  10. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/start_cmd.py +2 -3
  11. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/status.py +2 -1
  12. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/task_cmd.py +3 -2
  13. agentpack_cli-0.3.28/src/agentpack/commands/upgrade.py +93 -0
  14. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/workflow_cmd.py +5 -3
  15. agentpack_cli-0.3.28/src/agentpack/core/command_surface.py +136 -0
  16. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/context_pack.py +12 -1
  17. agentpack_cli-0.3.28/src/agentpack/core/e2e_benchmark.py +75 -0
  18. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/agentpack.md +7 -7
  19. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/.codex-plugin/plugin.json +1 -1
  20. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/skills/agentpack-refresh.md +3 -3
  21. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/antigravity.py +21 -9
  22. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/claude.py +25 -9
  23. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/codex.py +21 -9
  24. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/cursor.py +24 -25
  25. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/windsurf.py +21 -10
  26. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/agents.py +11 -9
  27. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/vscode_tasks.py +5 -2
  28. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/mcp_server.py +56 -0
  29. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/renderers/markdown.py +14 -3
  30. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/models.py +8 -0
  31. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/prompt_builder.py +34 -0
  32. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/scoring.py +24 -3
  33. agentpack_cli-0.3.28/src/agentpack/router/service.py +611 -0
  34. agentpack_cli-0.3.25/src/agentpack/commands/upgrade.py +0 -38
  35. agentpack_cli-0.3.25/src/agentpack/router/service.py +0 -258
  36. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/.gitignore +0 -0
  37. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/LICENSE +0 -0
  38. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/__init__.py +0 -0
  39. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/antigravity.py +0 -0
  40. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/base.py +0 -0
  41. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/claude.py +0 -0
  42. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/codex.py +0 -0
  43. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/cursor.py +0 -0
  44. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/detect.py +0 -0
  45. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/generic.py +0 -0
  46. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/adapters/windsurf.py +0 -0
  47. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/__init__.py +0 -0
  48. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/dependency_graph.py +0 -0
  49. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/go_imports.py +0 -0
  50. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/java_imports.py +0 -0
  51. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/js_ts_imports.py +0 -0
  52. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/monorepo.py +0 -0
  53. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/naming_signals.py +0 -0
  54. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/python_ast.py +0 -0
  55. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/python_imports.py +0 -0
  56. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/ranking.py +0 -0
  57. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/repo_map.py +0 -0
  58. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/role_inference.py +0 -0
  59. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/rust_imports.py +0 -0
  60. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/symbols.py +0 -0
  61. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/task_classifier.py +0 -0
  62. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/analysis/tests.py +0 -0
  63. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/application/__init__.py +0 -0
  64. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/cli.py +0 -0
  65. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/__init__.py +0 -0
  66. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/_shared.py +0 -0
  67. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/ci_cmd.py +0 -0
  68. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/claude_cmd.py +0 -0
  69. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/compress_output.py +0 -0
  70. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/dashboard.py +0 -0
  71. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/dev_check.py +0 -0
  72. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/diagnose_selection.py +0 -0
  73. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/diff.py +0 -0
  74. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/eval_cmd.py +0 -0
  75. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/explain.py +0 -0
  76. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/guard.py +0 -0
  77. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/hook_cmd.py +0 -0
  78. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/ignore_cmd.py +0 -0
  79. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/init.py +0 -0
  80. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/install.py +0 -0
  81. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/learn.py +0 -0
  82. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/mcp_cmd.py +0 -0
  83. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/memory.py +0 -0
  84. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/migrate.py +0 -0
  85. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/monitor.py +0 -0
  86. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/perf.py +0 -0
  87. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/quickstart.py +0 -0
  88. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/release_check.py +0 -0
  89. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/release_cmd.py +0 -0
  90. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/repair.py +0 -0
  91. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/retrieve.py +0 -0
  92. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/route.py +0 -0
  93. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/scan.py +0 -0
  94. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/skills.py +0 -0
  95. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/state_cmd.py +0 -0
  96. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/stats.py +0 -0
  97. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/summarize.py +0 -0
  98. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/threads.py +0 -0
  99. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/tune.py +0 -0
  100. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/verify_wheel.py +0 -0
  101. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/watch.py +0 -0
  102. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/commands/wrap.py +0 -0
  103. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/__init__.py +0 -0
  104. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/bootstrap.py +0 -0
  105. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/cache.py +0 -0
  106. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/changed_paths.py +0 -0
  107. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/config.py +0 -0
  108. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/diff.py +0 -0
  109. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/evals.py +0 -0
  110. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/execution_state.py +0 -0
  111. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/git.py +0 -0
  112. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/git_hooks.py +0 -0
  113. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/global_install.py +0 -0
  114. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/ignore.py +0 -0
  115. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/loop_protocol.py +0 -0
  116. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/merkle.py +0 -0
  117. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/models.py +0 -0
  118. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/modes.py +0 -0
  119. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/pack_registry.py +0 -0
  120. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/redactor.py +0 -0
  121. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/scanner.py +0 -0
  122. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/snapshot.py +0 -0
  123. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/task_freshness.py +0 -0
  124. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/thread_context.py +0 -0
  125. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/token_estimator.py +0 -0
  126. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/core/vscode_tasks.py +0 -0
  127. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/dashboard/__init__.py +0 -0
  128. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/dashboard/collectors.py +0 -0
  129. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/dashboard/models.py +0 -0
  130. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/dashboard/renderers.py +0 -0
  131. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/skills/agentpack-pack.md +0 -0
  132. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/skills/agentpack-review.md +0 -0
  133. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/skills/agentpack-route.md +0 -0
  134. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/data/codex_plugin/skills/agentpack.md +0 -0
  135. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/installers/__init__.py +0 -0
  136. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/__init__.py +0 -0
  137. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/git_hooks.py +0 -0
  138. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/global_install.py +0 -0
  139. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/integrations/platform.py +0 -0
  140. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/__init__.py +0 -0
  141. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/collector.py +0 -0
  142. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/extractor.py +0 -0
  143. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/feedback.py +0 -0
  144. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/lesson_ranker.py +0 -0
  145. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/models.py +0 -0
  146. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/provider.py +0 -0
  147. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/quality.py +0 -0
  148. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/renderers.py +0 -0
  149. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/learning/skill_map.py +0 -0
  150. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/output_compression/__init__.py +0 -0
  151. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/output_compression/core.py +0 -0
  152. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/renderers/__init__.py +0 -0
  153. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/renderers/compact.py +0 -0
  154. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/renderers/receipts.py +0 -0
  155. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/__init__.py +0 -0
  156. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/discovery.py +0 -0
  157. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/parser.py +0 -0
  158. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/router/skills_index.py +0 -0
  159. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/session/__init__.py +0 -0
  160. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/session/events.py +0 -0
  161. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/session/state.py +0 -0
  162. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/summaries/__init__.py +0 -0
  163. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/src/agentpack/summaries/base.py +0 -0
  164. {agentpack_cli-0.3.25 → agentpack_cli-0.3.28}/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.25
3
+ Version: 0.3.28
4
4
  Summary: Local context engine for AI coding agents that ranks relevant repo files and builds compact task-focused context packs for Claude Code, Codex, Cursor, Windsurf, MCP, and CI workflows.
5
5
  Project-URL: Homepage, https://github.com/vishal2612200/agentpack
6
6
  Project-URL: Documentation, https://vishal2612200.github.io/agentpack/
@@ -60,6 +60,7 @@ Description-Content-Type: text/markdown
60
60
  </p>
61
61
 
62
62
  <p align="center">
63
+ <a href="https://deepwiki.com/vishal2612200/agentpack"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
63
64
  <a href="https://pypi.org/project/agentpack-cli/"><img alt="PyPI version" src="https://img.shields.io/pypi/v/agentpack-cli.svg"></a>
64
65
  <a href="https://pepy.tech/projects/agentpack-cli"><img alt="PyPI downloads" src="https://static.pepy.tech/personalized-badge/agentpack-cli?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads"></a>
65
66
  <a href="https://www.npmjs.com/package/@vishal2612200/agentpack"><img alt="npm version" src="https://img.shields.io/npm/v/@vishal2612200/agentpack.svg"></a>
@@ -13,6 +13,7 @@
13
13
  </p>
14
14
 
15
15
  <p align="center">
16
+ <a href="https://deepwiki.com/vishal2612200/agentpack"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
16
17
  <a href="https://pypi.org/project/agentpack-cli/"><img alt="PyPI version" src="https://img.shields.io/pypi/v/agentpack-cli.svg"></a>
17
18
  <a href="https://pepy.tech/projects/agentpack-cli"><img alt="PyPI downloads" src="https://static.pepy.tech/personalized-badge/agentpack-cli?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads"></a>
18
19
  <a href="https://www.npmjs.com/package/@vishal2612200/agentpack"><img alt="npm version" src="https://img.shields.io/npm/v/@vishal2612200/agentpack.svg"></a>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.3.25"
3
+ version = "0.3.28"
4
4
  description = "Local context engine for AI coding agents that ranks relevant repo files and builds compact task-focused context packs for Claude Code, Codex, Cursor, Windsurf, MCP, and CI workflows."
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.25"
3
+ __version__ = "0.3.28"
@@ -9,6 +9,7 @@ from datetime import datetime, timezone
9
9
  from pathlib import Path
10
10
  from typing import Any
11
11
 
12
+ from agentpack import __version__
12
13
  from agentpack.core.config import load_config
13
14
  from agentpack.core.changed_paths import clear_changed_paths, read_changed_paths
14
15
  from agentpack.core.ignore import DEFAULT_AGENTIGNORE, load_spec
@@ -16,6 +17,7 @@ from agentpack.core.scanner import scan, scan_incremental
16
17
  from agentpack.core.snapshot import build_snapshot, save_snapshot, load_snapshot
17
18
  from agentpack.core.diff import diff_snapshots
18
19
  from agentpack.core import git
20
+ from agentpack.core.command_surface import refresh_commands
19
21
  from agentpack.core.context_pack import enrich_call_site_scores, select_files, save_pack_metadata, load_pack_metadata
20
22
  from agentpack.core.execution_state import build_execution_state, compact_execution_state
21
23
  from agentpack.core.models import (
@@ -1575,7 +1577,12 @@ def _build_freshness_metadata(
1575
1577
  ) -> dict[str, Any]:
1576
1578
  dirty = git.dirty_files(root) if git.is_git_repo(root) else set()
1577
1579
  metadata: dict[str, Any] = {
1580
+ "agentpack_version": __version__,
1578
1581
  "generated_at": datetime.now(timezone.utc).isoformat(),
1582
+ "cwd": str(Path.cwd()),
1583
+ "git_root": str(root),
1584
+ "worktree_path": str(root),
1585
+ "source_command": refresh_commands(request.agent).primary,
1579
1586
  "task_source": request.task_source,
1580
1587
  "changed_files_source": plan.changed_files_source,
1581
1588
  "snapshot_root_hash": snapshot_root_hash,
@@ -166,6 +166,7 @@ class E2EResult:
166
166
  expected_files_touched: list[str]
167
167
  missing_expected_edits: list[str]
168
168
  unexpected_files_touched: list[str]
169
+ agentpack_noise: list[str]
169
170
  agent_log_path: str
170
171
  test_log_path: str
171
172
  workdir: str
@@ -3226,6 +3227,7 @@ def _e2e_strategy_metrics(records: list[dict[str, Any]], strategy: str) -> dict[
3226
3227
  return {
3227
3228
  "runs": float(len(subset)),
3228
3229
  "success_rate": sum(1 for row in subset if row.get("passed")) / len(subset),
3230
+ "noise_rate": sum(1 for row in subset if row.get("agentpack_noise")) / len(subset),
3229
3231
  "expected_touch_rate": expected_touch_rate,
3230
3232
  "avg_input_tokens": avg("input_tokens"),
3231
3233
  "avg_output_tokens": avg("agent_output_tokens"),
@@ -3285,6 +3287,7 @@ def _print_e2e_ab_table(
3285
3287
  table.add_row("runs", f"{base['runs']:.0f}", f"{treat['runs']:.0f}", "-")
3286
3288
  table.add_row("task success", f"{base['success_rate']:.0%}", f"{treat['success_rate']:.0%}", f"{deltas['success_rate_pp']:+.1f} pp")
3287
3289
  table.add_row("expected file touched", f"{base['expected_touch_rate']:.0%}", f"{treat['expected_touch_rate']:.0%}", "-")
3290
+ table.add_row("AgentPack noise cases", f"{base['noise_rate']:.0%}", f"{treat['noise_rate']:.0%}", "-")
3288
3291
  table.add_row("tool calls", f"{base['avg_tool_calls']:.1f}", f"{treat['avg_tool_calls']:.1f}", f"{deltas['tool_calls_saved']:+.1f}")
3289
3292
  table.add_row("tokens", f"{base['avg_total_tokens']:,.0f}", f"{treat['avg_total_tokens']:,.0f}", f"{deltas['tokens_saved']:+,.0f}")
3290
3293
  table.add_row("cost", f"${base['avg_total_cost_usd']:.4f}", f"${treat['avg_total_cost_usd']:.4f}", _fmt_signed_usd(deltas["token_cost_saved_usd"]))
@@ -3317,6 +3320,7 @@ def _e2e_ab_markdown(
3317
3320
  f"| runs | {base['runs']:.0f} | {treat['runs']:.0f} | - |",
3318
3321
  f"| task success | {base['success_rate']:.0%} | {treat['success_rate']:.0%} | {deltas['success_rate_pp']:+.1f} pp |",
3319
3322
  f"| expected file touched | {base['expected_touch_rate']:.0%} | {treat['expected_touch_rate']:.0%} | - |",
3323
+ f"| AgentPack noise cases | {base['noise_rate']:.0%} | {treat['noise_rate']:.0%} | - |",
3320
3324
  f"| tool calls | {base['avg_tool_calls']:.1f} | {treat['avg_tool_calls']:.1f} | {deltas['tool_calls_saved']:+.1f} |",
3321
3325
  f"| tokens | {base['avg_total_tokens']:,.0f} | {treat['avg_total_tokens']:,.0f} | {deltas['tokens_saved']:+,.0f} |",
3322
3326
  f"| token cost | ${base['avg_total_cost_usd']:.4f} | ${treat['avg_total_cost_usd']:.4f} | {_fmt_signed_usd(deltas['token_cost_saved_usd'])} |",
@@ -3410,6 +3414,7 @@ def _run_e2e_case(
3410
3414
  expected_touched = _expected_files_touched(public_changed, case.expected_edit_paths)
3411
3415
  missing_expected = sorted(set(case.expected_edit_paths) - set(expected_touched))
3412
3416
  unexpected_touched = _unexpected_files_touched(public_changed, case.expected_edit_paths)
3417
+ agentpack_noise = _agentpack_noise(strategy, unexpected_touched, missing_expected, public_changed)
3413
3418
  time_to_first_expected_file = _time_to_first_expected_file(repo, expected_touched, agent_start_epoch)
3414
3419
  tool_calls = _estimate_agent_tool_calls(agent)
3415
3420
  duration = time.perf_counter() - start
@@ -3442,6 +3447,7 @@ def _run_e2e_case(
3442
3447
  expected_files_touched=expected_touched,
3443
3448
  missing_expected_edits=missing_expected,
3444
3449
  unexpected_files_touched=unexpected_touched,
3450
+ agentpack_noise=agentpack_noise,
3445
3451
  agent_log_path=str(agent_log_path),
3446
3452
  test_log_path=str(test_log_path),
3447
3453
  workdir=str(work_root),
@@ -3495,6 +3501,20 @@ def _unexpected_files_touched(changed: list[str], expected_edit_paths: list[str]
3495
3501
  return sorted(path for path in changed if path not in expected)
3496
3502
 
3497
3503
 
3504
+ def _agentpack_noise(strategy: str, unexpected: list[str], missing_expected: list[str], changed: list[str]) -> list[str]:
3505
+ if "agentpack" not in strategy:
3506
+ return []
3507
+ noise: list[str] = []
3508
+ if unexpected:
3509
+ noise.append(f"unexpected edits: {', '.join(unexpected[:5])}")
3510
+ if missing_expected:
3511
+ noise.append(f"missed expected files: {', '.join(missing_expected[:5])}")
3512
+ generated = [path for path in changed if _is_generated_e2e_path(path)]
3513
+ if generated:
3514
+ noise.append(f"generated AgentPack files changed: {', '.join(generated[:5])}")
3515
+ return noise
3516
+
3517
+
3498
3518
  def _process_output_tokens(result: subprocess.CompletedProcess[str]) -> int:
3499
3519
  return estimate_tokens("\n".join(part for part in (result.stdout, result.stderr) if part))
3500
3520
 
@@ -17,6 +17,7 @@ from agentpack.integrations.global_install import (
17
17
  _detect_rc_file,
18
18
  )
19
19
  from agentpack.commands._shared import console, _root
20
+ from agentpack.core.command_surface import available_cli_commands, installed_cli_status, refresh_commands
20
21
  from agentpack.core.context_pack import load_pack_metadata
21
22
  from agentpack.core.ignore import agentignore_sync_status, format_import_summary
22
23
  from agentpack.core.task_freshness import task_freshness
@@ -45,7 +46,12 @@ def register(app: typer.Typer) -> None:
45
46
 
46
47
  # --- CLI binary ---
47
48
  console.print("[bold]CLI[/]")
48
- binary = shutil.which("agentpack")
49
+ cli_status = installed_cli_status()
50
+ binary = cli_status.get("binary") or shutil.which("agentpack")
51
+ console.print(f" [green]✓[/] importable AgentPack version: {cli_status.get('agentpack_version')}")
52
+ importable = cli_status.get("importable_commands") or []
53
+ if importable:
54
+ console.print(f" [green]✓[/] importable commands: {', '.join(str(cmd) for cmd in importable)}")
49
55
  if binary:
50
56
  try:
51
57
  result = subprocess.run(["agentpack", "--version"], capture_output=True, text=True)
@@ -56,6 +62,15 @@ def register(app: typer.Typer) -> None:
56
62
  else:
57
63
  console.print(" [red]✗[/] agentpack not on PATH — run: pipx install agentpack-cli")
58
64
  ok = False
65
+ help_commands = cli_status.get("help_commands") or []
66
+ if help_commands:
67
+ console.print(f" [green]✓[/] installed CLI commands: {', '.join(str(cmd) for cmd in help_commands)}")
68
+ if binary and "guard" not in set(str(cmd) for cmd in help_commands) and "guard" in set(available_cli_commands()):
69
+ console.print(
70
+ " [yellow]![/] installed CLI command surface may be stale; "
71
+ f"exact repair: {cli_status.get('repair_command')}"
72
+ )
73
+ ok = False
59
74
 
60
75
  try:
61
76
  root = _root()
@@ -147,8 +162,19 @@ def register(app: typer.Typer) -> None:
147
162
  "MCP get_context auto-refreshes this, or run: agentpack pack --task auto"
148
163
  )
149
164
  ok = False
165
+ meta = load_pack_metadata(root) or {}
166
+ freshness = meta.get("freshness") or {}
167
+ if freshness:
168
+ console.print(
169
+ " [green]✓[/] context provenance: "
170
+ f"task_source={freshness.get('task_source', 'unknown')}, "
171
+ f"git_root={freshness.get('git_root', root)}, "
172
+ f"worktree={freshness.get('worktree_path', root)}, "
173
+ f"branch={freshness.get('git_branch', 'unknown')}, "
174
+ f"generated={freshness.get('generated_at', 'unknown')}"
175
+ )
150
176
  else:
151
- console.print(" [yellow]![/] No context pack yet — write .agentpack/task.md, then run: agentpack pack --task auto")
177
+ console.print(f" [yellow]![/] No context pack yet — write .agentpack/task.md, then run: {refresh_commands(agent).context_missing}")
152
178
 
153
179
  # --- Agent-specific config ---
154
180
  console.print("\n[bold]Agent config[/]")
@@ -249,6 +275,14 @@ def register(app: typer.Typer) -> None:
249
275
  pass
250
276
  if not _local_has_mcp and not _global_has_mcp:
251
277
  console.print(" [yellow]![/] MCP server not registered — mcp__agentpack__* tools unavailable")
278
+ console.print(" [yellow]![/] exact repair: agentpack install --agent claude")
279
+ ok = False
280
+ else:
281
+ console.print(
282
+ " [green]✓[/] expected MCP tools: "
283
+ "readiness, route_task, pack_context, get_context, refresh, get_stats"
284
+ )
285
+ console.print(" [green]✓[/] live exposure proof: call MCP tool `readiness` from the agent host")
252
286
 
253
287
  # --- Agent integration matrix ---
254
288
  console.print("\n[bold]Agent integration audit[/]")
@@ -268,8 +302,7 @@ def register(app: typer.Typer) -> None:
268
302
  ok = False
269
303
  if not selected_ok:
270
304
  console.print(
271
- f" [yellow]![/] executable guard repair: "
272
- f"agentpack guard --agent {selected} --repair-stale --refresh-context"
305
+ f" [yellow]![/] exact repair: {refresh_commands(selected).repair}"
273
306
  )
274
307
 
275
308
  # --- Concurrent threads ---
@@ -288,7 +321,7 @@ def register(app: typer.Typer) -> None:
288
321
  if findings:
289
322
  for finding in findings:
290
323
  console.print(f" [yellow]![/] {finding}")
291
- ok = False
324
+ console.print(" [dim]warning only; keep these out of commits/releases.[/]")
292
325
  else:
293
326
  console.print(" [green]✓[/] no generated release-noise files staged or untracked")
294
327
 
@@ -7,6 +7,7 @@ import typer
7
7
  from agentpack.commands._shared import console, _root, run_refresh
8
8
  from agentpack.commands.diagnose_selection import build_selection_diagnosis, _markdown_report
9
9
  from agentpack.commands.guard import _context_is_fresh
10
+ from agentpack.core.command_surface import refresh_commands
10
11
  from agentpack.core.config import load_config
11
12
  from agentpack.core.context_pack import load_pack_metadata
12
13
  from agentpack.core.loop_protocol import load_loop_state
@@ -34,7 +35,7 @@ def register(app: typer.Typer) -> None:
34
35
  stats = run_refresh(root, "auto", "balanced", 0)
35
36
  if stats:
36
37
  recommendations = [{"kind": "fixed", "command": "agentpack next", "reason": "refreshed stale context"}]
37
- fixes.append({"kind": "stale_context", "command": "agentpack guard --agent auto --repair-stale --refresh-context", "returncode": 0})
38
+ fixes.append({"kind": "stale_context", "command": refresh_commands("auto").repair, "returncode": 0})
38
39
  payload = {"recommendations": recommendations, "fixes": fixes, "ok": not recommendations}
39
40
  if json_output:
40
41
  typer.echo(json.dumps(payload, indent=2, sort_keys=True))
@@ -58,7 +59,7 @@ def _recommendations(root) -> list[dict[str, str]]:
58
59
  items.append({"kind": "missing_task", "command": 'agentpack start "describe the task"', "reason": "no concrete task is set"})
59
60
  fresh, reason = _context_is_fresh(root)
60
61
  if not fresh:
61
- items.append({"kind": "stale_context", "command": "agentpack guard --agent auto --repair-stale --refresh-context", "reason": reason})
62
+ items.append({"kind": "stale_context", "command": refresh_commands("auto").repair, "reason": reason})
62
63
  if _has_thread_conflicts(root):
63
64
  items.append({"kind": "thread_conflict", "command": "agentpack threads --conflicts", "reason": "active threads overlap on this branch/worktree"})
64
65
  if _pack_looks_noisy(root):
@@ -138,7 +139,7 @@ def _fix_all_safe(root, recommendations: list[dict[str, str]]) -> tuple[list[dic
138
139
  stats = run_refresh(root, "auto", "balanced", 0)
139
140
  fixes.append({
140
141
  "kind": "stale_context",
141
- "command": "agentpack guard --agent auto --repair-stale --refresh-context",
142
+ "command": refresh_commands("auto").repair,
142
143
  "returncode": 0 if stats else 1,
143
144
  })
144
145
  recommendations = _recommendations(root)
@@ -17,6 +17,7 @@ from agentpack.analysis.ranking import suggest_task_rewrite
17
17
  from agentpack.application.pack_service import PackRequest, PackService, PackResult
18
18
  from agentpack.commands._shared import console, _root, _file_hash, _now_iso
19
19
  from agentpack.core.changed_paths import record_changed_paths
20
+ from agentpack.core.command_surface import refresh_commands
20
21
  from agentpack.core.modes import MODE_HELP, invalid_mode_message, is_requested_mode
21
22
  from agentpack.integrations.agents import check_agent_integration, install_agent_integration
22
23
  from agentpack.session.state import TASK_FILE, load_session, save_session, log_activity
@@ -314,7 +315,7 @@ def _agent_integration_warnings(result: PackResult) -> list[str]:
314
315
  if not failing:
315
316
  return []
316
317
  return [
317
- f"Agent integration needs repair ({agent}); run `agentpack guard --agent {agent} --repair-stale --refresh-context`."
318
+ f"Agent integration needs repair ({agent}); run `{refresh_commands(agent).repair}`."
318
319
  ]
319
320
 
320
321
 
@@ -337,7 +338,7 @@ def _auto_repair_stale_agent_rules(agent: str) -> None:
337
338
  except Exception as exc:
338
339
  console.print(
339
340
  f"[yellow]Stale AgentPack integration detected for {agent}; "
340
- f"run `agentpack guard --agent {agent} --repair-stale --refresh-context`. ({exc})[/]"
341
+ f"run `{refresh_commands(agent).repair}`. ({exc})[/]"
341
342
  )
342
343
 
343
344
 
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  import typer
7
7
 
8
8
  from agentpack.commands._shared import console, _root
9
+ from agentpack.core.command_surface import refresh_command_args
9
10
  from agentpack.core.context_pack import load_pack_metadata
10
11
  from agentpack.core.thread_context import resolve_thread_option, thread_paths
11
12
  from agentpack.integrations.platform import cli_module_argv
@@ -42,9 +43,7 @@ def register(app: typer.Typer) -> None:
42
43
  if workspace:
43
44
  argv.extend(["--workspace", workspace])
44
45
  else:
45
- argv = cli_module_argv("guard", "--agent", agent, "--repair-stale", "--refresh-context", "--mode", mode)
46
- if budget:
47
- argv.extend(["--budget", str(budget)])
46
+ argv = cli_module_argv(*refresh_command_args(agent, mode, budget))
48
47
  if thread_id:
49
48
  argv.extend(["--thread", thread_id])
50
49
  result = subprocess.run(argv, cwd=root)
@@ -4,6 +4,7 @@ import typer
4
4
  import shutil
5
5
 
6
6
  from agentpack.core.config import load_config
7
+ from agentpack.core.command_surface import refresh_commands
7
8
  from agentpack.core.ignore import load_spec
8
9
  from agentpack.core.scanner import scan
9
10
  from agentpack.core.snapshot import build_snapshot
@@ -98,4 +99,4 @@ def _print_deep_health(root, meta: dict | None) -> None:
98
99
  if not check.ok:
99
100
  failing.append(check)
100
101
  if failing:
101
- console.print(f" [yellow]![/] Guard repair: agentpack guard --agent {agent} --repair-stale --refresh-context")
102
+ console.print(f" [yellow]![/] Repair: {refresh_commands(agent).repair}")
@@ -7,6 +7,7 @@ from pathlib import Path
7
7
  import typer
8
8
 
9
9
  from agentpack.commands._shared import console, _root
10
+ from agentpack.core.command_surface import refresh_command_args
10
11
  from agentpack.core.thread_context import resolve_thread_option, thread_paths
11
12
  from agentpack.integrations.platform import cli_module_argv
12
13
  from agentpack.session.state import TASK_FILE
@@ -47,7 +48,7 @@ def set_task(
47
48
  task_text: str = typer.Argument(..., help="Task text to write."),
48
49
  thread: str = typer.Option("", "--thread", help="Use thread-scoped task state."),
49
50
  pack: bool = typer.Option(False, "--pack", help="Run agentpack pack after writing the task."),
50
- guard: bool = typer.Option(False, "--guard", help="Run agentpack guard --repair-stale --refresh-context after writing."),
51
+ guard: bool = typer.Option(False, "--guard", help="Run the installed refresh/repair command after writing."),
51
52
  agent: str = typer.Option("auto", "--agent", help="Agent to pass to pack/guard."),
52
53
  mode: str = typer.Option("balanced", "--mode", help="Pack/guard mode."),
53
54
  ) -> None:
@@ -63,7 +64,7 @@ def set_task(
63
64
 
64
65
  thread_id = _thread_id(root, thread)
65
66
  if guard:
66
- _run_cli(["guard", "--agent", agent, "--repair-stale", "--refresh-context", "--mode", mode], thread_id=thread_id)
67
+ _run_cli(refresh_command_args(agent, mode), thread_id=thread_id)
67
68
  elif pack:
68
69
  _run_cli(["pack", "--agent", agent, "--task", "auto", "--mode", mode], thread_id=thread_id)
69
70
 
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+
5
+ import typer
6
+
7
+ from agentpack.commands._shared import console, _root
8
+ from agentpack.commands.install import (
9
+ _install_slash_command,
10
+ _print_global_template_results,
11
+ _print_install_results,
12
+ )
13
+ from agentpack.integrations import global_install as global_hooks
14
+ from agentpack.integrations.agents import SUPPORTED_AGENTS, expand_agents, install_agent_integration
15
+
16
+
17
+ def register(app: typer.Typer) -> None:
18
+ @app.command()
19
+ def upgrade(
20
+ agent: str = typer.Option(
21
+ "auto",
22
+ "--agent",
23
+ help=f"Agent integration to refresh after package upgrade ({' | '.join(SUPPORTED_AGENTS)}).",
24
+ ),
25
+ repair_existing_global_hooks: bool = typer.Option(
26
+ True,
27
+ "--repair-existing-global-hooks/--no-repair-existing-global-hooks",
28
+ help="Refresh already-installed global git/shell hooks after upgrading AgentPack.",
29
+ ),
30
+ ) -> None:
31
+ """Refresh existing AgentPack repo/global integration surfaces after a package upgrade."""
32
+ root = _root()
33
+ if agent not in SUPPORTED_AGENTS:
34
+ console.print(f"[yellow]Unknown agent: {agent}. Supported: {', '.join(SUPPORTED_AGENTS)}[/]")
35
+ raise typer.Exit(1)
36
+
37
+ agents = expand_agents(agent, root)
38
+ if agent == "auto":
39
+ console.print(f"[dim]Auto-detected agent: {agents[0]}[/]")
40
+
41
+ for selected in agents:
42
+ console.print(f"\n[bold]{selected}[/]")
43
+ results = install_agent_integration(
44
+ root,
45
+ selected,
46
+ install_slash_command=_install_slash_command,
47
+ )
48
+ _print_install_results(selected, results)
49
+
50
+ if repair_existing_global_hooks:
51
+ _repair_existing_global_hooks()
52
+
53
+ console.print("\n[bold green]Upgrade integration refresh complete.[/]")
54
+ console.print(f" Verify with: [bold]agentpack doctor --agent {agent}[/]")
55
+
56
+
57
+ def _repair_existing_global_hooks() -> None:
58
+ repaired = False
59
+ if _global_git_templates_are_installed():
60
+ repaired = True
61
+ console.print("\n[bold]Refreshing existing global git template hooks...[/]")
62
+ hook_results = global_hooks.install_git_template_hooks()
63
+ _print_global_template_results(hook_results)
64
+ git_cfg_action = global_hooks.configure_git_template_dir(dry_run=False)
65
+ console.print(f"[green]git config --global init.templateDir {git_cfg_action}.[/]")
66
+
67
+ rc_file = global_hooks._detect_rc_file()
68
+ if rc_file is not None and rc_file.exists() and global_hooks._SHELL_MARKER_START in rc_file.read_text(encoding="utf-8"):
69
+ repaired = True
70
+ console.print("\n[bold]Refreshing existing shell cd hook...[/]")
71
+ action, path = global_hooks.install_shell_hook(rc_file)
72
+ if path is not None:
73
+ console.print(f"[green]{path} {action}.[/]")
74
+
75
+ if not repaired:
76
+ console.print("\n[dim]No existing global AgentPack hooks found to refresh.[/]")
77
+
78
+
79
+ def _global_git_templates_are_installed() -> bool:
80
+ hooks_dir = global_hooks._GIT_TEMPLATE_DIR / "hooks"
81
+ for name in global_hooks._HOOK_SCRIPTS:
82
+ hook_path = hooks_dir / name
83
+ if hook_path.exists() and global_hooks._AGENTPACK_MARKER in hook_path.read_text(encoding="utf-8"):
84
+ return True
85
+ try:
86
+ result = subprocess.run(
87
+ ["git", "config", "--global", "init.templateDir"],
88
+ capture_output=True,
89
+ text=True,
90
+ )
91
+ except Exception:
92
+ return False
93
+ return result.stdout.strip() == str(global_hooks._GIT_TEMPLATE_DIR)
@@ -10,6 +10,7 @@ import typer
10
10
 
11
11
  from agentpack.commands._shared import console, _root, run_refresh
12
12
  from agentpack.commands.guard import _context_is_fresh
13
+ from agentpack.core.command_surface import refresh_commands
13
14
  from agentpack.core.config import load_config
14
15
  from agentpack.core.loop_protocol import (
15
16
  LoopCommandResult,
@@ -317,7 +318,7 @@ def _loop_finish_blockers(
317
318
  {
318
319
  "kind": "stale_context",
319
320
  "message": f"Context is stale: {reason}",
320
- "command": "agentpack guard --agent auto --repair-stale --refresh-context",
321
+ "command": refresh_commands("auto").repair,
321
322
  }
322
323
  )
323
324
  return blockers
@@ -434,6 +435,7 @@ def _read_task(root: Path, thread: str) -> str:
434
435
 
435
436
  def _refresh_loop_context(root: Path, agent: str, mode: str, budget: int, thread_id: str | None) -> LoopCommandResult:
436
437
  stats = run_refresh(root, agent, mode, budget, thread_id=thread_id)
438
+ command = refresh_commands(agent).primary
437
439
  if stats is None:
438
- return LoopCommandResult(command="agentpack guard --repair-stale --refresh-context", returncode=1, output_excerpt="context refresh failed")
439
- return LoopCommandResult(command="agentpack guard --repair-stale --refresh-context", returncode=0, output_excerpt=json.dumps(stats, sort_keys=True))
440
+ return LoopCommandResult(command=command, returncode=1, output_excerpt="context refresh failed")
441
+ return LoopCommandResult(command=command, returncode=0, output_excerpt=json.dumps(stats, sort_keys=True))
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ import subprocess
5
+ from dataclasses import dataclass
6
+ from typing import Iterable
7
+
8
+ from agentpack import __version__
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class RefreshCommands:
13
+ primary: str
14
+ context_missing: str
15
+ thread_auto: str | None
16
+ repair: str
17
+ used_guard: bool
18
+
19
+
20
+ def available_cli_commands() -> tuple[str, ...]:
21
+ """Return command names registered by the importable AgentPack CLI."""
22
+ try:
23
+ import typer.main
24
+
25
+ from agentpack.cli import app
26
+
27
+ click_cmd = typer.main.get_command(app)
28
+ return tuple(sorted(click_cmd.commands))
29
+ except Exception:
30
+ return ()
31
+
32
+
33
+ def has_cli_command(command: str) -> bool:
34
+ return command in available_cli_commands()
35
+
36
+
37
+ def refresh_commands(agent: str = "auto") -> RefreshCommands:
38
+ if has_cli_command("guard"):
39
+ base = f"agentpack guard --agent {agent} --repair-stale --refresh-context"
40
+ return RefreshCommands(
41
+ primary=base,
42
+ context_missing=base,
43
+ thread_auto=f"AGENTPACK_THREAD_ID=<stable-id> {base} --thread auto",
44
+ repair=base,
45
+ used_guard=True,
46
+ )
47
+ pack = f"agentpack pack --agent {agent} --task auto"
48
+ return RefreshCommands(
49
+ primary=pack,
50
+ context_missing='printf "%s\\n" "<task>" > .agentpack/task.md && ' + pack,
51
+ thread_auto=None,
52
+ repair="agentpack repair --agent " + agent if has_cli_command("repair") else pack,
53
+ used_guard=False,
54
+ )
55
+
56
+
57
+ def refresh_command_args(agent: str = "auto", mode: str = "balanced", budget: int = 0) -> list[str]:
58
+ """Return CLI argv parts for refreshing context with the current command surface."""
59
+ if has_cli_command("guard"):
60
+ args = ["guard", "--agent", agent, "--repair-stale", "--refresh-context", "--mode", mode]
61
+ else:
62
+ args = ["pack", "--agent", agent, "--task", "auto", "--mode", mode]
63
+ if budget:
64
+ args.extend(["--budget", str(budget)])
65
+ return args
66
+
67
+
68
+ def fallback_agent_guidance() -> str:
69
+ return (
70
+ "If AgentPack tools are unavailable or context looks stale/wrong-worktree, "
71
+ "do not trust old pack output. Use direct `rg`, PR diff inspection, and target-file reads, "
72
+ "then run focused validation."
73
+ )
74
+
75
+
76
+ def prompt_quality_guidance() -> str:
77
+ return (
78
+ "Prompt hygiene: for agent-mode coding work, prefer `Task`, `Files`, "
79
+ "`Acceptance criteria`, `Constraints`, `Validation`, and `Output` sections. "
80
+ "For short/simple questions, use Ask/Chat mode instead of agent mode. "
81
+ "Keep routine responses concise unless the user asks for detail."
82
+ )
83
+
84
+
85
+ def installed_cli_status() -> dict[str, object]:
86
+ binary = shutil.which("agentpack")
87
+ status: dict[str, object] = {
88
+ "agentpack_version": __version__,
89
+ "binary": binary,
90
+ "importable_commands": list(available_cli_commands()),
91
+ }
92
+ if not binary:
93
+ status["available"] = False
94
+ status["repair_command"] = "pipx install agentpack-cli"
95
+ return status
96
+ try:
97
+ version = subprocess.run(
98
+ [binary, "--version"],
99
+ capture_output=True,
100
+ text=True,
101
+ timeout=5,
102
+ )
103
+ help_result = subprocess.run(
104
+ [binary, "--help"],
105
+ capture_output=True,
106
+ text=True,
107
+ timeout=5,
108
+ )
109
+ except (OSError, subprocess.TimeoutExpired) as exc:
110
+ status["available"] = False
111
+ status["error"] = str(exc)
112
+ status["repair_command"] = "pipx upgrade agentpack-cli"
113
+ return status
114
+ status["available"] = version.returncode == 0
115
+ status["installed_version"] = (version.stdout.strip() or version.stderr.strip())
116
+ status["help_commands"] = _commands_from_help(help_result.stdout + "\n" + help_result.stderr)
117
+ status["repair_command"] = "pipx upgrade agentpack-cli"
118
+ return status
119
+
120
+
121
+ def _commands_from_help(text: str) -> list[str]:
122
+ commands: set[str] = set()
123
+ known = set(available_cli_commands())
124
+ for line in text.splitlines():
125
+ stripped = line.strip().strip("│").strip()
126
+ if not stripped:
127
+ continue
128
+ first = stripped.split()[0].strip("`")
129
+ if first in known:
130
+ commands.add(first)
131
+ return sorted(commands)
132
+
133
+
134
+ def missing_commands(commands: Iterable[str]) -> list[str]:
135
+ available = set(available_cli_commands())
136
+ return sorted({cmd for cmd in commands if cmd and cmd not in available})
@@ -91,7 +91,18 @@ def save_pack_metadata(
91
91
  "concurrent_context": concurrent_context or {},
92
92
  }
93
93
  if freshness:
94
- for key in ("git_sha", "git_branch", "task_source", "changed_files_source", "task_class"):
94
+ for key in (
95
+ "agentpack_version",
96
+ "source_command",
97
+ "cwd",
98
+ "git_root",
99
+ "worktree_path",
100
+ "git_sha",
101
+ "git_branch",
102
+ "task_source",
103
+ "changed_files_source",
104
+ "task_class",
105
+ ):
95
106
  if key in freshness:
96
107
  meta[key] = freshness[key]
97
108
  path = metadata_path or _metadata_path(root)