Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,8 @@ jobs:
python -m pip install 'flask>=3.0' 'fpdf2>=2.7' 'mypy>=1.10'

- name: Run mypy
# Transitional only (maintainer consensus): keeps CI green until `mypy` exits
# zero on this repo — then delete this line so type errors fail the job.
continue-on-error: true
# No `continue-on-error` — mypy now exits zero on this repo (closes #29),
# so type errors must fail the job from here on.
run: mypy --ignore-missing-imports --no-strict-optional --pretty .

# ── Secret scan: gitleaks ─────────────────────────────────────────────────
Expand Down
10 changes: 6 additions & 4 deletions api/composers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import json
import logging
import os
import sqlite3
from contextlib import closing
Expand All @@ -15,6 +16,7 @@
from utils.path_helpers import to_epoch_ms

bp = Blueprint("composers", __name__)
_logger = logging.getLogger(__name__)


def _read_json_file(path: str):
Expand Down Expand Up @@ -67,8 +69,8 @@ def list_composers():
composers.sort(key=lambda c: to_epoch_ms(c.get("lastUpdatedAt")), reverse=True)
return jsonify(composers)

except Exception as e:
print(f"Failed to get composers: {e}")
except Exception:
_logger.exception("Failed to get composers")
return jsonify({"error": "Failed to get composers"}), 500


Expand Down Expand Up @@ -122,6 +124,6 @@ def get_composer(composer_id):

return jsonify({"error": "Composer not found"}), 404

except Exception as e:
print(f"Failed to get composer: {e}")
except Exception:
_logger.exception("Failed to get composer")
return jsonify({"error": "Failed to get composer"}), 500
11 changes: 5 additions & 6 deletions api/export_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import os
import re
import sqlite3
import sys
import zipfile
from contextlib import closing
from datetime import datetime
Expand All @@ -18,7 +17,7 @@
from flask import Blueprint, Response, current_app, jsonify, request

from utils.workspace_path import resolve_workspace_path
from utils.path_helpers import normalize_file_path, get_workspace_folder_paths, to_epoch_ms
from utils.path_helpers import get_workspace_folder_paths, to_epoch_ms
from utils.text_extract import extract_text_from_bubble
from utils.tool_parser import parse_tool_call
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
Expand Down Expand Up @@ -390,15 +389,15 @@ def export_chats():
status_str = f" ({tool_status})" if tool_status else ""
md += f"> **Tool: {tool_summary}**{status_str}\n"
if t.get("input"):
md += f">\n> **INPUT:**\n> ```\n"
md += ">\n> **INPUT:**\n> ```\n"
for iline in str(t["input"]).split("\n"):
md += f"> {iline}\n"
md += f"> ```\n"
md += "> ```\n"
if t.get("output"):
md += f">\n> **OUTPUT:**\n> ```\n"
md += ">\n> **OUTPUT:**\n> ```\n"
for oline in str(t["output"]).split("\n"):
md += f"> {oline}\n"
md += f"> ```\n"
md += "> ```\n"
md += "\n"
md += "---\n\n"

Expand Down
2 changes: 1 addition & 1 deletion api/logs.py
Comment thread
timon0305 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def get_logs():
except Exception:
pass

logs.sort(key=lambda l: l.get("timestamp") or 0, reverse=True)
logs.sort(key=lambda log: log.get("timestamp") or 0, reverse=True)
return jsonify({"logs": logs})

except Exception as e:
Expand Down
20 changes: 11 additions & 9 deletions api/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

import json
import logging
import os
import re
import sqlite3
Expand All @@ -15,11 +16,12 @@

from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
from utils.workspace_path import resolve_workspace_path, get_cli_chats_path
from utils.path_helpers import normalize_file_path, get_workspace_folder_paths, to_epoch_ms
from utils.path_helpers import to_epoch_ms
from utils.text_extract import extract_text_from_bubble
from utils.cli_chat_reader import list_cli_projects, traverse_blobs, messages_to_bubbles

bp = Blueprint("search", __name__)
_logger = logging.getLogger(__name__)


def _json_dump_safe(value) -> str:
Expand Down Expand Up @@ -51,7 +53,7 @@ def _build_exclusion_searchable(
metadata_parts: list[str] | None = None,
) -> str:
"""Build broad searchable text so exclusion rules cover visible output."""
combined = []
combined: list = []
Comment thread
timon0305 marked this conversation as resolved.
Outdated
if content_parts:
combined.extend(p for p in content_parts if p)
if metadata_parts:
Expand Down Expand Up @@ -231,7 +233,7 @@ def search():
# Derive title from first bubble
for text in bubble_texts:
if text:
first_lines = [l for l in text.split("\n") if l.strip()]
first_lines = [ln for ln in text.split("\n") if ln.strip()]
if first_lines:
title = first_lines[0][:100]
break
Expand All @@ -250,8 +252,8 @@ def search():
except Exception:
pass

except Exception as e:
print(f"Error searching global storage: {e}")
except Exception:
_logger.exception("Error searching global storage")
finally:
if conn is not None:
conn.close()
Expand Down Expand Up @@ -435,8 +437,8 @@ def search():
"type": "cli_agent",
"source": "cli",
})
except Exception as e:
print(f"Error searching CLI sessions: {e}")
except Exception:
_logger.exception("Error searching CLI sessions")

# Sort by timestamp descending
def _ts(r):
Expand All @@ -451,6 +453,6 @@ def _ts(r):

return jsonify({"results": results})

except Exception as e:
print(f"Search failed: {e}")
except Exception:
_logger.exception("Search failed")
return jsonify({"error": "Search failed", "results": []}), 500
37 changes: 19 additions & 18 deletions api/workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from __future__ import annotations

import json
import logging
import os
import re
import sqlite3
Expand All @@ -32,9 +33,11 @@
to_epoch_ms,
)
from utils.text_extract import extract_text_from_bubble, format_tool_action
from utils.tool_parser import parse_tool_call as _parse_tool_call
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules

bp = Blueprint("workspaces", __name__)
_logger = logging.getLogger(__name__)


def _get_workspace_display_name(workspace_path: str, workspace_id: str) -> str:
Expand Down Expand Up @@ -663,7 +666,6 @@ def list_workspaces():
primary = group[0]
all_ws_ids = [e["name"] for e in group]

db_path = os.path.join(workspace_path, primary["name"], "state.vscdb")
try:
mtime = max(
os.path.getmtime(os.path.join(workspace_path, e["name"], "state.vscdb"))
Expand Down Expand Up @@ -758,14 +760,14 @@ def list_workspaces():
),
"source": "cli",
})
except Exception as e:
print(f"Failed to load CLI projects: {e}")
except Exception:
_logger.exception("Failed to load CLI projects")

projects.sort(key=lambda p: p["lastModified"], reverse=True)
return jsonify(projects)

except Exception as e:
print(f"Failed to get workspaces: {e}")
except Exception:
_logger.exception("Failed to get workspaces")
return jsonify({"error": "Failed to get workspaces"}), 500


Expand Down Expand Up @@ -839,16 +841,15 @@ def get_workspace(workspace_id):
"lastModified": datetime.fromtimestamp(mtime, tz=timezone.utc).isoformat(),
})

except Exception as e:
print(f"Failed to get workspace: {e}")
except Exception:
_logger.exception("Failed to get workspace")
return jsonify({"error": "Failed to get workspace"}), 500


# ---------------------------------------------------------------------------
# GET /api/workspaces/<id>/tabs
# ---------------------------------------------------------------------------

from utils.tool_parser import parse_tool_call as _parse_tool_call


def _get_cli_workspace_tabs(workspace_id: str):
Expand All @@ -872,8 +873,8 @@ def _get_cli_workspace_tabs(workspace_id: str):

try:
messages = traverse_blobs(session["db_path"])
except Exception as e:
print(f"CLI: could not read session {session_id}: {e}")
except Exception: # noqa: BLE001 — best-effort per-session skip; one corrupted session must not 500 the endpoint, and the failure mode is logged with exc_info so the concrete type is preserved.
_logger.warning("CLI: could not read session %s", session_id, exc_info=True)
continue

bubbles = messages_to_bubbles(messages, created_ms)
Expand All @@ -885,7 +886,7 @@ def _get_cli_workspace_tabs(workspace_id: str):
if not title or title.startswith("New Agent"):
for b in bubbles:
if b["type"] == "user" and b.get("text"):
first_lines = [l for l in b["text"].split("\n") if l.strip()]
first_lines = [ln for ln in b["text"].split("\n") if ln.strip()]
if first_lines:
title = first_lines[0][:100]
if len(title) == 100:
Expand Down Expand Up @@ -937,8 +938,8 @@ def _get_cli_workspace_tabs(workspace_id: str):
tabs.sort(key=lambda t: t.get("timestamp") or 0, reverse=True)
return jsonify({"tabs": tabs})

except Exception as e:
print(f"Failed to get CLI workspace tabs: {e}")
except Exception:
_logger.exception("Failed to get CLI workspace tabs")
return jsonify({"error": "Failed to get CLI workspace tabs"}), 500


Expand Down Expand Up @@ -1251,7 +1252,7 @@ def get_workspace_tabs(workspace_id):
if not cd.get("name") and bubbles:
first_msg = bubbles[0].get("text", "")
if first_msg:
first_lines = [l for l in first_msg.split("\n") if l.strip()]
first_lines = [ln for ln in first_msg.split("\n") if ln.strip()]
if first_lines:
title = first_lines[0][:100]
if len(title) == 100:
Expand Down Expand Up @@ -1394,14 +1395,14 @@ def get_workspace_tabs(workspace_id):

response["tabs"].append(tab)

except Exception as e:
print(f"Error parsing composer data for {composer_id}: {e}")
except Exception: # noqa: BLE001 — best-effort per-composer skip in a read-many loop; one malformed row must not 500 the tabs endpoint, and exc_info captures the concrete type for debugging.
_logger.warning("Error parsing composer data for %s", composer_id, exc_info=True)

# Sort tabs by timestamp descending (newest first)
response["tabs"].sort(key=lambda t: t.get("timestamp") or 0, reverse=True)

return jsonify(response)

except Exception as e:
print(f"Failed to get workspace tabs: {e}")
except Exception:
_logger.exception("Failed to get workspace tabs")
return jsonify({"error": "Failed to get workspace tabs"}), 500
1 change: 0 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ def favicon():

if __name__ == "__main__":
import argparse
import sys

parser = argparse.ArgumentParser(description="Cursor Chat Browser (Python)")
parser.add_argument("--port", type=int, default=3000)
Expand Down
Empty file added scripts/__init__.py
Empty file.
Loading
Loading