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.
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/PKG-INFO +14 -3
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/README.md +13 -2
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/pyproject.toml +1 -1
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/__init__.py +1 -1
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/review_cmd.py +165 -34
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/config.py +12 -0
- agentpack_cli-0.3.31/src/agentpack/core/pack_handoff.py +169 -0
- agentpack_cli-0.3.31/src/agentpack/core/structured_format.py +47 -0
- agentpack_cli-0.3.31/src/agentpack/core/toon_parser.py +165 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack-review.md +5 -3
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/.codex-plugin/plugin.json +7 -1
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/README.md +11 -0
- agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/assets/icon.svg +1 -0
- agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/assets/route-demo.svg +1 -0
- agentpack_cli-0.3.31/src/agentpack/data/codex_plugin/skills/agentpack-review.md +27 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/review/stage1-understanding.md +1 -1
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/review/stage2-judge.md +4 -4
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/antigravity.py +1 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/claude.py +2 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/codex.py +82 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/cursor.py +1 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/windsurf.py +1 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/agents.py +77 -3
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/mcp_server.py +27 -21
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/markdown.py +34 -8
- agentpack_cli-0.3.31/src/agentpack/renderers/toon.py +157 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/models.py +3 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/prompt_builder.py +6 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/service.py +23 -0
- agentpack_cli-0.3.30/src/agentpack/core/pack_handoff.py +0 -77
- agentpack_cli-0.3.30/src/agentpack/data/codex_plugin/assets/icon.svg +0 -10
- agentpack_cli-0.3.30/src/agentpack/data/codex_plugin/skills/agentpack-review.md +0 -25
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/.gitignore +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/LICENSE +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/antigravity.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/base.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/claude.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/codex.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/cursor.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/detect.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/generic.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/adapters/windsurf.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/dependency_graph.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/go_imports.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/java_imports.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/js_ts_imports.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/monorepo.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/naming_signals.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/python_ast.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/python_imports.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/ranking.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/repo_map.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/role_inference.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/rust_imports.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/symbols.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/task_classifier.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/analysis/tests.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/application/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/application/pack_service.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/cli.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/_shared.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/benchmark.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/ci_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/claude_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/compress_output.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/dashboard.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/dev_check.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/diagnose_selection.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/diff.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/doctor.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/eval_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/explain.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/guard.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/hook_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/ignore_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/init.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/install.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/learn.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/mcp_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/memory.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/migrate.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/monitor.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/next_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/pack.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/perf.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/quickstart.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/release_check.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/release_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/repair.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/retrieve.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/route.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/scan.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/skills.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/start_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/state_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/stats.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/status.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/summarize.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/task_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/threads.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/tune.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/upgrade.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/verify_wheel.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/watch.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/workflow_cmd.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/commands/wrap.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/bootstrap.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/cache.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/changed_paths.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/command_surface.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/context_pack.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/diff.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/e2e_benchmark.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/evals.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/execution_state.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/git.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/git_hooks.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/global_install.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/ignore.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/loop_protocol.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/merkle.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/models.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/modes.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/pack_registry.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/redactor.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/scanner.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/snapshot.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/task_freshness.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/thread_context.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/token_estimator.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/core/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/collectors.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/models.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/dashboard/renderers.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack-learn.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/agentpack.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/.codexignore +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/LICENSE +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/SECURITY.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-learn.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-pack.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-refresh.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack-route.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/data/codex_plugin/skills/agentpack.md +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/installers/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/git_hooks.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/global_install.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/platform.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/integrations/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/collector.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/extractor.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/feedback.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/lesson_ranker.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/models.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/provider.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/quality.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/renderers.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/learning/skill_map.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/output_compression/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/output_compression/core.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/compact.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/renderers/receipts.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/discovery.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/parser.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/scoring.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/router/skills_index.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/events.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/references.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/session/state.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/summaries/__init__.py +0 -0
- {agentpack_cli-0.3.30 → agentpack_cli-0.3.31}/src/agentpack/summaries/base.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|
|
@@ -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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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,
|
|
73
|
-
branch =
|
|
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
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
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(
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
"
|
|
125
|
-
"
|
|
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
|
|
154
|
-
"2. Confirm the understanding
|
|
155
|
-
"3. Read the Stage 2 prompt file completely and produce the findings
|
|
156
|
-
"4. Confirm the findings
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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(["", "
|
|
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)
|