agentpack-cli 0.1.24__tar.gz → 0.1.26__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 (86) hide show
  1. agentpack_cli-0.1.26/.gitignore +33 -0
  2. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/PKG-INFO +15 -7
  3. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/README.md +14 -6
  4. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/pyproject.toml +1 -1
  5. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/__init__.py +1 -1
  6. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/application/pack_service.py +51 -2
  7. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/claude_cmd.py +1 -0
  8. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/diff.py +7 -1
  9. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/doctor.py +74 -1
  10. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/explain.py +52 -0
  11. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/hook_cmd.py +116 -7
  12. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/init.py +56 -0
  13. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/pack.py +24 -3
  14. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/scan.py +7 -1
  15. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/stats.py +156 -2
  16. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/status.py +34 -3
  17. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/summarize.py +7 -1
  18. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/watch.py +1 -0
  19. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/config.py +12 -0
  20. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/context_pack.py +2 -0
  21. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/scanner.py +4 -0
  22. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/data/agentpack.md +5 -4
  23. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/antigravity.py +8 -0
  24. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/codex.py +2 -0
  25. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/cursor.py +9 -4
  26. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/windsurf.py +4 -2
  27. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/renderers/markdown.py +3 -2
  28. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/session/state.py +2 -0
  29. agentpack_cli-0.1.24/.gitignore +0 -21
  30. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/LICENSE +0 -0
  31. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/__init__.py +0 -0
  32. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/antigravity.py +0 -0
  33. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/base.py +0 -0
  34. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/claude.py +0 -0
  35. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/codex.py +0 -0
  36. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/cursor.py +0 -0
  37. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/detect.py +0 -0
  38. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/generic.py +0 -0
  39. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/adapters/windsurf.py +0 -0
  40. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/__init__.py +0 -0
  41. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/dependency_graph.py +0 -0
  42. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/go_imports.py +0 -0
  43. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/java_imports.py +0 -0
  44. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/js_ts_imports.py +0 -0
  45. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/python_imports.py +0 -0
  46. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/ranking.py +0 -0
  47. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/rust_imports.py +0 -0
  48. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/symbols.py +0 -0
  49. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/analysis/tests.py +0 -0
  50. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/application/__init__.py +0 -0
  51. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/cli.py +0 -0
  52. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/__init__.py +0 -0
  53. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/_shared.py +0 -0
  54. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/benchmark.py +0 -0
  55. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/install.py +0 -0
  56. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/mcp_cmd.py +0 -0
  57. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/monitor.py +0 -0
  58. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/commands/quickstart.py +0 -0
  59. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/__init__.py +0 -0
  60. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/bootstrap.py +0 -0
  61. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/cache.py +0 -0
  62. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/diff.py +0 -0
  63. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/git.py +0 -0
  64. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/git_hooks.py +0 -0
  65. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/global_install.py +0 -0
  66. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/ignore.py +0 -0
  67. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/merkle.py +0 -0
  68. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/models.py +0 -0
  69. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/redactor.py +0 -0
  70. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/snapshot.py +0 -0
  71. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/token_estimator.py +0 -0
  72. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/core/vscode_tasks.py +0 -0
  73. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/__init__.py +0 -0
  74. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/installers/claude.py +0 -0
  75. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/integrations/__init__.py +0 -0
  76. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/integrations/git_hooks.py +0 -0
  77. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/integrations/global_install.py +0 -0
  78. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/integrations/vscode_tasks.py +0 -0
  79. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/mcp_server.py +0 -0
  80. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/renderers/__init__.py +0 -0
  81. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/renderers/compact.py +0 -0
  82. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/renderers/receipts.py +0 -0
  83. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/session/__init__.py +0 -0
  84. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/summaries/__init__.py +0 -0
  85. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/summaries/base.py +0 -0
  86. {agentpack_cli-0.1.24 → agentpack_cli-0.1.26}/src/agentpack/summaries/offline.py +0 -0
@@ -0,0 +1,33 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+
9
+ .venv/
10
+ venv/
11
+ env/
12
+
13
+ # agentpack:start
14
+ # AgentPack generated context/cache (safe to ignore)
15
+ .agentpack/cache/
16
+ .agentpack/snapshots/
17
+ .agentpack/context*
18
+ .agentpack/metrics.jsonl
19
+ .agentpack/pack_metadata.json
20
+ .agentpack/activity.log
21
+ .agentpack/.gitignore
22
+ .agentpack/.mcp_reminded
23
+ .agentpack/session.json
24
+ .agentpack/task.md
25
+ .agentpack/benchmark_results.jsonl
26
+ .agent/skills/agentpack/
27
+ # agentpack:end
28
+
29
+ .pytest_cache/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ *.dist-info/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentpack-cli
3
- Version: 0.1.24
3
+ Version: 0.1.26
4
4
  Summary: Task-aware context packing for AI coding agents — Claude, Cursor, Windsurf, Codex, and Antigravity
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -44,7 +44,7 @@ Description-Content-Type: text/markdown
44
44
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
45
45
  [![CI](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg)](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
46
46
 
47
- > **Status: alpha (v0.1.24).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
47
+ > **Status: alpha (v0.1.25).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
48
48
  >
49
49
  > **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
50
50
 
@@ -377,7 +377,7 @@ Configures:
377
377
  - `CLAUDE.md` — tells Claude to read the context pack before each task
378
378
  - `.claude/settings.json` — two hooks:
379
379
  - `SessionStart`: clears injection sentinel so first prompt gets context
380
- - `UserPromptSubmit`: runs `agentpack hook` — detects repo changes via `root_hash`, triggers background repack using your prompt as task. With MCP: emits Option-B hint (~100 tokens, task + top files). Without MCP: emits capped fallback (top 8 files, ≤3k chars)
380
+ - `UserPromptSubmit`: runs `agentpack hook` — detects repo changes via `root_hash`, detects clear task switches, updates `.agentpack/task.md`, and triggers background repack using your prompt as task. With MCP: emits Option-B hint (~100 tokens, task + top files). Without MCP: emits capped fallback (top 8 files, ≤3k chars)
381
381
 
382
382
  After this, context is injected automatically into every Claude Code session. No `/agentpack` command needed — it just happens.
383
383
 
@@ -388,7 +388,7 @@ agentpack install --agent cursor
388
388
  ```
389
389
 
390
390
  Configures:
391
- - `.cursorrules` — rule: read `.agentpack/context.md` before every conversation
391
+ - `.cursorrules` — rule: write current task, run `agentpack pack --task auto`, then read `.agentpack/context.md`
392
392
  - `.cursor/rules/agentpack.mdc` — `alwaysApply: true` rule (Cursor v0.43+)
393
393
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
394
394
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
@@ -400,7 +400,7 @@ agentpack install --agent windsurf
400
400
  ```
401
401
 
402
402
  Configures:
403
- - `.windsurfrules` — rule: read `.agentpack/context.md` before every conversation
403
+ - `.windsurfrules` — rule: write current task, run `agentpack pack --task auto`, then read `.agentpack/context.md`
404
404
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
405
405
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
406
406
 
@@ -411,7 +411,7 @@ agentpack install --agent codex
411
411
  ```
412
412
 
413
413
  Configures:
414
- - `AGENTS.md` — tells Codex to read the context pack before each task
414
+ - `AGENTS.md` — tells Codex to write current task, repack, and read the context pack before each task
415
415
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
416
416
 
417
417
  ### Antigravity
@@ -422,7 +422,7 @@ agentpack install --agent antigravity
422
422
 
423
423
  Configures:
424
424
  - `.agent/skills/agentpack/SKILL.md` — AgentPack context as a Skill; Antigravity activates it automatically for coding tasks
425
- - `GEMINI.md` — registers the agentpack skill reference
425
+ - `GEMINI.md` — registers the agentpack skill reference and task-switch protocol
426
426
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
427
427
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
428
428
 
@@ -642,6 +642,7 @@ agentpack init --share-cache # commit cache/ to git for team sharing
642
642
 
643
643
  Creates:
644
644
  ```
645
+ .gitignore # patched idempotently with AgentPack generated artifacts
645
646
  .agentignore # gitignore-style file exclusion rules
646
647
  .agentpack/
647
648
  config.toml # configuration (safe to commit)
@@ -1062,6 +1063,10 @@ include_tests = true
1062
1063
  include_configs = true
1063
1064
  include_receipts = true
1064
1065
 
1066
+ [hooks]
1067
+ task_switch_detection = true
1068
+ task_switch_min_terms = 1
1069
+
1065
1070
  [agents.claude]
1066
1071
  output = ".agentpack/context.claude.md"
1067
1072
  patch_claude_md = true
@@ -1114,8 +1119,11 @@ Works like `.gitignore`. Default rules exclude:
1114
1119
  .agentignore ✓ commit
1115
1120
  .agentpack/config.toml ✓ commit
1116
1121
  .agentpack/cache/ ✓ commit if --share-cache (recommended for teams)
1122
+ .agentpack/.gitignore ✗ gitignored
1117
1123
  .agentpack/snapshots/ ✗ gitignored
1118
1124
  .agentpack/context.* ✗ gitignored
1125
+ .agentpack/task.md ✗ gitignored (local current task)
1126
+ .agent/skills/agentpack/ ✗ gitignored (generated Antigravity context)
1119
1127
  ```
1120
1128
 
1121
1129
  ---
@@ -5,7 +5,7 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![CI](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml/badge.svg)](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
7
7
 
8
- > **Status: alpha (v0.1.24).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
8
+ > **Status: alpha (v0.1.25).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Not yet validated across a wide range of repos. API may change before 1.0.
9
9
  >
10
10
  > **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
11
11
 
@@ -338,7 +338,7 @@ Configures:
338
338
  - `CLAUDE.md` — tells Claude to read the context pack before each task
339
339
  - `.claude/settings.json` — two hooks:
340
340
  - `SessionStart`: clears injection sentinel so first prompt gets context
341
- - `UserPromptSubmit`: runs `agentpack hook` — detects repo changes via `root_hash`, triggers background repack using your prompt as task. With MCP: emits Option-B hint (~100 tokens, task + top files). Without MCP: emits capped fallback (top 8 files, ≤3k chars)
341
+ - `UserPromptSubmit`: runs `agentpack hook` — detects repo changes via `root_hash`, detects clear task switches, updates `.agentpack/task.md`, and triggers background repack using your prompt as task. With MCP: emits Option-B hint (~100 tokens, task + top files). Without MCP: emits capped fallback (top 8 files, ≤3k chars)
342
342
 
343
343
  After this, context is injected automatically into every Claude Code session. No `/agentpack` command needed — it just happens.
344
344
 
@@ -349,7 +349,7 @@ agentpack install --agent cursor
349
349
  ```
350
350
 
351
351
  Configures:
352
- - `.cursorrules` — rule: read `.agentpack/context.md` before every conversation
352
+ - `.cursorrules` — rule: write current task, run `agentpack pack --task auto`, then read `.agentpack/context.md`
353
353
  - `.cursor/rules/agentpack.mdc` — `alwaysApply: true` rule (Cursor v0.43+)
354
354
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
355
355
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
@@ -361,7 +361,7 @@ agentpack install --agent windsurf
361
361
  ```
362
362
 
363
363
  Configures:
364
- - `.windsurfrules` — rule: read `.agentpack/context.md` before every conversation
364
+ - `.windsurfrules` — rule: write current task, run `agentpack pack --task auto`, then read `.agentpack/context.md`
365
365
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
366
366
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
367
367
 
@@ -372,7 +372,7 @@ agentpack install --agent codex
372
372
  ```
373
373
 
374
374
  Configures:
375
- - `AGENTS.md` — tells Codex to read the context pack before each task
375
+ - `AGENTS.md` — tells Codex to write current task, repack, and read the context pack before each task
376
376
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
377
377
 
378
378
  ### Antigravity
@@ -383,7 +383,7 @@ agentpack install --agent antigravity
383
383
 
384
384
  Configures:
385
385
  - `.agent/skills/agentpack/SKILL.md` — AgentPack context as a Skill; Antigravity activates it automatically for coding tasks
386
- - `GEMINI.md` — registers the agentpack skill reference
386
+ - `GEMINI.md` — registers the agentpack skill reference and task-switch protocol
387
387
  - `.git/hooks/post-commit`, `post-merge`, `post-checkout` — background repack on tree change
388
388
  - `.vscode/tasks.json` — "AgentPack: Repack context" in Command Palette + `runOn: folderOpen`
389
389
 
@@ -603,6 +603,7 @@ agentpack init --share-cache # commit cache/ to git for team sharing
603
603
 
604
604
  Creates:
605
605
  ```
606
+ .gitignore # patched idempotently with AgentPack generated artifacts
606
607
  .agentignore # gitignore-style file exclusion rules
607
608
  .agentpack/
608
609
  config.toml # configuration (safe to commit)
@@ -1023,6 +1024,10 @@ include_tests = true
1023
1024
  include_configs = true
1024
1025
  include_receipts = true
1025
1026
 
1027
+ [hooks]
1028
+ task_switch_detection = true
1029
+ task_switch_min_terms = 1
1030
+
1026
1031
  [agents.claude]
1027
1032
  output = ".agentpack/context.claude.md"
1028
1033
  patch_claude_md = true
@@ -1075,8 +1080,11 @@ Works like `.gitignore`. Default rules exclude:
1075
1080
  .agentignore ✓ commit
1076
1081
  .agentpack/config.toml ✓ commit
1077
1082
  .agentpack/cache/ ✓ commit if --share-cache (recommended for teams)
1083
+ .agentpack/.gitignore ✗ gitignored
1078
1084
  .agentpack/snapshots/ ✗ gitignored
1079
1085
  .agentpack/context.* ✗ gitignored
1086
+ .agentpack/task.md ✗ gitignored (local current task)
1087
+ .agent/skills/agentpack/ ✗ gitignored (generated Antigravity context)
1080
1088
  ```
1081
1089
 
1082
1090
  ---
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.1.24"
3
+ version = "0.1.26"
4
4
  description = "Task-aware context packing for AI coding agents — Claude, Cursor, Windsurf, Codex, and Antigravity"
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.1.24"
3
+ __version__ = "0.1.26"
@@ -16,6 +16,7 @@ from agentpack.core import git
16
16
  from agentpack.core.context_pack import select_files, save_pack_metadata
17
17
  from agentpack.core.models import ContextPack, DependencyGraph, FileInfo, ScanResult, SelectedFile, Receipt
18
18
  from agentpack.core.token_estimator import estimate_tokens
19
+ from agentpack.renderers.markdown import render_generic
19
20
  from agentpack.analysis.ranking import (
20
21
  score_files,
21
22
  extract_keyword_weights,
@@ -193,6 +194,7 @@ class PackPlanner:
193
194
  previous_snapshot=previous_snap,
194
195
  include_globs=cfg.project.include_globs or None,
195
196
  exclude_globs=cfg.project.exclude_globs or None,
197
+ always_skip_paths=AdapterRegistry.generated_output_paths(root, cfg),
196
198
  )
197
199
  phase_times["scan"] = time.perf_counter() - t0
198
200
 
@@ -255,7 +257,7 @@ class AdapterRegistry:
255
257
  """Maps agent names to adapter instances; extensible without touching PackService."""
256
258
 
257
259
  @staticmethod
258
- def get(agent: str, cfg: Any) -> Any:
260
+ def _factories(cfg: Any) -> dict[str, Any]:
259
261
  from agentpack.adapters.antigravity import AntigravityAdapter
260
262
  from agentpack.adapters.claude import ClaudeAdapter
261
263
  from agentpack.adapters.codex import CodexAdapter
@@ -263,15 +265,33 @@ class AdapterRegistry:
263
265
  from agentpack.adapters.windsurf import WindsurfAdapter
264
266
  from agentpack.adapters.generic import GenericAdapter
265
267
 
266
- adapters = {
268
+ return {
267
269
  "antigravity": lambda: AntigravityAdapter(),
268
270
  "claude": lambda: ClaudeAdapter(cfg.agents.claude.output),
269
271
  "cursor": lambda: CursorAdapter(cfg.agents.generic.output),
270
272
  "windsurf": lambda: WindsurfAdapter(cfg.agents.generic.output),
271
273
  "codex": lambda: CodexAdapter(cfg.agents.generic.output),
274
+ "generic": lambda: GenericAdapter(cfg.agents.generic.output),
272
275
  }
276
+
277
+ @staticmethod
278
+ def get(agent: str, cfg: Any) -> Any:
279
+ from agentpack.adapters.generic import GenericAdapter
280
+
281
+ adapters = AdapterRegistry._factories(cfg)
273
282
  return adapters.get(agent, lambda: GenericAdapter(cfg.agents.generic.output))()
274
283
 
284
+ @staticmethod
285
+ def generated_output_paths(root: Path, cfg: Any) -> set[str]:
286
+ paths: set[str] = set()
287
+ for factory in AdapterRegistry._factories(cfg).values():
288
+ try:
289
+ out_path = factory().output_path(root)
290
+ paths.add(str(out_path.relative_to(root)).replace("\\", "/"))
291
+ except (OSError, ValueError):
292
+ continue
293
+ return paths
294
+
275
295
 
276
296
  class PackService:
277
297
  """Materializes a plan from PackPlanner into a written context file."""
@@ -319,6 +339,7 @@ class PackService:
319
339
 
320
340
  t0 = time.perf_counter()
321
341
  out_path = adapter.write(pack_obj, root)
342
+ _write_canonical_context(pack_obj, root, out_path)
322
343
  plan.phase_times["render"] = time.perf_counter() - t0
323
344
 
324
345
  save_snapshot(plan.current_snap, root)
@@ -333,6 +354,7 @@ class PackService:
333
354
  token_estimate=packed_tokens,
334
355
  freshness=freshness,
335
356
  freshness_warnings=freshness_warnings,
357
+ selected_files=_selected_file_metadata(plan.selected),
336
358
  )
337
359
  excluded_receipts = [r for r in plan.receipts if r.action == "excluded"]
338
360
  # Budget-cut: files that scored OK but didn't fit — more useful signal than "score too low"
@@ -368,6 +390,33 @@ class PackService:
368
390
  )
369
391
 
370
392
 
393
+ def _write_canonical_context(pack: ContextPack, root: Path, out_path: Path) -> None:
394
+ """Keep .agentpack/context.md fresh even when the target agent writes elsewhere."""
395
+ canonical_path = root / ".agentpack" / "context.md"
396
+ try:
397
+ if out_path.resolve() == canonical_path.resolve():
398
+ return
399
+ except OSError:
400
+ if out_path == canonical_path:
401
+ return
402
+ canonical_path.parent.mkdir(parents=True, exist_ok=True)
403
+ canonical_path.write_text(render_generic(pack), encoding="utf-8")
404
+
405
+
406
+ def _selected_file_metadata(selected: list[SelectedFile]) -> list[dict[str, Any]]:
407
+ return [
408
+ {
409
+ "path": sf.path,
410
+ "mode": sf.include_mode,
411
+ "score": round(sf.score, 1),
412
+ "why": sf.reasons[0] if sf.reasons else "",
413
+ "reasons": sf.reasons,
414
+ "tokens": _sf_tokens(sf),
415
+ }
416
+ for sf in selected
417
+ ]
418
+
419
+
371
420
  def _sf_tokens(sf: SelectedFile) -> int:
372
421
  if sf.content:
373
422
  return estimate_tokens(sf.content)
@@ -30,6 +30,7 @@ def register(app: typer.Typer) -> None:
30
30
  state.last_refresh_at = _now_iso()
31
31
  state.refresh_count += 1
32
32
  state.last_task_hash = _file_hash(root / TASK_FILE)
33
+ state.last_resolved_agent = state.agent
33
34
  save_session(root, state)
34
35
  log_activity(root, f"claude cmd — {result['files']} files, {result['tokens']:,} tokens")
35
36
  else:
@@ -8,6 +8,7 @@ from agentpack.core.ignore import load_spec
8
8
  from agentpack.core.scanner import scan
9
9
  from agentpack.core.snapshot import build_snapshot, load_snapshot
10
10
  from agentpack.core.diff import diff_snapshots
11
+ from agentpack.application.pack_service import AdapterRegistry
11
12
  from agentpack.commands._shared import console, _root
12
13
 
13
14
 
@@ -19,7 +20,12 @@ def register(app: typer.Typer) -> None:
19
20
  cfg = load_config(root)
20
21
  ignore_spec = load_spec(root / cfg.project.ignore_file)
21
22
 
22
- scan_result = scan(root, ignore_spec, cfg.context.max_file_tokens)
23
+ scan_result = scan(
24
+ root,
25
+ ignore_spec,
26
+ cfg.context.max_file_tokens,
27
+ always_skip_paths=AdapterRegistry.generated_output_paths(root, cfg),
28
+ )
23
29
  current = build_snapshot(scan_result.packable)
24
30
  previous = load_snapshot(root)
25
31
  result = diff_snapshots(previous, current)
@@ -16,6 +16,7 @@ from agentpack.integrations.global_install import (
16
16
  _detect_rc_file,
17
17
  )
18
18
  from agentpack.commands._shared import console, _root
19
+ from agentpack.core.context_pack import load_pack_metadata
19
20
 
20
21
 
21
22
  def register(app: typer.Typer) -> None:
@@ -104,11 +105,11 @@ def register(app: typer.Typer) -> None:
104
105
  return
105
106
 
106
107
  config_path = root / ".agentpack" / "config.toml"
107
- context_path = root / ".agentpack" / "context.claude.md"
108
108
  if not config_path.exists():
109
109
  console.print(f" [yellow]![/] Not initialized in {root} — run: agentpack init")
110
110
  else:
111
111
  console.print(" [green]✓[/] .agentpack/config.toml present")
112
+ context_path = _latest_context_path(root)
112
113
  if context_path.exists():
113
114
  import time
114
115
  age = time.time() - context_path.stat().st_mtime
@@ -216,6 +217,16 @@ def register(app: typer.Typer) -> None:
216
217
  if not _local_has_mcp and not _global_has_mcp:
217
218
  console.print(" [yellow]![/] MCP server not registered — mcp__agentpack__* tools unavailable")
218
219
 
220
+ # --- Release hygiene ---
221
+ console.print("\n[bold]Release hygiene[/]")
222
+ findings = _release_hygiene_findings(root)
223
+ if findings:
224
+ for finding in findings:
225
+ console.print(f" [yellow]![/] {finding}")
226
+ ok = False
227
+ else:
228
+ console.print(" [green]✓[/] no generated release-noise files staged or untracked")
229
+
219
230
  # --- Slash command ---
220
231
  console.print("\n[bold]Slash command (/agentpack)[/]")
221
232
  local_cmd = root / ".claude" / "commands" / "agentpack.md"
@@ -244,6 +255,23 @@ def _check_agent_file(root: Path, filename: str, agent: str) -> None:
244
255
  console.print(f" [dim]-[/] {filename} not present (optional)")
245
256
 
246
257
 
258
+ def _latest_context_path(root: Path) -> Path:
259
+ meta = load_pack_metadata(root)
260
+ if meta and meta.get("context_path"):
261
+ candidate = root / str(meta["context_path"])
262
+ if candidate.exists():
263
+ return candidate
264
+ for rel in (
265
+ ".agentpack/context.md",
266
+ ".agentpack/context.claude.md",
267
+ ".agent/skills/agentpack/SKILL.md",
268
+ ):
269
+ candidate = root / rel
270
+ if candidate.exists():
271
+ return candidate
272
+ return root / ".agentpack" / "context.md"
273
+
274
+
247
275
  def _source_checkout_warning(
248
276
  root: Path,
249
277
  package_file: Path,
@@ -268,6 +296,51 @@ def _source_checkout_warning(
268
296
  )
269
297
 
270
298
 
299
+ _RELEASE_NOISE_PREFIXES = (
300
+ ".agent/",
301
+ ".agentpack/",
302
+ ".claude/worktrees/",
303
+ ".codex/",
304
+ ".cursor/",
305
+ ".vscode/",
306
+ )
307
+ _RELEASE_NOISE_FILES = {".coverage"}
308
+
309
+
310
+ def _release_hygiene_findings(root: Path) -> list[str]:
311
+ """Flag local generated artifacts that should not be staged or released."""
312
+ try:
313
+ result = subprocess.run(
314
+ ["git", "status", "--short"],
315
+ cwd=root,
316
+ capture_output=True,
317
+ text=True,
318
+ timeout=5,
319
+ )
320
+ except (OSError, subprocess.TimeoutExpired):
321
+ return ["could not inspect git status for release hygiene"]
322
+ if result.returncode != 0:
323
+ return []
324
+
325
+ noisy: list[str] = []
326
+ for raw in result.stdout.splitlines():
327
+ if not raw.strip():
328
+ continue
329
+ status = raw[:2].strip() or "modified"
330
+ path = raw[3:].strip()
331
+ if " -> " in path:
332
+ path = path.rsplit(" -> ", 1)[1]
333
+ norm = path.replace("\\", "/")
334
+ if norm in _RELEASE_NOISE_FILES or any(norm.startswith(prefix) for prefix in _RELEASE_NOISE_PREFIXES):
335
+ noisy.append(f"{status} {norm}")
336
+
337
+ if not noisy:
338
+ return []
339
+ sample = ", ".join(noisy[:8])
340
+ extra = f", ... {len(noisy) - 8} more" if len(noisy) > 8 else ""
341
+ return [f"generated/local artifacts present: {sample}{extra}"]
342
+
343
+
271
344
  def _print_summary(ok: bool) -> None:
272
345
  console.print("")
273
346
  if ok:
@@ -10,6 +10,7 @@ from agentpack.core.context_pack import select_files
10
10
  from agentpack.commands._shared import console, _root
11
11
  from agentpack.commands.pack import _resolve_task
12
12
  from agentpack.core.config import load_config, ScoringWeights
13
+ from agentpack.analysis.ranking import extract_keyword_weights, generic_task_term_ratio, _GENERIC_TASK_TERMS
13
14
 
14
15
 
15
16
  def _resolve_signal_weight(reason: str, weights: ScoringWeights) -> float:
@@ -111,6 +112,50 @@ def _print_file_detail(
111
112
  console.print()
112
113
 
113
114
 
115
+ def _noise_report(task: str, plan: object) -> list[str]:
116
+ keyword_weights = extract_keyword_weights(task)
117
+ generic_terms = sorted(term for term in keyword_weights if term in _GENERIC_TASK_TERMS)
118
+ specific_terms = sorted(term for term in keyword_weights if term not in _GENERIC_TASK_TERMS)
119
+ selected = list(plan.selected) # type: ignore[attr-defined]
120
+ summary_count = sum(1 for sf in selected if sf.include_mode == "summary")
121
+ filename_count = sum(1 for sf in selected if "filename keyword match" in sf.reasons)
122
+ symbol_count = sum(1 for sf in selected if "symbol keyword match" in sf.reasons)
123
+ excluded = [r for r in plan.receipts if r.action == "excluded"] # type: ignore[attr-defined]
124
+ summary_cap = sum(1 for r in excluded if r.reason == "summary cap reached")
125
+ score_floor = sum(1 for r in excluded if r.reason == "summary score below floor")
126
+
127
+ lines = [
128
+ "## Pack noise report",
129
+ "",
130
+ f"- generic task ratio: {generic_task_term_ratio(task):.0%}",
131
+ f"- generic terms: {', '.join(generic_terms) if generic_terms else '(none)'}",
132
+ f"- specific terms: {', '.join(specific_terms) if specific_terms else '(none)'}",
133
+ f"- selected summaries: {summary_count}/{len(selected)}",
134
+ f"- filename-match selections: {filename_count}/{len(selected)}",
135
+ f"- symbol-match selections: {symbol_count}/{len(selected)}",
136
+ f"- excluded by summary cap: {summary_cap}",
137
+ f"- excluded by weak summary score: {score_floor}",
138
+ "",
139
+ "### Sharpen task wording",
140
+ "",
141
+ ]
142
+ if generic_terms:
143
+ lines.append("- Replace broad terms with subsystem, file, or symptom words.")
144
+ lines.append(f"- Broad terms driving matches: {', '.join(generic_terms[:8])}.")
145
+ else:
146
+ lines.append("- Task terms are already specific; inspect changed files or score weights next.")
147
+ if summary_count and selected and summary_count / len(selected) >= 0.7:
148
+ lines.append("- Try `--mode minimal` for edit work, or add exact module/file names.")
149
+ if filename_count and selected and filename_count / len(selected) >= 0.6:
150
+ lines.append("- Filename matches dominate; add behavior words that appear inside target files.")
151
+ return lines
152
+
153
+
154
+ def _print_noise_report(task: str, plan: object) -> None:
155
+ for line in _noise_report(task, plan):
156
+ console.print(line)
157
+
158
+
114
159
  def register(app: typer.Typer) -> None:
115
160
  @app.command()
116
161
  def explain(
@@ -120,6 +165,7 @@ def register(app: typer.Typer) -> None:
120
165
  since: Optional[str] = typer.Option(None, "--since", help="Git ref to compare against (e.g. HEAD~1, main)."),
121
166
  file: Optional[str] = typer.Option(None, "--file", help="Show detailed score breakdown for a specific file."),
122
167
  omitted: bool = typer.Option(False, "--omitted", is_flag=True, help="Show top-10 excluded files and why."),
168
+ why_noisy: bool = typer.Option(False, "--why-noisy", is_flag=True, help="Explain broad task terms and noisy selection signals."),
123
169
  ) -> None:
124
170
  """Explain which files would be selected and why, without writing a context file."""
125
171
  if mode not in ("minimal", "balanced", "deep"):
@@ -199,6 +245,12 @@ def register(app: typer.Typer) -> None:
199
245
  console.print()
200
246
  return
201
247
 
248
+ if why_noisy:
249
+ console.print(f"\n[bold]Task:[/] [cyan]{resolved_task}[/] [dim]mode={mode} budget={plan.budget:,}[/]\n")
250
+ _print_noise_report(resolved_task, plan)
251
+ console.print()
252
+ return
253
+
202
254
  console.print(f"\n[bold]Task:[/] [cyan]{resolved_task}[/] [dim]mode={mode} budget={plan.budget:,}[/]\n")
203
255
 
204
256
  console.print("[bold]Top selected files (ranked):[/]")