agent-session-analytics 0.2.0__tar.gz → 0.2.1__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.
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/.gitignore +3 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/CHANGELOG.md +8 -1
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/PKG-INFO +5 -3
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/README.md +4 -2
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/pyproject.toml +1 -1
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/cursor.py +1 -1
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_db.py +12 -2
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/references/canonical-schema.md +3 -2
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/references/harness-coverage.md +1 -1
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/conftest.py +1 -1
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_paths.py +41 -2
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/LICENSE +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/__init__.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/__main__.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/__init__.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/_jsonl.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/claude.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/codex.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/copilot.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_adapters/pi_family.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_cli.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_emitters.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_ingest.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_query.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_tables.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/py.typed +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/references/query-conventions.md +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/__init__.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_cli.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_codex_envelope.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_codex_ingest.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_duckdb_settings_edges.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_flattened_equivalence.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_flattened_tables.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_malformed_rows.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_pi_family_tokens.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_user_turns.py +0 -0
- {agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_user_turns_edges.py +0 -0
|
@@ -13,6 +13,9 @@ __pycache__/
|
|
|
13
13
|
.cheese/
|
|
14
14
|
.context/
|
|
15
15
|
|
|
16
|
+
# Skill archives: build output. `just build` writes them; CI publishes them on dist (ADR-012).
|
|
17
|
+
skills/*/scripts/*.pyz
|
|
18
|
+
|
|
16
19
|
# Skill-creator iteration workspaces (eval runs, benchmarks, viewer HTML)
|
|
17
20
|
skills/*-workspace/
|
|
18
21
|
|
|
@@ -13,6 +13,12 @@ Push a `agent-session-analytics-v<version>` tag after the release commit merges.
|
|
|
13
13
|
|
|
14
14
|
## [Unreleased]
|
|
15
15
|
|
|
16
|
+
## [0.2.1] - 2026-10-25
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The default database path is now `<cache>/agent-session-analytics/sessions.duckdb`, not `<cache>/dotfiles/session-analytics/sessions.duckdb`. If the new file does not exist and the legacy file exists, the package keeps using the legacy file. It never moves, copies, or deletes the legacy file. `SESSIONS_DB` still overrides both.
|
|
21
|
+
|
|
16
22
|
## [0.2.0] - 2026-10-24
|
|
17
23
|
|
|
18
24
|
### Changed
|
|
@@ -61,6 +67,7 @@ Push a `agent-session-analytics-v<version>` tag after the release commit merges.
|
|
|
61
67
|
|
|
62
68
|
- The canned `query.sh` reports. Callers write their own SQL with `sql`.
|
|
63
69
|
|
|
64
|
-
[Unreleased]: https://github.com/paulnsorensen/skillz-that-grillz/compare/agent-session-analytics-v0.2.
|
|
70
|
+
[Unreleased]: https://github.com/paulnsorensen/skillz-that-grillz/compare/agent-session-analytics-v0.2.1...HEAD
|
|
71
|
+
[0.2.1]: https://github.com/paulnsorensen/skillz-that-grillz/compare/agent-session-analytics-v0.2.0...agent-session-analytics-v0.2.1
|
|
65
72
|
[0.2.0]: https://github.com/paulnsorensen/skillz-that-grillz/compare/agent-session-analytics-v0.1.0...agent-session-analytics-v0.2.0
|
|
66
73
|
[0.1.0]: https://github.com/paulnsorensen/skillz-that-grillz/releases/tag/agent-session-analytics-v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-session-analytics
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Ingest coding-agent session logs into DuckDB and query them read-only.
|
|
5
5
|
Project-URL: Homepage, https://github.com/paulnsorensen/skillz-that-grillz/tree/main/packages/agent-session-analytics
|
|
6
6
|
Project-URL: Source, https://github.com/paulnsorensen/skillz-that-grillz/tree/main/packages/agent-session-analytics
|
|
@@ -96,8 +96,10 @@ A consumer that queries the database directly runs
|
|
|
96
96
|
- `SESSIONS_DB` names the database. A relative value joins the current
|
|
97
97
|
directory. The package does not expand `~` in a set value.
|
|
98
98
|
- Otherwise an absolute `XDG_CACHE_HOME` selects
|
|
99
|
-
`<cache>/
|
|
100
|
-
value uses `~/.cache`.
|
|
99
|
+
`<cache>/agent-session-analytics/sessions.duckdb`. A relative or unset
|
|
100
|
+
value uses `~/.cache`. If that file does not exist and the legacy
|
|
101
|
+
`<cache>/dotfiles/session-analytics/sessions.duckdb` exists, the package uses
|
|
102
|
+
the legacy file. It never moves, copies, or deletes the legacy file.
|
|
101
103
|
- `SESSIONS_DUCKDB_MEMORY_LIMIT` sets the DuckDB memory limit, from 16MiB up to
|
|
102
104
|
but not including 16EiB. The default is `4GB`.
|
|
103
105
|
- `SESSIONS_DUCKDB_THREADS` sets the DuckDB thread count, from 1 to 256. The
|
|
@@ -73,8 +73,10 @@ A consumer that queries the database directly runs
|
|
|
73
73
|
- `SESSIONS_DB` names the database. A relative value joins the current
|
|
74
74
|
directory. The package does not expand `~` in a set value.
|
|
75
75
|
- Otherwise an absolute `XDG_CACHE_HOME` selects
|
|
76
|
-
`<cache>/
|
|
77
|
-
value uses `~/.cache`.
|
|
76
|
+
`<cache>/agent-session-analytics/sessions.duckdb`. A relative or unset
|
|
77
|
+
value uses `~/.cache`. If that file does not exist and the legacy
|
|
78
|
+
`<cache>/dotfiles/session-analytics/sessions.duckdb` exists, the package uses
|
|
79
|
+
the legacy file. It never moves, copies, or deletes the legacy file.
|
|
78
80
|
- `SESSIONS_DUCKDB_MEMORY_LIMIT` sets the DuckDB memory limit, from 16MiB up to
|
|
79
81
|
but not including 16EiB. The default is `4GB`.
|
|
80
82
|
- `SESSIONS_DUCKDB_THREADS` sets the DuckDB thread count, from 1 to 256. The
|
|
@@ -52,7 +52,7 @@ def _cursor_resolve_slug(slug: str, fs_root: str = "/") -> str:
|
|
|
52
52
|
"""Decode a dash-encoded absolute path, preferring the longest existing dir.
|
|
53
53
|
|
|
54
54
|
Cursor project slugs replace path separators with dashes
|
|
55
|
-
(``Users-
|
|
55
|
+
(``Users-alex-src-app``). A directory whose own name contains a
|
|
56
56
|
dash is ambiguous if every dash is treated as a separator. Walk
|
|
57
57
|
left-to-right and at each step take the longest prefix of remaining
|
|
58
58
|
segments that exists under ``fs_root``; fall back to a naive split for
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_db.py
RENAMED
|
@@ -36,8 +36,18 @@ def _xdg_cache_home() -> str:
|
|
|
36
36
|
return os.path.abspath(os.path.expanduser("~/.cache"))
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
def _default_db_path() -> str:
|
|
40
|
+
# The legacy `dotfiles/` cache keeps working when it is the only one that
|
|
41
|
+
# exists. Nothing moves, copies, or deletes it; another tool may share it.
|
|
42
|
+
cache = _xdg_cache_home()
|
|
43
|
+
path = os.path.join(cache, "agent-session-analytics", "sessions.duckdb")
|
|
44
|
+
legacy = os.path.join(cache, "dotfiles", "session-analytics", "sessions.duckdb")
|
|
45
|
+
if not os.path.exists(path) and os.path.exists(legacy):
|
|
46
|
+
return legacy
|
|
47
|
+
return path
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
DB_PATH = configured_path("SESSIONS_DB", _default_db_path())
|
|
41
51
|
DB_DIR = os.path.dirname(DB_PATH)
|
|
42
52
|
DB_TMP_PATH = f"{DB_PATH}.tmp"
|
|
43
53
|
# Derived from the database file name, not DB_DIR, so a SESSIONS_DB pointed
|
|
@@ -4,8 +4,9 @@ Contents: [tool_uses](#tool_uses), [tool_results](#tool_results), [stop_events](
|
|
|
4
4
|
|
|
5
5
|
The shape every harness adapter normalizes into and every analytics pack queries
|
|
6
6
|
against. `agent-session-analytics ingest` loads one canonical row shape (the Claude envelope plus a
|
|
7
|
-
`harness` tag) into `${XDG_CACHE_HOME:-$HOME/.cache}/
|
|
8
|
-
the tables below.
|
|
7
|
+
`harness` tag) into `${XDG_CACHE_HOME:-$HOME/.cache}/agent-session-analytics/sessions.duckdb` by default, then flattens it into
|
|
8
|
+
the tables below. If that file is absent, an existing legacy `dotfiles/session-analytics/` database is used.
|
|
9
|
+
**Pack authors: write SQL against these tables; never reach
|
|
9
10
|
into a harness's native format.**
|
|
10
11
|
|
|
11
12
|
Every session-scoped table carries a `harness` column
|
|
@@ -133,7 +133,7 @@ slashes).
|
|
|
133
133
|
A directory whose own name contains a dash is ambiguous if every dash
|
|
134
134
|
is a separator.
|
|
135
135
|
So the adapter walks left-to-right. At each step it takes the longest prefix of remaining segments that exists on disk
|
|
136
|
-
(`Users-
|
|
136
|
+
(`Users-alex-src-app` → `/Users/alex/src/app` when that path is
|
|
137
137
|
real). Unresolved tails fall back to a naive split. `gitBranch` is always null
|
|
138
138
|
(not recorded).
|
|
139
139
|
|
|
@@ -27,7 +27,7 @@ class Sandbox:
|
|
|
27
27
|
|
|
28
28
|
@property
|
|
29
29
|
def database(self) -> Path:
|
|
30
|
-
return self.root / "cache" / "
|
|
30
|
+
return self.root / "cache" / "agent-session-analytics" / "sessions.duckdb"
|
|
31
31
|
|
|
32
32
|
def run(
|
|
33
33
|
self, *args: str, extra_env: dict[str, str] | None = None
|
|
@@ -36,7 +36,7 @@ def eval_ingest(expr: str, env: dict[str, str], cwd: Path, *argv: str) -> str:
|
|
|
36
36
|
return result.stdout.strip()
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
DEFAULT = "home/.cache/
|
|
39
|
+
DEFAULT = "home/.cache/agent-session-analytics/sessions.duckdb"
|
|
40
40
|
|
|
41
41
|
DB_PATH_CASES: dict[str, tuple[Callable[[Path], dict[str, str]], Callable[[Path], str]]] = {
|
|
42
42
|
"sessions_db_absolute": (
|
|
@@ -53,7 +53,7 @@ DB_PATH_CASES: dict[str, tuple[Callable[[Path], dict[str, str]], Callable[[Path]
|
|
|
53
53
|
),
|
|
54
54
|
"xdg_cache_absolute": (
|
|
55
55
|
lambda root: {"XDG_CACHE_HOME": str(root / "cache")},
|
|
56
|
-
lambda root: str(root / "cache" / "
|
|
56
|
+
lambda root: str(root / "cache" / "agent-session-analytics" / "sessions.duckdb"),
|
|
57
57
|
),
|
|
58
58
|
"xdg_cache_relative_is_ignored": (
|
|
59
59
|
lambda root: {"XDG_CACHE_HOME": "rel/c"},
|
|
@@ -92,6 +92,45 @@ def test_db_path_command_honours_sessions_db(sandbox: Sandbox) -> None:
|
|
|
92
92
|
assert json.loads(result.stdout) == {"path": str(override)}
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
def _touch(path: Path) -> None:
|
|
96
|
+
path.parent.mkdir(parents=True)
|
|
97
|
+
_ = path.write_text("")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _db_path(sandbox: Sandbox, extra_env: dict[str, str] | None = None) -> str:
|
|
101
|
+
result = sandbox.run("db-path", extra_env=extra_env)
|
|
102
|
+
assert result.returncode == 0, result.stderr
|
|
103
|
+
return cast(str, json.loads(result.stdout)["path"])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_default_is_the_new_path_when_nothing_exists(sandbox: Sandbox) -> None:
|
|
107
|
+
assert _db_path(sandbox) == str(sandbox.database)
|
|
108
|
+
assert "dotfiles" not in sandbox.database.parts
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_default_falls_back_to_the_legacy_path_when_only_it_exists(sandbox: Sandbox) -> None:
|
|
112
|
+
legacy = sandbox.root / "cache" / "dotfiles" / "session-analytics" / "sessions.duckdb"
|
|
113
|
+
_touch(legacy)
|
|
114
|
+
assert _db_path(sandbox) == str(legacy)
|
|
115
|
+
assert legacy.exists()
|
|
116
|
+
assert not sandbox.database.exists()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_default_prefers_the_new_path_when_both_exist(sandbox: Sandbox) -> None:
|
|
120
|
+
legacy = sandbox.root / "cache" / "dotfiles" / "session-analytics" / "sessions.duckdb"
|
|
121
|
+
_touch(legacy)
|
|
122
|
+
_touch(sandbox.database)
|
|
123
|
+
assert _db_path(sandbox) == str(sandbox.database)
|
|
124
|
+
assert legacy.exists()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_sessions_db_beats_an_existing_legacy_file(sandbox: Sandbox) -> None:
|
|
128
|
+
legacy = sandbox.root / "cache" / "dotfiles" / "session-analytics" / "sessions.duckdb"
|
|
129
|
+
_touch(legacy)
|
|
130
|
+
override = sandbox.root / "elsewhere.duckdb"
|
|
131
|
+
assert _db_path(sandbox, {"SESSIONS_DB": str(override)}) == str(override)
|
|
132
|
+
|
|
133
|
+
|
|
95
134
|
NAMES = ("CLAUDE_CONFIG_DIR", "CODEX_HOME", "CURSOR_HOME")
|
|
96
135
|
|
|
97
136
|
|
|
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
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/src/agent_session_analytics/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_codex_envelope.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_duckdb_settings_edges.py
RENAMED
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_flattened_equivalence.py
RENAMED
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_flattened_tables.py
RENAMED
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_malformed_rows.py
RENAMED
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_pi_family_tokens.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_session_analytics-0.2.0 → agent_session_analytics-0.2.1}/tests/test_user_turns_edges.py
RENAMED
|
File without changes
|