agentpack-cli 0.1.25__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 (85) hide show
  1. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/PKG-INFO +10 -6
  2. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/README.md +9 -5
  3. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/pyproject.toml +1 -1
  4. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/__init__.py +1 -1
  5. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/hook_cmd.py +116 -7
  6. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/stats.py +4 -1
  7. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/status.py +27 -2
  8. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/config.py +12 -0
  9. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/data/agentpack.md +5 -4
  10. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/antigravity.py +8 -0
  11. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/codex.py +2 -0
  12. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/cursor.py +9 -4
  13. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/windsurf.py +4 -2
  14. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/renderers/markdown.py +3 -2
  15. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/.gitignore +0 -0
  16. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/LICENSE +0 -0
  17. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/__init__.py +0 -0
  18. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/antigravity.py +0 -0
  19. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/base.py +0 -0
  20. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/claude.py +0 -0
  21. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/codex.py +0 -0
  22. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/cursor.py +0 -0
  23. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/detect.py +0 -0
  24. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/generic.py +0 -0
  25. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/adapters/windsurf.py +0 -0
  26. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/__init__.py +0 -0
  27. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/dependency_graph.py +0 -0
  28. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/go_imports.py +0 -0
  29. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/java_imports.py +0 -0
  30. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/js_ts_imports.py +0 -0
  31. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/python_imports.py +0 -0
  32. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/ranking.py +0 -0
  33. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/rust_imports.py +0 -0
  34. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/symbols.py +0 -0
  35. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/analysis/tests.py +0 -0
  36. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/application/__init__.py +0 -0
  37. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/application/pack_service.py +0 -0
  38. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/cli.py +0 -0
  39. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/__init__.py +0 -0
  40. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/_shared.py +0 -0
  41. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/benchmark.py +0 -0
  42. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/claude_cmd.py +0 -0
  43. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/diff.py +0 -0
  44. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/doctor.py +0 -0
  45. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/explain.py +0 -0
  46. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/init.py +0 -0
  47. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/install.py +0 -0
  48. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/mcp_cmd.py +0 -0
  49. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/monitor.py +0 -0
  50. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/pack.py +0 -0
  51. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/quickstart.py +0 -0
  52. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/scan.py +0 -0
  53. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/summarize.py +0 -0
  54. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/commands/watch.py +0 -0
  55. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/__init__.py +0 -0
  56. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/bootstrap.py +0 -0
  57. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/cache.py +0 -0
  58. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/context_pack.py +0 -0
  59. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/diff.py +0 -0
  60. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/git.py +0 -0
  61. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/git_hooks.py +0 -0
  62. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/global_install.py +0 -0
  63. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/ignore.py +0 -0
  64. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/merkle.py +0 -0
  65. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/models.py +0 -0
  66. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/redactor.py +0 -0
  67. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/scanner.py +0 -0
  68. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/snapshot.py +0 -0
  69. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/token_estimator.py +0 -0
  70. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/core/vscode_tasks.py +0 -0
  71. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/__init__.py +0 -0
  72. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/installers/claude.py +0 -0
  73. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/integrations/__init__.py +0 -0
  74. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/integrations/git_hooks.py +0 -0
  75. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/integrations/global_install.py +0 -0
  76. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/integrations/vscode_tasks.py +0 -0
  77. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/mcp_server.py +0 -0
  78. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/renderers/__init__.py +0 -0
  79. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/renderers/compact.py +0 -0
  80. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/renderers/receipts.py +0 -0
  81. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/session/__init__.py +0 -0
  82. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/session/state.py +0 -0
  83. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/summaries/__init__.py +0 -0
  84. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/src/agentpack/summaries/base.py +0 -0
  85. {agentpack_cli-0.1.25 → agentpack_cli-0.1.26}/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.1.25
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
@@ -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
 
@@ -1063,6 +1063,10 @@ include_tests = true
1063
1063
  include_configs = true
1064
1064
  include_receipts = true
1065
1065
 
1066
+ [hooks]
1067
+ task_switch_detection = true
1068
+ task_switch_min_terms = 1
1069
+
1066
1070
  [agents.claude]
1067
1071
  output = ".agentpack/context.claude.md"
1068
1072
  patch_claude_md = true
@@ -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
 
@@ -1024,6 +1024,10 @@ include_tests = true
1024
1024
  include_configs = true
1025
1025
  include_receipts = true
1026
1026
 
1027
+ [hooks]
1028
+ task_switch_detection = true
1029
+ task_switch_min_terms = 1
1030
+
1027
1031
  [agents.claude]
1028
1032
  output = ".agentpack/context.claude.md"
1029
1033
  patch_claude_md = true
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.1.25"
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.25"
3
+ __version__ = "0.1.26"
@@ -10,6 +10,7 @@ import typer
10
10
 
11
11
  from agentpack.commands._shared import _root
12
12
  from agentpack.core import git as _git
13
+ from agentpack.core.config import load_config
13
14
 
14
15
  _TASK_FILE = ".agentpack/task.md"
15
16
  _TASK_FILE_DEFAULT_MARKER = "Write or update the current coding task here."
@@ -17,6 +18,37 @@ _CODING_PROMPT_RE = re.compile(
17
18
  r"(?:fix|add|refactor|impl|implement|update|write|debug|test|build|migrate|remove|delete|rename|optimize)\b",
18
19
  re.IGNORECASE,
19
20
  )
21
+ _TASK_STOPWORDS = {
22
+ "add",
23
+ "all",
24
+ "and",
25
+ "bug",
26
+ "build",
27
+ "can",
28
+ "change",
29
+ "changes",
30
+ "code",
31
+ "delete",
32
+ "fix",
33
+ "for",
34
+ "implement",
35
+ "improve",
36
+ "make",
37
+ "please",
38
+ "refactor",
39
+ "remove",
40
+ "task",
41
+ "test",
42
+ "that",
43
+ "the",
44
+ "these",
45
+ "this",
46
+ "update",
47
+ "with",
48
+ "work",
49
+ "write",
50
+ "you",
51
+ }
20
52
 
21
53
 
22
54
  def register(app: typer.Typer) -> None:
@@ -83,13 +115,66 @@ def _looks_like_coding_prompt(prompt: str) -> bool:
83
115
  return bool(_CODING_PROMPT_RE.search(stripped))
84
116
 
85
117
 
86
- def _resolve_task(root: Path, prompt: str) -> str:
118
+ def _prompt_task(prompt: str) -> str:
119
+ if not prompt or not _looks_like_coding_prompt(prompt):
120
+ return ""
121
+ task = " ".join(prompt.strip().split())[:200]
122
+ if not _task_terms(task):
123
+ return ""
124
+ return task
125
+
126
+
127
+ def _task_terms(text: str) -> set[str]:
128
+ terms: set[str] = set()
129
+ for raw in re.findall(r"[A-Za-z0-9][A-Za-z0-9_-]*", text.lower()):
130
+ for part in re.split(r"[-_]", raw):
131
+ if len(part) >= 3 and part not in _TASK_STOPWORDS:
132
+ terms.add(part)
133
+ return terms
134
+
135
+
136
+ def _looks_like_task_switch(current_task: str, prompt: str, min_terms: int = 1) -> bool:
137
+ """Heuristic: a coding prompt with disjoint concrete terms likely starts a new task."""
138
+ prompt_task = _prompt_task(prompt)
139
+ if not current_task or not prompt_task:
140
+ return False
141
+ if current_task.strip().lower() == prompt_task.lower():
142
+ return False
143
+ current_terms = _task_terms(current_task)
144
+ prompt_terms = _task_terms(prompt_task)
145
+ required_terms = max(1, min_terms)
146
+ if len(current_terms) < required_terms or len(prompt_terms) < required_terms:
147
+ return False
148
+ return bool(current_terms and prompt_terms and current_terms.isdisjoint(prompt_terms))
149
+
150
+
151
+ def _write_task_md(root: Path, task: str) -> None:
152
+ task_path = root / _TASK_FILE
153
+ task_path.parent.mkdir(parents=True, exist_ok=True)
154
+ task_path.write_text(task.strip() + "\n", encoding="utf-8")
155
+
156
+
157
+ def _resolve_task(
158
+ root: Path,
159
+ prompt: str,
160
+ *,
161
+ task_switch_detection: bool = True,
162
+ task_switch_min_terms: int = 1,
163
+ ) -> str:
87
164
  """Merge task.md + prompt into best task description for repack."""
88
165
  task_md = _load_task_md(root)
166
+ prompt_task = _prompt_task(prompt)
167
+ if (
168
+ task_switch_detection
169
+ and task_md
170
+ and prompt_task
171
+ and _looks_like_task_switch(task_md, prompt_task, min_terms=task_switch_min_terms)
172
+ ):
173
+ return prompt_task
89
174
  if task_md:
90
175
  return task_md
91
- if prompt and _looks_like_coding_prompt(prompt):
92
- return prompt[:200].strip()
176
+ if prompt_task:
177
+ return prompt_task
93
178
  return "auto"
94
179
 
95
180
 
@@ -177,13 +262,37 @@ def _run_user_prompt_submit(root: Path) -> None:
177
262
  except Exception:
178
263
  prompt = ""
179
264
 
180
- task = _resolve_task(root, prompt)
265
+ cfg = load_config(root)
266
+ task_md = _load_task_md(root)
267
+ task_switched = bool(
268
+ cfg.hooks.task_switch_detection
269
+ and _looks_like_task_switch(
270
+ task_md,
271
+ prompt,
272
+ min_terms=cfg.hooks.task_switch_min_terms,
273
+ )
274
+ )
275
+ task = _resolve_task(
276
+ root,
277
+ prompt,
278
+ task_switch_detection=cfg.hooks.task_switch_detection,
279
+ task_switch_min_terms=cfg.hooks.task_switch_min_terms,
280
+ )
281
+ if task_switched and task != "auto":
282
+ try:
283
+ _write_task_md(root, task)
284
+ except Exception:
285
+ pass
181
286
 
182
287
  current_hash = _current_root_hash(root)
183
288
  reminded_hash = snap_sentinel.read_text().strip() if snap_sentinel.exists() else None
184
289
  repo_changed = current_hash != reminded_hash
290
+ packed_task = _load_pack_task(root)
291
+ pack_task_changed = bool(task != "auto" and packed_task and packed_task != task)
292
+
293
+ should_repack = repo_changed or task_switched or pack_task_changed
185
294
 
186
- if repo_changed:
295
+ if should_repack:
187
296
  subprocess.Popen(
188
297
  ["agentpack", "pack", "--task", task, "--mode", "balanced", "--since", "HEAD~1"],
189
298
  stdout=subprocess.DEVNULL,
@@ -203,7 +312,7 @@ def _run_user_prompt_submit(root: Path) -> None:
203
312
  f" - {h['path']}" + (f" — {h['why']}" if h.get("why") else "")
204
313
  for h in hints
205
314
  )
206
- status_note = "(repacking — call pack_context for fresh results)" if repo_changed else "(index fresh)"
315
+ status_note = "(repacking — call pack_context for fresh results)" if should_repack else "(index fresh)"
207
316
  current_task = _load_task_md(root) or _infer_live_task(root)
208
317
  msg = (
209
318
  f"AgentPack {status_note}\n"
@@ -224,7 +333,7 @@ def _run_user_prompt_submit(root: Path) -> None:
224
333
  f" - {h['path']}" + (f" — {h['why']}" if h.get("why") else "")
225
334
  for h in hints
226
335
  )
227
- changed_note = " (repacking in background)" if repo_changed else ""
336
+ changed_note = " (repacking in background)" if should_repack else ""
228
337
  msg = (
229
338
  f"AgentPack context{changed_note}\n"
230
339
  f"task: {current_task}\n"
@@ -245,7 +245,10 @@ def _freshness_diagnostics(
245
245
 
246
246
  task_md = _task_md_body(root)
247
247
  if task_md and task_md != meta.get("task"):
248
- diagnostics.append(".agentpack/task.md differs from the latest packed task.")
248
+ diagnostics.append(
249
+ ".agentpack/task.md differs from the latest packed task "
250
+ f"(packed: {meta.get('task')}; current: {task_md})."
251
+ )
249
252
 
250
253
  if meta.get("snapshot_root_hash") and meta.get("snapshot_root_hash") != current_root_hash:
251
254
  diagnostics.append("Files changed since the latest pack; refresh before trusting top included files.")
@@ -11,6 +11,24 @@ from agentpack.application.pack_service import AdapterRegistry
11
11
  from agentpack.commands._shared import console, _root
12
12
 
13
13
 
14
+ def _task_md_body(root) -> str:
15
+ path = root / ".agentpack" / "task.md"
16
+ if not path.exists():
17
+ return ""
18
+ try:
19
+ lines = [
20
+ line.strip()
21
+ for line in path.read_text(encoding="utf-8").splitlines()
22
+ if line.strip() and not line.startswith("#")
23
+ ]
24
+ except OSError:
25
+ return ""
26
+ body = lines[0] if lines else ""
27
+ if "Write or update the current coding task here." in body:
28
+ return ""
29
+ return body
30
+
31
+
14
32
  def register(app: typer.Typer) -> None:
15
33
  @app.command()
16
34
  def status() -> None:
@@ -32,12 +50,19 @@ def register(app: typer.Typer) -> None:
32
50
  )
33
51
  current = build_snapshot(scan_result.packable)
34
52
 
35
- if current["root_hash"] == meta.get("snapshot_root_hash"):
53
+ task_md = _task_md_body(root)
54
+ task_changed = bool(task_md and task_md != meta.get("task"))
55
+ if current["root_hash"] == meta.get("snapshot_root_hash") and not task_changed:
36
56
  console.print("[green]Context pack is up to date.[/]")
37
57
  console.print(f" Task: {meta.get('task')}")
38
58
  console.print(f" Generated: {meta.get('generated_at')}")
39
59
  else:
40
- console.print("[yellow]Context pack is STALE.[/] Files changed since last pack.")
60
+ if task_changed:
61
+ console.print("[yellow]Context pack is STALE.[/] .agentpack/task.md changed since last pack.")
62
+ console.print(f" Packed task: {meta.get('task')}")
63
+ console.print(f" Current task: {task_md}")
64
+ else:
65
+ console.print("[yellow]Context pack is STALE.[/] Files changed since last pack.")
41
66
  console.print(f" Last generated: {meta.get('generated_at')}")
42
67
  console.print(" Run [bold]agentpack pack[/] to refresh.")
43
68
  raise typer.Exit(1)
@@ -36,6 +36,11 @@ class SummaryConfig(BaseModel):
36
36
  schema_version: int = 1
37
37
 
38
38
 
39
+ class HooksConfig(BaseModel):
40
+ task_switch_detection: bool = True
41
+ task_switch_min_terms: int = 1
42
+
43
+
39
44
  class AgentConfig(BaseModel):
40
45
  output: str
41
46
  patch_claude_md: bool = False
@@ -78,6 +83,7 @@ class Config(BaseModel):
78
83
  project: ProjectConfig = Field(default_factory=ProjectConfig)
79
84
  context: ContextConfig = Field(default_factory=ContextConfig)
80
85
  summary: SummaryConfig = Field(default_factory=SummaryConfig)
86
+ hooks: HooksConfig = Field(default_factory=HooksConfig)
81
87
  agents: AgentsConfig = Field(default_factory=AgentsConfig)
82
88
  scoring: ScoringWeights = Field(default_factory=ScoringWeights)
83
89
 
@@ -105,6 +111,12 @@ include_tests = true
105
111
  include_configs = true
106
112
  include_receipts = true
107
113
 
114
+ [hooks]
115
+ # Claude UserPromptSubmit can detect a clearly different coding prompt,
116
+ # update .agentpack/task.md, and repack even if files did not change.
117
+ task_switch_detection = true
118
+ task_switch_min_terms = 1
119
+
108
120
  [scoring]
109
121
  # Scoring weights — higher wins budget allocation.
110
122
  # Tune these to make agentpack favour your team's file layout.
@@ -33,9 +33,9 @@ Pack repo context and immediately start working on the task.
33
33
 
34
34
  If a session is already running (`.agentpack/session.json` exists and `"active": true`):
35
35
 
36
- 1. Read `.agentpack/context.md` context is already fresh.
37
- 2. If the user gives a new coding task, write a one-line summary to `.agentpack/task.md`.
38
- 3. Re-read `.agentpack/context.md` after watch mode refreshes it (a few seconds).
36
+ 1. If the user gives a new coding task, write a one-line summary to `.agentpack/task.md`.
37
+ 2. Run `agentpack pack --task auto` unless watch mode already refreshed after the task write.
38
+ 3. Read `.agentpack/context.md` context now matches the current task.
39
39
  4. Proceed with the task using the context you just read.
40
40
 
41
41
  To start a session:
@@ -95,8 +95,9 @@ test -f .agentpack/config.toml || agentpack init --yes
95
95
  ### Step 3: Determine workflow
96
96
 
97
97
  **Session active** (`.agentpack/session.json` exists, `"active": true`):
98
- - Read `.agentpack/context.md`
99
98
  - Update `.agentpack/task.md` if task changed
99
+ - Run `agentpack pack --task auto` unless watch already refreshed it
100
+ - Read `.agentpack/context.md`
100
101
  - Proceed immediately
101
102
 
102
103
  **No session**:
@@ -10,6 +10,14 @@ _GEMINI_BLOCK = """\
10
10
  <!-- agentpack:block:start -->
11
11
  skills:
12
12
  - agentpack
13
+
14
+ At the start of every coding task:
15
+ 1. Write a one-line task summary to `.agentpack/task.md` (overwrite the whole file).
16
+ 2. Run `agentpack pack --task auto` to refresh context for that task.
17
+ 3. Read `.agent/skills/agentpack/SKILL.md`.
18
+ 4. Use files listed in context as starting points, but verify with actual code before editing.
19
+
20
+ When the user switches to a different coding task, repeat steps 1-3 before editing.
13
21
  <!-- agentpack:block:end -->"""
14
22
 
15
23
  _BLOCK_RE = re.compile(
@@ -15,6 +15,8 @@ At the start of every coding task:
15
15
  2. Run `agentpack pack --task auto` to refresh context.
16
16
  3. Read `.agentpack/context.md` — it lists the files most relevant to your task.
17
17
  4. Use those files as starting points, but verify with actual code before editing.
18
+
19
+ When the user switches to a different coding task, repeat steps 1-3 before editing.
18
20
  <!-- agentpack:end -->"""
19
21
 
20
22
  _BLOCK_RE = re.compile(
@@ -10,8 +10,10 @@ _CURSOR_RULE = """\
10
10
  <!-- agentpack:rule:start -->
11
11
  At the start of every coding task:
12
12
  1. Write a one-line task summary to `.agentpack/task.md` (overwrite the whole file).
13
- 2. Read `.agentpack/context.md` git hooks keep it fresh automatically.
14
- 3. Use files listed in context as starting points, but verify with actual code before editing.
13
+ 2. Run `agentpack pack --task auto` to refresh context for that task.
14
+ 3. Read `.agentpack/context.md`.
15
+ 4. Use files listed in context as starting points, but verify with actual code before editing.
16
+ When the user switches to a different coding task, repeat steps 1-3 before editing.
15
17
  If context is missing: `agentpack pack --agent cursor --task "<task>"`
16
18
  <!-- agentpack:rule:end -->"""
17
19
 
@@ -58,8 +60,11 @@ alwaysApply: true
58
60
  At the start of every coding task:
59
61
 
60
62
  1. Write a one-line task summary to `.agentpack/task.md` (overwrite the whole file).
61
- 2. Read `.agentpack/context.md` git hooks keep it fresh automatically.
62
- 3. Use files listed in context as starting points, but verify with actual code before editing.
63
+ 2. Run `agentpack pack --task auto` to refresh context for that task.
64
+ 3. Read `.agentpack/context.md`.
65
+ 4. Use files listed in context as starting points, but verify with actual code before editing.
66
+
67
+ When the user switches to a different coding task, repeat steps 1-3 before editing.
63
68
 
64
69
  If context is missing: `agentpack pack --agent cursor --task "<task>"`
65
70
  """
@@ -10,8 +10,10 @@ _WINDSURF_RULE = """\
10
10
  <!-- agentpack:rule:start -->
11
11
  At the start of every coding task:
12
12
  1. Write a one-line task summary to `.agentpack/task.md` (overwrite the whole file).
13
- 2. Read `.agentpack/context.md` git hooks keep it fresh automatically.
14
- 3. Use files listed in context as starting points, but verify with actual code before editing.
13
+ 2. Run `agentpack pack --task auto` to refresh context for that task.
14
+ 3. Read `.agentpack/context.md`.
15
+ 4. Use files listed in context as starting points, but verify with actual code before editing.
16
+ When the user switches to a different coding task, repeat steps 1-3 before editing.
15
17
  If context is missing: `agentpack pack --agent windsurf --task "<task>"`
16
18
  <!-- agentpack:rule:end -->"""
17
19
 
@@ -104,8 +104,9 @@ def render_claude(pack: ContextPack) -> str:
104
104
  "Priority order: changed files → keyword-matched files → dependencies → summaries.\n"
105
105
  "Files marked `full` contain complete source. Files marked `symbols` contain relevant "
106
106
  "function/class bodies. Files marked `summary` are unchanged context.\n"
107
- "If the pack looks stale (changed files list is empty but you expect changes), "
108
- "ask the user to run `agentpack pack --task \"<task>\"` to refresh."
107
+ "If this pack's task does not match the user's current task, write the new task to "
108
+ "`.agentpack/task.md`, run `agentpack pack --task auto`, re-read the context, then proceed. "
109
+ "If the pack looks stale (changed files list is empty but you expect changes), refresh the pack before editing."
109
110
  )
110
111
  sections.append("")
111
112
 
File without changes