clicodelog 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl

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.
clicodelog/__init__.py CHANGED
@@ -2,5 +2,5 @@
2
2
  CLI Code Log - Browse, inspect, and export logs from CLI-based AI coding agents.
3
3
  """
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = "0.2.0"
6
6
  __author__ = "monk1337"
clicodelog/app.py CHANGED
@@ -1,20 +1,21 @@
1
+ #!/usr/bin/env python3
1
2
  """
2
- cli code log
3
- A web app to browse, inspect, and export logs from CLI-based AI coding agents.
4
- Data is copied from source directories to ~/.clicodelog/data/ for backup and local use.
3
+ AI Conversation History Viewer
4
+ A web app to browse and view Claude Code, OpenAI Codex, and Google Gemini conversation history.
5
+ Data is copied from source directories to ./data/ for backup and local use.
5
6
  Background sync runs every hour to keep data updated.
6
7
  """
7
8
 
8
- import base64
9
9
  import json
10
10
  import os
11
11
  import shutil
12
+ import signal
13
+ import subprocess
12
14
  import threading
13
15
  import time
14
16
  from datetime import datetime
15
17
  from pathlib import Path
16
-
17
- from flask import Flask, Response, jsonify, render_template, request
18
+ from flask import Flask, render_template, jsonify, request
18
19
  from flask_cors import CORS
19
20
 
20
21
  # Package directory for templates
@@ -51,13 +52,9 @@ sync_lock = threading.Lock()
51
52
  last_sync_time = {} # Track per-source sync times
52
53
  current_source = "claude-code" # Default source
53
54
 
54
- # Create Flask app with template folder from package
55
- app = Flask(__name__, template_folder=str(PACKAGE_DIR / "templates"))
56
- CORS(app)
57
-
58
55
 
59
56
  def sync_data(source_id=None, silent=False):
60
- """Copy data from source directory to data dir for backup."""
57
+ """Copy data from source directory to ./data/{source}/ for backup."""
61
58
  global last_sync_time
62
59
 
63
60
  if source_id is None:
@@ -79,7 +76,7 @@ def sync_data(source_id=None, silent=False):
79
76
  return False
80
77
 
81
78
  # Create data directory if it doesn't exist
82
- DATA_DIR.mkdir(parents=True, exist_ok=True)
79
+ DATA_DIR.mkdir(exist_ok=True)
83
80
 
84
81
  # Copy source directory
85
82
  if not silent:
@@ -156,11 +153,13 @@ def background_sync():
156
153
 
157
154
  def encode_path_id(path):
158
155
  """Encode a path as a safe ID using base64."""
156
+ import base64
159
157
  return base64.urlsafe_b64encode(path.encode()).decode().rstrip('=')
160
158
 
161
159
 
162
160
  def decode_path_id(encoded_id):
163
161
  """Decode a base64-encoded path ID."""
162
+ import base64
164
163
  # Add back padding if needed
165
164
  padding = 4 - len(encoded_id) % 4
166
165
  if padding != 4:
@@ -577,11 +576,18 @@ def parse_codex_conversation(session_file, session_id):
577
576
 
578
577
  elif payload_type == "reasoning":
579
578
  # Reasoning/thinking block
580
- summary_parts = payload.get("summary", [])
581
579
  thinking_text = ""
582
- for part in summary_parts:
583
- if isinstance(part, dict) and part.get("type") == "summary_text":
584
- thinking_text += part.get("text", "") + "\n"
580
+
581
+ # Check if content is encrypted
582
+ if payload.get("encrypted_content"):
583
+ thinking_text = "[Reasoning content is encrypted and cannot be displayed]\n\nOpenAI Codex encrypts extended thinking for privacy. The model used reasoning here, but the content is not accessible in the logs."
584
+ else:
585
+ # Try to extract from summary (unencrypted format)
586
+ summary_parts = payload.get("summary", [])
587
+ for part in summary_parts:
588
+ if isinstance(part, dict) and part.get("type") == "summary_text":
589
+ thinking_text += part.get("text", "") + "\n"
590
+
585
591
  if thinking_text:
586
592
  messages.append({
587
593
  "role": "assistant",
@@ -711,6 +717,11 @@ def parse_gemini_conversation(session_file, session_id):
711
717
  }
712
718
 
713
719
 
720
+ # Create Flask app
721
+ app = Flask(__name__, template_folder=str(PACKAGE_DIR / "templates"))
722
+ CORS(app)
723
+
724
+
714
725
  @app.route('/')
715
726
  def index():
716
727
  return render_template('index.html')
@@ -851,7 +862,7 @@ def api_export(project_id, session_id):
851
862
  if conversation.get("summaries"):
852
863
  lines.append("SUMMARIES:")
853
864
  for s in conversation["summaries"]:
854
- lines.append(f" * {s}")
865
+ lines.append(f" {s}")
855
866
  lines.append("")
856
867
  lines.append("-" * 60)
857
868
  lines.append("")
@@ -901,6 +912,7 @@ def api_export(project_id, session_id):
901
912
 
902
913
  text_content = "\n".join(lines)
903
914
 
915
+ from flask import Response
904
916
  return Response(
905
917
  text_content,
906
918
  mimetype="text/plain",
@@ -938,27 +950,42 @@ def api_status():
938
950
  })
939
951
 
940
952
 
941
- def find_available_port(host, start_port, max_attempts=100):
942
- """Find an available port starting from start_port."""
943
- import socket
944
-
945
- for port in range(start_port, start_port + max_attempts):
946
- try:
947
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
948
- s.bind((host, port))
949
- return port
950
- except OSError:
951
- continue
952
- raise RuntimeError(f"Could not find an available port in range {start_port}-{start_port + max_attempts}")
953
-
953
+ def kill_process_on_port(port):
954
+ """Kill any process running on the specified port."""
955
+ try:
956
+ # Find process ID on the port
957
+ result = subprocess.run(
958
+ ["lsof", "-ti", f":{port}"],
959
+ capture_output=True,
960
+ text=True
961
+ )
962
+ if result.returncode == 0 and result.stdout.strip():
963
+ pid = result.stdout.strip()
964
+ print(f"⚠️ Port {port} is in use by process {pid}")
965
+ print(f"🔄 Killing process {pid}...")
966
+ try:
967
+ os.kill(int(pid), signal.SIGTERM)
968
+ time.sleep(0.5) # Give it a moment to terminate
969
+ print(f"✓ Process killed successfully")
970
+ except ProcessLookupError:
971
+ pass # Process already gone
972
+ except Exception as e:
973
+ print(f"Warning: Could not kill process: {e}")
974
+ except FileNotFoundError:
975
+ # lsof not available (not on Unix-like system)
976
+ pass
977
+ except Exception as e:
978
+ print(f"Warning: Could not check port: {e}")
954
979
 
955
- def run_server(host="127.0.0.1", port=5050, skip_sync=False, debug=False):
956
- """Run the Flask server."""
957
- from clicodelog import __version__
958
980
 
981
+ def run_server(host="127.0.0.1", port=6126, skip_sync=False, debug=False):
982
+ """Run the Flask development server."""
959
983
  print("=" * 60)
960
- print(f"cli code log v{__version__}")
984
+ print("CLI Code Log - AI Conversation History Viewer")
961
985
  print("=" * 60)
986
+
987
+ # Kill any process on the port
988
+ kill_process_on_port(port)
962
989
 
963
990
  if not skip_sync:
964
991
  # Sync data from all sources
@@ -969,28 +996,24 @@ def run_server(host="127.0.0.1", port=5050, skip_sync=False, debug=False):
969
996
  print(f" Backup: {DATA_DIR / config['data_subdir']}")
970
997
 
971
998
  if sync_data(source_id=source_id):
972
- print(" Sync completed!")
999
+ print(f" Sync completed!")
973
1000
  else:
974
- print(" Warning: Could not sync. Using existing local data if available.")
975
-
976
- # Start background sync thread
977
- print(f"\nBackground sync: Every {SYNC_INTERVAL // 3600} hour(s)")
978
- sync_thread = threading.Thread(target=background_sync, daemon=True)
979
- sync_thread.start()
980
- print("Background sync thread started.")
1001
+ print(f" Warning: Could not sync. Using existing local data if available.")
981
1002
  else:
982
1003
  print("\nSkipping initial sync (--no-sync flag)")
983
1004
 
984
- # Find available port
985
- actual_port = find_available_port(host, port)
986
- if actual_port != port:
987
- print(f"\nPort {port} is busy, using port {actual_port} instead")
1005
+ # Start background sync thread
1006
+ print(f"\nBackground sync: Every {SYNC_INTERVAL // 3600} hour(s)")
1007
+ sync_thread = threading.Thread(target=background_sync, daemon=True)
1008
+ sync_thread.start()
1009
+ print("Background sync thread started.")
988
1010
 
989
1011
  print(f"\nStarting server...")
990
- print(f"Open http://{host}:{actual_port} in your browser")
1012
+ print(f"Open http://{host}:{port} in your browser")
991
1013
  print("=" * 60)
992
- app.run(host=host, port=actual_port, debug=debug, use_reloader=False)
1014
+ app.run(host=host, port=port, debug=debug, use_reloader=False)
993
1015
 
994
1016
 
995
1017
  if __name__ == '__main__':
996
- run_server()
1018
+ # For direct execution
1019
+ run_server(debug=True)
clicodelog/cli.py CHANGED
@@ -19,8 +19,8 @@ def main():
19
19
  parser.add_argument(
20
20
  "--port", "-p",
21
21
  type=int,
22
- default=5050,
23
- help="Port to run the server on (default: 5050)",
22
+ default=6126,
23
+ help="Port to run the server on (default: 6126)",
24
24
  )
25
25
  parser.add_argument(
26
26
  "--host", "-H",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clicodelog
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A lightweight, local-first web app to browse, inspect, and export logs from CLI-based AI coding agents
5
5
  Author: monk1337
6
6
  License-Expression: MIT
@@ -34,7 +34,10 @@ Requires-Dist: twine; extra == "dev"
34
34
  Dynamic: license-file
35
35
 
36
36
  <div align="center">
37
- <h1>cli code log</h1>
37
+ <div align="center">
38
+ <img width="220px" src="https://raw.githubusercontent.com/monk1337/clicodelog/refs/heads/main/screenshots/logo.png">
39
+ </div>
40
+
38
41
  <p>
39
42
  A lightweight, local-first web app to browse, inspect, and export logs from
40
43
  CLI-based AI coding agents — Claude Code, OpenAI Codex, and Gemini CLI.
@@ -58,10 +61,92 @@ CLI-based AI coding agents — Claude Code, OpenAI Codex, and Gemini CLI.
58
61
  </p>
59
62
  </div>
60
63
 
61
- ![Gemini CLI Screenshot](screenshots/dark.png)
64
+ <!-- <div align="center">
65
+ <table>
66
+ <tr>
67
+ <td align="center">
68
+ <img src="screenshots/claude.png" width="80" alt="Claude Code"><br>
69
+ <sub><b>Claude Code</b></sub>
70
+ </td>
71
+ <td align="center">
72
+ <img src="screenshots/codex.png" width="80" alt="OpenAI Codex"><br>
73
+ <sub><b>OpenAI Codex</b></sub>
74
+ </td>
75
+ <td align="center">
76
+ <img src="screenshots/gemini.png" width="80" alt="Gemini CLI"><br>
77
+ <sub><b>Gemini CLI</b></sub>
78
+ </td>
79
+ </tr>
80
+ </table>
81
+ </div> -->
62
82
 
63
83
  ---
64
84
 
85
+ ## Installation
86
+
87
+ ### Via uv (Recommended)
88
+
89
+ [uv](https://github.com/astral-sh/uv) is a fast Python package installer. Install it first if you haven't:
90
+
91
+ ```bash
92
+ curl -LsSf https://astral.sh/uv/install.sh | sh
93
+ ```
94
+
95
+ Then install clicodelog:
96
+
97
+ ```bash
98
+ uv pip install clicodelog
99
+ ```
100
+
101
+ ### Via pip
102
+
103
+ ```bash
104
+ pip install clicodelog
105
+ ```
106
+
107
+ ### From source
108
+
109
+ ```bash
110
+ git clone https://github.com/monk1337/clicodelog.git
111
+ cd clicodelog
112
+ uv pip install -e .
113
+ # or with pip:
114
+ pip install -e .
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Usage
120
+
121
+ After installation, simply run:
122
+
123
+ ```bash
124
+ clicodelog
125
+ ```
126
+
127
+ The app will:
128
+ - Auto-kill any process on port **6126** (if occupied)
129
+ - Sync data from all AI coding agent sources
130
+ - Start a web server at **http://localhost:6126**
131
+
132
+ ### Command Options
133
+
134
+ ```bash
135
+ clicodelog --help # Show all options
136
+ clicodelog --port 8080 # Use custom port
137
+ clicodelog --host 0.0.0.0 # Bind to all interfaces
138
+ clicodelog --no-sync # Skip initial data sync
139
+ clicodelog --debug # Run in debug mode
140
+ ```
141
+
142
+ ### Alternative: Run from source
143
+
144
+ ```bash
145
+ git clone https://github.com/monk1337/clicodelog.git
146
+ cd clicodelog
147
+ python -m clicodelog.cli
148
+ ```
149
+
65
150
  ## Features
66
151
 
67
152
  - **Multi-source support** — View logs from Claude Code, OpenAI Codex, and Gemini CLI
@@ -104,56 +189,18 @@ CLI-based AI coding agents — Claude Code, OpenAI Codex, and Gemini CLI.
104
189
 
105
190
  ---
106
191
 
107
- ## Installation
108
-
109
- ### Via pip (Recommended)
110
-
111
- ```bash
112
- pip install clicodelog
113
- ```
114
-
115
- ### From source
116
-
117
- ```bash
118
- git clone https://github.com/monk1337/clicodelog.git
119
- cd clicodelog
120
- pip install -e .
121
- ```
122
-
123
- ---
124
-
125
- ## Usage
126
-
127
- If installed via pip:
128
-
129
- ```bash
130
- clicodelog
131
- ```
132
-
133
- Or run directly from source:
134
-
135
- ```bash
136
- ./run.sh
137
- ```
138
-
139
- Or manually:
192
+ ### CLI Options
140
193
 
141
194
  ```bash
142
- pip install -r requirements.txt
143
- python app.py
195
+ clicodelog --help # Show help message
196
+ clicodelog --version # Show version
197
+ clicodelog --port 8080 # Run on custom port (default: 6126)
198
+ clicodelog --host 0.0.0.0 # Bind to all interfaces (default: 127.0.0.1)
199
+ clicodelog --no-sync # Skip initial data sync
200
+ clicodelog --debug # Run in debug mode
144
201
  ```
145
202
 
146
- Open http://localhost:5050 in your browser.
147
-
148
- ### CLI Options
149
-
150
- ```
151
- clicodelog --help
152
- clicodelog --port 8080 # Run on custom port
153
- clicodelog --host 0.0.0.0 # Bind to all interfaces
154
- clicodelog --no-sync # Skip initial data sync
155
- clicodelog --debug # Run in debug mode
156
- ```
203
+ **Note:** The app automatically kills any process running on the specified port before starting.
157
204
 
158
205
  ---
159
206
 
@@ -289,17 +336,3 @@ MIT
289
336
  </div>
290
337
 
291
338
  ```
292
-
293
- @misc{clicodelog2026,
294
- title = {clicodelog: Browse, inspect CLI-based AI coding agents},
295
- author = {Pal, Ankit},
296
- year = {2026},
297
- howpublished = {\url{https://github.com/monk1337/clicodelog}},
298
- note = {A lightweight, local-first web app to browse, inspect, and export logs from CLI-based AI coding agents — Claude Code, OpenAI Codex, and Gemini CLI.}
299
- }
300
-
301
- ```
302
-
303
- ## 💁 Contributing
304
-
305
- Welcome any contributions to open source project, including new features, improvements to infrastructure, and more comprehensive documentation.
@@ -0,0 +1,11 @@
1
+ clicodelog/__init__.py,sha256=qs3bijM7cWYmV4ckBeuZd5dLhViUzuaUal4AoJUT_io,136
2
+ clicodelog/__main__.py,sha256=JDb49rUInCUlT5Ya0aGfusfJm8e7wGqA447mrnVqDkA,119
3
+ clicodelog/app.py,sha256=iHoeIK3IVQtTpm-CjqJO_Tn5L8krUeoOUfSdeqGAJK4,38870
4
+ clicodelog/cli.py,sha256=c34P5fOpE5Hy_DJog52BGO4ab4cUBRZZTsW5nxaFUL4,1297
5
+ clicodelog/templates/index.html,sha256=kUYQJivjWOLonREjinUsIHvgiGUHbhuJvlLWyjIst9Y,34392
6
+ clicodelog-0.2.0.dist-info/licenses/LICENSE,sha256=enO5ZtMm5_HoQnB9jA_d3CCajd9I6Kzg-m1FmNCsAvk,1065
7
+ clicodelog-0.2.0.dist-info/METADATA,sha256=6vj1ryFe10ijvIWqaLtLSQfPB1qk8EqTIQGWWA9caaM,8993
8
+ clicodelog-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ clicodelog-0.2.0.dist-info/entry_points.txt,sha256=6Zodty_o0mVbDoIcNPQ31aI-P-KLZ0Xr_qsfrQkDYck,51
10
+ clicodelog-0.2.0.dist-info/top_level.txt,sha256=YvkoZJtBm4QSRL2YlLr5TeVILsiptIF7D43i29RG5oc,11
11
+ clicodelog-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- clicodelog/__init__.py,sha256=HYMXrxX39PPodu0qTmWqbgTr09RLzUtl3QNFDQCz_r4,136
2
- clicodelog/__main__.py,sha256=JDb49rUInCUlT5Ya0aGfusfJm8e7wGqA447mrnVqDkA,119
3
- clicodelog/app.py,sha256=W-7mQSwcB5N69ZEYhdOkg-fs93wxR4XccPAQ2H1GKO8,37823
4
- clicodelog/cli.py,sha256=mH_fjEVg2RovP2sPpZPOyIzejnLcvaqi0hf41Xnxnao,1297
5
- clicodelog/templates/index.html,sha256=kUYQJivjWOLonREjinUsIHvgiGUHbhuJvlLWyjIst9Y,34392
6
- clicodelog-0.1.0.dist-info/licenses/LICENSE,sha256=enO5ZtMm5_HoQnB9jA_d3CCajd9I6Kzg-m1FmNCsAvk,1065
7
- clicodelog-0.1.0.dist-info/METADATA,sha256=fVvrn6ehZcS2z3CD_5E64vNVZwR6O6cSaYi1jNWkxNM,8058
8
- clicodelog-0.1.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
9
- clicodelog-0.1.0.dist-info/entry_points.txt,sha256=6Zodty_o0mVbDoIcNPQ31aI-P-KLZ0Xr_qsfrQkDYck,51
10
- clicodelog-0.1.0.dist-info/top_level.txt,sha256=YvkoZJtBm4QSRL2YlLr5TeVILsiptIF7D43i29RG5oc,11
11
- clicodelog-0.1.0.dist-info/RECORD,,