Skip to content

Commit cb28c16

Browse files
committed
refactor(api): split api/workspaces.py 1,407 → 142 LOC into services/ (closes #25)
The eval flagged api/workspaces.py as a 1,400+ LOC monolith co-locating database access, dict transformation, workspace resolution, project assignment heuristics, CLI session handling, and HTTP response formatting in one file (Section 5.2). The get_workspace_tabs handler alone spanned ~450 lines; _determine_project_for_conversation was 120+ lines of cascading fallbacks. Helpers (_read_json_file) were duplicated across api/workspaces.py and api/composers.py. This refactor extracts the file into focused service modules and leaves three thin route handlers. Behaviour-preserving: same JSON response shapes, same URL routes, same sort orders. All 178 existing tests pass without modification; a small back-compat shim re-exports _infer_invalid_workspace_aliases, _determine_project_for_conversation, and _infer_workspace_name_from_context from api/workspaces so existing test imports continue to work (production callers should import from services/ directly). Extraction layout (matches the order in #25's spec for minimal merge- conflict risk against parallel work): utils/workspace_descriptor.py — pure helpers; one _read_json_file consolidating the duplicate copies from api/workspaces.py and api/composers.py. services/workspace_db.py — _open_global_db, _collect_workspace _entries, _build_composer_id_to_ workspace_id, _collect_invalid_ workspace_ids. services/workspace_resolver.py — _determine_project_for_conversation, _infer_invalid_workspace_aliases, _create_project_name_to_workspace_id _map, _create_workspace_path_to_id_ map, _get_workspace_display_name, _infer_workspace_name_from_context, _get_project_from_file_path. services/cli_tabs.py — _get_cli_workspace_tabs (the CLI-session branch of the tabs route). services/workspace_listing.py — list_workspace_projects (the entire body of GET /api/workspaces). services/workspace_tabs.py — assemble_workspace_tabs (the entire body of GET /api/workspaces/<id>/tabs for non-CLI workspaces). api/workspaces.py is now 142 LOC: three route handlers that parse the request, hand off to the service layer, and JSONify the result. Verified locally: - 178 unit tests pass without modification. - mypy clean on the new modules (no new errors on touched files). - Live smoke on real Cursor workspaceStorage: * GET / -> HTTP 200 * GET /api/workspaces -> HTTP 200, 2 workspaces * GET /api/workspaces/global/tabs -> HTTP 200, 5 tabs * GET /api/search?q=test -> HTTP 200, 7 results No exceptions, no behaviour change observed.
1 parent 95d3140 commit cb28c16

9 files changed

Lines changed: 1335 additions & 1289 deletions

File tree

api/composers.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,11 @@
1313

1414
from utils.workspace_path import resolve_workspace_path
1515
from utils.path_helpers import to_epoch_ms
16+
from utils.workspace_descriptor import _read_json_file
1617

1718
bp = Blueprint("composers", __name__)
1819

1920

20-
def _read_json_file(path: str):
21-
with open(path, "r", encoding="utf-8") as f:
22-
return json.load(f)
23-
24-
2521
@bp.route("/api/composers")
2622
def list_composers():
2723
try:

0 commit comments

Comments
 (0)