agentpack-cli 0.3.17__tar.gz → 0.3.19__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/PKG-INFO +5 -5
  2. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/README.md +4 -4
  3. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/pyproject.toml +1 -1
  4. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/__init__.py +1 -1
  5. agentpack_cli-0.3.19/src/agentpack/analysis/python_ast.py +12 -0
  6. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/python_imports.py +3 -1
  7. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/role_inference.py +2 -1
  8. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/symbols.py +2 -1
  9. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/application/pack_service.py +49 -0
  10. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/cli.py +10 -0
  11. agentpack_cli-0.3.19/src/agentpack/commands/compress_output.py +33 -0
  12. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/init.py +19 -11
  13. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/learn.py +113 -4
  14. agentpack_cli-0.3.19/src/agentpack/commands/memory.py +45 -0
  15. agentpack_cli-0.3.19/src/agentpack/commands/perf.py +69 -0
  16. agentpack_cli-0.3.19/src/agentpack/commands/release_check.py +278 -0
  17. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/release_cmd.py +11 -1
  18. agentpack_cli-0.3.19/src/agentpack/commands/retrieve.py +53 -0
  19. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/stats.py +13 -0
  20. agentpack_cli-0.3.19/src/agentpack/commands/wrap.py +114 -0
  21. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/config.py +24 -0
  22. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/models.py +1 -0
  23. agentpack_cli-0.3.19/src/agentpack/core/pack_registry.py +259 -0
  24. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/dashboard/collectors.py +187 -2
  25. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/dashboard/models.py +3 -0
  26. agentpack_cli-0.3.19/src/agentpack/dashboard/renderers.py +382 -0
  27. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/collector.py +29 -1
  28. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/extractor.py +90 -2
  29. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/feedback.py +110 -0
  30. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/models.py +12 -0
  31. agentpack_cli-0.3.19/src/agentpack/learning/provider.py +180 -0
  32. agentpack_cli-0.3.19/src/agentpack/learning/renderers.py +366 -0
  33. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/mcp_server.py +57 -0
  34. agentpack_cli-0.3.19/src/agentpack/output_compression/__init__.py +3 -0
  35. agentpack_cli-0.3.19/src/agentpack/output_compression/core.py +161 -0
  36. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/renderers/compact.py +11 -7
  37. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/renderers/markdown.py +39 -28
  38. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/models.py +1 -0
  39. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/parser.py +1 -0
  40. agentpack_cli-0.3.19/src/agentpack/session/events.py +63 -0
  41. agentpack_cli-0.3.17/src/agentpack/commands/release_check.py +0 -119
  42. agentpack_cli-0.3.17/src/agentpack/dashboard/renderers.py +0 -275
  43. agentpack_cli-0.3.17/src/agentpack/learning/provider.py +0 -52
  44. agentpack_cli-0.3.17/src/agentpack/learning/renderers.py +0 -235
  45. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/.gitignore +0 -0
  46. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/LICENSE +0 -0
  47. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/__init__.py +0 -0
  48. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/antigravity.py +0 -0
  49. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/base.py +0 -0
  50. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/claude.py +0 -0
  51. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/codex.py +0 -0
  52. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/cursor.py +0 -0
  53. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/detect.py +0 -0
  54. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/generic.py +0 -0
  55. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/adapters/windsurf.py +0 -0
  56. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/__init__.py +0 -0
  57. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/dependency_graph.py +0 -0
  58. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/go_imports.py +0 -0
  59. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/java_imports.py +0 -0
  60. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/js_ts_imports.py +0 -0
  61. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/monorepo.py +0 -0
  62. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/naming_signals.py +0 -0
  63. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/ranking.py +0 -0
  64. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/repo_map.py +0 -0
  65. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/rust_imports.py +0 -0
  66. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/task_classifier.py +0 -0
  67. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/analysis/tests.py +0 -0
  68. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/application/__init__.py +0 -0
  69. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/__init__.py +0 -0
  70. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/_shared.py +0 -0
  71. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/benchmark.py +0 -0
  72. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/ci_cmd.py +0 -0
  73. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/claude_cmd.py +0 -0
  74. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/dashboard.py +0 -0
  75. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/dev_check.py +0 -0
  76. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/diagnose_selection.py +0 -0
  77. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/diff.py +0 -0
  78. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/doctor.py +0 -0
  79. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/eval_cmd.py +0 -0
  80. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/explain.py +0 -0
  81. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/guard.py +0 -0
  82. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/hook_cmd.py +0 -0
  83. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/ignore_cmd.py +0 -0
  84. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/install.py +0 -0
  85. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/mcp_cmd.py +0 -0
  86. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/migrate.py +0 -0
  87. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/monitor.py +0 -0
  88. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/next_cmd.py +0 -0
  89. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/pack.py +0 -0
  90. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/quickstart.py +0 -0
  91. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/repair.py +0 -0
  92. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/route.py +0 -0
  93. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/scan.py +0 -0
  94. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/skills.py +0 -0
  95. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/start_cmd.py +0 -0
  96. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/state_cmd.py +0 -0
  97. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/status.py +0 -0
  98. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/summarize.py +0 -0
  99. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/task_cmd.py +0 -0
  100. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/threads.py +0 -0
  101. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/tune.py +0 -0
  102. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/verify_wheel.py +0 -0
  103. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/watch.py +0 -0
  104. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/commands/workflow_cmd.py +0 -0
  105. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/__init__.py +0 -0
  106. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/bootstrap.py +0 -0
  107. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/cache.py +0 -0
  108. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/changed_paths.py +0 -0
  109. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/context_pack.py +0 -0
  110. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/diff.py +0 -0
  111. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/evals.py +0 -0
  112. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/execution_state.py +0 -0
  113. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/git.py +0 -0
  114. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/git_hooks.py +0 -0
  115. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/global_install.py +0 -0
  116. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/ignore.py +0 -0
  117. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/loop_protocol.py +0 -0
  118. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/merkle.py +0 -0
  119. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/redactor.py +0 -0
  120. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/scanner.py +0 -0
  121. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/snapshot.py +0 -0
  122. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/task_freshness.py +0 -0
  123. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/thread_context.py +0 -0
  124. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/token_estimator.py +0 -0
  125. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/core/vscode_tasks.py +0 -0
  126. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/dashboard/__init__.py +0 -0
  127. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/data/agentpack.md +0 -0
  128. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/__init__.py +0 -0
  129. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/antigravity.py +0 -0
  130. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/claude.py +0 -0
  131. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/codex.py +0 -0
  132. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/cursor.py +0 -0
  133. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/installers/windsurf.py +0 -0
  134. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/__init__.py +0 -0
  135. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/agents.py +0 -0
  136. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/git_hooks.py +0 -0
  137. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/global_install.py +0 -0
  138. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/platform.py +0 -0
  139. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/integrations/vscode_tasks.py +0 -0
  140. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/__init__.py +0 -0
  141. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/lesson_ranker.py +0 -0
  142. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/quality.py +0 -0
  143. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/learning/skill_map.py +0 -0
  144. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/renderers/__init__.py +0 -0
  145. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/renderers/receipts.py +0 -0
  146. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/__init__.py +0 -0
  147. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/discovery.py +0 -0
  148. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/prompt_builder.py +0 -0
  149. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/scoring.py +0 -0
  150. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/service.py +0 -0
  151. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/router/skills_index.py +0 -0
  152. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/session/__init__.py +0 -0
  153. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/session/state.py +0 -0
  154. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/summaries/__init__.py +0 -0
  155. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/src/agentpack/summaries/base.py +0 -0
  156. {agentpack_cli-0.3.17 → agentpack_cli-0.3.19}/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.17
3
+ Version: 0.3.19
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
@@ -55,7 +55,7 @@ Claude Code, Codex, Cursor, and other coding agents can waste tool calls redisco
55
55
 
56
56
  AgentPack gives them a ranked map of likely relevant files, tests, rules, and skills for each task. It analyzes your repo locally and packages compact context for CLI and MCP workflows.
57
57
 
58
- No cloud indexing. No embeddings required. No API calls for scan, summarize, rank, pack, stats, or benchmark. AgentPack is a context preparation tool, not a coding agent.
58
+ How it works: AgentPack compresses repo context into ranked packs, caches scans/summaries/pack metadata for fast refreshes, and retrieves exact file or symbol blocks later when an agent needs more detail. Rendered packs also put stable instructions before volatile task, timestamp, git, and file sections so provider prompt-prefix caches can reuse the front of repeated refreshes. No cloud indexing, embeddings, or API calls are required for scan, summarize, rank, pack, stats, or benchmark.
59
59
 
60
60
  Try the read-only task router without writing context files:
61
61
 
@@ -141,12 +141,12 @@ Suggested commands:
141
141
 
142
142
  - **Task-focused packing**: ranks files from git changes, task terms, symbols, imports, related tests, configs, churn, repo history, and deterministic offline summaries.
143
143
  - **Budget-aware compression**: emits `full`, `diff`, `symbols`, `skeleton`, or `summary` views instead of all-or-nothing file dumps.
144
- - **Rendered-token accounting**: budgets against the actual markdown context, not only file payloads.
145
- - **Reserve buckets**: changed files, tests, docs, and dependencies each get protected selection capacity so one dirty area cannot starve the others.
144
+ - **Rendered-token accounting and reserve buckets**: budgets against actual markdown while protecting changed files, tests, docs, and dependencies.
146
145
  - **Execution state**: optional task state files and git-derived fallback status show whether work is planned, in progress, blocked, done, committed, or committed but not pushed.
147
146
  - **Thread-scoped context**: explicit `--thread <id>` or `--thread auto` isolates task/context files for multiple agents in one repo and warns on same-branch file overlap.
148
147
  - **Task router**: MCP and CLI surfaces route a task to relevant files, scoped rules, installed skills, suggested commands, and safety warnings without executing skills automatically.
149
- - **Learning layer**: turns task diffs into developer learning notes, skill evidence, and compact lessons future agents can reuse.
148
+ - **Reversible registry and learning layer**: retrieves packed context by block ID, writes developer lessons, and feeds bounded selected-file miss feedback into future ranking.
149
+ - **Runtime scorecard and output compression**: `perf --history` tracks activity, while `compress-output` preserves failures, paths, diffs, and search hits from noisy logs.
150
150
  - **Agent integrations**: installs Claude Code, Cursor, Windsurf, Codex, Antigravity, VS Code tasks, git hooks, and MCP configuration.
151
151
  - **Local and measurable**: no API calls for scan, summarize, rank, pack, stats, or benchmark; quality is measured with expected-file evals.
152
152
 
@@ -14,7 +14,7 @@ Claude Code, Codex, Cursor, and other coding agents can waste tool calls redisco
14
14
 
15
15
  AgentPack gives them a ranked map of likely relevant files, tests, rules, and skills for each task. It analyzes your repo locally and packages compact context for CLI and MCP workflows.
16
16
 
17
- No cloud indexing. No embeddings required. No API calls for scan, summarize, rank, pack, stats, or benchmark. AgentPack is a context preparation tool, not a coding agent.
17
+ How it works: AgentPack compresses repo context into ranked packs, caches scans/summaries/pack metadata for fast refreshes, and retrieves exact file or symbol blocks later when an agent needs more detail. Rendered packs also put stable instructions before volatile task, timestamp, git, and file sections so provider prompt-prefix caches can reuse the front of repeated refreshes. No cloud indexing, embeddings, or API calls are required for scan, summarize, rank, pack, stats, or benchmark.
18
18
 
19
19
  Try the read-only task router without writing context files:
20
20
 
@@ -100,12 +100,12 @@ Suggested commands:
100
100
 
101
101
  - **Task-focused packing**: ranks files from git changes, task terms, symbols, imports, related tests, configs, churn, repo history, and deterministic offline summaries.
102
102
  - **Budget-aware compression**: emits `full`, `diff`, `symbols`, `skeleton`, or `summary` views instead of all-or-nothing file dumps.
103
- - **Rendered-token accounting**: budgets against the actual markdown context, not only file payloads.
104
- - **Reserve buckets**: changed files, tests, docs, and dependencies each get protected selection capacity so one dirty area cannot starve the others.
103
+ - **Rendered-token accounting and reserve buckets**: budgets against actual markdown while protecting changed files, tests, docs, and dependencies.
105
104
  - **Execution state**: optional task state files and git-derived fallback status show whether work is planned, in progress, blocked, done, committed, or committed but not pushed.
106
105
  - **Thread-scoped context**: explicit `--thread <id>` or `--thread auto` isolates task/context files for multiple agents in one repo and warns on same-branch file overlap.
107
106
  - **Task router**: MCP and CLI surfaces route a task to relevant files, scoped rules, installed skills, suggested commands, and safety warnings without executing skills automatically.
108
- - **Learning layer**: turns task diffs into developer learning notes, skill evidence, and compact lessons future agents can reuse.
107
+ - **Reversible registry and learning layer**: retrieves packed context by block ID, writes developer lessons, and feeds bounded selected-file miss feedback into future ranking.
108
+ - **Runtime scorecard and output compression**: `perf --history` tracks activity, while `compress-output` preserves failures, paths, diffs, and search hits from noisy logs.
109
109
  - **Agent integrations**: installs Claude Code, Cursor, Windsurf, Codex, Antigravity, VS Code tasks, git hooks, and MCP configuration.
110
110
  - **Local and measurable**: no API calls for scan, summarize, rank, pack, stats, or benchmark; quality is measured with expected-file evals.
111
111
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.3.17"
3
+ version = "0.3.19"
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"
@@ -1,3 +1,3 @@
1
1
  """AgentPack — task-aware context packing for AI coding agents."""
2
2
 
3
- __version__ = "0.3.17"
3
+ __version__ = "0.3.19"
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import warnings
5
+ from pathlib import Path
6
+
7
+
8
+ def parse_python_source(source: str, path: Path | str) -> ast.Module:
9
+ """Parse scanned Python source without surfacing user-code SyntaxWarnings."""
10
+ with warnings.catch_warnings():
11
+ warnings.simplefilter("ignore", SyntaxWarning)
12
+ return ast.parse(source, filename=str(path))
@@ -3,11 +3,13 @@ from __future__ import annotations
3
3
  import ast
4
4
  from pathlib import Path
5
5
 
6
+ from agentpack.analysis.python_ast import parse_python_source
7
+
6
8
 
7
9
  def extract_imports(path: Path, text: str | None = None) -> list[str]:
8
10
  try:
9
11
  source = text if text is not None else path.read_text(errors="replace")
10
- tree = ast.parse(source)
12
+ tree = parse_python_source(source, path)
11
13
  except SyntaxError:
12
14
  return []
13
15
 
@@ -6,6 +6,7 @@ from collections import defaultdict
6
6
  from dataclasses import dataclass, field
7
7
  from pathlib import Path
8
8
 
9
+ from agentpack.analysis.python_ast import parse_python_source
9
10
  from agentpack.core.models import Symbol
10
11
 
11
12
 
@@ -239,7 +240,7 @@ def extract_failure_hints(text: str) -> list[str]:
239
240
 
240
241
  def _python_intelligence(path: str, text: str, symbols: list[Symbol]) -> CodeIntelligence:
241
242
  try:
242
- tree = ast.parse(text)
243
+ tree = parse_python_source(text, path)
243
244
  except SyntaxError:
244
245
  return CodeIntelligence(defines=[s.name for s in symbols[:40]])
245
246
 
@@ -4,13 +4,14 @@ import ast
4
4
  import re
5
5
  from pathlib import Path
6
6
 
7
+ from agentpack.analysis.python_ast import parse_python_source
7
8
  from agentpack.core.models import Symbol
8
9
 
9
10
 
10
11
  def extract_python_symbols(path: Path) -> list[Symbol]:
11
12
  try:
12
13
  source = path.read_text(errors="replace")
13
- tree = ast.parse(source)
14
+ tree = parse_python_source(source, path)
14
15
  except (SyntaxError, OSError):
15
16
  return []
16
17
 
@@ -27,6 +27,7 @@ from agentpack.core.models import (
27
27
  ScanResult,
28
28
  SelectedFile,
29
29
  )
30
+ from agentpack.core.pack_registry import save_pack_registry
30
31
  from agentpack.core.task_freshness import read_task_md, task_metadata
31
32
  from agentpack.core.thread_context import (
32
33
  append_thread_index,
@@ -36,6 +37,7 @@ from agentpack.core.thread_context import (
36
37
  thread_paths,
37
38
  )
38
39
  from agentpack.core.token_estimator import estimate_tokens
40
+ from agentpack.learning.feedback import ranking_feedback_boosts
39
41
  from agentpack.renderers.markdown import render_claude, render_generic
40
42
  from agentpack.analysis.ranking import (
41
43
  build_keyword_plan,
@@ -59,6 +61,7 @@ from agentpack.analysis.task_classifier import classify_task
59
61
  from agentpack.analysis.tests import find_related_tests
60
62
  from agentpack.analysis import dependency_graph as dep_graph_mod
61
63
  from agentpack.summaries.base import build_all_summaries
64
+ from agentpack.session.events import record_event
62
65
 
63
66
 
64
67
  @dataclass
@@ -267,6 +270,7 @@ class FileRanker:
267
270
  changes.all_changed,
268
271
  generic_ratio=generic_ratio,
269
272
  )
273
+ scored = _apply_ranking_feedback_boosts(root, scored, task, changes.all_changed)
270
274
  return RankResult(
271
275
  keywords=keywords,
272
276
  keyword_plan=keyword_plan,
@@ -673,6 +677,9 @@ class AdapterRegistry:
673
677
  cfg.learning.dashboard_output,
674
678
  cfg.learning.team_lessons_output,
675
679
  cfg.learning.feedback_output,
680
+ cfg.learning.ranking_feedback_output,
681
+ cfg.runtime.pack_registry_output,
682
+ cfg.runtime.session_events_output,
676
683
  }
677
684
  )
678
685
  return paths
@@ -789,6 +796,29 @@ class PackService:
789
796
  concurrent_context=pack_obj.concurrent_context,
790
797
  metadata_path=scoped_paths.metadata if scoped_paths else None,
791
798
  )
799
+ save_pack_registry(
800
+ root,
801
+ pack_obj,
802
+ packable,
803
+ output_path=cfg.runtime.pack_registry_output,
804
+ max_records=cfg.runtime.max_registry_records,
805
+ )
806
+ record_event(
807
+ root,
808
+ "pack",
809
+ {
810
+ "task": request.task,
811
+ "agent": request.agent,
812
+ "mode": plan.mode,
813
+ "packed_tokens": packed_tokens,
814
+ "raw_tokens": all_tokens,
815
+ "selected_files": len(pack_obj.selected_files),
816
+ "omitted_files": len(pack_obj.omitted_relevant_files),
817
+ "changed_files": len(pack_obj.changed_files),
818
+ "context_path": str(out_path.relative_to(root)),
819
+ },
820
+ output_path=cfg.runtime.session_events_output,
821
+ )
792
822
  if thread_row:
793
823
  append_thread_index(root, thread_row)
794
824
  excluded_receipts = [r for r in pack_obj.receipts if r.action == "excluded"]
@@ -1145,6 +1175,25 @@ def _apply_history_penalties(
1145
1175
  return adjusted
1146
1176
 
1147
1177
 
1178
+ def _apply_ranking_feedback_boosts(
1179
+ root: Path,
1180
+ scored: list[tuple[Any, float, list[str]]],
1181
+ task: str,
1182
+ changed_paths: set[str],
1183
+ ) -> list[tuple[Any, float, list[str]]]:
1184
+ boosts = ranking_feedback_boosts(root, task)
1185
+ if not boosts:
1186
+ return scored
1187
+ adjusted: list[tuple[Any, float, list[str]]] = []
1188
+ for fi, score, reasons in scored:
1189
+ boost = boosts.get(fi.path, 0.0)
1190
+ if boost <= 0 or fi.path in changed_paths:
1191
+ adjusted.append((fi, score, reasons))
1192
+ continue
1193
+ adjusted.append((fi, score + boost, [*reasons, f"learning feedback miss boost +{boost:.0f}"]))
1194
+ return adjusted
1195
+
1196
+
1148
1197
  def _history_noise_counts(root: Path, *, window: int = 20) -> dict[str, int]:
1149
1198
  metrics_path = root / ".agentpack" / "metrics.jsonl"
1150
1199
  if not metrics_path.exists():
@@ -5,6 +5,7 @@ from agentpack.commands import (
5
5
  benchmark,
6
6
  claude_cmd,
7
7
  ci_cmd,
8
+ compress_output,
8
9
  dashboard,
9
10
  dev_check,
10
11
  diagnose_selection,
@@ -18,14 +19,17 @@ from agentpack.commands import (
18
19
  init,
19
20
  install,
20
21
  learn,
22
+ memory,
21
23
  mcp_cmd,
22
24
  migrate,
23
25
  monitor,
24
26
  next_cmd,
25
27
  pack,
28
+ perf,
26
29
  quickstart,
27
30
  release_cmd,
28
31
  release_check,
32
+ retrieve,
29
33
  repair,
30
34
  route,
31
35
  scan,
@@ -40,6 +44,7 @@ from agentpack.commands import (
40
44
  tune,
41
45
  verify_wheel,
42
46
  watch,
47
+ wrap,
43
48
  workflow_cmd,
44
49
  )
45
50
  from agentpack import __version__
@@ -73,8 +78,11 @@ for mod in [
73
78
  stats,
74
79
  dashboard,
75
80
  summarize,
81
+ compress_output,
76
82
  learn,
83
+ memory,
77
84
  pack,
85
+ perf,
78
86
  install,
79
87
  repair,
80
88
  route,
@@ -99,7 +107,9 @@ for mod in [
99
107
  skills,
100
108
  release_check,
101
109
  release_cmd,
110
+ retrieve,
102
111
  start_cmd,
112
+ wrap,
103
113
  workflow_cmd,
104
114
  ]:
105
115
  mod.register(app)
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ import typer
6
+
7
+ from agentpack.commands._shared import _root
8
+ from agentpack.core.config import load_config
9
+ from agentpack.output_compression import compress_output
10
+ from agentpack.session.events import record_event
11
+
12
+
13
+ def register(app: typer.Typer) -> None:
14
+ @app.command("compress-output")
15
+ def compress_output_cmd(
16
+ file: str = typer.Argument("-", help="Output file to summarize, or '-' for stdin."),
17
+ kind: str = typer.Option("auto", "--kind", help="Output kind: auto|pytest|npm|git-diff|rg|ls."),
18
+ ) -> None:
19
+ """Summarize noisy command output while preserving actionable lines."""
20
+ root = _root()
21
+ cfg = load_config(root)
22
+ if file == "-":
23
+ content = sys.stdin.read()
24
+ else:
25
+ content = (root / file).read_text(encoding="utf-8", errors="replace")
26
+ result = compress_output(content, kind=kind, max_items=cfg.runtime.max_output_summary_items)
27
+ record_event(
28
+ root,
29
+ "compress_output",
30
+ {"kind": kind, "input_chars": len(content), "output_chars": len(result)},
31
+ output_path=cfg.runtime.session_events_output,
32
+ )
33
+ typer.echo(result, nl=False)
@@ -55,13 +55,9 @@ def _repo_gitignore_entries(share_cache: bool = False, agent: str = "generic") -
55
55
  ".agentpack/snapshots/",
56
56
  ".agentpack/context*",
57
57
  ".agentpack/metrics.jsonl",
58
+ ".agentpack/session-events.jsonl",
58
59
  ".agentpack/pack_metadata.json",
59
- ".agentpack/activity.log",
60
- ".agentpack/.gitignore",
61
- ".agentpack/.mcp_reminded",
62
- ".agentpack/session.json",
63
- ".agentpack/task.md",
64
- ".agentpack/benchmark_results.jsonl",
60
+ ".agentpack/pack-registry.json",
65
61
  ".agentpack/learning.md",
66
62
  ".agentpack/daily-summary.md",
67
63
  ".agentpack/skills-progress.json",
@@ -71,6 +67,14 @@ def _repo_gitignore_entries(share_cache: bool = False, agent: str = "generic") -
71
67
  ".agentpack/learning-dashboard.html",
72
68
  ".agentpack/team-lessons.md",
73
69
  ".agentpack/learning-feedback.jsonl",
70
+ ".agentpack/ranking-feedback.jsonl",
71
+ ".agentpack/learning-inputs.json",
72
+ ".agentpack/activity.log",
73
+ ".agentpack/.gitignore",
74
+ ".agentpack/.mcp_reminded",
75
+ ".agentpack/session.json",
76
+ ".agentpack/task.md",
77
+ ".agentpack/benchmark_results.jsonl",
74
78
  ".agentpack/loop_state.json",
75
79
  ".agentpack/progress.md",
76
80
  ".agentpack/loop_events.jsonl",
@@ -101,12 +105,9 @@ def _agentpack_gitignore_content(share_cache: bool = False) -> str:
101
105
  "snapshots/",
102
106
  "context.*",
103
107
  "metrics.jsonl",
108
+ "session-events.jsonl",
104
109
  "pack_metadata.json",
105
- "activity.log",
106
- ".mcp_reminded",
107
- "session.json",
108
- "task.md",
109
- "benchmark_results.jsonl",
110
+ "pack-registry.json",
110
111
  "learning.md",
111
112
  "daily-summary.md",
112
113
  "skills-progress.json",
@@ -116,6 +117,13 @@ def _agentpack_gitignore_content(share_cache: bool = False) -> str:
116
117
  "learning-dashboard.html",
117
118
  "team-lessons.md",
118
119
  "learning-feedback.jsonl",
120
+ "ranking-feedback.jsonl",
121
+ "learning-inputs.json",
122
+ "activity.log",
123
+ ".mcp_reminded",
124
+ "session.json",
125
+ "task.md",
126
+ "benchmark_results.jsonl",
119
127
  "loop_state.json",
120
128
  "progress.md",
121
129
  "loop_events.jsonl",
@@ -6,12 +6,19 @@ from datetime import datetime
6
6
  import typer
7
7
 
8
8
  from agentpack.commands._shared import _atomic_write, _root, console
9
+ from agentpack.commands.dashboard import _open_file
9
10
  from agentpack.core.config import load_config
10
11
  from agentpack.learning.collector import collect_learning_inputs
11
12
  from agentpack.learning.extractor import build_learning_report
12
- from agentpack.learning.feedback import apply_feedback_to_report, load_feedback_summary, record_learning_feedback
13
+ from agentpack.learning.feedback import (
14
+ apply_feedback_to_report,
15
+ load_feedback_summary,
16
+ record_direct_learning_feedback,
17
+ record_learning_feedback,
18
+ record_ranking_feedback,
19
+ )
13
20
  from agentpack.learning.lesson_ranker import rank_agent_lessons
14
- from agentpack.learning.provider import LearningProviderError, run_provider_command
21
+ from agentpack.learning.provider import LearningProviderError, run_concept_provider_command, run_provider_command
15
22
  from agentpack.learning.quality import score_learning_report
16
23
  from agentpack.learning.renderers import (
17
24
  learning_report_to_dict,
@@ -26,11 +33,14 @@ from agentpack.learning.renderers import (
26
33
  render_team_lessons_markdown,
27
34
  )
28
35
  from agentpack.learning.skill_map import apply_skill_feedback, recommend_practice_drills, render_skill_summary, update_skill_map
36
+ from agentpack.session.events import record_event
29
37
 
30
38
 
31
39
  def register(app: typer.Typer) -> None:
32
40
  @app.command()
33
41
  def learn(
42
+ action: str = typer.Argument("", help="Optional action: feedback."),
43
+ value: str = typer.Argument("", help="Feedback value for `agentpack learn feedback`: helpful|not-helpful."),
34
44
  task: str = typer.Option("auto", "--task", help="Task source. Only 'auto' is supported."),
35
45
  since: str | None = typer.Option(None, "--since", help="Git ref to compare against, e.g. HEAD~1 or main."),
36
46
  today: bool = typer.Option(False, "--today", help="Use today's work scope label for the report."),
@@ -40,14 +50,30 @@ def register(app: typer.Typer) -> None:
40
50
  pr_comment: bool = typer.Option(False, "--pr-comment", help="Write a PR-comment-ready learning summary artifact."),
41
51
  provider_preview: bool = typer.Option(False, "--provider-preview", help="Print the bounded provider payload without making a network call."),
42
52
  provider_command: str = typer.Option("", "--provider-command", help="Run a local JSON-in/JSON-out provider command to enrich the report."),
53
+ concept_provider_command: str = typer.Option(
54
+ "",
55
+ "--concept-provider-command",
56
+ help="Run a local JSON-in/JSON-out provider command to enrich detected learning concepts.",
57
+ ),
58
+ no_concept_provider: bool = typer.Option(
59
+ False,
60
+ "--no-concept-provider",
61
+ help="Disable configured concept provider enrichment for this run.",
62
+ ),
43
63
  dashboard: bool = typer.Option(False, "--dashboard", help="Write a static HTML learning dashboard artifact."),
64
+ open_dashboard: bool = typer.Option(False, "--open", help="Open the generated learning dashboard in a browser."),
44
65
  team_export: bool = typer.Option(False, "--team-export", help="Write an opt-in team lesson export without personal skill history."),
45
66
  ci: bool = typer.Option(False, "--ci", help="Fail when learning quality is below the configured threshold."),
46
67
  skills: bool = typer.Option(False, "--skills", help="Print the local skill memory summary and exit."),
47
68
  drills: bool = typer.Option(False, "--drills", help="Print recommended practice drills from local skill memory and exit."),
48
69
  feedback: str = typer.Option("", "--feedback", help="Record feedback for this learning output (helpful|not-helpful)."),
49
- feedback_note: str = typer.Option("", "--feedback-note", help="Optional note stored with --feedback."),
50
- feedback_target: str = typer.Option("", "--feedback-target", help="Optional target such as skill:CLI design, lesson:retry, rename:old=>new, or merge:old=>new."),
70
+ feedback_note: str = typer.Option("", "--feedback-note", "--note", help="Optional note stored with --feedback."),
71
+ feedback_target: str = typer.Option(
72
+ "",
73
+ "--feedback-target",
74
+ "--target",
75
+ help="Optional target such as skill:CLI design, lesson:retry, rename:old=>new, or merge:old=>new.",
76
+ ),
51
77
  suppress_skill: str = typer.Option("", "--suppress-skill", help="Suppress a noisy skill in future skill views and generation."),
52
78
  rename_skill: str = typer.Option("", "--rename-skill", help="Rename a skill using old=>new."),
53
79
  merge_skill: str = typer.Option("", "--merge-skill", help="Merge a skill using old=>new."),
@@ -62,6 +88,32 @@ def register(app: typer.Typer) -> None:
62
88
 
63
89
  root = _root()
64
90
  cfg = load_config(root)
91
+ if action:
92
+ if action != "feedback":
93
+ console.print("[red]Unknown learn action. Supported action: feedback.[/]")
94
+ raise typer.Exit(2)
95
+ if not value:
96
+ console.print("[red]Feedback value required: helpful|not-helpful.[/]")
97
+ raise typer.Exit(2)
98
+ try:
99
+ payload = record_direct_learning_feedback(
100
+ root / cfg.learning.feedback_output,
101
+ value,
102
+ task=_task_text(root),
103
+ note=feedback_note,
104
+ target=feedback_target,
105
+ )
106
+ except ValueError as exc:
107
+ console.print(f"[red]{exc}[/]")
108
+ raise typer.Exit(2) from exc
109
+ record_event(
110
+ root,
111
+ "learn_feedback",
112
+ {"feedback": payload["feedback"], "target": payload["target"]},
113
+ output_path=cfg.runtime.session_events_output,
114
+ )
115
+ console.print(f"[green]✓[/] Recorded learning feedback in {cfg.learning.feedback_output}")
116
+ return
65
117
  skill_map_path = root / cfg.learning.skill_map_output
66
118
  if skills:
67
119
  typer.echo(render_skill_summary(skill_map_path), nl=False)
@@ -100,6 +152,11 @@ def register(app: typer.Typer) -> None:
100
152
  feedback_summary = load_feedback_summary(root / cfg.learning.feedback_output)
101
153
  report = apply_feedback_to_report(report, feedback_summary)
102
154
  report.agent_lessons = rank_agent_lessons(report, feedback_summary, limit=cfg.learning.max_cards)
155
+ ranking_feedback_count = record_ranking_feedback(
156
+ root,
157
+ report,
158
+ output_path=cfg.learning.ranking_feedback_output,
159
+ )
103
160
  if today:
104
161
  report.scope = "today"
105
162
  if since_date:
@@ -109,6 +166,21 @@ def register(app: typer.Typer) -> None:
109
166
  typer.echo(render_provider_preview_markdown(report), nl=False)
110
167
  return
111
168
 
169
+ concept_command = concept_provider_command or ("" if no_concept_provider else cfg.learning.concept_provider_command)
170
+ if concept_command:
171
+ try:
172
+ report = run_concept_provider_command(
173
+ concept_command,
174
+ inputs,
175
+ report,
176
+ timeout_seconds=cfg.learning.concept_provider_timeout_seconds,
177
+ )
178
+ except LearningProviderError as exc:
179
+ if concept_provider_command or cfg.learning.concept_provider_required:
180
+ console.print(f"[red]Concept provider command failed:[/] {exc}")
181
+ raise typer.Exit(1) from exc
182
+ console.print(f"[yellow]Concept provider skipped:[/] {exc}")
183
+
112
184
  command = provider_command or cfg.learning.provider_command
113
185
  if command:
114
186
  try:
@@ -134,10 +206,15 @@ def register(app: typer.Typer) -> None:
134
206
  pr_path = root / cfg.learning.pr_comment_output
135
207
  pr_path.parent.mkdir(parents=True, exist_ok=True)
136
208
  _atomic_write(pr_path, render_pr_comment_markdown(report))
209
+ if open_dashboard:
210
+ dashboard = True
137
211
  if dashboard:
138
212
  dashboard_path = root / cfg.learning.dashboard_output
139
213
  dashboard_path.parent.mkdir(parents=True, exist_ok=True)
140
214
  _atomic_write(dashboard_path, render_dashboard_html(report))
215
+ console.print(f"[green]✓[/] Wrote {dashboard_path.relative_to(root)}")
216
+ if open_dashboard:
217
+ _open_file(dashboard_path)
141
218
  if team_export:
142
219
  team_path = root / cfg.learning.team_lessons_output
143
220
  team_path.parent.mkdir(parents=True, exist_ok=True)
@@ -147,6 +224,12 @@ def register(app: typer.Typer) -> None:
147
224
  console.print("[red]--feedback must be helpful or not-helpful.[/]")
148
225
  raise typer.Exit(2)
149
226
  record_learning_feedback(root / cfg.learning.feedback_output, report, feedback, feedback_note, feedback_target)
227
+ record_event(
228
+ root,
229
+ "learn_feedback",
230
+ {"feedback": feedback, "target": feedback_target},
231
+ output_path=cfg.runtime.session_events_output,
232
+ )
150
233
  if ci:
151
234
  typer.echo(render_quality_markdown(report, quality.score, quality.issues), nl=False)
152
235
  if quality.score < cfg.learning.min_groundedness_score:
@@ -160,6 +243,19 @@ def register(app: typer.Typer) -> None:
160
243
  out_path = root / (output or default_output)
161
244
  out_path.parent.mkdir(parents=True, exist_ok=True)
162
245
  _atomic_write(out_path, render_learning_markdown(report))
246
+ record_event(
247
+ root,
248
+ "learn",
249
+ {
250
+ "task": report.task,
251
+ "changed_files": len(report.source_files),
252
+ "concepts": report.concepts,
253
+ "selected_hits": len(report.selected_hits),
254
+ "selected_misses": len(report.selected_misses),
255
+ "ranking_feedback_paths": ranking_feedback_count,
256
+ },
257
+ output_path=cfg.runtime.session_events_output,
258
+ )
163
259
  console.print(f"[green]✓[/] Wrote {out_path.relative_to(root)}")
164
260
 
165
261
 
@@ -168,6 +264,19 @@ def _today_start_iso() -> str:
168
264
  return now.replace(hour=0, minute=0, second=0, microsecond=0).isoformat()
169
265
 
170
266
 
267
+ def _task_text(root) -> str:
268
+ task_path = root / ".agentpack" / "task.md"
269
+ if task_path.exists():
270
+ lines = [
271
+ line.strip()
272
+ for line in task_path.read_text(encoding="utf-8").splitlines()
273
+ if line.strip() and not line.startswith("#")
274
+ ]
275
+ if lines:
276
+ return lines[0]
277
+ return "Current work"
278
+
279
+
171
280
  def _split_mapping(value: str, flag: str) -> tuple[str, str]:
172
281
  if "=>" not in value:
173
282
  console.print(f"[red]{flag} expects old=>new.[/]")
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections import Counter
5
+
6
+ import typer
7
+ from rich.table import Table
8
+ from rich import box
9
+
10
+ from agentpack.commands._shared import _root, console
11
+ from agentpack.core.config import load_config
12
+ from agentpack.session.events import read_events
13
+
14
+
15
+ def register(app: typer.Typer) -> None:
16
+ @app.command()
17
+ def memory(json_output: bool = typer.Option(False, "--json", help="Print JSON.")) -> None:
18
+ """Show local cross-agent task memory from events and learning artifacts."""
19
+ root = _root()
20
+ cfg = load_config(root)
21
+ events = read_events(root, output_path=cfg.runtime.session_events_output, limit=500)
22
+ tasks = [str(event.get("task")) for event in events if event.get("task")]
23
+ concepts = Counter(
24
+ concept
25
+ for event in events
26
+ for concept in (event.get("concepts") or [])
27
+ if isinstance(concept, str)
28
+ )
29
+ payload = {
30
+ "recent_tasks": tasks[-20:],
31
+ "top_concepts": concepts.most_common(20),
32
+ "event_count": len(events),
33
+ }
34
+ if json_output:
35
+ typer.echo(json.dumps(payload, indent=2))
36
+ return
37
+ table = Table(title="AgentPack Memory", box=box.SIMPLE, show_header=True, padding=(0, 1))
38
+ table.add_column("kind", style="dim")
39
+ table.add_column("value")
40
+ table.add_row("events", str(len(events)))
41
+ for task in tasks[-10:]:
42
+ table.add_row("task", task)
43
+ for concept, count in concepts.most_common(10):
44
+ table.add_row("concept", f"{concept} ({count})")
45
+ console.print(table)