agentpack-cli 0.1.19__tar.gz → 0.1.20__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 (84) hide show
  1. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/PKG-INFO +19 -3
  2. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/README.md +18 -2
  3. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/pyproject.toml +1 -1
  4. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/__init__.py +1 -1
  5. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/hook_cmd.py +28 -3
  6. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/pack.py +13 -2
  7. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/git.py +64 -27
  8. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/.gitignore +0 -0
  9. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/LICENSE +0 -0
  10. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/__init__.py +0 -0
  11. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/antigravity.py +0 -0
  12. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/base.py +0 -0
  13. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/claude.py +0 -0
  14. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/codex.py +0 -0
  15. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/cursor.py +0 -0
  16. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/detect.py +0 -0
  17. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/generic.py +0 -0
  18. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/windsurf.py +0 -0
  19. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/__init__.py +0 -0
  20. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/dependency_graph.py +0 -0
  21. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/go_imports.py +0 -0
  22. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/java_imports.py +0 -0
  23. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/js_ts_imports.py +0 -0
  24. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/python_imports.py +0 -0
  25. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/ranking.py +0 -0
  26. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/rust_imports.py +0 -0
  27. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/symbols.py +0 -0
  28. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/tests.py +0 -0
  29. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/application/__init__.py +0 -0
  30. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/application/pack_service.py +0 -0
  31. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/cli.py +0 -0
  32. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/__init__.py +0 -0
  33. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/_shared.py +0 -0
  34. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/benchmark.py +0 -0
  35. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/claude_cmd.py +0 -0
  36. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/diff.py +0 -0
  37. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/doctor.py +0 -0
  38. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/explain.py +0 -0
  39. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/init.py +0 -0
  40. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/install.py +0 -0
  41. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/mcp_cmd.py +0 -0
  42. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/monitor.py +0 -0
  43. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/scan.py +0 -0
  44. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/stats.py +0 -0
  45. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/status.py +0 -0
  46. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/summarize.py +0 -0
  47. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/watch.py +0 -0
  48. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/__init__.py +0 -0
  49. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/bootstrap.py +0 -0
  50. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/cache.py +0 -0
  51. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/config.py +0 -0
  52. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/context_pack.py +0 -0
  53. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/diff.py +0 -0
  54. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/git_hooks.py +0 -0
  55. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/global_install.py +0 -0
  56. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/ignore.py +0 -0
  57. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/merkle.py +0 -0
  58. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/models.py +0 -0
  59. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/redactor.py +0 -0
  60. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/scanner.py +0 -0
  61. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/snapshot.py +0 -0
  62. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/token_estimator.py +0 -0
  63. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/vscode_tasks.py +0 -0
  64. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/data/agentpack.md +0 -0
  65. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/__init__.py +0 -0
  66. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/antigravity.py +0 -0
  67. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/claude.py +0 -0
  68. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/codex.py +0 -0
  69. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/cursor.py +0 -0
  70. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/windsurf.py +0 -0
  71. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/__init__.py +0 -0
  72. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/git_hooks.py +0 -0
  73. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/global_install.py +0 -0
  74. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/vscode_tasks.py +0 -0
  75. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/mcp_server.py +0 -0
  76. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/__init__.py +0 -0
  77. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/compact.py +0 -0
  78. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/markdown.py +0 -0
  79. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/receipts.py +0 -0
  80. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/session/__init__.py +0 -0
  81. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/session/state.py +0 -0
  82. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/summaries/__init__.py +0 -0
  83. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/summaries/base.py +0 -0
  84. {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/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.19
3
+ Version: 0.1.20
4
4
  Summary: Token-aware context packing for AI coding agents — Claude, Cursor, Windsurf, and Codex
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.19).** 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.20).** 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
 
@@ -1298,7 +1298,23 @@ Skip writing a task description — agentpack infers it from your branch name, c
1298
1298
  agentpack pack --task auto
1299
1299
  ```
1300
1300
 
1301
- Priority order: branch name changed file paths → recent commit message. The more descriptive your branch names (`feat/add-rate-limiting` beats `dev`), the better the inferred task.
1301
+ Priority order (strongestweakest):
1302
+
1303
+ | Source | Example output |
1304
+ |--------|---------------|
1305
+ | `task.md` (explicit) | `"migrate DB schema to multi-tenant"` |
1306
+ | branch + staged files | `"feat add-rate-limiting: payments, throttle"` |
1307
+ | staged files only | `"payments, throttle"` |
1308
+ | branch + unstaged | `"feat add-rate-limiting: session, token"` |
1309
+ | branch + latest commit | `"feat add-rate-limiting: fix token expiry check"` |
1310
+ | branch name alone | `"feat add-rate-limiting"` |
1311
+ | unstaged files | `"session, token"` |
1312
+ | recent commit messages | `"fix token expiry check; add pagination"` |
1313
+ | recently modified files | `"session, payments"` (noisy — last resort) |
1314
+
1315
+ The heuristic that fired is logged: `Auto task (branch+staged): feat add-rate-limiting: payments`.
1316
+
1317
+ The more descriptive your branch names (`feat/add-rate-limiting` beats `dev`) and the more you stage before running, the more accurate the inference.
1302
1318
 
1303
1319
  ### Concept synonym expansion
1304
1320
 
@@ -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.19).** 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.20).** 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
 
@@ -1259,7 +1259,23 @@ Skip writing a task description — agentpack infers it from your branch name, c
1259
1259
  agentpack pack --task auto
1260
1260
  ```
1261
1261
 
1262
- Priority order: branch name changed file paths → recent commit message. The more descriptive your branch names (`feat/add-rate-limiting` beats `dev`), the better the inferred task.
1262
+ Priority order (strongestweakest):
1263
+
1264
+ | Source | Example output |
1265
+ |--------|---------------|
1266
+ | `task.md` (explicit) | `"migrate DB schema to multi-tenant"` |
1267
+ | branch + staged files | `"feat add-rate-limiting: payments, throttle"` |
1268
+ | staged files only | `"payments, throttle"` |
1269
+ | branch + unstaged | `"feat add-rate-limiting: session, token"` |
1270
+ | branch + latest commit | `"feat add-rate-limiting: fix token expiry check"` |
1271
+ | branch name alone | `"feat add-rate-limiting"` |
1272
+ | unstaged files | `"session, token"` |
1273
+ | recent commit messages | `"fix token expiry check; add pagination"` |
1274
+ | recently modified files | `"session, payments"` (noisy — last resort) |
1275
+
1276
+ The heuristic that fired is logged: `Auto task (branch+staged): feat add-rate-limiting: payments`.
1277
+
1278
+ The more descriptive your branch names (`feat/add-rate-limiting` beats `dev`) and the more you stage before running, the more accurate the inference.
1263
1279
 
1264
1280
  ### Concept synonym expansion
1265
1281
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentpack-cli"
3
- version = "0.1.19"
3
+ version = "0.1.20"
4
4
  description = "Token-aware context packing for AI coding agents — Claude, Cursor, Windsurf, and Codex"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """AgentPack — token-aware context packing for AI coding agents."""
2
2
 
3
- __version__ = "0.1.19"
3
+ __version__ = "0.1.20"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import re
4
5
  import subprocess
5
6
  import sys
6
7
  from pathlib import Path
@@ -8,9 +9,14 @@ from pathlib import Path
8
9
  import typer
9
10
 
10
11
  from agentpack.commands._shared import _root
12
+ from agentpack.core import git as _git
11
13
 
12
14
  _TASK_FILE = ".agentpack/task.md"
13
15
  _TASK_FILE_DEFAULT_MARKER = "Write or update the current coding task here."
16
+ _CODING_PROMPT_RE = re.compile(
17
+ r"(?:fix|add|refactor|impl|implement|update|write|debug|test|build|migrate|remove|delete|rename|optimize)\b",
18
+ re.IGNORECASE,
19
+ )
14
20
 
15
21
 
16
22
  def register(app: typer.Typer) -> None:
@@ -69,12 +75,22 @@ def _load_task_md(root: Path) -> str:
69
75
  return ""
70
76
 
71
77
 
78
+ def _looks_like_coding_prompt(prompt: str) -> bool:
79
+ """Return True if prompt looks like a coding task (not a slash command or chat)."""
80
+ stripped = prompt.strip()
81
+ if stripped.startswith("/"):
82
+ return False
83
+ return bool(_CODING_PROMPT_RE.search(stripped))
84
+
85
+
72
86
  def _resolve_task(root: Path, prompt: str) -> str:
73
87
  """Merge task.md + prompt into best task description for repack."""
74
88
  task_md = _load_task_md(root)
75
89
  if task_md:
76
90
  return task_md
77
- return prompt[:200].strip() if prompt else "auto"
91
+ if prompt and _looks_like_coding_prompt(prompt):
92
+ return prompt[:200].strip()
93
+ return "auto"
78
94
 
79
95
 
80
96
  def _load_hints(root: Path, n: int = 5) -> list[dict]:
@@ -116,6 +132,15 @@ def _load_pack_task(root: Path) -> str:
116
132
  return ""
117
133
 
118
134
 
135
+ def _infer_live_task(root: Path) -> str:
136
+ """Live task: git priority chain (no stale metadata). Falls back to 'unknown'."""
137
+ try:
138
+ task, _ = _git.infer_task_with_source(root)
139
+ return task
140
+ except Exception:
141
+ return "unknown"
142
+
143
+
119
144
  def _current_root_hash(root: Path) -> str | None:
120
145
  snap = root / ".agentpack" / "snapshots" / "latest.json"
121
146
  if not snap.exists():
@@ -179,7 +204,7 @@ def _run_user_prompt_submit(root: Path) -> None:
179
204
  for h in hints
180
205
  )
181
206
  status_note = "(repacking — call pack_context for fresh results)" if repo_changed else "(index fresh)"
182
- current_task = _load_task_md(root) or _load_pack_task(root) or "unknown"
207
+ current_task = _load_task_md(root) or _infer_live_task(root)
183
208
  msg = (
184
209
  f"AgentPack {status_note}\n"
185
210
  f"task: {current_task}\n"
@@ -193,7 +218,7 @@ def _run_user_prompt_submit(root: Path) -> None:
193
218
  )
194
219
  else:
195
220
  hints = _load_hints(root, n=8)
196
- current_task = _load_task_md(root) or _load_pack_task(root) or "unknown"
221
+ current_task = _load_task_md(root) or _infer_live_task(root)
197
222
  if hints:
198
223
  files_lines = "\n".join(
199
224
  f" - {h['path']}" + (f" — {h['why']}" if h.get("why") else "")
@@ -64,8 +64,19 @@ def _resolve_agent(agent: str) -> str:
64
64
  def _resolve_task(task: str) -> str:
65
65
  if task != "auto":
66
66
  return task
67
- inferred = git.infer_task_from_git(_root())
68
- console.print(f"[dim]Auto task: {inferred}[/]")
67
+ root = _root()
68
+ # task.md takes priority over all git heuristics
69
+ task_md_path = root / ".agentpack" / "task.md"
70
+ if task_md_path.exists():
71
+ content = task_md_path.read_text(encoding="utf-8").strip()
72
+ lines = [ln for ln in content.splitlines() if ln.strip() and not ln.startswith("#")]
73
+ body = lines[0].strip() if lines else ""
74
+ _PLACEHOLDER = "Write or update the current coding task here."
75
+ if body and _PLACEHOLDER not in body:
76
+ console.print(f"[dim]Auto task (task.md): {body}[/]")
77
+ return body
78
+ inferred, source = git.infer_task_with_source(root)
79
+ console.print(f"[dim]Auto task ({source}): {inferred}[/]")
69
80
  return inferred
70
81
 
71
82
 
@@ -94,12 +94,44 @@ def file_churn_counts(root: Path, max_commits: int = 200) -> dict[str, int]:
94
94
  return counts
95
95
 
96
96
 
97
- def infer_task_from_git(root: Path) -> str:
98
- """Infer a task description from branch name, changed files, and recent commits.
99
-
100
- Priority: branch name + changed files → branch name → changed files →
101
- recent commit messages (up to 3) → recently modified files → fallback.
97
+ def staged_files(root: Path) -> set[str]:
98
+ """Files staged for commit (git index only)."""
99
+ out = _run(["git", "diff", "--cached", "--name-only"], root)
100
+ if not out:
101
+ return set()
102
+ return {line.strip() for line in out.splitlines() if line.strip()}
103
+
104
+
105
+ def infer_task_with_source(root: Path) -> tuple[str, str]:
106
+ """Infer task description with the heuristic that fired.
107
+
108
+ Priority (strongest → weakest):
109
+ branch+staged staged files present + branch name
110
+ staged staged files, no branch
111
+ branch+unstaged unstaged changes + branch name
112
+ branch+commit branch + latest commit message
113
+ branch branch name alone
114
+ unstaged unstaged changes, no branch
115
+ commits recent commit messages
116
+ recently_modified git log history (noisy — last resort)
117
+ fallback "general development"
102
118
  """
119
+ if not is_git_repo(root):
120
+ return "general development", "fallback"
121
+
122
+ staged = staged_files(root)
123
+
124
+ unstaged_out = _run(["git", "diff", "--name-only"], root)
125
+ unstaged: set[str] = set()
126
+ if unstaged_out:
127
+ for line in unstaged_out.splitlines():
128
+ line = line.strip()
129
+ if line:
130
+ unstaged.add(line)
131
+
132
+ staged_topic = _topic_from_paths(staged) if staged else None
133
+ unstaged_topic = _topic_from_paths(unstaged) if unstaged else None
134
+
103
135
  branch: str | None = None
104
136
  branch_out = _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], root)
105
137
  if branch_out:
@@ -108,11 +140,6 @@ def infer_task_from_git(root: Path) -> str:
108
140
  slug = b.split("/", 1)[-1]
109
141
  branch = slug.replace("-", " ").replace("_", " ")
110
142
 
111
- # Changed files are the strongest signal for *current* work
112
- changed = changed_files(root)
113
- file_topic = _topic_from_paths(changed) if changed else None
114
-
115
- # Collect recent non-merge commit messages (up to 3) for richer fallback
116
143
  commit_msgs: list[str] = []
117
144
  log_out = _run(["git", "log", "--oneline", "-10"], root)
118
145
  if log_out:
@@ -126,26 +153,36 @@ def infer_task_from_git(root: Path) -> str:
126
153
  if len(commit_msgs) == 3:
127
154
  break
128
155
 
129
- # When branch is clean (no changed files), fall back to recently touched files
130
- # so keyword scoring has something to work with beyond the branch name alone.
131
- if not file_topic and not branch:
132
- recent = recently_modified_files(root, n=10)
133
- file_topic = _topic_from_paths(set(recent)) if recent else None
134
-
135
- if branch and file_topic:
136
- return f"{branch}: {file_topic}"
156
+ if branch and staged_topic:
157
+ return f"{branch}: {staged_topic}", "branch+staged"
158
+ if staged_topic:
159
+ return staged_topic, "staged"
160
+ if branch and unstaged_topic:
161
+ return f"{branch}: {unstaged_topic}", "branch+unstaged"
137
162
  if branch and commit_msgs:
138
- # Augment bare branch name with latest commit context
139
- return f"{branch}: {commit_msgs[0]}"
163
+ return f"{branch}: {commit_msgs[0]}", "branch+commit"
140
164
  if branch:
141
- return branch
142
- if file_topic and commit_msgs:
143
- return f"{file_topic}: {commit_msgs[0]}"
144
- if file_topic:
145
- return file_topic
165
+ return branch, "branch"
166
+ if unstaged_topic and commit_msgs:
167
+ return f"{unstaged_topic}: {commit_msgs[0]}", "unstaged+commit"
168
+ if unstaged_topic:
169
+ return unstaged_topic, "unstaged"
146
170
  if commit_msgs:
147
- return "; ".join(commit_msgs[:2])
148
- return "general development"
171
+ return "; ".join(commit_msgs[:2]), "commits"
172
+
173
+ # Last resort: historical git log — only fires when no live signal found
174
+ recent = recently_modified_files(root, n=10)
175
+ recent_topic = _topic_from_paths(set(recent)) if recent else None
176
+ if recent_topic:
177
+ return recent_topic, "recently_modified"
178
+
179
+ return "general development", "fallback"
180
+
181
+
182
+ def infer_task_from_git(root: Path) -> str:
183
+ """Infer a task description from git state. See infer_task_with_source for priority chain."""
184
+ task, _ = infer_task_with_source(root)
185
+ return task
149
186
 
150
187
 
151
188
  def _topic_from_paths(paths: set[str]) -> str | None:
File without changes