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.
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/PKG-INFO +19 -3
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/README.md +18 -2
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/pyproject.toml +1 -1
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/__init__.py +1 -1
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/hook_cmd.py +28 -3
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/pack.py +13 -2
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/git.py +64 -27
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/.gitignore +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/LICENSE +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/antigravity.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/base.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/claude.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/codex.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/cursor.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/detect.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/generic.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/adapters/windsurf.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/dependency_graph.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/go_imports.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/java_imports.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/js_ts_imports.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/python_imports.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/ranking.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/rust_imports.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/symbols.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/analysis/tests.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/application/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/application/pack_service.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/cli.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/_shared.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/benchmark.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/claude_cmd.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/diff.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/doctor.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/explain.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/init.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/install.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/mcp_cmd.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/monitor.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/scan.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/stats.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/status.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/summarize.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/commands/watch.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/bootstrap.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/cache.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/config.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/context_pack.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/diff.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/git_hooks.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/global_install.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/ignore.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/merkle.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/models.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/redactor.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/scanner.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/snapshot.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/token_estimator.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/core/vscode_tasks.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/data/agentpack.md +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/antigravity.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/claude.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/codex.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/cursor.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/installers/windsurf.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/git_hooks.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/global_install.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/integrations/vscode_tasks.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/mcp_server.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/compact.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/markdown.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/renderers/receipts.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/session/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/session/state.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/summaries/__init__.py +0 -0
- {agentpack_cli-0.1.19 → agentpack_cli-0.1.20}/src/agentpack/summaries/base.py +0 -0
- {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.
|
|
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
|
[](https://opensource.org/licenses/MIT)
|
|
45
45
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
46
46
|
|
|
47
|
-
> **Status: alpha (v0.1.
|
|
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
|
|
1301
|
+
Priority order (strongest → weakest):
|
|
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
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
|
-
> **Status: alpha (v0.1.
|
|
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
|
|
1262
|
+
Priority order (strongest → weakest):
|
|
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,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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
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
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
if
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
|
143
|
-
return f"{
|
|
144
|
-
if
|
|
145
|
-
return
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|