Skip to content

Commit 5cef4f9

Browse files
committed
fix: rename _-prefixed functions to normal
1 parent 46bda64 commit 5cef4f9

7 files changed

Lines changed: 205 additions & 88 deletions

File tree

api/export_api.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121
from utils.exclusion_rules import build_searchable_text, is_excluded_by_rules
2222
from utils.cursor_md_exporter import cursor_ide_chat_to_markdown
2323
from services.workspace_db import (
24-
_build_composer_id_to_workspace_id,
25-
_collect_workspace_entries,
24+
build_composer_id_to_workspace_id,
25+
collect_workspace_entries,
2626
load_bubble_map,
2727
load_code_block_diff_map,
28-
_open_global_db,
28+
open_global_db,
2929
)
3030
from services.workspace_resolver import (
31-
_get_workspace_display_name,
32-
_create_project_name_to_workspace_id_map,
31+
create_project_name_to_workspace_id_map,
32+
lookup_workspace_display_name,
3333
)
3434

3535
bp = Blueprint("export_api", __name__)
@@ -96,15 +96,15 @@ def export_chats():
9696
last_export_ms = to_epoch_ms(ts_str)
9797

9898
# ── Workspace scanning via service layer ──────────────────────────────
99-
workspace_entries = _collect_workspace_entries(workspace_path)
100-
composer_id_to_ws = _build_composer_id_to_workspace_id(workspace_path, workspace_entries)
101-
project_name_map = _create_project_name_to_workspace_id_map(workspace_entries)
99+
workspace_entries = collect_workspace_entries(workspace_path)
100+
composer_id_to_ws = build_composer_id_to_workspace_id(workspace_path, workspace_entries)
101+
project_name_map = create_project_name_to_workspace_id_map(workspace_entries)
102102

103103
# Build display-name and slug maps
104104
ws_id_to_slug: dict[str, str] = {}
105105
ws_id_to_display_name: dict[str, str] = {}
106106
for e in workspace_entries:
107-
display = _get_workspace_display_name(workspace_path, e["name"])
107+
display = lookup_workspace_display_name(workspace_path, e["name"])
108108
if display != e["name"]:
109109
ws_id_to_display_name[e["name"]] = display
110110
ws_id_to_slug[e["name"]] = slug(display)
@@ -114,7 +114,7 @@ def export_chats():
114114
rules = current_app.config.get("EXCLUSION_RULES") or []
115115

116116
# ── Database reading via service layer ────────────────────────────────
117-
with _open_global_db(workspace_path) as (global_db, global_db_path):
117+
with open_global_db(workspace_path) as (global_db, global_db_path):
118118
if global_db is None:
119119
return jsonify({"error": "Cursor global storage not found"}), 404
120120

api/workspaces.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@
2222
)
2323
from utils.workspace_descriptor import read_json_file
2424
from services.workspace_resolver import (
25-
_infer_workspace_name_from_context,
26-
# Re-exported for back-compat with existing tests that import from api.workspaces
27-
# directly (test_invalid_workspace_aliases, test_workspace_assignment_fallback,
28-
# test_workspace_name_inference, test_models_wired_at_read_sites).
29-
# Production callers should import from services.workspace_resolver instead.
30-
_determine_project_for_conversation, # noqa: F401
31-
_infer_invalid_workspace_aliases, # noqa: F401
32-
_get_workspace_display_name, # noqa: F401
25+
determine_project_for_conversation,
26+
infer_invalid_workspace_aliases,
27+
infer_workspace_name_from_context,
28+
lookup_workspace_display_name,
3329
)
34-
from services.cli_tabs import _get_cli_workspace_tabs
30+
from services.cli_tabs import get_cli_workspace_tabs
3531
from services.workspace_listing import list_workspace_projects
3632
from services.workspace_tabs import assemble_workspace_tabs
3733

34+
# Re-exported for back-compat with tests that import from api.workspaces directly.
35+
_determine_project_for_conversation = determine_project_for_conversation # noqa: F401
36+
_infer_invalid_workspace_aliases = infer_invalid_workspace_aliases # noqa: F401
37+
_get_workspace_display_name = lookup_workspace_display_name # noqa: F401
38+
_infer_workspace_name_from_context = infer_workspace_name_from_context # noqa: F401
39+
3840
# Re-exported for tests/test_models_wired_at_read_sites.py — the typed-model
3941
# spy harness patches `workspaces_mod.Bubble` / `.Composer` / `.Workspace` to
4042
# verify that production read paths actually call from_dict. The classes
@@ -121,12 +123,12 @@ def get_workspace(workspace_id):
121123
if derived_name:
122124
workspace_name = derived_name
123125
elif workspace_name == workspace_id:
124-
inferred = _infer_workspace_name_from_context(workspace_path, workspace_id)
126+
inferred = infer_workspace_name_from_context(workspace_path, workspace_id)
125127
if inferred:
126128
workspace_name = inferred
127129
except Exception as e:
128130
warn_workspace_json_read(_logger, workspace_id, e)
129-
inferred = _infer_workspace_name_from_context(workspace_path, workspace_id)
131+
inferred = infer_workspace_name_from_context(workspace_path, workspace_id)
130132
if inferred:
131133
workspace_name = inferred
132134

@@ -150,7 +152,7 @@ def get_workspace(workspace_id):
150152
@bp.route("/api/workspaces/<workspace_id>/tabs")
151153
def get_workspace_tabs(workspace_id):
152154
if workspace_id.startswith("cli:"):
153-
return _get_cli_workspace_tabs(workspace_id)
155+
return get_cli_workspace_tabs(workspace_id)
154156
try:
155157
workspace_path = resolve_workspace_path()
156158
rules = current_app.config.get("EXCLUSION_RULES") or []

services/cli_tabs.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@
1212
from utils.workspace_path import get_cli_chats_path
1313

1414

15-
def _get_cli_workspace_tabs(workspace_id: str):
16-
"""Return tabs for a Cursor CLI project (workspace_id starts with "cli:")."""
15+
def get_cli_workspace_tabs(workspace_id: str):
16+
"""Return Flask JSON response with tabs for a Cursor CLI project.
17+
18+
Args:
19+
workspace_id: Workspace id with ``cli:`` prefix (e.g. ``cli:proj-1``).
20+
21+
Returns:
22+
``(jsonify({...}), status)`` tuple suitable for a Flask route handler.
23+
Status is 404 when the project is missing, 500 on unexpected errors.
24+
"""
1725
try:
1826
project_id = workspace_id[4:]
1927
cli_projects = list_cli_projects(get_cli_chats_path())
@@ -136,3 +144,7 @@ def _get_cli_workspace_tabs(workspace_id: str):
136144
exc_info=True,
137145
)
138146
return jsonify({"error": "Failed to get CLI workspace tabs"}), 500
147+
148+
149+
# Backward-compatible alias for tests and legacy imports.
150+
_get_cli_workspace_tabs = get_cli_workspace_tabs

services/workspace_db.py

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# ── Global-DB KV loaders ────────────────────────────────────────────────────
1717
# Each function accepts an already-opened sqlite3.Connection (row_factory must
18-
# be set to sqlite3.Row by the caller, as _open_global_db does) and returns
18+
# be set to sqlite3.Row by the caller, as open_global_db does) and returns
1919
# a populated dict. sqlite3.Error is caught internally so a missing or
2020
# corrupt table cannot propagate to callers.
2121

@@ -113,8 +113,16 @@ def load_code_block_diff_map(global_db) -> dict[str, list]:
113113
return diff_map
114114

115115

116-
def _collect_workspace_entries(workspace_path: str) -> list[dict]:
117-
"""Scan workspace directory and return entries with workspace.json."""
116+
def collect_workspace_entries(workspace_path: str) -> list[dict]:
117+
"""Scan workspace directory and return entries with workspace.json.
118+
119+
Args:
120+
workspace_path: Cursor workspace storage root (parent of per-workspace folders).
121+
122+
Returns:
123+
List of dicts with keys ``name`` (folder id) and ``workspaceJsonPath``.
124+
Returns an empty list if ``workspace_path`` is missing or unreadable.
125+
"""
118126
entries = []
119127
try:
120128
for name in os.listdir(workspace_path):
@@ -131,8 +139,15 @@ def _collect_workspace_entries(workspace_path: str) -> list[dict]:
131139
return entries
132140

133141

134-
def _collect_invalid_workspace_ids(workspace_entries: list[dict]) -> set[str]:
135-
"""Workspace IDs whose descriptors have no resolvable folder paths."""
142+
def collect_invalid_workspace_ids(workspace_entries: list[dict]) -> set[str]:
143+
"""Return workspace IDs whose descriptors have no resolvable folder paths.
144+
145+
Args:
146+
workspace_entries: Output of :func:`collect_workspace_entries`.
147+
148+
Returns:
149+
Set of workspace folder names that cannot be mapped to a folder path.
150+
"""
136151
invalid: set[str] = set()
137152
for entry in workspace_entries:
138153
try:
@@ -149,8 +164,19 @@ def _collect_invalid_workspace_ids(workspace_entries: list[dict]) -> set[str]:
149164
return invalid
150165

151166

152-
def _build_composer_id_to_workspace_id(workspace_path: str, workspace_entries: list) -> dict:
153-
"""Build mapping: composerId -> workspaceId from per-workspace state.vscdb."""
167+
def build_composer_id_to_workspace_id(workspace_path: str, workspace_entries: list) -> dict:
168+
"""Build mapping from composer ID to workspace folder name.
169+
170+
Reads ``composer.composerData`` from each workspace's ``state.vscdb``.
171+
Skips workspaces with missing databases or malformed JSON.
172+
173+
Args:
174+
workspace_path: Cursor workspace storage root.
175+
workspace_entries: Output of :func:`collect_workspace_entries`.
176+
177+
Returns:
178+
Dict mapping ``composerId`` strings to workspace folder names.
179+
"""
154180
mapping: dict = {}
155181
for entry in workspace_entries:
156182
db_path = os.path.join(workspace_path, entry["name"], "state.vscdb")
@@ -187,8 +213,17 @@ def _build_composer_id_to_workspace_id(workspace_path: str, workspace_entries: l
187213

188214

189215
@contextmanager
190-
def _open_global_db(workspace_path: str):
191-
"""Yield (conn, path) for the global-storage SQLite db (read-only); (None, path) if the file is missing."""
216+
def open_global_db(workspace_path: str):
217+
"""Open Cursor global storage SQLite database read-only.
218+
219+
Args:
220+
workspace_path: Cursor workspace storage root.
221+
222+
Yields:
223+
``(conn, path)`` where ``conn`` is a :class:`sqlite3.Connection` with
224+
``row_factory=sqlite3.Row``, or ``None`` if the database file is missing
225+
or cannot be opened. ``path`` is always the resolved global DB path.
226+
"""
192227
global_db_path = os.path.join(workspace_path, "..", "globalStorage", "state.vscdb")
193228
global_db_path = os.path.normpath(global_db_path)
194229
if not os.path.isfile(global_db_path):
@@ -205,3 +240,10 @@ def _open_global_db(workspace_path: str):
205240
yield conn, global_db_path
206241
finally:
207242
conn.close()
243+
244+
245+
# Backward-compatible aliases for tests and legacy imports.
246+
_collect_workspace_entries = collect_workspace_entries
247+
_collect_invalid_workspace_ids = collect_invalid_workspace_ids
248+
_build_composer_id_to_workspace_id = build_composer_id_to_workspace_id
249+
_open_global_db = open_global_db

services/workspace_listing.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,37 @@
2020
from utils.workspace_path import get_cli_chats_path
2121
from models import Composer, ParseWarningCollector, SchemaError
2222
from services.workspace_db import (
23-
_build_composer_id_to_workspace_id,
24-
_collect_invalid_workspace_ids,
25-
_collect_workspace_entries,
23+
build_composer_id_to_workspace_id,
24+
collect_invalid_workspace_ids,
25+
collect_workspace_entries,
2626
load_bubble_map,
2727
load_project_layouts_map,
28-
_open_global_db,
28+
open_global_db,
2929
)
3030
from services.workspace_resolver import (
31-
_create_project_name_to_workspace_id_map,
32-
_create_workspace_path_to_id_map,
33-
_determine_project_for_conversation,
34-
_get_workspace_display_name,
35-
_infer_invalid_workspace_aliases,
36-
_infer_workspace_name_from_context,
31+
create_project_name_to_workspace_id_map,
32+
create_workspace_path_to_id_map,
33+
determine_project_for_conversation,
34+
infer_invalid_workspace_aliases,
35+
infer_workspace_name_from_context,
36+
lookup_workspace_display_name,
3737
)
3838

3939

4040
def list_workspace_projects(workspace_path: str, rules: list) -> tuple[list[dict], list[dict]]:
4141
"""Return (projects, warnings) for GET /api/workspaces."""
4242
parse_warnings = ParseWarningCollector()
43-
workspace_entries = _collect_workspace_entries(workspace_path)
44-
invalid_workspace_ids = _collect_invalid_workspace_ids(workspace_entries)
43+
workspace_entries = collect_workspace_entries(workspace_path)
44+
invalid_workspace_ids = collect_invalid_workspace_ids(workspace_entries)
4545

46-
project_name_map = _create_project_name_to_workspace_id_map(workspace_entries)
47-
workspace_path_map = _create_workspace_path_to_id_map(workspace_entries)
48-
composer_id_to_ws = _build_composer_id_to_workspace_id(workspace_path, workspace_entries)
46+
project_name_map = create_project_name_to_workspace_id_map(workspace_entries)
47+
workspace_path_map = create_workspace_path_to_id_map(workspace_entries)
48+
composer_id_to_ws = build_composer_id_to_workspace_id(workspace_path, workspace_entries)
4949

5050
conversation_map: dict[str, list] = {}
5151

5252
# closing semantics now baked into the context manager (issue #17).
53-
with _open_global_db(workspace_path) as (global_db, _):
53+
with open_global_db(workspace_path) as (global_db, _):
5454
if global_db:
5555
def _safe_fetchall(query: str, params: tuple = ()) -> list:
5656
try:
@@ -65,7 +65,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
6565
project_layouts_map: dict[str, list] = load_project_layouts_map(global_db)
6666
bubble_map: dict[str, dict] = load_bubble_map(global_db)
6767

68-
invalid_workspace_aliases = _infer_invalid_workspace_aliases(
68+
invalid_workspace_aliases = infer_invalid_workspace_aliases(
6969
composer_rows=composer_rows,
7070
project_layouts_map=project_layouts_map,
7171
project_name_map=project_name_map,
@@ -107,7 +107,7 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
107107
continue
108108
cd = composer.raw
109109
try:
110-
pid = _determine_project_for_conversation(
110+
pid = determine_project_for_conversation(
111111
cd, cid, project_layouts_map,
112112
project_name_map, workspace_path_map,
113113
workspace_entries, bubble_map, composer_id_to_ws, invalid_workspace_ids,
@@ -192,9 +192,9 @@ def _safe_fetchall(query: str, params: tuple = ()) -> list:
192192
)
193193
mtime = 0
194194

195-
workspace_name = _get_workspace_display_name(workspace_path, primary["name"])
195+
workspace_name = lookup_workspace_display_name(workspace_path, primary["name"])
196196
if workspace_name == primary["name"]:
197-
inferred = _infer_workspace_name_from_context(workspace_path, primary["name"])
197+
inferred = infer_workspace_name_from_context(workspace_path, primary["name"])
198198
workspace_name = inferred or f"Project {primary['name'][:8]}"
199199

200200
if is_excluded_by_rules(rules, workspace_name):

0 commit comments

Comments
 (0)