claude-session-status 0.1.0__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.
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-session-status
3
+ Version: 0.1.0
4
+ Summary: Track warm/cold status of Claude Code sessions in a dedicated DuckDB file
5
+ Project-URL: Homepage, https://github.com/keith-fajardo/claude-session-status
6
+ Project-URL: Repository, https://github.com/keith-fajardo/claude-session-status
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: duckdb>=0.9
10
+
11
+ # claude-session-status
12
+
13
+ Tracks warm/cold status of Claude Code sessions in a dedicated DuckDB file — separate from `claude-session-logger` to avoid file-lock conflicts.
14
+
15
+ ## Why
16
+
17
+ `claude-session-logger` writes to `sessions.duckdb` at session end. If a TUI holds a persistent read-only connection to that file, the write blocks. This package uses its own DB (`~/.claude/claude-session-status/status.duckdb`) and **never holds an open connection between operations**.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install claude-session-status
23
+ ```
24
+
25
+ ## Hook integration
26
+
27
+ Add to `~/.claude/settings.json`:
28
+
29
+ ```json
30
+ {
31
+ "hooks": {
32
+ "SessionStart": [
33
+ {"type": "command", "command": "claude-session-status mark-warm"}
34
+ ],
35
+ "SessionEnd": [
36
+ {"type": "command", "command": "claude-session-status mark-cold"}
37
+ ]
38
+ }
39
+ }
40
+ ```
41
+
42
+ Claude Code pipes session JSON to stdin; the scripts extract `session_id`, `project`, and `cwd` automatically.
43
+
44
+ ## Commands
45
+
46
+ ```bash
47
+ # Live-refresh table of warm/cold sessions (Ctrl-C to quit)
48
+ claude-session-status watch
49
+
50
+ # Called by hooks (reads JSON from stdin)
51
+ claude-session-status mark-warm
52
+ claude-session-status mark-cold
53
+ ```
54
+
55
+ ## DB schema
56
+
57
+ ```sql
58
+ CREATE TABLE session_status (
59
+ session_id TEXT PRIMARY KEY,
60
+ project TEXT,
61
+ cwd TEXT,
62
+ started_at TIMESTAMP,
63
+ updated_at TIMESTAMP,
64
+ status TEXT -- 'warm' | 'cold'
65
+ )
66
+ ```
67
+
68
+ DB location: `~/.claude/claude-session-status/status.duckdb`
69
+
70
+ ## Design
71
+
72
+ - Runtime dependency: `duckdb` only
73
+ - Every DB access: open → read/write → close. No persistent connections.
74
+ - `mark-warm` / `mark-cold`: silent on success, errors go to stderr (never block Claude Code)
75
+ - 3-retry lock backoff on connect
@@ -0,0 +1,65 @@
1
+ # claude-session-status
2
+
3
+ Tracks warm/cold status of Claude Code sessions in a dedicated DuckDB file — separate from `claude-session-logger` to avoid file-lock conflicts.
4
+
5
+ ## Why
6
+
7
+ `claude-session-logger` writes to `sessions.duckdb` at session end. If a TUI holds a persistent read-only connection to that file, the write blocks. This package uses its own DB (`~/.claude/claude-session-status/status.duckdb`) and **never holds an open connection between operations**.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install claude-session-status
13
+ ```
14
+
15
+ ## Hook integration
16
+
17
+ Add to `~/.claude/settings.json`:
18
+
19
+ ```json
20
+ {
21
+ "hooks": {
22
+ "SessionStart": [
23
+ {"type": "command", "command": "claude-session-status mark-warm"}
24
+ ],
25
+ "SessionEnd": [
26
+ {"type": "command", "command": "claude-session-status mark-cold"}
27
+ ]
28
+ }
29
+ }
30
+ ```
31
+
32
+ Claude Code pipes session JSON to stdin; the scripts extract `session_id`, `project`, and `cwd` automatically.
33
+
34
+ ## Commands
35
+
36
+ ```bash
37
+ # Live-refresh table of warm/cold sessions (Ctrl-C to quit)
38
+ claude-session-status watch
39
+
40
+ # Called by hooks (reads JSON from stdin)
41
+ claude-session-status mark-warm
42
+ claude-session-status mark-cold
43
+ ```
44
+
45
+ ## DB schema
46
+
47
+ ```sql
48
+ CREATE TABLE session_status (
49
+ session_id TEXT PRIMARY KEY,
50
+ project TEXT,
51
+ cwd TEXT,
52
+ started_at TIMESTAMP,
53
+ updated_at TIMESTAMP,
54
+ status TEXT -- 'warm' | 'cold'
55
+ )
56
+ ```
57
+
58
+ DB location: `~/.claude/claude-session-status/status.duckdb`
59
+
60
+ ## Design
61
+
62
+ - Runtime dependency: `duckdb` only
63
+ - Every DB access: open → read/write → close. No persistent connections.
64
+ - `mark-warm` / `mark-cold`: silent on success, errors go to stderr (never block Claude Code)
65
+ - 3-retry lock backoff on connect
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "claude-session-status"
7
+ version = "0.1.0"
8
+ description = "Track warm/cold status of Claude Code sessions in a dedicated DuckDB file"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ dependencies = ["duckdb>=0.9"]
12
+
13
+ [project.urls]
14
+ Homepage = "https://github.com/keith-fajardo/claude-session-status"
15
+ Repository = "https://github.com/keith-fajardo/claude-session-status"
16
+
17
+ [project.scripts]
18
+ claude-session-status = "claude_session_status.cli:main"
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,115 @@
1
+ import json
2
+ import sys
3
+ import time
4
+ import argparse
5
+ from . import db
6
+
7
+ _GREEN = "\033[32m"
8
+ _DIM = "\033[2m"
9
+ _RESET = "\033[0m"
10
+ _CLEAR = "\033[2J\033[H"
11
+
12
+
13
+ def _read_stdin_json() -> dict:
14
+ raw = sys.stdin.read()
15
+ if not raw.strip():
16
+ return {}
17
+ return json.loads(raw)
18
+
19
+
20
+ def _session_id_from(data: dict) -> str:
21
+ return (
22
+ data.get("session_id")
23
+ or data.get("sessionId")
24
+ or data.get("id")
25
+ or ""
26
+ )
27
+
28
+
29
+ def _project_from(data: dict) -> str:
30
+ return (
31
+ data.get("project")
32
+ or data.get("project_name")
33
+ or data.get("projectName")
34
+ or ""
35
+ )
36
+
37
+
38
+ def _cwd_from(data: dict) -> str:
39
+ return data.get("cwd") or ""
40
+
41
+
42
+ def cmd_mark_warm(_args):
43
+ try:
44
+ data = _read_stdin_json()
45
+ session_id = _session_id_from(data)
46
+ if not session_id:
47
+ print("claude-session-status: no session_id in stdin payload", file=sys.stderr)
48
+ return
49
+ db.mark_warm(session_id, _project_from(data), _cwd_from(data))
50
+ except Exception as e:
51
+ print(f"claude-session-status mark-warm error: {e}", file=sys.stderr)
52
+
53
+
54
+ def cmd_mark_cold(_args):
55
+ try:
56
+ data = _read_stdin_json()
57
+ session_id = _session_id_from(data)
58
+ if not session_id:
59
+ print("claude-session-status: no session_id in stdin payload", file=sys.stderr)
60
+ return
61
+ db.mark_cold(session_id, _project_from(data), _cwd_from(data))
62
+ except Exception as e:
63
+ print(f"claude-session-status mark-cold error: {e}", file=sys.stderr)
64
+
65
+
66
+ def _render(rows: list[dict]) -> str:
67
+ lines = [f"{'SESSION':>12} {'PROJECT':<20} {'STARTED':<19} STATUS"]
68
+ lines.append("-" * 70)
69
+ for r in rows:
70
+ sid = str(r["session_id"])[-12:]
71
+ proj = (r["project"] or r["cwd"] or "?")[:20]
72
+ ts = str(r["started_at"])[:19] if r["started_at"] else "?"
73
+ if r["status"] == "warm":
74
+ status = f"{_GREEN}warm{_RESET}"
75
+ else:
76
+ status = f"{_DIM}cold{_RESET}"
77
+ lines.append(f"{sid:>12} {proj:<20} {ts:<19} {status}")
78
+ return "\n".join(lines)
79
+
80
+
81
+ def cmd_watch(_args):
82
+ try:
83
+ while True:
84
+ rows = db.fetch_all()
85
+ sys.stdout.write(_CLEAR)
86
+ print(f"claude-session-status (refreshes every 5s, Ctrl-C to quit)\n")
87
+ print(_render(rows))
88
+ sys.stdout.flush()
89
+ time.sleep(5)
90
+ except KeyboardInterrupt:
91
+ pass
92
+ except Exception as e:
93
+ print(f"claude-session-status watch error: {e}", file=sys.stderr)
94
+ sys.exit(1)
95
+
96
+
97
+ def main():
98
+ parser = argparse.ArgumentParser(prog="claude-session-status")
99
+ sub = parser.add_subparsers(dest="cmd")
100
+
101
+ sub.add_parser("mark-warm", help="Upsert session as warm (reads JSON from stdin)")
102
+ sub.add_parser("mark-cold", help="Upsert session as cold (reads JSON from stdin)")
103
+ sub.add_parser("watch", help="Live-refresh warm/cold session table")
104
+
105
+ args = parser.parse_args()
106
+
107
+ if args.cmd == "mark-warm":
108
+ cmd_mark_warm(args)
109
+ elif args.cmd == "mark-cold":
110
+ cmd_mark_cold(args)
111
+ elif args.cmd == "watch":
112
+ cmd_watch(args)
113
+ else:
114
+ parser.print_help()
115
+ sys.exit(1)
@@ -0,0 +1,85 @@
1
+ import time
2
+ import duckdb
3
+ from pathlib import Path
4
+
5
+ DB_PATH = Path.home() / ".claude" / "claude-session-status" / "status.duckdb"
6
+
7
+ _CREATE_TABLE = """
8
+ CREATE TABLE IF NOT EXISTS session_status (
9
+ session_id TEXT PRIMARY KEY,
10
+ project TEXT,
11
+ cwd TEXT,
12
+ started_at TIMESTAMP,
13
+ updated_at TIMESTAMP,
14
+ status TEXT
15
+ )
16
+ """
17
+
18
+ _UPSERT_WARM = """
19
+ INSERT INTO session_status (session_id, project, cwd, started_at, updated_at, status)
20
+ VALUES (?, ?, ?, now(), now(), 'warm')
21
+ ON CONFLICT (session_id) DO UPDATE SET
22
+ project = excluded.project,
23
+ cwd = excluded.cwd,
24
+ updated_at = now(),
25
+ status = 'warm'
26
+ """
27
+
28
+ _UPSERT_COLD = """
29
+ INSERT INTO session_status (session_id, project, cwd, started_at, updated_at, status)
30
+ VALUES (?, ?, ?, now(), now(), 'cold')
31
+ ON CONFLICT (session_id) DO UPDATE SET
32
+ updated_at = now(),
33
+ status = 'cold'
34
+ """
35
+
36
+ _SELECT_ALL = """
37
+ SELECT session_id, project, cwd, started_at, updated_at, status
38
+ FROM session_status
39
+ ORDER BY updated_at DESC
40
+ """
41
+
42
+
43
+ def _connect():
44
+ DB_PATH.parent.mkdir(parents=True, exist_ok=True)
45
+ last_err = None
46
+ for attempt in range(3):
47
+ try:
48
+ return duckdb.connect(str(DB_PATH))
49
+ except duckdb.IOException as e:
50
+ last_err = e
51
+ time.sleep(0.2 * (attempt + 1))
52
+ raise last_err
53
+
54
+
55
+ def _ensure_table(con):
56
+ con.execute(_CREATE_TABLE)
57
+
58
+
59
+ def mark_warm(session_id: str, project: str, cwd: str) -> None:
60
+ con = _connect()
61
+ try:
62
+ _ensure_table(con)
63
+ con.execute(_UPSERT_WARM, [session_id, project, cwd])
64
+ finally:
65
+ con.close()
66
+
67
+
68
+ def mark_cold(session_id: str, project: str, cwd: str) -> None:
69
+ con = _connect()
70
+ try:
71
+ _ensure_table(con)
72
+ con.execute(_UPSERT_COLD, [session_id, project, cwd])
73
+ finally:
74
+ con.close()
75
+
76
+
77
+ def fetch_all() -> list[dict]:
78
+ con = _connect()
79
+ try:
80
+ _ensure_table(con)
81
+ rows = con.execute(_SELECT_ALL).fetchall()
82
+ cols = ["session_id", "project", "cwd", "started_at", "updated_at", "status"]
83
+ return [dict(zip(cols, row)) for row in rows]
84
+ finally:
85
+ con.close()
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-session-status
3
+ Version: 0.1.0
4
+ Summary: Track warm/cold status of Claude Code sessions in a dedicated DuckDB file
5
+ Project-URL: Homepage, https://github.com/keith-fajardo/claude-session-status
6
+ Project-URL: Repository, https://github.com/keith-fajardo/claude-session-status
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: duckdb>=0.9
10
+
11
+ # claude-session-status
12
+
13
+ Tracks warm/cold status of Claude Code sessions in a dedicated DuckDB file — separate from `claude-session-logger` to avoid file-lock conflicts.
14
+
15
+ ## Why
16
+
17
+ `claude-session-logger` writes to `sessions.duckdb` at session end. If a TUI holds a persistent read-only connection to that file, the write blocks. This package uses its own DB (`~/.claude/claude-session-status/status.duckdb`) and **never holds an open connection between operations**.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install claude-session-status
23
+ ```
24
+
25
+ ## Hook integration
26
+
27
+ Add to `~/.claude/settings.json`:
28
+
29
+ ```json
30
+ {
31
+ "hooks": {
32
+ "SessionStart": [
33
+ {"type": "command", "command": "claude-session-status mark-warm"}
34
+ ],
35
+ "SessionEnd": [
36
+ {"type": "command", "command": "claude-session-status mark-cold"}
37
+ ]
38
+ }
39
+ }
40
+ ```
41
+
42
+ Claude Code pipes session JSON to stdin; the scripts extract `session_id`, `project`, and `cwd` automatically.
43
+
44
+ ## Commands
45
+
46
+ ```bash
47
+ # Live-refresh table of warm/cold sessions (Ctrl-C to quit)
48
+ claude-session-status watch
49
+
50
+ # Called by hooks (reads JSON from stdin)
51
+ claude-session-status mark-warm
52
+ claude-session-status mark-cold
53
+ ```
54
+
55
+ ## DB schema
56
+
57
+ ```sql
58
+ CREATE TABLE session_status (
59
+ session_id TEXT PRIMARY KEY,
60
+ project TEXT,
61
+ cwd TEXT,
62
+ started_at TIMESTAMP,
63
+ updated_at TIMESTAMP,
64
+ status TEXT -- 'warm' | 'cold'
65
+ )
66
+ ```
67
+
68
+ DB location: `~/.claude/claude-session-status/status.duckdb`
69
+
70
+ ## Design
71
+
72
+ - Runtime dependency: `duckdb` only
73
+ - Every DB access: open → read/write → close. No persistent connections.
74
+ - `mark-warm` / `mark-cold`: silent on success, errors go to stderr (never block Claude Code)
75
+ - 3-retry lock backoff on connect
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/claude_session_status/__init__.py
4
+ src/claude_session_status/cli.py
5
+ src/claude_session_status/db.py
6
+ src/claude_session_status.egg-info/PKG-INFO
7
+ src/claude_session_status.egg-info/SOURCES.txt
8
+ src/claude_session_status.egg-info/dependency_links.txt
9
+ src/claude_session_status.egg-info/entry_points.txt
10
+ src/claude_session_status.egg-info/requires.txt
11
+ src/claude_session_status.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ claude-session-status = claude_session_status.cli:main