agentpack-cli 0.3.16__tar.gz → 0.3.17__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.16 → agentpack_cli-0.3.17}/PKG-INFO +4 -1
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/README.md +2 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/pyproject.toml +3 -1
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/__init__.py +1 -1
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/cli.py +2 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/benchmark.py +10 -10
- agentpack_cli-0.3.17/src/agentpack/commands/dashboard.py +43 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/eval_cmd.py +7 -7
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/explain.py +3 -3
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/init.py +8 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/next_cmd.py +36 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/scan.py +1 -1
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/skills.py +7 -5
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/tune.py +1 -1
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/workflow_cmd.py +110 -5
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/config.py +30 -0
- agentpack_cli-0.3.17/src/agentpack/core/loop_protocol.py +349 -0
- agentpack_cli-0.3.17/src/agentpack/dashboard/__init__.py +1 -0
- agentpack_cli-0.3.17/src/agentpack/dashboard/collectors.py +520 -0
- agentpack_cli-0.3.17/src/agentpack/dashboard/models.py +156 -0
- agentpack_cli-0.3.17/src/agentpack/dashboard/renderers.py +275 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/discovery.py +16 -31
- agentpack_cli-0.3.17/src/agentpack/router/skills_index.py +191 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/.gitignore +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/LICENSE +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/antigravity.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/base.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/claude.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/codex.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/cursor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/detect.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/generic.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/adapters/windsurf.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/dependency_graph.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/go_imports.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/java_imports.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/js_ts_imports.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/monorepo.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/naming_signals.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/python_imports.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/ranking.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/repo_map.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/role_inference.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/rust_imports.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/symbols.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/task_classifier.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/analysis/tests.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/application/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/application/pack_service.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/_shared.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/ci_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/claude_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/dev_check.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/diagnose_selection.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/diff.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/doctor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/guard.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/hook_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/ignore_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/install.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/learn.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/mcp_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/migrate.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/monitor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/pack.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/quickstart.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/release_check.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/release_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/repair.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/route.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/start_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/state_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/stats.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/status.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/summarize.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/task_cmd.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/threads.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/verify_wheel.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/commands/watch.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/bootstrap.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/cache.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/changed_paths.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/context_pack.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/diff.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/evals.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/execution_state.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/git.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/git_hooks.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/global_install.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/ignore.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/merkle.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/models.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/redactor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/scanner.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/snapshot.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/task_freshness.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/thread_context.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/token_estimator.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/core/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/data/agentpack.md +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/antigravity.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/claude.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/codex.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/cursor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/installers/windsurf.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/agents.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/git_hooks.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/global_install.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/platform.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/integrations/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/collector.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/extractor.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/feedback.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/lesson_ranker.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/models.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/provider.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/quality.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/renderers.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/learning/skill_map.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/mcp_server.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/renderers/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/renderers/compact.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/renderers/markdown.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/renderers/receipts.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/models.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/parser.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/prompt_builder.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/scoring.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/router/service.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/session/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/session/state.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/summaries/__init__.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/src/agentpack/summaries/base.py +0 -0
- {agentpack_cli-0.3.16 → agentpack_cli-0.3.17}/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.17
|
|
4
4
|
Summary: Local MCP context router for Claude Code, Codex, Cursor, and AI coding agents.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -29,6 +29,7 @@ Requires-Dist: watchdog>=4.0.0; extra == 'all'
|
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: mypy; extra == 'dev'
|
|
31
31
|
Requires-Dist: pytest; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
32
33
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
33
34
|
Requires-Dist: ruff; extra == 'dev'
|
|
34
35
|
Requires-Dist: tomli>=2.0.0; (python_version < '3.11') and extra == 'dev'
|
|
@@ -376,6 +377,7 @@ gate.
|
|
|
376
377
|
| `agentpack guard --repair-stale --refresh-context` | Check freshness, repair stale rules, refresh context |
|
|
377
378
|
| `agentpack status` | Show context freshness and git/task state |
|
|
378
379
|
| `agentpack stats` | Show pack size, token savings, and top files |
|
|
380
|
+
| `agentpack dashboard` | Local HTML control plane for context, skills, learning, and benchmark quality |
|
|
379
381
|
| `agentpack explain --task auto` | Debug selected and omitted files |
|
|
380
382
|
| `agentpack diagnose-selection` | Turn latest pack/benchmark signals into concrete tuning actions |
|
|
381
383
|
| `agentpack ignore suggest|apply` | Suggest or apply `.agentignore` improvements |
|
|
@@ -459,6 +461,7 @@ AgentPack writes local artifacts under `.agentpack/`:
|
|
|
459
461
|
| `.agentpack/learning.prompt.md` | optional source-backed prompt for external LLM refinement |
|
|
460
462
|
| `.agentpack/pr-learning-comment.md` | optional PR-comment-ready learning summary |
|
|
461
463
|
| `.agentpack/learning-dashboard.html` | optional static dashboard from `agentpack learn --dashboard` |
|
|
464
|
+
| `.agentpack/dashboard.html` | local project dashboard from `agentpack dashboard` |
|
|
462
465
|
| `.agentpack/team-lessons.md` | optional shared lesson export from `agentpack learn --team-export` |
|
|
463
466
|
| `.agentpack/learning-feedback.jsonl` | optional local helpful/not-helpful feedback records |
|
|
464
467
|
| `.agentpack/pack_metadata.json` | freshness and pack metadata |
|
|
@@ -336,6 +336,7 @@ gate.
|
|
|
336
336
|
| `agentpack guard --repair-stale --refresh-context` | Check freshness, repair stale rules, refresh context |
|
|
337
337
|
| `agentpack status` | Show context freshness and git/task state |
|
|
338
338
|
| `agentpack stats` | Show pack size, token savings, and top files |
|
|
339
|
+
| `agentpack dashboard` | Local HTML control plane for context, skills, learning, and benchmark quality |
|
|
339
340
|
| `agentpack explain --task auto` | Debug selected and omitted files |
|
|
340
341
|
| `agentpack diagnose-selection` | Turn latest pack/benchmark signals into concrete tuning actions |
|
|
341
342
|
| `agentpack ignore suggest|apply` | Suggest or apply `.agentignore` improvements |
|
|
@@ -419,6 +420,7 @@ AgentPack writes local artifacts under `.agentpack/`:
|
|
|
419
420
|
| `.agentpack/learning.prompt.md` | optional source-backed prompt for external LLM refinement |
|
|
420
421
|
| `.agentpack/pr-learning-comment.md` | optional PR-comment-ready learning summary |
|
|
421
422
|
| `.agentpack/learning-dashboard.html` | optional static dashboard from `agentpack learn --dashboard` |
|
|
423
|
+
| `.agentpack/dashboard.html` | local project dashboard from `agentpack dashboard` |
|
|
422
424
|
| `.agentpack/team-lessons.md` | optional shared lesson export from `agentpack learn --team-export` |
|
|
423
425
|
| `.agentpack/learning-feedback.jsonl` | optional local helpful/not-helpful feedback records |
|
|
424
426
|
| `.agentpack/pack_metadata.json` | freshness and pack metadata |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentpack-cli"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.17"
|
|
4
4
|
description = "Local MCP context router for Claude Code, Codex, Cursor, and AI coding agents."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -47,6 +47,7 @@ all = [
|
|
|
47
47
|
|
|
48
48
|
dev = [
|
|
49
49
|
"pytest",
|
|
50
|
+
"pytest-asyncio",
|
|
50
51
|
"pytest-cov",
|
|
51
52
|
"ruff",
|
|
52
53
|
"mypy",
|
|
@@ -55,6 +56,7 @@ dev = [
|
|
|
55
56
|
|
|
56
57
|
[tool.pytest.ini_options]
|
|
57
58
|
pythonpath = ["src"]
|
|
59
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
58
60
|
markers = [
|
|
59
61
|
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
60
62
|
]
|
|
@@ -1930,21 +1930,21 @@ def benchmark(
|
|
|
1930
1930
|
mode: str = typer.Option("balanced", "--mode", help="Mode for single-task run (lite|minimal|balanced|deep)."),
|
|
1931
1931
|
workspace: str = typer.Option("", "--workspace", help="Restrict benchmark packs to a workspace, e.g. apps/web."),
|
|
1932
1932
|
cases: str = typer.Option("", "--cases", help="Path to TOML cases file (default: .agentpack/benchmark.toml)."),
|
|
1933
|
-
compare: bool = typer.Option(False, "--compare",
|
|
1934
|
-
init: bool = typer.Option(False, "--init",
|
|
1935
|
-
results_template: bool = typer.Option(False, "--results-template",
|
|
1933
|
+
compare: bool = typer.Option(False, "--compare", help="Compare minimal/balanced/deep for each task."),
|
|
1934
|
+
init: bool = typer.Option(False, "--init", help="Scaffold a benchmark.toml and exit."),
|
|
1935
|
+
results_template: bool = typer.Option(False, "--results-template", help="Create benchmarks/results/YYYY-MM-DD.md for publishing benchmark evidence."),
|
|
1936
1936
|
from_history: int = typer.Option(0, "--from-history", help="Sample last N unique tasks from metrics.jsonl history."),
|
|
1937
1937
|
write_cases: bool = typer.Option(False, "--write-cases", help="Append --from-history cases to .agentpack/benchmark.toml."),
|
|
1938
|
-
sample_fixtures: bool = typer.Option(False, "--sample-fixtures",
|
|
1939
|
-
release_gate: bool = typer.Option(False, "--release-gate",
|
|
1940
|
-
public_repos: bool = typer.Option(False, "--public-repos",
|
|
1938
|
+
sample_fixtures: bool = typer.Option(False, "--sample-fixtures", help="Run bundled FastAPI/Next.js/mixed-repo fixture evals from a source checkout."),
|
|
1939
|
+
release_gate: bool = typer.Option(False, "--release-gate", help="Run the public real-repo release gate."),
|
|
1940
|
+
public_repos: bool = typer.Option(False, "--public-repos", help="Run real public-repo commit cases from benchmarks/public-repos.toml."),
|
|
1941
1941
|
public_repos_file: str = typer.Option("", "--public-repos-file", help="Path to public repo benchmark manifest."),
|
|
1942
1942
|
public_repos_cache: str = typer.Option("", "--public-repos-cache", help="Directory for cached public repo clones."),
|
|
1943
|
-
refresh_public_repos: bool = typer.Option(False, "--refresh-public-repos",
|
|
1944
|
-
public_table: bool = typer.Option(False, "--public-table",
|
|
1943
|
+
refresh_public_repos: bool = typer.Option(False, "--refresh-public-repos", help="Delete and reclone public repo benchmark cache before running."),
|
|
1944
|
+
public_table: bool = typer.Option(False, "--public-table", help="Write a publishable Markdown benchmark table under benchmarks/results/."),
|
|
1945
1945
|
no_public_table: bool = typer.Option(False, "--no-public-table", help="Do not write a benchmark results markdown table."),
|
|
1946
|
-
misses: bool = typer.Option(False, "--misses",
|
|
1947
|
-
prove_targets: bool = typer.Option(False, "--prove-targets",
|
|
1946
|
+
misses: bool = typer.Option(False, "--misses", help="Show diagnostics for expected files that were not selected."),
|
|
1947
|
+
prove_targets: bool = typer.Option(False, "--prove-targets", help="Exit non-zero unless recall/token precision targets pass."),
|
|
1948
1948
|
min_recall: float = typer.Option(0.60, "--min-recall", help="Recall target for --prove-targets."),
|
|
1949
1949
|
min_token_precision: float = typer.Option(0.50, "--min-token-precision", help="Token precision target for --prove-targets."),
|
|
1950
1950
|
) -> None:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from agentpack.commands._shared import _atomic_write, _root, console
|
|
11
|
+
from agentpack.dashboard.collectors import build_project_dashboard_snapshot
|
|
12
|
+
from agentpack.dashboard.renderers import render_dashboard_html
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register(app: typer.Typer) -> None:
|
|
16
|
+
@app.command()
|
|
17
|
+
def dashboard(
|
|
18
|
+
json_output: bool = typer.Option(False, "--json", help="Print normalized dashboard snapshot JSON."),
|
|
19
|
+
open_browser: bool = typer.Option(False, "--open", help="Open the generated HTML dashboard."),
|
|
20
|
+
output: str = typer.Option("", "--output", "-o", help="Dashboard HTML output path."),
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Generate a local AgentPack dashboard."""
|
|
23
|
+
root = _root()
|
|
24
|
+
snapshot = build_project_dashboard_snapshot(root)
|
|
25
|
+
if json_output:
|
|
26
|
+
typer.echo(json.dumps(snapshot.model_dump(mode="json"), indent=2, sort_keys=True))
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
out = root / (output or ".agentpack/dashboard.html")
|
|
30
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
_atomic_write(out, render_dashboard_html(snapshot))
|
|
32
|
+
console.print(f"[green]✓[/] Wrote [bold]{out}[/]")
|
|
33
|
+
if open_browser:
|
|
34
|
+
_open_file(out)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _open_file(path: Path) -> None:
|
|
38
|
+
if sys.platform == "darwin":
|
|
39
|
+
subprocess.run(["open", str(path)], check=False)
|
|
40
|
+
elif sys.platform.startswith("win"):
|
|
41
|
+
subprocess.run(["cmd", "/c", "start", "", str(path)], check=False)
|
|
42
|
+
else:
|
|
43
|
+
subprocess.run(["xdg-open", str(path)], check=False)
|
|
@@ -28,25 +28,25 @@ from agentpack.core.evals import (
|
|
|
28
28
|
def register(app: typer.Typer) -> None:
|
|
29
29
|
@app.command(name="eval")
|
|
30
30
|
def eval_command(
|
|
31
|
-
init: bool = typer.Option(False, "--init",
|
|
31
|
+
init: bool = typer.Option(False, "--init", help="Scaffold .agentpack/evals.toml and exit."),
|
|
32
32
|
cases: str = typer.Option("", "--cases", help="Path to eval TOML file (default: .agentpack/evals.toml)."),
|
|
33
33
|
case: str = typer.Option("", "--case", help="Run one eval case by id."),
|
|
34
|
-
prove_targets: bool = typer.Option(False, "--prove-targets",
|
|
34
|
+
prove_targets: bool = typer.Option(False, "--prove-targets", help="Exit non-zero when any eval case fails."),
|
|
35
35
|
capture: str = typer.Option("", "--capture", help="Append a case from current git diff using this id."),
|
|
36
36
|
failure_class: str = typer.Option("context", "--failure-class", help=f"Failure class ({' | '.join(FAILURE_CLASSES)})."),
|
|
37
37
|
failure_source: str = typer.Option("agent_failed", "--failure-source", help="Failure source for captured cases."),
|
|
38
38
|
check: list[str] | None = typer.Option(None, "--check", help="Deterministic command check for --capture. Repeatable."),
|
|
39
39
|
task: str = typer.Option("", "--task", help="Task text for --capture."),
|
|
40
40
|
base_ref: str = typer.Option("HEAD", "--base-ref", help="Git base ref for diff checks."),
|
|
41
|
-
report: bool = typer.Option(False, "--report",
|
|
42
|
-
ci_template: bool = typer.Option(False, "--ci-template",
|
|
41
|
+
report: bool = typer.Option(False, "--report", help="Write benchmarks/results/YYYY-MM-DD-eval.md."),
|
|
42
|
+
ci_template: bool = typer.Option(False, "--ci-template", help="Scaffold .github/workflows/agentpack-eval.yml and exit."),
|
|
43
43
|
variant: str = typer.Option("agentpack", "--variant", help="Result variant label, e.g. baseline or agentpack."),
|
|
44
44
|
compare_variants: str = typer.Option("", "--compare-variants", help="Compare latest results as BASELINE:VARIANT."),
|
|
45
|
-
replay: bool = typer.Option(False, "--replay",
|
|
46
|
-
watch: bool = typer.Option(False, "--watch",
|
|
45
|
+
replay: bool = typer.Option(False, "--replay", help="Run cases in isolated git worktrees using captured patch_file artifacts."),
|
|
46
|
+
watch: bool = typer.Option(False, "--watch", help="Rerun evals when git diff state changes."),
|
|
47
47
|
interval: float = typer.Option(2.0, "--interval", help="Watch polling interval in seconds."),
|
|
48
48
|
max_runs: int = typer.Option(0, "--max-runs", help="Maximum watch runs (0 = unlimited)."),
|
|
49
|
-
until_pass: bool = typer.Option(False, "--until-pass",
|
|
49
|
+
until_pass: bool = typer.Option(False, "--until-pass", help="Stop watch mode after all cases pass."),
|
|
50
50
|
agent: str = typer.Option("", "--agent", help="Agent label to store with --capture metadata."),
|
|
51
51
|
prompt_file: str = typer.Option("", "--prompt-file", help="Prompt artifact path to store with --capture."),
|
|
52
52
|
context_file: str = typer.Option(".agentpack/context.md", "--context-file", help="Context artifact path to store with --capture."),
|
|
@@ -294,9 +294,9 @@ def register(app: typer.Typer) -> None:
|
|
|
294
294
|
budget: int = typer.Option(0, "--budget", help="Token budget (0 = use config default)."),
|
|
295
295
|
since: Optional[str] = typer.Option(None, "--since", help="Git ref to compare against (e.g. HEAD~1, main)."),
|
|
296
296
|
file: Optional[str] = typer.Option(None, "--file", help="Show detailed score breakdown for a specific file."),
|
|
297
|
-
omitted: bool = typer.Option(False, "--omitted",
|
|
298
|
-
why_noisy: bool = typer.Option(False, "--why-noisy",
|
|
299
|
-
budget_plan: bool = typer.Option(False, "--budget-plan",
|
|
297
|
+
omitted: bool = typer.Option(False, "--omitted", help="Show top-10 excluded files and why."),
|
|
298
|
+
why_noisy: bool = typer.Option(False, "--why-noisy", help="Explain broad task terms and noisy selection signals."),
|
|
299
|
+
budget_plan: bool = typer.Option(False, "--budget-plan", help="Show selected modes, token costs, and value per token."),
|
|
300
300
|
) -> None:
|
|
301
301
|
"""Explain which files would be selected and why, without writing a context file."""
|
|
302
302
|
if mode not in ("lite", "minimal", "balanced", "deep"):
|
|
@@ -71,6 +71,10 @@ def _repo_gitignore_entries(share_cache: bool = False, agent: str = "generic") -
|
|
|
71
71
|
".agentpack/learning-dashboard.html",
|
|
72
72
|
".agentpack/team-lessons.md",
|
|
73
73
|
".agentpack/learning-feedback.jsonl",
|
|
74
|
+
".agentpack/loop_state.json",
|
|
75
|
+
".agentpack/progress.md",
|
|
76
|
+
".agentpack/loop_events.jsonl",
|
|
77
|
+
".agentpack/loop_failures.jsonl",
|
|
74
78
|
".agentignore",
|
|
75
79
|
]
|
|
76
80
|
)
|
|
@@ -112,6 +116,10 @@ def _agentpack_gitignore_content(share_cache: bool = False) -> str:
|
|
|
112
116
|
"learning-dashboard.html",
|
|
113
117
|
"team-lessons.md",
|
|
114
118
|
"learning-feedback.jsonl",
|
|
119
|
+
"loop_state.json",
|
|
120
|
+
"progress.md",
|
|
121
|
+
"loop_events.jsonl",
|
|
122
|
+
"loop_failures.jsonl",
|
|
115
123
|
]
|
|
116
124
|
)
|
|
117
125
|
return "\n".join(entries) + "\n"
|
|
@@ -7,9 +7,12 @@ import typer
|
|
|
7
7
|
from agentpack.commands._shared import console, _root, run_refresh
|
|
8
8
|
from agentpack.commands.diagnose_selection import build_selection_diagnosis, _markdown_report
|
|
9
9
|
from agentpack.commands.guard import _context_is_fresh
|
|
10
|
+
from agentpack.core.config import load_config
|
|
10
11
|
from agentpack.core.context_pack import load_pack_metadata
|
|
12
|
+
from agentpack.core.loop_protocol import load_loop_state
|
|
11
13
|
from agentpack.core.thread_context import detect_conflicts, list_thread_rows
|
|
12
14
|
from agentpack.integrations.platform import cli_module_argv
|
|
15
|
+
from agentpack.router.skills_index import ensure_inventory_index
|
|
13
16
|
from agentpack.session.state import TASK_FILE
|
|
14
17
|
import subprocess
|
|
15
18
|
|
|
@@ -60,6 +63,8 @@ def _recommendations(root) -> list[dict[str, str]]:
|
|
|
60
63
|
items.append({"kind": "thread_conflict", "command": "agentpack threads --conflicts", "reason": "active threads overlap on this branch/worktree"})
|
|
61
64
|
if _pack_looks_noisy(root):
|
|
62
65
|
items.append({"kind": "selection_noise", "command": "agentpack diagnose-selection", "reason": "latest pack has broad/noisy selection signals"})
|
|
66
|
+
items.extend(_skills_index_recommendations(root))
|
|
67
|
+
items.extend(_loop_recommendations(root))
|
|
63
68
|
return items
|
|
64
69
|
|
|
65
70
|
|
|
@@ -90,6 +95,37 @@ def _pack_looks_noisy(root) -> bool:
|
|
|
90
95
|
return False
|
|
91
96
|
|
|
92
97
|
|
|
98
|
+
def _skills_index_recommendations(root) -> list[dict[str, str]]:
|
|
99
|
+
cfg = load_config(root)
|
|
100
|
+
try:
|
|
101
|
+
ensure_inventory_index(root, cfg.skills.paths)
|
|
102
|
+
except Exception as exc:
|
|
103
|
+
return [
|
|
104
|
+
{
|
|
105
|
+
"kind": "skills_index_failed",
|
|
106
|
+
"command": "agentpack skills index",
|
|
107
|
+
"reason": f"automatic skills index refresh failed: {exc}",
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
return []
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _loop_recommendations(root) -> list[dict[str, str]]:
|
|
114
|
+
cfg = load_config(root)
|
|
115
|
+
if not cfg.loop.enabled:
|
|
116
|
+
return []
|
|
117
|
+
state = load_loop_state(root)
|
|
118
|
+
if state is None:
|
|
119
|
+
return []
|
|
120
|
+
if not state.runner:
|
|
121
|
+
return [{"kind": "loop_runner_missing", "command": 'agentpack work "..." --run --runner "..."', "reason": "Ralph Loop state exists but no runner is configured"}]
|
|
122
|
+
if state.status == "ready_to_finish":
|
|
123
|
+
return [{"kind": "loop_ready_to_finish", "command": "agentpack finish --since main", "reason": "Ralph Loop verification passed"}]
|
|
124
|
+
if state.status == "blocked":
|
|
125
|
+
return [{"kind": "loop_blocked", "command": "agentpack dashboard", "reason": f"Ralph Loop blocked: {state.blocked_reason or 'inspect loop failures'}"}]
|
|
126
|
+
return [{"kind": "loop_continue", "command": f'agentpack work "{state.task}" --run', "reason": f"Ralph Loop is {state.status}"}]
|
|
127
|
+
|
|
128
|
+
|
|
93
129
|
def _fix_all_safe(root, recommendations: list[dict[str, str]]) -> tuple[list[dict[str, str]], list[dict[str, str | int]]]:
|
|
94
130
|
fixes: list[dict[str, str | int]] = []
|
|
95
131
|
if any(item["kind"] == "init" for item in recommendations):
|
|
@@ -16,7 +16,7 @@ def register(app: typer.Typer) -> None:
|
|
|
16
16
|
@app.command(name="scan")
|
|
17
17
|
def scan_cmd(
|
|
18
18
|
largest: int = typer.Option(10, "--largest", min=0, help="Show the N largest packable files by estimated tokens."),
|
|
19
|
-
ignored_summary: bool = typer.Option(False, "--ignored-summary",
|
|
19
|
+
ignored_summary: bool = typer.Option(False, "--ignored-summary", help="Group ignored/binary files by directory or extension."),
|
|
20
20
|
) -> None:
|
|
21
21
|
"""Scan the repository and report file statistics."""
|
|
22
22
|
root = _root()
|
|
@@ -9,7 +9,8 @@ from rich.table import Table
|
|
|
9
9
|
from agentpack.commands._shared import _root, console
|
|
10
10
|
from agentpack.core.config import load_config
|
|
11
11
|
from agentpack.router.prompt_builder import render_plain
|
|
12
|
-
from agentpack.router.discovery import discover_inventory
|
|
12
|
+
from agentpack.router.discovery import discover_inventory
|
|
13
|
+
from agentpack.router.skills_index import ensure_inventory_index
|
|
13
14
|
from agentpack.router.service import RouteService
|
|
14
15
|
|
|
15
16
|
skills_app = typer.Typer(help="Inspect and index local agent skills and rules.")
|
|
@@ -44,10 +45,10 @@ def index_skills() -> None:
|
|
|
44
45
|
"""Write .agentpack/skills_index.json."""
|
|
45
46
|
root = _root()
|
|
46
47
|
cfg = load_config(root)
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
result = ensure_inventory_index(root, cfg.skills.paths, force=True)
|
|
49
|
+
inventory = result.document.inventory
|
|
49
50
|
console.print(
|
|
50
|
-
f"Indexed {len(inventory.skills)} skills and {len(inventory.rules)} rules at {path}"
|
|
51
|
+
f"Indexed {len(inventory.skills)} skills and {len(inventory.rules)} rules at {result.path}"
|
|
51
52
|
)
|
|
52
53
|
|
|
53
54
|
|
|
@@ -102,5 +103,6 @@ def record_skill_feedback(
|
|
|
102
103
|
"tests_passed": tests_passed,
|
|
103
104
|
"user_feedback": user_feedback.strip(),
|
|
104
105
|
}
|
|
105
|
-
out.open("a", encoding="utf-8")
|
|
106
|
+
with out.open("a", encoding="utf-8") as handle:
|
|
107
|
+
handle.write(json.dumps(record) + "\n")
|
|
106
108
|
console.print(f"[green]✓[/] Recorded skill feedback in [bold]{out}[/]")
|
|
@@ -22,7 +22,7 @@ def register(app: typer.Typer) -> None:
|
|
|
22
22
|
@app.command()
|
|
23
23
|
def tune(
|
|
24
24
|
from_benchmark: bool = typer.Option(True, "--from-benchmark/--no-benchmark", help="Use .agentpack/benchmark_results.jsonl."),
|
|
25
|
-
write: bool = typer.Option(False, "--write",
|
|
25
|
+
write: bool = typer.Option(False, "--write", help="Write suggestions to .agentpack/tuning.md."),
|
|
26
26
|
) -> None:
|
|
27
27
|
"""Suggest tuning actions from benchmark misses and recent pack metrics."""
|
|
28
28
|
root = _root()
|
|
@@ -7,7 +7,18 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
|
-
from agentpack.commands._shared import console, _root
|
|
10
|
+
from agentpack.commands._shared import console, _root, run_refresh
|
|
11
|
+
from agentpack.commands.guard import _context_is_fresh
|
|
12
|
+
from agentpack.core.config import load_config
|
|
13
|
+
from agentpack.core.loop_protocol import (
|
|
14
|
+
LoopCommandResult,
|
|
15
|
+
dry_run_plan,
|
|
16
|
+
finish_blockers,
|
|
17
|
+
initialize_loop,
|
|
18
|
+
load_loop_state,
|
|
19
|
+
mark_done,
|
|
20
|
+
run_loop,
|
|
21
|
+
)
|
|
11
22
|
from agentpack.core.thread_context import resolve_thread_option
|
|
12
23
|
from agentpack.integrations.platform import cli_module_argv
|
|
13
24
|
|
|
@@ -24,6 +35,11 @@ def register(app: typer.Typer) -> None:
|
|
|
24
35
|
pack_only: bool = typer.Option(False, "--pack-only", help="Run pack directly instead of guard."),
|
|
25
36
|
no_init: bool = typer.Option(False, "--no-init", help="Do not initialize the repo when .agentpack/config.toml is missing."),
|
|
26
37
|
no_next: bool = typer.Option(False, "--no-next", help="Do not print next-step diagnostics after context refresh."),
|
|
38
|
+
run_loop_requested: bool = typer.Option(False, "--run", help="Run the configured Ralph Loop after preparing context."),
|
|
39
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Plan Ralph Loop execution without running the configured runner."),
|
|
40
|
+
runner: str = typer.Option("", "--runner", help="Generic shell command for the Ralph Loop runner."),
|
|
41
|
+
max_iterations: int = typer.Option(0, "--max-iterations", help="Override [loop].max_iterations for this run."),
|
|
42
|
+
verify: list[str] = typer.Option([], "--verify", help="Verification command for Ralph Loop. Repeatable."),
|
|
27
43
|
json_output: bool = typer.Option(False, "--json", help="Emit JSON."),
|
|
28
44
|
) -> None:
|
|
29
45
|
"""Initialize if needed, write a task, refresh context, and show next steps."""
|
|
@@ -44,9 +60,39 @@ def register(app: typer.Typer) -> None:
|
|
|
44
60
|
if pack_only:
|
|
45
61
|
start_args.append("--pack-only")
|
|
46
62
|
stages.append(_run("start", cli_module_argv(*start_args), root))
|
|
47
|
-
if stages[-1]["returncode"] == 0 and not no_next:
|
|
63
|
+
if stages[-1]["returncode"] == 0 and not no_next and not run_loop_requested and not dry_run:
|
|
48
64
|
stages.append(_run("next", cli_module_argv("next"), root))
|
|
49
|
-
|
|
65
|
+
if stages[-1]["returncode"] != 0:
|
|
66
|
+
_finish(stages, json_output)
|
|
67
|
+
|
|
68
|
+
loop_plan = None
|
|
69
|
+
loop_summary = None
|
|
70
|
+
if run_loop_requested or dry_run:
|
|
71
|
+
cfg = load_config(root)
|
|
72
|
+
state = initialize_loop(
|
|
73
|
+
root,
|
|
74
|
+
task_text,
|
|
75
|
+
cfg.loop,
|
|
76
|
+
runner_override=runner,
|
|
77
|
+
max_iterations_override=max_iterations,
|
|
78
|
+
verification_overrides=list(verify) if verify else None,
|
|
79
|
+
)
|
|
80
|
+
if dry_run:
|
|
81
|
+
loop_plan = dry_run_plan(root, state).model_dump(mode="json")
|
|
82
|
+
_finish(stages, json_output, loop_plan=loop_plan)
|
|
83
|
+
return
|
|
84
|
+
if not state.runner:
|
|
85
|
+
console.print("[red]Ralph Loop runner missing.[/] Set [loop].runner or pass --runner.")
|
|
86
|
+
raise typer.Exit(1)
|
|
87
|
+
loop_summary = run_loop(
|
|
88
|
+
root,
|
|
89
|
+
state,
|
|
90
|
+
refresh=lambda: _refresh_loop_context(root, agent, mode, budget, resolve_thread_option(thread)),
|
|
91
|
+
).model_dump(mode="json")
|
|
92
|
+
if loop_summary["status"] != "ready_to_finish":
|
|
93
|
+
_finish(stages, json_output, loop_summary=loop_summary)
|
|
94
|
+
raise typer.Exit(1)
|
|
95
|
+
_finish(stages, json_output, loop_plan=loop_plan, loop_summary=loop_summary)
|
|
50
96
|
|
|
51
97
|
@app.command("finish")
|
|
52
98
|
def finish(
|
|
@@ -64,6 +110,16 @@ def register(app: typer.Typer) -> None:
|
|
|
64
110
|
"""Run finish checks, capture benchmark evidence, and mark work done."""
|
|
65
111
|
root = _root()
|
|
66
112
|
stages: list[dict[str, Any]] = []
|
|
113
|
+
loop_state = load_loop_state(root)
|
|
114
|
+
cfg = load_config(root)
|
|
115
|
+
finish_task = task or _read_task(root, thread) or (loop_state.task if loop_state else "")
|
|
116
|
+
loop_applies = loop_state is not None and cfg.loop.enabled and (not finish_task or finish_task == loop_state.task)
|
|
117
|
+
if loop_applies:
|
|
118
|
+
blockers = _loop_finish_blockers(root, cfg.loop, loop_state, thread)
|
|
119
|
+
if blockers:
|
|
120
|
+
_finish_blocked(blockers, json_output)
|
|
121
|
+
raise typer.Exit(1)
|
|
122
|
+
|
|
67
123
|
if not skip_diagnosis:
|
|
68
124
|
stages.append(_run("diagnose-selection", cli_module_argv("diagnose-selection", "--write"), root))
|
|
69
125
|
if not skip_benchmark_capture and since:
|
|
@@ -84,6 +140,8 @@ def register(app: typer.Typer) -> None:
|
|
|
84
140
|
if thread_id:
|
|
85
141
|
state_args.extend(["--thread", thread_id])
|
|
86
142
|
stages.append(_run("state-done", cli_module_argv(*state_args), root))
|
|
143
|
+
if stages[-1]["returncode"] == 0 and loop_applies:
|
|
144
|
+
mark_done(root, summary)
|
|
87
145
|
if archive_thread and thread_id and stages[-1]["returncode"] == 0:
|
|
88
146
|
stages.append(_run("threads-archive", cli_module_argv("threads", "archive", thread_id, "--summary", summary), root))
|
|
89
147
|
_finish(stages, json_output)
|
|
@@ -100,10 +158,21 @@ def _run(name: str, command: list[str], root: Path) -> dict[str, Any]:
|
|
|
100
158
|
}
|
|
101
159
|
|
|
102
160
|
|
|
103
|
-
def _finish(
|
|
161
|
+
def _finish(
|
|
162
|
+
stages: list[dict[str, Any]],
|
|
163
|
+
json_output: bool,
|
|
164
|
+
*,
|
|
165
|
+
loop_plan: dict[str, Any] | None = None,
|
|
166
|
+
loop_summary: dict[str, Any] | None = None,
|
|
167
|
+
) -> None:
|
|
104
168
|
passed = all(stage["returncode"] == 0 for stage in stages)
|
|
105
169
|
if json_output:
|
|
106
|
-
|
|
170
|
+
payload: dict[str, Any] = {"passed": passed, "stages": stages}
|
|
171
|
+
if loop_plan is not None:
|
|
172
|
+
payload["loop_plan"] = loop_plan
|
|
173
|
+
if loop_summary is not None:
|
|
174
|
+
payload["loop_summary"] = loop_summary
|
|
175
|
+
typer.echo(json.dumps(payload, indent=2, sort_keys=True))
|
|
107
176
|
else:
|
|
108
177
|
for stage in stages:
|
|
109
178
|
marker = "[green]✓[/]" if stage["returncode"] == 0 else "[red]✗[/]"
|
|
@@ -112,10 +181,39 @@ def _finish(stages: list[dict[str, Any]], json_output: bool) -> None:
|
|
|
112
181
|
console.print(f" rerun: [bold]{stage['command']}[/]")
|
|
113
182
|
if stage.get("detail"):
|
|
114
183
|
console.print(f" [dim]{stage['detail']}[/]")
|
|
184
|
+
if loop_plan is not None:
|
|
185
|
+
console.print(f"[green]✓[/] Ralph Loop dry run: {loop_plan['next_action']}")
|
|
186
|
+
if loop_summary is not None:
|
|
187
|
+
marker = "[green]✓[/]" if loop_summary.get("status") == "ready_to_finish" else "[yellow]![/]"
|
|
188
|
+
console.print(f"{marker} Ralph Loop {loop_summary.get('status')}: {loop_summary.get('reason') or loop_summary.get('next_command')}")
|
|
115
189
|
if not passed:
|
|
116
190
|
raise typer.Exit(1)
|
|
117
191
|
|
|
118
192
|
|
|
193
|
+
def _loop_finish_blockers(root: Path, loop_cfg, loop_state, thread: str) -> list[dict[str, Any]]:
|
|
194
|
+
blockers = [blocker.model_dump(mode="json") for blocker in finish_blockers(root, loop_cfg, loop_state)]
|
|
195
|
+
fresh, reason = _context_is_fresh(root, thread_id=resolve_thread_option(thread))
|
|
196
|
+
if not fresh:
|
|
197
|
+
blockers.append(
|
|
198
|
+
{
|
|
199
|
+
"kind": "stale_context",
|
|
200
|
+
"message": f"Context is stale: {reason}",
|
|
201
|
+
"command": "agentpack guard --agent auto --repair-stale --refresh-context",
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
return blockers
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _finish_blocked(blockers: list[dict[str, Any]], json_output: bool) -> None:
|
|
208
|
+
if json_output:
|
|
209
|
+
typer.echo(json.dumps({"passed": False, "stages": [], "loop_blockers": blockers}, indent=2, sort_keys=True))
|
|
210
|
+
return
|
|
211
|
+
console.print("[red]Ralph Loop completion blockers:[/]")
|
|
212
|
+
for blocker in blockers:
|
|
213
|
+
console.print(f" [yellow]![/] {blocker['message']}")
|
|
214
|
+
console.print(f" Run: [bold]{blocker['command']}[/]")
|
|
215
|
+
|
|
216
|
+
|
|
119
217
|
def _read_task(root: Path, thread: str) -> str:
|
|
120
218
|
thread_id = resolve_thread_option(thread)
|
|
121
219
|
if thread_id:
|
|
@@ -125,3 +223,10 @@ def _read_task(root: Path, thread: str) -> str:
|
|
|
125
223
|
if not path.exists():
|
|
126
224
|
return ""
|
|
127
225
|
return path.read_text(encoding="utf-8").strip().splitlines()[0].strip()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _refresh_loop_context(root: Path, agent: str, mode: str, budget: int, thread_id: str | None) -> LoopCommandResult:
|
|
229
|
+
stats = run_refresh(root, agent, mode, budget, thread_id=thread_id)
|
|
230
|
+
if stats is None:
|
|
231
|
+
return LoopCommandResult(command="agentpack guard --repair-stale --refresh-context", returncode=1, output_excerpt="context refresh failed")
|
|
232
|
+
return LoopCommandResult(command="agentpack guard --repair-stale --refresh-context", returncode=0, output_excerpt=json.dumps(stats, sort_keys=True))
|
|
@@ -67,6 +67,21 @@ class LearningConfig(BaseModel):
|
|
|
67
67
|
min_groundedness_score: int = 70
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
class LoopConfig(BaseModel):
|
|
71
|
+
enabled: bool = True
|
|
72
|
+
runner: str = ""
|
|
73
|
+
max_iterations: int = 10
|
|
74
|
+
verification_commands: list[str] = Field(default_factory=list)
|
|
75
|
+
require_verification: bool = True
|
|
76
|
+
require_progress_update: bool = True
|
|
77
|
+
require_clean_tree: bool = True
|
|
78
|
+
auto_commit: bool = False
|
|
79
|
+
auto_push: bool = False
|
|
80
|
+
runner_timeout_seconds: int = 600
|
|
81
|
+
verification_timeout_seconds: int = 600
|
|
82
|
+
max_repeated_failures: int = 3
|
|
83
|
+
|
|
84
|
+
|
|
70
85
|
class HooksConfig(BaseModel):
|
|
71
86
|
task_switch_detection: bool = True
|
|
72
87
|
task_switch_min_terms: int = 1
|
|
@@ -137,6 +152,7 @@ class Config(BaseModel):
|
|
|
137
152
|
context_lite: LiteContextConfig = Field(default_factory=LiteContextConfig)
|
|
138
153
|
summary: SummaryConfig = Field(default_factory=SummaryConfig)
|
|
139
154
|
learning: LearningConfig = Field(default_factory=LearningConfig)
|
|
155
|
+
loop: LoopConfig = Field(default_factory=LoopConfig)
|
|
140
156
|
hooks: HooksConfig = Field(default_factory=HooksConfig)
|
|
141
157
|
skills: SkillsConfig = Field(default_factory=SkillsConfig)
|
|
142
158
|
agents: AgentsConfig = Field(default_factory=AgentsConfig)
|
|
@@ -199,6 +215,20 @@ max_cards = 5
|
|
|
199
215
|
max_quiz_questions = 5
|
|
200
216
|
min_groundedness_score = 70
|
|
201
217
|
|
|
218
|
+
[loop]
|
|
219
|
+
enabled = true
|
|
220
|
+
runner = ""
|
|
221
|
+
max_iterations = 10
|
|
222
|
+
verification_commands = []
|
|
223
|
+
require_verification = true
|
|
224
|
+
require_progress_update = true
|
|
225
|
+
require_clean_tree = true
|
|
226
|
+
auto_commit = false
|
|
227
|
+
auto_push = false
|
|
228
|
+
runner_timeout_seconds = 600
|
|
229
|
+
verification_timeout_seconds = 600
|
|
230
|
+
max_repeated_failures = 3
|
|
231
|
+
|
|
202
232
|
[hooks]
|
|
203
233
|
# Claude UserPromptSubmit can detect a clearly different coding prompt,
|
|
204
234
|
# update .agentpack/task.md, and repack even if files did not change.
|