agentpack-cli 0.3.30__tar.gz → 0.3.31__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 (182) hide show
  1. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/PKG-INFO +14 -3
  2. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/README.md +13 -2
  3. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/pyproject.toml +1 -1
  4. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/__init__.py +1 -1
  5. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/review_cmd.py +165 -34
  6. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/config.py +12 -0
  7. agentpack_cli-0.3.31/src/agentpack/core/pack_handoff.py +169 -0
  8. agentpack_cli-0.3.31/src/agentpack/core/structured_format.py +47 -0
  9. agentpack_cli-0.3.31/src/agentpack/core/toon_parser.py +165 -0
  10. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack-review.md +5 -3
  11. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/.codex-plugin/plugin.json +7 -1
  12. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/README.md +11 -0
  13. agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/assets/icon.svg +1 -0
  14. agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/assets/route-demo.svg +1 -0
  15. agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/skills/agentpack-review.md +27 -0
  16. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/review/stage1-understanding.md +1 -1
  17. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/review/stage2-judge.md +4 -4
  18. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/antigravity.py +1 -0
  19. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/claude.py +2 -0
  20. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/codex.py +82 -0
  21. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/cursor.py +1 -0
  22. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/windsurf.py +1 -0
  23. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/agents.py +77 -3
  24. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/mcp_server.py +27 -21
  25. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/markdown.py +34 -8
  26. agentpack_cli-0.3.31/src/agentpack/renderers/toon.py +157 -0
  27. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/models.py +3 -0
  28. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/prompt_builder.py +6 -0
  29. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/service.py +23 -0
  30. agentpack_cli-0.3.30/src/agentpack/core/pack_handoff.py +0 -77
  31. agentpack_cli-0.3.30/src/agentpack/data/codex_plugin/assets/icon.svg +0 -10
  32. agentpack_cli-0.3.30/src/agentpack/data/codex_plugin/skills/agentpack-review.md +0 -25
  33. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/.gitignore +0 -0
  34. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/LICENSE +0 -0
  35. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/__init__.py +0 -0
  36. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/antigravity.py +0 -0
  37. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/base.py +0 -0
  38. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/claude.py +0 -0
  39. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/codex.py +0 -0
  40. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/cursor.py +0 -0
  41. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/detect.py +0 -0
  42. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/generic.py +0 -0
  43. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/windsurf.py +0 -0
  44. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/__init__.py +0 -0
  45. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/dependency_graph.py +0 -0
  46. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/go_imports.py +0 -0
  47. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/java_imports.py +0 -0
  48. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/js_ts_imports.py +0 -0
  49. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/monorepo.py +0 -0
  50. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/naming_signals.py +0 -0
  51. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/python_ast.py +0 -0
  52. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/python_imports.py +0 -0
  53. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/ranking.py +0 -0
  54. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/repo_map.py +0 -0
  55. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/role_inference.py +0 -0
  56. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/rust_imports.py +0 -0
  57. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/symbols.py +0 -0
  58. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/task_classifier.py +0 -0
  59. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/tests.py +0 -0
  60. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/application/__init__.py +0 -0
  61. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/application/pack_service.py +0 -0
  62. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/cli.py +0 -0
  63. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/__init__.py +0 -0
  64. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/_shared.py +0 -0
  65. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/benchmark.py +0 -0
  66. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/ci_cmd.py +0 -0
  67. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/claude_cmd.py +0 -0
  68. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/compress_output.py +0 -0
  69. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/dashboard.py +0 -0
  70. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/dev_check.py +0 -0
  71. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/diagnose_selection.py +0 -0
  72. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/diff.py +0 -0
  73. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/doctor.py +0 -0
  74. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/eval_cmd.py +0 -0
  75. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/explain.py +0 -0
  76. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/guard.py +0 -0
  77. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/hook_cmd.py +0 -0
  78. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/ignore_cmd.py +0 -0
  79. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/init.py +0 -0
  80. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/install.py +0 -0
  81. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/learn.py +0 -0
  82. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/mcp_cmd.py +0 -0
  83. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/memory.py +0 -0
  84. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/migrate.py +0 -0
  85. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/monitor.py +0 -0
  86. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/next_cmd.py +0 -0
  87. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/pack.py +0 -0
  88. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/perf.py +0 -0
  89. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/quickstart.py +0 -0
  90. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/release_check.py +0 -0
  91. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/release_cmd.py +0 -0
  92. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/repair.py +0 -0
  93. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/retrieve.py +0 -0
  94. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/route.py +0 -0
  95. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/scan.py +0 -0
  96. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/skills.py +0 -0
  97. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/start_cmd.py +0 -0
  98. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/state_cmd.py +0 -0
  99. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/stats.py +0 -0
  100. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/status.py +0 -0
  101. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/summarize.py +0 -0
  102. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/task_cmd.py +0 -0
  103. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/threads.py +0 -0
  104. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/tune.py +0 -0
  105. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/upgrade.py +0 -0
  106. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/verify_wheel.py +0 -0
  107. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/watch.py +0 -0
  108. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/workflow_cmd.py +0 -0
  109. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/wrap.py +0 -0
  110. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/__init__.py +0 -0
  111. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/bootstrap.py +0 -0
  112. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/cache.py +0 -0
  113. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/changed_paths.py +0 -0
  114. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/command_surface.py +0 -0
  115. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/context_pack.py +0 -0
  116. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/diff.py +0 -0
  117. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/e2e_benchmark.py +0 -0
  118. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/evals.py +0 -0
  119. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/execution_state.py +0 -0
  120. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/git.py +0 -0
  121. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/git_hooks.py +0 -0
  122. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/global_install.py +0 -0
  123. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/ignore.py +0 -0
  124. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/loop_protocol.py +0 -0
  125. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/merkle.py +0 -0
  126. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/models.py +0 -0
  127. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/modes.py +0 -0
  128. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/pack_registry.py +0 -0
  129. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/redactor.py +0 -0
  130. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/scanner.py +0 -0
  131. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/snapshot.py +0 -0
  132. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/task_freshness.py +0 -0
  133. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/thread_context.py +0 -0
  134. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/token_estimator.py +0 -0
  135. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/vscode_tasks.py +0 -0
  136. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/__init__.py +0 -0
  137. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/collectors.py +0 -0
  138. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/models.py +0 -0
  139. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/renderers.py +0 -0
  140. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack-learn.md +0 -0
  141. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack.md +0 -0
  142. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/.codexignore +0 -0
  143. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/LICENSE +0 -0
  144. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/SECURITY.md +0 -0
  145. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-learn.md +0 -0
  146. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-pack.md +0 -0
  147. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-refresh.md +0 -0
  148. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-route.md +0 -0
  149. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack.md +0 -0
  150. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/__init__.py +0 -0
  151. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/__init__.py +0 -0
  152. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/git_hooks.py +0 -0
  153. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/global_install.py +0 -0
  154. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/platform.py +0 -0
  155. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/vscode_tasks.py +0 -0
  156. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/__init__.py +0 -0
  157. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/collector.py +0 -0
  158. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/extractor.py +0 -0
  159. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/feedback.py +0 -0
  160. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/lesson_ranker.py +0 -0
  161. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/models.py +0 -0
  162. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/provider.py +0 -0
  163. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/quality.py +0 -0
  164. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/renderers.py +0 -0
  165. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/skill_map.py +0 -0
  166. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/output_compression/__init__.py +0 -0
  167. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/output_compression/core.py +0 -0
  168. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/__init__.py +0 -0
  169. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/compact.py +0 -0
  170. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/receipts.py +0 -0
  171. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/__init__.py +0 -0
  172. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/discovery.py +0 -0
  173. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/parser.py +0 -0
  174. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/scoring.py +0 -0
  175. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/skills_index.py +0 -0
  176. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/__init__.py +0 -0
  177. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/events.py +0 -0
  178. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/references.py +0 -0
  179. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/state.py +0 -0
  180. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/summaries/__init__.py +0 -0
  181. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/summaries/base.py +0 -0
  182. {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/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.30
3
+ Version: 0.3.31
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/
@@ -65,6 +65,8 @@ Description-Content-Type: text/markdown
65
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>
66
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>
67
67
  <a href="https://www.npmjs.com/package/@vishal2612200/agentpack"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@vishal2612200/agentpack.svg"></a>
68
+ <a href="https://hol.org/registry/plugins/agentpack%2Fagentpack"><img alt="HOL trust score" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhol.org%2Fapi%2Fregistry%2Fbadges%2Fplugin%3Fslug%3Dagentpack%252Fagentpack%26metric%3Dtrust%26style%3Dflat"></a>
69
+ <a href="https://hol.org/registry/plugins/agentpack%2Fagentpack"><img alt="HOL security score" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhol.org%2Fapi%2Fregistry%2Fbadges%2Fplugin%3Fslug%3Dagentpack%252Fagentpack%26metric%3Dsecurity%26style%3Dflat"></a>
68
70
  <a href="https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg"></a>
69
71
  <a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
70
72
  </p>
@@ -227,10 +229,19 @@ Inside Codex:
227
229
  ```text
228
230
  @agentpack-route fix auth token expiry
229
231
  @agentpack-pack fix auth token expiry
230
- @agentpack-review
232
+ @agentpack-review focus on backward compatibility
231
233
  ```
232
234
 
233
- The plugin calls the local AgentPack engine. It does not upload code and does not turn AgentPack into a coding agent.
235
+ The Codex plugin calls the local AgentPack engine. Codex setup enables the
236
+ local `agentpack@local` bundle so commands like `@agentpack-review` match the
237
+ installed CLI version. Verify with `agentpack doctor --agent codex` after
238
+ upgrades.
239
+
240
+ The review flow prepares a local two-stage PR review bundle: preflight metadata,
241
+ a runbook, stage prompts, and branch-scoped understanding/findings JSON files.
242
+ It does not replace `gh pr view`, `git diff`, direct code reads, or tests.
243
+
244
+ AgentPack does not upload code and does not turn AgentPack into a coding agent.
234
245
 
235
246
  See [`docs/agent-plugins.md`](docs/agent-plugins.md) and [`docs/codex-plugin.md`](docs/codex-plugin.md).
236
247
 
@@ -18,6 +18,8 @@
18
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>
19
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>
20
20
  <a href="https://www.npmjs.com/package/@vishal2612200/agentpack"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@vishal2612200/agentpack.svg"></a>
21
+ <a href="https://hol.org/registry/plugins/agentpack%2Fagentpack"><img alt="HOL trust score" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhol.org%2Fapi%2Fregistry%2Fbadges%2Fplugin%3Fslug%3Dagentpack%252Fagentpack%26metric%3Dtrust%26style%3Dflat"></a>
22
+ <a href="https://hol.org/registry/plugins/agentpack%2Fagentpack"><img alt="HOL security score" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhol.org%2Fapi%2Fregistry%2Fbadges%2Fplugin%3Fslug%3Dagentpack%252Fagentpack%26metric%3Dsecurity%26style%3Dflat"></a>
21
23
  <a href="https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg"></a>
22
24
  <a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
23
25
  </p>
@@ -180,10 +182,19 @@ Inside Codex:
180
182
  ```text
181
183
  @agentpack-route fix auth token expiry
182
184
  @agentpack-pack fix auth token expiry
183
- @agentpack-review
185
+ @agentpack-review focus on backward compatibility
184
186
  ```
185
187
 
186
- The plugin calls the local AgentPack engine. It does not upload code and does not turn AgentPack into a coding agent.
188
+ The Codex plugin calls the local AgentPack engine. Codex setup enables the
189
+ local `agentpack@local` bundle so commands like `@agentpack-review` match the
190
+ installed CLI version. Verify with `agentpack doctor --agent codex` after
191
+ upgrades.
192
+
193
+ The review flow prepares a local two-stage PR review bundle: preflight metadata,
194
+ a runbook, stage prompts, and branch-scoped understanding/findings JSON files.
195
+ It does not replace `gh pr view`, `git diff`, direct code reads, or tests.
196
+
197
+ AgentPack does not upload code and does not turn AgentPack into a coding agent.
187
198
 
188
199
  See [`docs/agent-plugins.md`](docs/agent-plugins.md) and [`docs/codex-plugin.md`](docs/codex-plugin.md).
189
200
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.3.30"
3
+ version = "0.3.31"
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.30"
3
+ __version__ = "0.3.31"
@@ -6,23 +6,28 @@ import shutil
6
6
  import subprocess
7
7
  from pathlib import Path
8
8
  from typing import Any
9
+ from uuid import uuid4
9
10
 
10
11
  import typer
11
12
 
12
13
  from agentpack.analysis.tests import find_related_tests
13
14
  from agentpack.commands._shared import _atomic_write, _now_iso, _root, console
14
15
  from agentpack.core import git as git_core
16
+ from agentpack.core.toon_parser import ToonParseError, load_toon
15
17
 
16
18
  _PREFLIGHT_PATH = Path(".agentpack/review-preflight.json")
17
19
  _RUNBOOK_PATH = Path(".agentpack/review.prompt.md")
18
20
  _UNDERSTANDING_PROMPT_PATH = Path(".agentpack/review-understanding.prompt.md")
19
21
  _JUDGE_PROMPT_PATH = Path(".agentpack/review-judge.prompt.md")
22
+ _REVIEW_RUNS_DIR = Path(".agentpack/reviews")
23
+ _LLM_REVIEW_FORMAT = "TOON"
20
24
 
21
25
 
22
26
  def register(app: typer.Typer) -> None:
23
27
  @app.command("review")
24
28
  def review(
25
29
  review_context: str = typer.Argument("", help="Optional reviewer or developer context for this PR review."),
30
+ resume: str = typer.Option("", "--resume", help="Resume a previous review run by run id."),
26
31
  ) -> None:
27
32
  """Prepare the full two-stage PR review bundle for the current branch or PR."""
28
33
  root = _root()
@@ -30,8 +35,17 @@ def register(app: typer.Typer) -> None:
30
35
  console.print("[red]agentpack review requires a git repository.[/]")
31
36
  raise typer.Exit(1)
32
37
 
33
- outputs = _review_output_paths(root)
34
- preflight = _build_review_preflight(root, review_context.strip(), outputs)
38
+ if resume.strip():
39
+ preflight = _load_review_run(root, resume.strip())
40
+ outputs = _review_output_paths(
41
+ root,
42
+ branch_prefix=preflight["review"]["branch_prefix"],
43
+ run_id=preflight["review"]["run_id"],
44
+ )
45
+ else:
46
+ outputs = _review_output_paths(root)
47
+ preflight = _build_review_preflight(root, review_context.strip(), outputs)
48
+
35
49
  runbook = _render_review_runbook(preflight)
36
50
  understanding_prompt = _render_stage_prompt(
37
51
  "stage1-understanding.md",
@@ -46,16 +60,23 @@ def register(app: typer.Typer) -> None:
46
60
  prior_path=outputs["understanding"],
47
61
  )
48
62
 
49
- for rel_path, content in {
63
+ artifacts = {
64
+ outputs["preflight"]: json.dumps(preflight, indent=2) + "\n",
65
+ outputs["runbook"]: runbook,
66
+ outputs["understanding_prompt"]: understanding_prompt,
67
+ outputs["judge_prompt"]: judge_prompt,
50
68
  _PREFLIGHT_PATH: json.dumps(preflight, indent=2) + "\n",
51
69
  _RUNBOOK_PATH: runbook,
52
70
  _UNDERSTANDING_PROMPT_PATH: understanding_prompt,
53
71
  _JUDGE_PROMPT_PATH: judge_prompt,
54
- }.items():
72
+ }
73
+ for rel_path, content in artifacts.items():
55
74
  abs_path = root / rel_path
56
75
  abs_path.parent.mkdir(parents=True, exist_ok=True)
57
76
  _atomic_write(abs_path, content)
58
77
 
78
+ console.print(f"[green]✓[/] Review run id: [bold]{preflight['review']['run_id']}[/]")
79
+ console.print(f"[green]✓[/] Review run dir: [bold]{preflight['paths']['run_dir']}[/]")
59
80
  console.print(f"[green]✓[/] Review preflight: [bold]{_PREFLIGHT_PATH}[/]")
60
81
  console.print(f"[green]✓[/] Review runbook: [bold]{_RUNBOOK_PATH}[/]")
61
82
  console.print(f"[green]✓[/] Stage 1 prompt: [bold]{_UNDERSTANDING_PROMPT_PATH}[/]")
@@ -69,8 +90,8 @@ def register(app: typer.Typer) -> None:
69
90
  console.print("Use the runbook from your agent host; it drives understanding, then judge, against exact diff and code evidence.")
70
91
 
71
92
 
72
- def _build_review_preflight(root: Path, review_context: str, outputs: dict[str, Path]) -> dict[str, Any]:
73
- branch = git_core.current_branch(root) or "HEAD"
93
+ def _build_review_preflight(root: Path, review_context: str, outputs: dict[str, Any]) -> dict[str, Any]:
94
+ branch = outputs["branch"]
74
95
  sha = git_core.current_sha(root) or ""
75
96
  pr = _gh_pr_metadata(root)
76
97
  all_paths = _repo_paths(root)
@@ -88,6 +109,12 @@ def _build_review_preflight(root: Path, review_context: str, outputs: dict[str,
88
109
  return {
89
110
  "generated_at": _now_iso(),
90
111
  "review_context": review_context,
112
+ "review": {
113
+ "mode": "fresh",
114
+ "run_id": outputs["run_id"],
115
+ "branch": branch,
116
+ "branch_prefix": outputs["branch_prefix"],
117
+ },
91
118
  "git": {
92
119
  "branch": branch,
93
120
  "branch_prefix": outputs["branch_prefix"],
@@ -102,30 +129,78 @@ def _build_review_preflight(root: Path, review_context: str, outputs: dict[str,
102
129
  "changed_files_count": len(changed_files),
103
130
  },
104
131
  "paths": {
105
- "preflight": str(_PREFLIGHT_PATH),
106
- "runbook": str(_RUNBOOK_PATH),
107
- "understanding_prompt": str(_UNDERSTANDING_PROMPT_PATH),
108
- "judge_prompt": str(_JUDGE_PROMPT_PATH),
132
+ "run_dir": _rel_to_root(outputs["run_dir"], root),
133
+ "preflight": _rel_to_root(outputs["preflight"], root),
134
+ "runbook": _rel_to_root(outputs["runbook"], root),
135
+ "understanding_prompt": _rel_to_root(outputs["understanding_prompt"], root),
136
+ "judge_prompt": _rel_to_root(outputs["judge_prompt"], root),
109
137
  "understanding_output": _rel_to_root(outputs["understanding"], root),
110
138
  "findings_output": _rel_to_root(outputs["findings"], root),
139
+ "active_preflight": str(_PREFLIGHT_PATH),
140
+ "active_runbook": str(_RUNBOOK_PATH),
141
+ "active_understanding_prompt": str(_UNDERSTANDING_PROMPT_PATH),
142
+ "active_judge_prompt": str(_JUDGE_PROMPT_PATH),
111
143
  },
112
144
  "changed_files": changed_files,
113
145
  "warnings": warnings,
114
146
  }
115
147
 
116
148
 
117
- def _review_output_paths(root: Path) -> dict[str, Any]:
149
+ def _review_output_paths(
150
+ root: Path,
151
+ *,
152
+ branch_prefix: str | None = None,
153
+ run_id: str | None = None,
154
+ ) -> dict[str, Any]:
118
155
  branch = git_core.current_branch(root) or "HEAD"
119
- branch_prefix = branch.replace("/", "-")
120
- understanding = root / f"{branch_prefix}_understanding.json"
121
- findings = root / f"{branch_prefix}_findings.json"
156
+ branch_prefix = branch_prefix or branch.replace("/", "-")
157
+ run_id = run_id or _new_review_run_id()
158
+ run_dir = root / _REVIEW_RUNS_DIR / branch_prefix / run_id
122
159
  return {
160
+ "branch": branch,
123
161
  "branch_prefix": branch_prefix,
124
- "understanding": understanding,
125
- "findings": findings,
162
+ "run_id": run_id,
163
+ "run_dir": run_dir,
164
+ "preflight": run_dir / "preflight.json",
165
+ "runbook": run_dir / "runbook.md",
166
+ "understanding_prompt": run_dir / "understanding.prompt.md",
167
+ "judge_prompt": run_dir / "judge.prompt.md",
168
+ "understanding": run_dir / "understanding.toon",
169
+ "findings": run_dir / "findings.toon",
126
170
  }
127
171
 
128
172
 
173
+ def _new_review_run_id() -> str:
174
+ return f"{_now_iso().replace(':', '').replace('-', '').replace('.', '')}-{uuid4().hex[:8]}"
175
+
176
+
177
+ def _load_review_run(root: Path, run_id: str) -> dict[str, Any]:
178
+ branch = git_core.current_branch(root) or "HEAD"
179
+ branch_prefix = branch.replace("/", "-")
180
+ preflight_path = root / _REVIEW_RUNS_DIR / branch_prefix / run_id / "preflight.json"
181
+ if not preflight_path.exists():
182
+ console.print(f"[red]Review run not found:[/] {preflight_path}")
183
+ raise typer.Exit(1)
184
+ try:
185
+ preflight = json.loads(preflight_path.read_text(encoding="utf-8"))
186
+ except json.JSONDecodeError:
187
+ console.print(f"[red]Review run preflight is invalid JSON:[/] {preflight_path}")
188
+ raise typer.Exit(1)
189
+ understanding_path = preflight_path.parent / "understanding.toon"
190
+ findings_path = preflight_path.parent / "findings.toon"
191
+ try:
192
+ if understanding_path.exists():
193
+ _validate_review_artifact(understanding_path, kind="understanding")
194
+ if findings_path.exists():
195
+ _validate_review_artifact(findings_path, kind="findings")
196
+ except ValueError as exc:
197
+ console.print(f"[red]Review run artifact invalid:[/] {exc}")
198
+ raise typer.Exit(1)
199
+ preflight.setdefault("review", {})
200
+ preflight["review"]["mode"] = "resume"
201
+ return preflight
202
+
203
+
129
204
  def _render_review_runbook(preflight: dict[str, Any]) -> str:
130
205
  return (
131
206
  "# AgentPack Review Workflow\n\n"
@@ -135,6 +210,9 @@ def _render_review_runbook(preflight: dict[str, Any]) -> str:
135
210
  "## Reviewer Context\n\n"
136
211
  f"{preflight['review_context'] or '(none)'}\n\n"
137
212
  "## Preflight\n\n"
213
+ f"- Review mode: {preflight['review'].get('mode', 'fresh')}\n"
214
+ f"- Review run id: {preflight['review']['run_id']}\n"
215
+ f"- Review run dir: {preflight['paths']['run_dir']}\n"
138
216
  f"- Branch: {preflight['git']['branch']}\n"
139
217
  f"- Branch prefix: {preflight['git']['branch_prefix']}\n"
140
218
  f"- Head SHA: {preflight['git']['head_sha']}\n"
@@ -147,13 +225,13 @@ def _render_review_runbook(preflight: dict[str, Any]) -> str:
147
225
  f"- Preflight JSON: `{preflight['paths']['preflight']}`\n"
148
226
  f"- Stage 1 prompt: `{preflight['paths']['understanding_prompt']}`\n"
149
227
  f"- Stage 2 prompt: `{preflight['paths']['judge_prompt']}`\n"
150
- f"- Stage 1 output: `{preflight['paths']['understanding_output']}`\n"
151
- f"- Stage 2 output: `{preflight['paths']['findings_output']}`\n\n"
228
+ f"- Stage 1 output ({_LLM_REVIEW_FORMAT}): `{preflight['paths']['understanding_output']}`\n"
229
+ f"- Stage 2 output ({_LLM_REVIEW_FORMAT}): `{preflight['paths']['findings_output']}`\n\n"
152
230
  "## Workflow\n\n"
153
- "1. Read the Stage 1 prompt file completely and produce the understanding JSON at the declared output path.\n"
154
- "2. Confirm the understanding JSON parses before moving on.\n"
155
- "3. Read the Stage 2 prompt file completely and produce the findings JSON at the declared output path.\n"
156
- "4. Confirm the findings JSON parses before reporting back.\n"
231
+ f"1. Read the Stage 1 prompt file completely and produce the understanding {_LLM_REVIEW_FORMAT} at the declared output path.\n"
232
+ f"2. Confirm the understanding {_LLM_REVIEW_FORMAT} file exists and follows the declared schema before moving on.\n"
233
+ f"3. Read the Stage 2 prompt file completely and produce the findings {_LLM_REVIEW_FORMAT} at the declared output path.\n"
234
+ f"4. Confirm the findings {_LLM_REVIEW_FORMAT} file exists and follows the declared schema before reporting back.\n"
157
235
  "5. In the final user-facing response, summarize findings and validation gaps without exposing internal stage names.\n"
158
236
  )
159
237
 
@@ -167,23 +245,28 @@ def _render_stage_prompt(
167
245
  ) -> str:
168
246
  root = _root().resolve()
169
247
  abs_output = output_path.resolve()
170
- lines = [
171
- "# AgentPack Review Stage",
172
- "",
173
- "## AgentPack Context",
174
- "",
175
- f"- Reviewer context: {preflight['review_context'] or '(none)'}",
176
- f"- Preflight JSON: {preflight['paths']['preflight']}",
177
- f"- Diff range: {preflight['diff']['range']}",
178
- f"- Diff source: {preflight['diff']['source']}",
179
- f"- Output path: {_rel_to_root(abs_output, root)}",
180
- ]
248
+ lines = [_load_review_template(template_name)]
249
+ lines.extend(
250
+ [
251
+ "",
252
+ "## AgentPack Run Inputs",
253
+ "",
254
+ f"- Review run id: {preflight['review']['run_id']}",
255
+ f"- Review mode: {preflight['review'].get('mode', 'fresh')}",
256
+ f"- Preflight JSON: {preflight['paths']['preflight']}",
257
+ f"- Head SHA: {preflight['git']['head_sha']}",
258
+ f"- Diff range: {preflight['diff']['range']}",
259
+ f"- Diff source: {preflight['diff']['source']}",
260
+ f"- Output path: {_rel_to_root(abs_output, root)}",
261
+ f"- Structured output format: {_LLM_REVIEW_FORMAT}",
262
+ ]
263
+ )
181
264
  if prior_path is not None:
182
265
  lines.append(f"- Input path: {_rel_to_root(prior_path.resolve(), root)}")
183
266
  if preflight["warnings"]:
184
267
  lines.extend(["", "## Warnings", ""])
185
268
  lines.extend(f"- {warning}" for warning in preflight["warnings"])
186
- lines.extend(["", "## Stage Prompt", "", _load_review_template(template_name)])
269
+ lines.extend(["", "Reviewer context:", preflight["review_context"] or "(none)"])
187
270
  return "\n".join(lines).rstrip() + "\n"
188
271
 
189
272
 
@@ -296,6 +379,7 @@ def _warnings(root: Path, pr: dict[str, Any] | None, diff_info: dict[str, str],
296
379
  dirty = sorted(git_core.dirty_files(root))
297
380
  if dirty:
298
381
  warnings.append(f"dirty tree has {len(dirty)} path(s); review the checked-out PR head, not local edits")
382
+ warnings.extend(_incomplete_review_run_warnings(root))
299
383
  if not pr:
300
384
  warnings.append("gh PR metadata unavailable; review is using local git context only")
301
385
  if diff_info["source"] != "pr-base":
@@ -308,6 +392,53 @@ def _warnings(root: Path, pr: dict[str, Any] | None, diff_info: dict[str, str],
308
392
  return warnings
309
393
 
310
394
 
395
+ def _incomplete_review_run_warnings(root: Path) -> list[str]:
396
+ branch = git_core.current_branch(root) or "HEAD"
397
+ branch_prefix = branch.replace("/", "-")
398
+ branch_dir = root / _REVIEW_RUNS_DIR / branch_prefix
399
+ if not branch_dir.exists():
400
+ return []
401
+ warnings: list[str] = []
402
+ for run_dir in sorted((path for path in branch_dir.iterdir() if path.is_dir()), reverse=True):
403
+ understanding = run_dir / "understanding.toon"
404
+ findings = run_dir / "findings.toon"
405
+ if understanding.exists():
406
+ try:
407
+ _validate_review_artifact(understanding, kind="understanding")
408
+ except ValueError as exc:
409
+ warnings.append(f"invalid understanding TOON in {run_dir.name}: {exc}")
410
+ break
411
+ if findings.exists():
412
+ try:
413
+ _validate_review_artifact(findings, kind="findings")
414
+ except ValueError as exc:
415
+ warnings.append(f"invalid findings TOON in {run_dir.name}: {exc}")
416
+ break
417
+ if understanding.exists() and not findings.exists():
418
+ warnings.append(
419
+ f"incomplete previous review run {run_dir.name}; start fresh by default or resume with `agentpack review --resume {run_dir.name}`"
420
+ )
421
+ break
422
+ return warnings
423
+
424
+
425
+ def _validate_review_artifact(path: Path, *, kind: str) -> dict[str, Any]:
426
+ try:
427
+ payload = load_toon(path)
428
+ except (OSError, ToonParseError, json.JSONDecodeError) as exc:
429
+ raise ValueError(f"{path.name} is not valid TOON: {exc}") from exc
430
+ if not isinstance(payload, dict):
431
+ raise ValueError(f"{path.name} must decode to an object")
432
+ if kind == "understanding":
433
+ required = ("intent", "change_units", "open_questions")
434
+ else:
435
+ required = ("findings", "coverage")
436
+ missing = [key for key in required if key not in payload]
437
+ if missing:
438
+ raise ValueError(f"{path.name} missing required key(s): {', '.join(missing)}")
439
+ return payload
440
+
441
+
311
442
  def _rel_to_root(path: Path, root: Path) -> str:
312
443
  try:
313
444
  return str(path.relative_to(root)).replace("\\", "/")
@@ -128,6 +128,12 @@ class AgentConfig(BaseModel):
128
128
  patch_claude_md: bool = False
129
129
 
130
130
 
131
+ class AgenticConfig(BaseModel):
132
+ llm_structured_format: str = "auto"
133
+ enforce_llm_toon: bool = True
134
+ toon_fallback_when_larger: bool = True
135
+
136
+
131
137
  class AgentsConfig(BaseModel):
132
138
  claude: AgentConfig = Field(
133
139
  default_factory=lambda: AgentConfig(
@@ -175,6 +181,7 @@ class Config(BaseModel):
175
181
  runtime: RuntimeConfig = Field(default_factory=RuntimeConfig)
176
182
  hooks: HooksConfig = Field(default_factory=HooksConfig)
177
183
  skills: SkillsConfig = Field(default_factory=SkillsConfig)
184
+ agentic: AgenticConfig = Field(default_factory=AgenticConfig)
178
185
  agents: AgentsConfig = Field(default_factory=AgentsConfig)
179
186
  scoring: ScoringWeights = Field(default_factory=ScoringWeights)
180
187
 
@@ -275,6 +282,11 @@ max_selected = 3
275
282
  always_recommend = ["karpathy-guidelines"]
276
283
  allow_external_side_effects = false
277
284
 
285
+ [agentic]
286
+ llm_structured_format = "auto"
287
+ enforce_llm_toon = true
288
+ toon_fallback_when_larger = true
289
+
278
290
  [scoring]
279
291
  # Scoring weights — higher wins budget allocation.
280
292
  # Tune these to make agentpack favour your team's file layout.
@@ -0,0 +1,169 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import Counter
4
+ from typing import Any, Literal
5
+
6
+ from agentpack.core.models import ContextPack, OmittedRelevantFile
7
+
8
+ NextAction = Literal[
9
+ "ready_to_inspect_selected",
10
+ "inspect_omitted_first",
11
+ "refresh_context",
12
+ "deepen_pack",
13
+ "run_direct_search",
14
+ ]
15
+
16
+
17
+ def build_pack_handoff(pack: ContextPack) -> dict[str, Any]:
18
+ """Return a compact operational receipt for agents before editing.
19
+
20
+ The receipt intentionally avoids correctness/confidence claims. It only
21
+ summarizes signals AgentPack already computed and recommends the next
22
+ inspection step before code changes.
23
+ """
24
+ omitted_relevant_files = pack.pack_handoff_omitted_relevant_files or pack.omitted_relevant_files
25
+ high_risk_omitted = [item for item in omitted_relevant_files if item.risk == "high"]
26
+ excluded_receipts = [receipt for receipt in pack.receipts if receipt.action == "excluded"]
27
+ stale = pack.stale or bool(pack.freshness_warnings)
28
+ budget_pressure = pack.budget > 0 and pack.token_estimate >= int(pack.budget * 0.95)
29
+ omitted_reason_counts = Counter(_omitted_reason_bucket(_omitted_reason(item)) for item in omitted_relevant_files)
30
+ excluded_reason_counts = Counter(_excluded_reason_bucket(receipt.reason) for receipt in excluded_receipts)
31
+ freshness_warnings = list(pack.freshness_warnings)
32
+
33
+ if stale:
34
+ action: NextAction = "refresh_context"
35
+ reason = "pack is stale or has freshness warnings"
36
+ elif high_risk_omitted:
37
+ action = "inspect_omitted_first"
38
+ reason = "high-risk omitted relevant files matched the task"
39
+ elif budget_pressure:
40
+ action = "deepen_pack"
41
+ reason = "pack rendered close to the token budget"
42
+ elif not pack.selected_files:
43
+ action = "run_direct_search"
44
+ reason = "no selected files were included in the pack"
45
+ else:
46
+ action = "ready_to_inspect_selected"
47
+ reason = "selected files are available and no refresh or omitted-file gate fired"
48
+
49
+ verifier_hint = _verifier_hint(action)
50
+ return {
51
+ "schema_version": 2,
52
+ "recommended_action": action,
53
+ "reason": reason,
54
+ "before_editing": {
55
+ "recommended_action": action,
56
+ "verifier_hint": verifier_hint,
57
+ },
58
+ "task": pack.task,
59
+ "task_hash": pack.freshness.get("packed_task_hash") or pack.freshness.get("task_hash") or "",
60
+ "context_path": pack.freshness.get("context_path", ""),
61
+ "repo_ref": {
62
+ "branch": pack.freshness.get("git_branch", ""),
63
+ "sha": pack.freshness.get("git_sha", ""),
64
+ },
65
+ "pack_snapshot": {
66
+ "generated_at": pack.freshness.get("generated_at", ""),
67
+ "snapshot_hash": pack.freshness.get("snapshot_root_hash", ""),
68
+ },
69
+ # Backward-compatible aliases for existing consumers.
70
+ "git_sha": pack.freshness.get("git_sha", ""),
71
+ "git_branch": pack.freshness.get("git_branch", ""),
72
+ "budget": {
73
+ "target_tokens": pack.budget,
74
+ "rendered_tokens": pack.token_estimate,
75
+ "pressure": budget_pressure,
76
+ },
77
+ "selected": {
78
+ "files": len(pack.selected_files),
79
+ "tests": sum(1 for item in pack.selected_files if _looks_like_test(item.path)),
80
+ },
81
+ "omitted_relevant": {
82
+ "files": len(omitted_relevant_files),
83
+ "high_risk": len(high_risk_omitted),
84
+ "top": [item.path for item in high_risk_omitted[:5]],
85
+ "reason_counts": _sorted_counts(omitted_reason_counts),
86
+ },
87
+ "skipped_uncertain": {
88
+ "excluded_files": len(excluded_receipts),
89
+ "excluded_reason_counts": _sorted_counts(excluded_reason_counts),
90
+ "freshness_warnings": freshness_warnings,
91
+ },
92
+ "freshness": {
93
+ "refresh_required": stale,
94
+ "warnings": freshness_warnings,
95
+ },
96
+ # Machine-readable extension point for non-markdown consumers. The
97
+ # markdown renderer already prints the same verifier hint inline.
98
+ "suggested_checks": [verifier_hint] if verifier_hint else [],
99
+ }
100
+
101
+
102
+ def _looks_like_test(path: str) -> bool:
103
+ lower = path.lower()
104
+ name = lower.rsplit("/", 1)[-1]
105
+ return lower.startswith(("tests/", "test/")) or name.startswith("test_") or name.endswith(("_test.py", ".test.ts", ".test.js", ".spec.ts", ".spec.js"))
106
+
107
+
108
+ def _omitted_reason(item: OmittedRelevantFile) -> str:
109
+ if item.reasons:
110
+ return item.reasons[0]
111
+ return item.omission_reason
112
+
113
+
114
+ def _sorted_counts(counts: Counter[str]) -> dict[str, int]:
115
+ return dict(sorted(counts.items(), key=lambda item: (-item[1], item[0])))
116
+
117
+
118
+ def _omitted_reason_bucket(reason: str) -> str:
119
+ lower = reason.lower()
120
+ if lower.startswith(("related test", "test for")) or "has related tests" in lower:
121
+ return "related_test"
122
+ if lower.startswith(("caller of selected symbol", "reverse dependency")):
123
+ return "caller_or_reverse_dependency"
124
+ if lower.startswith(("direct dependency", "direct content evidence")):
125
+ return "direct_evidence"
126
+ if lower.startswith(("keyword phrase match:", "literal definition match:", "quoted literal match:")):
127
+ return "literal_or_phrase_match"
128
+ if lower.startswith(("matched call:", "matched define:", "matched entrypoint:")):
129
+ return "symbol_or_entrypoint_match"
130
+ if lower.startswith(("matched env read:", "matched external system:", "matched side effect:")):
131
+ return "side_effect_or_external_match"
132
+ if lower.startswith("multi-term path match"):
133
+ return "multi_term_path_match"
134
+ if lower.startswith("api route owner match"):
135
+ return "api_route_owner_match"
136
+ if lower.startswith("api endpoint pair with"):
137
+ return "api_endpoint_pair"
138
+ if lower.startswith("api producer for frontend call"):
139
+ return "api_producer_for_frontend_call"
140
+ if lower.startswith("generic public api penalty"):
141
+ return "generic_public_api_penalty"
142
+ if lower.startswith("weak filename-only match"):
143
+ return "weak_filename_only_match"
144
+ if lower.startswith(("matched domain:", "matched naming keyword:", "matched ranking keyword:", "matched role keyword:")):
145
+ return "broad_keyword_match"
146
+ if lower.startswith(("workspace match", "release/version metadata", "build/dependency metadata")):
147
+ return "repo_metadata_match"
148
+ if lower.startswith("budget"):
149
+ return "budget"
150
+ return reason
151
+
152
+
153
+ def _excluded_reason_bucket(reason: str) -> str:
154
+ lower = reason.lower()
155
+ if lower.startswith("marginal slot replaced by"):
156
+ return "marginal_slot_replaced"
157
+ return _omitted_reason_bucket(reason)
158
+
159
+
160
+ def _verifier_hint(action: NextAction) -> str:
161
+ if action == "refresh_context":
162
+ return "run `agentpack pack` before editing"
163
+ if action == "inspect_omitted_first":
164
+ return "inspect high-risk omitted files before editing shared behavior"
165
+ if action == "deepen_pack":
166
+ return "increase the budget or rerun a deeper pack before broad edits"
167
+ if action == "run_direct_search":
168
+ return "run direct search because no files were selected"
169
+ return "inspect selected files before editing"
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, Literal
6
+
7
+ from agentpack.core.config import load_config
8
+ from agentpack.core.token_estimator import estimate_tokens
9
+ from agentpack.renderers.toon import is_toon_friendly, render_toon
10
+
11
+ StructuredFormat = Literal["auto", "toon", "json"]
12
+
13
+
14
+ def to_machine(value: Any) -> str:
15
+ return json.dumps(value, indent=2, sort_keys=True)
16
+
17
+
18
+ def choose_llm_format(root: Path, value: Any, *, requested: StructuredFormat = "auto") -> Literal["toon", "json"]:
19
+ if requested == "json":
20
+ return "json"
21
+ if requested == "toon":
22
+ return "toon"
23
+
24
+ cfg = load_config(root)
25
+ configured = cfg.agentic.llm_structured_format
26
+ if configured == "json":
27
+ return "json"
28
+ if configured == "toon":
29
+ return "toon"
30
+ if not cfg.agentic.enforce_llm_toon:
31
+ return "json"
32
+
33
+ if not is_toon_friendly(value):
34
+ return "json"
35
+
36
+ json_text = json.dumps(value, indent=2, sort_keys=True)
37
+ toon_text = render_toon(value)
38
+ if cfg.agentic.toon_fallback_when_larger and estimate_tokens(toon_text) >= estimate_tokens(json_text):
39
+ return "json"
40
+ return "toon"
41
+
42
+
43
+ def to_llm(root: Path, value: Any, *, requested: StructuredFormat = "auto", root_name: str | None = None) -> str:
44
+ chosen = choose_llm_format(root, value, requested=requested)
45
+ if chosen == "json":
46
+ return to_machine(value)
47
+ return render_toon(value, root_name=root_name)