code-analysis-client 1.0.3__tar.gz → 1.0.5__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.
Files changed (23) hide show
  1. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/PKG-INFO +24 -1
  2. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/README.md +23 -0
  3. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/__init__.py +14 -0
  4. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/client.py +7 -1
  5. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/file_session.py +28 -24
  6. code_analysis_client-1.0.5/code_analysis_client/responses.py +45 -0
  7. code_analysis_client-1.0.5/code_analysis_client/server_api.py +113 -0
  8. code_analysis_client-1.0.5/code_analysis_client/universal_file.py +117 -0
  9. code_analysis_client-1.0.5/code_analysis_client/version.txt +1 -0
  10. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client.egg-info/PKG-INFO +24 -1
  11. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client.egg-info/SOURCES.txt +3 -0
  12. code_analysis_client-1.0.3/code_analysis_client/version.txt +0 -1
  13. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/commands_proxy.py +0 -0
  14. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/config.py +0 -0
  15. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/exceptions.py +0 -0
  16. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/py.typed +0 -0
  17. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/server_schema.py +0 -0
  18. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client/validation.py +0 -0
  19. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client.egg-info/dependency_links.txt +0 -0
  20. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client.egg-info/requires.txt +0 -0
  21. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/code_analysis_client.egg-info/top_level.txt +0 -0
  22. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/pyproject.toml +0 -0
  23. {code_analysis_client-1.0.3 → code_analysis_client-1.0.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-analysis-client
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: Async JSON-RPC client for the code-analysis MCP server (mcp-proxy-adapter JsonRpcClient)
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -75,6 +75,29 @@ async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
75
75
 
76
76
  Use `call_unified_validated` when you need queue polling. Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
77
77
 
78
+ ## High-level facades (aligned with server 1.0.3)
79
+
80
+ The client does **not** wrap CST commands (`cst_load_file`, …) or legacy file I/O
81
+ (`universal_file_read`, `read_project_text_file`, …). Those commands are removed
82
+ from the server registry. Use the facades below or generic `call` / `commands.*`.
83
+
84
+ | Facade | Property | Server commands |
85
+ |--------|----------|-----------------|
86
+ | Client DB sessions + transfer | `client.file_sessions` | `session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` |
87
+ | Universal edit sessions | `client.universal_files` | `universal_file_open`, `edit`, `write`, `close`, `preview` |
88
+ | Any registered command | `client.call` / `client.commands.<name>` | schema from live `help()` |
89
+
90
+ Canonical command lists: `code_analysis_client.server_api` (also exported as
91
+ `REMOVED_COMMANDS`, `CLIENT_FACADE_COMMANDS`).
92
+
93
+ Sync check (in-process registry):
94
+
95
+ ```bash
96
+ pytest tests/test_client_server_api_sync.py
97
+ ```
98
+
99
+ Package version matches the **code-analysis** server project (currently **1.0.4**).
100
+
78
101
  ## Examples (this repository)
79
102
 
80
103
  Runnable scripts live under `client/examples/`. **Long-form “man page” style
@@ -63,6 +63,29 @@ async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
63
63
 
64
64
  Use `call_unified_validated` when you need queue polling. Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
65
65
 
66
+ ## High-level facades (aligned with server 1.0.3)
67
+
68
+ The client does **not** wrap CST commands (`cst_load_file`, …) or legacy file I/O
69
+ (`universal_file_read`, `read_project_text_file`, …). Those commands are removed
70
+ from the server registry. Use the facades below or generic `call` / `commands.*`.
71
+
72
+ | Facade | Property | Server commands |
73
+ |--------|----------|-----------------|
74
+ | Client DB sessions + transfer | `client.file_sessions` | `session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` |
75
+ | Universal edit sessions | `client.universal_files` | `universal_file_open`, `edit`, `write`, `close`, `preview` |
76
+ | Any registered command | `client.call` / `client.commands.<name>` | schema from live `help()` |
77
+
78
+ Canonical command lists: `code_analysis_client.server_api` (also exported as
79
+ `REMOVED_COMMANDS`, `CLIENT_FACADE_COMMANDS`).
80
+
81
+ Sync check (in-process registry):
82
+
83
+ ```bash
84
+ pytest tests/test_client_server_api_sync.py
85
+ ```
86
+
87
+ Package version matches the **code-analysis** server project (currently **1.0.4**).
88
+
66
89
  ## Examples (this repository)
67
90
 
68
91
  Runnable scripts live under `client/examples/`. **Long-form “man page” style
@@ -18,6 +18,14 @@ from code_analysis_client.config import (
18
18
  )
19
19
  from code_analysis_client.exceptions import ClientValidationError
20
20
  from code_analysis_client.file_session import FileSessionClient, SessionNotFoundError
21
+ from code_analysis_client.server_api import (
22
+ CLIENT_FACADE_COMMANDS,
23
+ CST_REMOVED_COMMANDS,
24
+ LEGACY_REMOVED_COMMANDS,
25
+ REMOVED_COMMANDS,
26
+ UNIVERSAL_FILE_COMMANDS,
27
+ )
28
+ from code_analysis_client.universal_file import UniversalFileClient
21
29
  from code_analysis_client.server_schema import (
22
30
  fetch_command_schema_from_server,
23
31
  parse_schema_from_help_payload,
@@ -28,10 +36,16 @@ from code_analysis_client.validation import (
28
36
  )
29
37
 
30
38
  __all__ = [
39
+ "CLIENT_FACADE_COMMANDS",
40
+ "CST_REMOVED_COMMANDS",
31
41
  "ClientValidationError",
32
42
  "CodeAnalysisAsyncClient",
33
43
  "FileSessionClient",
44
+ "LEGACY_REMOVED_COMMANDS",
45
+ "REMOVED_COMMANDS",
34
46
  "SessionNotFoundError",
47
+ "UNIVERSAL_FILE_COMMANDS",
48
+ "UniversalFileClient",
35
49
  "ValidatedCommandsProxy",
36
50
  "adapter_settings_from_server_config",
37
51
  "adapter_settings_to_jsonrpc_kwargs",
@@ -22,6 +22,7 @@ from code_analysis_client.config import (
22
22
  load_server_config,
23
23
  )
24
24
  from code_analysis_client.file_session import FileSessionClient
25
+ from code_analysis_client.universal_file import UniversalFileClient
25
26
  from code_analysis_client.server_schema import fetch_command_schema_from_server
26
27
  from code_analysis_client.validation import (
27
28
  prepare_params_for_schema,
@@ -122,9 +123,14 @@ class CodeAnalysisAsyncClient:
122
123
 
123
124
  @property
124
125
  def file_sessions(self) -> FileSessionClient:
125
- """Session-scoped download/upload and lock workflow (no CST/universal edit details)."""
126
+ """Session-scoped download/upload and lock workflow (``session_*``, transfer)."""
126
127
  return FileSessionClient(self)
127
128
 
129
+ @property
130
+ def universal_files(self) -> UniversalFileClient:
131
+ """Universal edit-session workflow (``universal_file_*`` commands)."""
132
+ return UniversalFileClient(self)
133
+
128
134
  async def get_command_schema(
129
135
  self, command: str, *, refresh: bool = False
130
136
  ) -> Dict[str, Any]:
@@ -15,6 +15,7 @@ from pathlib import Path
15
15
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
16
16
 
17
17
  from code_analysis_client.exceptions import ClientValidationError
18
+ from code_analysis_client.responses import unwrap_command_result
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from code_analysis_client.client import CodeAnalysisAsyncClient
@@ -25,24 +26,22 @@ class SessionNotFoundError(ClientValidationError):
25
26
 
26
27
 
27
28
  def _unwrap(data: Dict[str, Any]) -> Dict[str, Any]:
28
- if data.get("success"):
29
- inner = data.get("data")
30
- return inner if isinstance(inner, dict) else data
31
- code_raw = data.get("code")
32
- message = data.get("message")
33
- err = data.get("error")
34
- if code_raw is None and isinstance(err, dict):
35
- code_raw = err.get("code")
36
- if message is None:
37
- message = err.get("message")
38
- elif code_raw is None and isinstance(err, str):
39
- code_raw = err
40
- code = str(code_raw or "COMMAND_FAILED")
41
- if message is None:
42
- message = str(data)
43
- if code == "SESSION_NOT_FOUND":
44
- raise SessionNotFoundError(message, field="session_id", details=data)
45
- raise ClientValidationError(message, field="command", details=data)
29
+ return unwrap_command_result(
30
+ data,
31
+ session_not_found_type=SessionNotFoundError,
32
+ )
33
+
34
+
35
+ def _extract_file_id(payload: Dict[str, Any], *, field: str = "file_id") -> str:
36
+ """Return non-empty ``file_id`` from a command payload or raise."""
37
+ fid = str(payload.get(field) or "").strip()
38
+ if not fid:
39
+ raise ClientValidationError(
40
+ f"{field} missing from server response",
41
+ field=field,
42
+ details=payload,
43
+ )
44
+ return fid
46
45
 
47
46
 
48
47
  class FileSessionClient:
@@ -230,7 +229,7 @@ class FileSessionClient:
230
229
  chunk_size: Optional[int] = None,
231
230
  include_backup_history: bool = False,
232
231
  ) -> Dict[str, Any]:
233
- """Start a chunked download and acquire an advisory lock (``project_file_transfer_download_begin``)."""
232
+ """Begin chunked download; returns payload including ``file_id`` and ``transfer_id``."""
234
233
  await self.assert_session_exists(session_id)
235
234
  params: Dict[str, Any] = {
236
235
  "session_id": session_id,
@@ -246,12 +245,14 @@ class FileSessionClient:
246
245
  params["file_path"] = file_path
247
246
  if chunk_size is not None:
248
247
  params["chunk_size"] = chunk_size
249
- return _unwrap(
248
+ payload = _unwrap(
250
249
  await self._client.call_validated(
251
250
  "project_file_transfer_download_begin",
252
251
  params,
253
252
  )
254
253
  )
254
+ _extract_file_id(payload)
255
+ return payload
255
256
 
256
257
  async def download_to_path(
257
258
  self,
@@ -275,7 +276,7 @@ class FileSessionClient:
275
276
  compression: str = "identity",
276
277
  lock_mode: str = "full",
277
278
  ) -> Tuple[Dict[str, Any], Any]:
278
- """Download + lock in one workflow; returns (begin_payload, download_receipt)."""
279
+ """Download + lock; returns (begin_payload with ``file_id``, download_receipt)."""
279
280
  begin = await self.begin_download_locked(
280
281
  session_id,
281
282
  project_id=project_id,
@@ -326,7 +327,7 @@ class FileSessionClient:
326
327
  dry_run: bool = False,
327
328
  backup: bool = True,
328
329
  ) -> Dict[str, Any]:
329
- """Persist uploaded buffer and release locks (``project_file_transfer_upload_save``)."""
330
+ """Persist uploaded buffer and release locks; response includes ``file_id``."""
330
331
  await self.assert_session_exists(session_id)
331
332
  params: Dict[str, Any] = {
332
333
  "session_id": session_id,
@@ -341,12 +342,15 @@ class FileSessionClient:
341
342
  params["file_id"] = file_id
342
343
  if file_path is not None:
343
344
  params["file_path"] = file_path
344
- return _unwrap(
345
+ payload = _unwrap(
345
346
  await self._client.call_validated(
346
347
  "project_file_transfer_upload_save",
347
348
  params,
348
349
  )
349
350
  )
351
+ if not dry_run:
352
+ _extract_file_id(payload)
353
+ return payload
350
354
 
351
355
  async def upload_file_and_unlock(
352
356
  self,
@@ -361,7 +365,7 @@ class FileSessionClient:
361
365
  unlock_after_write: bool = True,
362
366
  backup: bool = True,
363
367
  ) -> Dict[str, Any]:
364
- """Upload bytes then save to the project file and unlock."""
368
+ """Upload bytes, save to project file, unlock; returns save payload with ``file_id``."""
365
369
  name = filename or (Path(file_path).name if file_path else "payload.bin")
366
370
  receipt = await self.upload_bytes(
367
371
  payload,
@@ -0,0 +1,45 @@
1
+ """Parse JSON-RPC command envelopes from code-analysis-server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict
6
+
7
+ from code_analysis_client.exceptions import ClientValidationError
8
+
9
+
10
+ def command_error_code(data: Dict[str, Any]) -> str:
11
+ """Return domain or JSON-RPC error code from a failed command response."""
12
+ if data.get("success") is True:
13
+ return ""
14
+ top = data.get("code")
15
+ if top is not None and str(top).strip():
16
+ return str(top).strip()
17
+ err = data.get("error")
18
+ if isinstance(err, dict):
19
+ nested = err.get("code")
20
+ if nested is not None:
21
+ return str(nested).strip()
22
+ if isinstance(err, str) and err.strip():
23
+ return err.strip()
24
+ return ""
25
+
26
+
27
+ def unwrap_command_result(
28
+ data: Dict[str, Any],
29
+ *,
30
+ session_not_found_type: type[ClientValidationError] | None = None,
31
+ ) -> Dict[str, Any]:
32
+ """Return success ``data`` dict or raise :class:`ClientValidationError`."""
33
+ if data.get("success"):
34
+ inner = data.get("data")
35
+ return inner if isinstance(inner, dict) else data
36
+ code = command_error_code(data)
37
+ message = data.get("message")
38
+ err = data.get("error")
39
+ if message is None and isinstance(err, dict):
40
+ message = err.get("message")
41
+ if message is None:
42
+ message = str(data)
43
+ if code == "SESSION_NOT_FOUND" and session_not_found_type is not None:
44
+ raise session_not_found_type(message, field="session_id", details=data)
45
+ raise ClientValidationError(message, field="command", details=data)
@@ -0,0 +1,113 @@
1
+ """
2
+ Canonical command names for the high-level client API vs the live server registry.
3
+
4
+ The generic client (:class:`CodeAnalysisAsyncClient.call`) can invoke any command
5
+ returned by server ``help``. Facade modules (:mod:`file_session`, :mod:`universal_file`)
6
+ must only reference commands listed here — and every name in
7
+ :data:`CLIENT_FACADE_COMMANDS` must be registered on the server.
8
+
9
+ Author: Vasiliy Zdanovskiy
10
+ email: vasilyvz@gmail.com
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Callable, FrozenSet
16
+
17
+ # Removed from the public server surface — do not use in client facades or docs.
18
+ LEGACY_REMOVED_COMMANDS: FrozenSet[str] = frozenset(
19
+ {
20
+ "universal_file_read",
21
+ "universal_file_save",
22
+ "universal_file_replace",
23
+ "universal_file_delete",
24
+ "read_project_text_file",
25
+ "write_project_text_lines",
26
+ "create_text_file",
27
+ "replace_file_lines",
28
+ }
29
+ )
30
+
31
+ # CST tree MCP workflow — modules may exist in the repo but commands are not registered.
32
+ CST_REMOVED_COMMANDS: FrozenSet[str] = frozenset(
33
+ {
34
+ "cst_load_file",
35
+ "cst_save_tree",
36
+ "cst_modify_tree",
37
+ "cst_create_file",
38
+ "cst_find_node",
39
+ "cst_get_node_info",
40
+ "cst_get_node_by_range",
41
+ "cst_get_node_at_line",
42
+ "cst_reload_tree",
43
+ "cst_convert_and_save",
44
+ "list_cst_blocks",
45
+ "query_cst",
46
+ "get_file_lines",
47
+ }
48
+ )
49
+
50
+ REMOVED_COMMANDS: FrozenSet[str] = LEGACY_REMOVED_COMMANDS | CST_REMOVED_COMMANDS
51
+
52
+ FILE_SESSION_COMMANDS: FrozenSet[str] = frozenset(
53
+ {
54
+ "session_create",
55
+ "session_delete",
56
+ "session_list",
57
+ "session_open_file",
58
+ "session_close_file",
59
+ "session_list_file_locks",
60
+ }
61
+ )
62
+
63
+ TRANSFER_AND_LOCK_COMMANDS: FrozenSet[str] = frozenset(
64
+ {
65
+ "project_file_transfer_download_begin",
66
+ "project_file_transfer_upload_save",
67
+ "project_file_advisory_lock_batch",
68
+ }
69
+ )
70
+
71
+ UNIVERSAL_FILE_COMMANDS: FrozenSet[str] = frozenset(
72
+ {
73
+ "universal_file_open",
74
+ "universal_file_edit",
75
+ "universal_file_write",
76
+ "universal_file_close",
77
+ "universal_file_preview",
78
+ }
79
+ )
80
+
81
+ CLIENT_FACADE_COMMANDS: FrozenSet[str] = (
82
+ FILE_SESSION_COMMANDS | TRANSFER_AND_LOCK_COMMANDS | UNIVERSAL_FILE_COMMANDS
83
+ )
84
+
85
+
86
+ def _command_registered(get_command: Callable[[str], object], name: str) -> bool:
87
+ try:
88
+ get_command(name)
89
+ return True
90
+ except KeyError:
91
+ return False
92
+
93
+
94
+ def assert_facade_commands_registered(get_command: Callable[[str], object]) -> None:
95
+ """Raise ``KeyError`` if any facade command is missing from the server registry."""
96
+ missing = [
97
+ name
98
+ for name in sorted(CLIENT_FACADE_COMMANDS)
99
+ if not _command_registered(get_command, name)
100
+ ]
101
+ if missing:
102
+ raise KeyError(f"Client facade commands not registered on server: {missing}")
103
+
104
+
105
+ def assert_removed_commands_absent(get_command: Callable[[str], object]) -> None:
106
+ """Raise ``AssertionError`` if a removed command is still registered."""
107
+ present = [
108
+ name
109
+ for name in sorted(REMOVED_COMMANDS)
110
+ if _command_registered(get_command, name)
111
+ ]
112
+ if present:
113
+ raise AssertionError(f"Removed commands still registered: {present}")
@@ -0,0 +1,117 @@
1
+ """
2
+ Universal file edit-session workflow on top of :class:`CodeAnalysisAsyncClient`.
3
+
4
+ Wraps ``universal_file_open`` / ``edit`` / ``write`` / ``close`` / ``preview`` only.
5
+ Legacy ``universal_file_read`` / ``universal_file_save`` and CST commands are not
6
+ part of this API (see :mod:`server_api`).
7
+
8
+ Author: Vasiliy Zdanovskiy
9
+ email: vasilyvz@gmail.com
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
15
+
16
+ from code_analysis_client.responses import unwrap_command_result
17
+
18
+ if TYPE_CHECKING:
19
+ from code_analysis_client.client import CodeAnalysisAsyncClient
20
+
21
+
22
+ class UniversalFileClient:
23
+ """Edit-session workflow for project files (open → edit → write → close)."""
24
+
25
+ __slots__ = ("_client",)
26
+
27
+ def __init__(self, client: CodeAnalysisAsyncClient) -> None:
28
+ self._client = client
29
+
30
+ async def open(
31
+ self,
32
+ project_id: str,
33
+ file_path: str,
34
+ *,
35
+ create: bool = False,
36
+ initial_content: Optional[str] = None,
37
+ ) -> Dict[str, Any]:
38
+ """Start an edit session (``universal_file_open``)."""
39
+ params: Dict[str, Any] = {
40
+ "project_id": project_id,
41
+ "file_path": file_path,
42
+ "create": create,
43
+ }
44
+ if initial_content is not None:
45
+ params["initial_content"] = initial_content
46
+ return unwrap_command_result(
47
+ await self._client.call_validated("universal_file_open", params)
48
+ )
49
+
50
+ async def edit(
51
+ self,
52
+ project_id: str,
53
+ session_id: str,
54
+ operations: List[Dict[str, Any]],
55
+ ) -> Dict[str, Any]:
56
+ """Apply draft mutations (``universal_file_edit``)."""
57
+ return unwrap_command_result(
58
+ await self._client.call_validated(
59
+ "universal_file_edit",
60
+ {
61
+ "project_id": project_id,
62
+ "session_id": session_id,
63
+ "operations": operations,
64
+ },
65
+ )
66
+ )
67
+
68
+ async def write(
69
+ self,
70
+ project_id: str,
71
+ session_id: str,
72
+ *,
73
+ write_mode: str = "commit",
74
+ ) -> Dict[str, Any]:
75
+ """Persist or preview draft (``universal_file_write``)."""
76
+ return unwrap_command_result(
77
+ await self._client.call_validated(
78
+ "universal_file_write",
79
+ {
80
+ "project_id": project_id,
81
+ "session_id": session_id,
82
+ "write_mode": write_mode,
83
+ },
84
+ )
85
+ )
86
+
87
+ async def close(
88
+ self,
89
+ project_id: str,
90
+ session_id: str,
91
+ ) -> Dict[str, Any]:
92
+ """End edit session (``universal_file_close``)."""
93
+ return unwrap_command_result(
94
+ await self._client.call_validated(
95
+ "universal_file_close",
96
+ {
97
+ "project_id": project_id,
98
+ "session_id": session_id,
99
+ },
100
+ )
101
+ )
102
+
103
+ async def preview(
104
+ self,
105
+ project_id: str,
106
+ file_path: str,
107
+ **kwargs: Any,
108
+ ) -> Dict[str, Any]:
109
+ """Structured read-only preview (``universal_file_preview``)."""
110
+ params: Dict[str, Any] = {
111
+ "project_id": project_id,
112
+ "file_path": file_path,
113
+ }
114
+ params.update(kwargs)
115
+ return unwrap_command_result(
116
+ await self._client.call_validated("universal_file_preview", params)
117
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-analysis-client
3
- Version: 1.0.3
3
+ Version: 1.0.5
4
4
  Summary: Async JSON-RPC client for the code-analysis MCP server (mcp-proxy-adapter JsonRpcClient)
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -75,6 +75,29 @@ async with CodeAnalysisAsyncClient(host="127.0.0.1", port=15001) as client:
75
75
 
76
76
  Use `call_unified_validated` when you need queue polling. Pass `refresh_schema=True` on a single call to bypass the in-memory schema cache.
77
77
 
78
+ ## High-level facades (aligned with server 1.0.3)
79
+
80
+ The client does **not** wrap CST commands (`cst_load_file`, …) or legacy file I/O
81
+ (`universal_file_read`, `read_project_text_file`, …). Those commands are removed
82
+ from the server registry. Use the facades below or generic `call` / `commands.*`.
83
+
84
+ | Facade | Property | Server commands |
85
+ |--------|----------|-----------------|
86
+ | Client DB sessions + transfer | `client.file_sessions` | `session_*`, `project_file_transfer_*`, `project_file_advisory_lock_batch` |
87
+ | Universal edit sessions | `client.universal_files` | `universal_file_open`, `edit`, `write`, `close`, `preview` |
88
+ | Any registered command | `client.call` / `client.commands.<name>` | schema from live `help()` |
89
+
90
+ Canonical command lists: `code_analysis_client.server_api` (also exported as
91
+ `REMOVED_COMMANDS`, `CLIENT_FACADE_COMMANDS`).
92
+
93
+ Sync check (in-process registry):
94
+
95
+ ```bash
96
+ pytest tests/test_client_server_api_sync.py
97
+ ```
98
+
99
+ Package version matches the **code-analysis** server project (currently **1.0.4**).
100
+
78
101
  ## Examples (this repository)
79
102
 
80
103
  Runnable scripts live under `client/examples/`. **Long-form “man page” style
@@ -7,7 +7,10 @@ code_analysis_client/config.py
7
7
  code_analysis_client/exceptions.py
8
8
  code_analysis_client/file_session.py
9
9
  code_analysis_client/py.typed
10
+ code_analysis_client/responses.py
11
+ code_analysis_client/server_api.py
10
12
  code_analysis_client/server_schema.py
13
+ code_analysis_client/universal_file.py
11
14
  code_analysis_client/validation.py
12
15
  code_analysis_client/version.txt
13
16
  code_analysis_client.egg-info/PKG-INFO