deepcell-cli 0.6.1__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.
Files changed (67) hide show
  1. deepcell_cli/__init__.py +12 -0
  2. deepcell_cli/__main__.py +5 -0
  3. deepcell_cli/_findings.py +84 -0
  4. deepcell_cli/capabilities.py +560 -0
  5. deepcell_cli/capability-contract.json +15622 -0
  6. deepcell_cli/client.py +503 -0
  7. deepcell_cli/commands/__init__.py +1 -0
  8. deepcell_cli/commands/_batch_input.py +29 -0
  9. deepcell_cli/commands/_datatypes.py +56 -0
  10. deepcell_cli/commands/_negative_args.py +133 -0
  11. deepcell_cli/commands/_swapped_args.py +153 -0
  12. deepcell_cli/commands/_version_display.py +40 -0
  13. deepcell_cli/commands/_write_opts.py +139 -0
  14. deepcell_cli/commands/account.py +123 -0
  15. deepcell_cli/commands/auth.py +610 -0
  16. deepcell_cli/commands/changes.py +307 -0
  17. deepcell_cli/commands/deck.py +594 -0
  18. deepcell_cli/commands/defs.py +3890 -0
  19. deepcell_cli/commands/describe.py +902 -0
  20. deepcell_cli/commands/doc.py +529 -0
  21. deepcell_cli/commands/doctor.py +257 -0
  22. deepcell_cli/commands/download.py +36 -0
  23. deepcell_cli/commands/edit.py +384 -0
  24. deepcell_cli/commands/example.py +161 -0
  25. deepcell_cli/commands/export.py +81 -0
  26. deepcell_cli/commands/export_docx.py +57 -0
  27. deepcell_cli/commands/export_pdf.py +66 -0
  28. deepcell_cli/commands/export_pptx.py +45 -0
  29. deepcell_cli/commands/files.py +386 -0
  30. deepcell_cli/commands/grep.py +90 -0
  31. deepcell_cli/commands/guide.py +431 -0
  32. deepcell_cli/commands/help_cmd.py +348 -0
  33. deepcell_cli/commands/impact.py +382 -0
  34. deepcell_cli/commands/import_cmd.py +208 -0
  35. deepcell_cli/commands/ingest.py +110 -0
  36. deepcell_cli/commands/merge.py +399 -0
  37. deepcell_cli/commands/query.py +718 -0
  38. deepcell_cli/commands/reasoning.py +2981 -0
  39. deepcell_cli/commands/ref.py +279 -0
  40. deepcell_cli/commands/replace.py +326 -0
  41. deepcell_cli/commands/rules.py +206 -0
  42. deepcell_cli/commands/share.py +186 -0
  43. deepcell_cli/commands/sync.py +804 -0
  44. deepcell_cli/commands/upgrade.py +185 -0
  45. deepcell_cli/commands/variant.py +353 -0
  46. deepcell_cli/commands/version.py +445 -0
  47. deepcell_cli/commands/viewer.py +54 -0
  48. deepcell_cli/commands/workspace.py +101 -0
  49. deepcell_cli/config.py +352 -0
  50. deepcell_cli/context.py +187 -0
  51. deepcell_cli/errors.py +141 -0
  52. deepcell_cli/logging_setup.py +161 -0
  53. deepcell_cli/main.py +518 -0
  54. deepcell_cli/mcp_server.py +906 -0
  55. deepcell_cli/oauth_provider.py +580 -0
  56. deepcell_cli/output.py +503 -0
  57. deepcell_cli/revision.py +164 -0
  58. deepcell_cli/stages.py +223 -0
  59. deepcell_cli/surface.py +628 -0
  60. deepcell_cli/sync_state.py +120 -0
  61. deepcell_cli/upgrade_check.py +399 -0
  62. deepcell_cli/xml_replace.py +89 -0
  63. deepcell_cli-0.6.1.dist-info/METADATA +264 -0
  64. deepcell_cli-0.6.1.dist-info/RECORD +67 -0
  65. deepcell_cli-0.6.1.dist-info/WHEEL +5 -0
  66. deepcell_cli-0.6.1.dist-info/entry_points.txt +3 -0
  67. deepcell_cli-0.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,12 @@
1
+ """DeepCell CLI — command-line interface for the DeepCell platform.
2
+
3
+ ``__version__`` is the *runtime* version — what `--version` prints, what the
4
+ client header sends, and what the upgrade check compares against PyPI. The
5
+ packaging version in ``cli/pyproject.toml`` is a second copy that must say the
6
+ same thing, and a third is stamped into ``docs/cli-surface.json`` by
7
+ ``scripts/gen_cli_surface.py``. All three are pinned together by
8
+ ``cli/tests/test_version_parity.py`` — bumping one alone ships a build that
9
+ reports a version it is not, and skipping the regen lands a red build.
10
+ """
11
+
12
+ __version__ = "0.6.1"
@@ -0,0 +1,5 @@
1
+ """Allow running as ``python -m deepcell_cli``."""
2
+
3
+ from deepcell_cli.main import cli
4
+
5
+ cli()
@@ -0,0 +1,84 @@
1
+ """Shared rendering for lint findings that cite a house rule.
2
+
3
+ The highest-leverage place to put a cross-reference is the failure site: the
4
+ agent is already there and already paying attention, so a rule id printed
5
+ beside the finding costs zero navigation. This module is the one place that
6
+ decides how that citation looks, so the three CLI surfaces that render
7
+ findings — ``describe --lint``, ``reasoning lint``, and the post-write findings
8
+ from ``reasoning add-*`` — cannot drift into three notations.
9
+
10
+ Findings carry ``house_rule`` (e.g. ``"R8"``) only when their lint code
11
+ enforces one. Most codes are structural checks no house rule claims, so an
12
+ unstamped finding is normal and renders exactly as it did before.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any, Iterable
18
+
19
+
20
+ def rule_citation(finding: dict[str, Any]) -> str:
21
+ """Return ``" rule:R2"`` for a stamped finding, or ``""`` for the rest.
22
+
23
+ Includes its own leading space so callers can append unconditionally
24
+ without emitting a trailing space when there is no rule.
25
+ """
26
+ rule_id = finding.get("house_rule")
27
+ return f" rule:{rule_id}" if rule_id else ""
28
+
29
+
30
+ def provenance(finding: dict[str, Any]) -> str:
31
+ """Return ``" (pre-existing)"`` / ``" (new)"``, or ``""`` when unknown.
32
+
33
+ A finding on its own does not say whether the change that surfaced it is
34
+ what caused it, and a caller who cannot tell reads every finding as damage
35
+ they just did. The 2026-08-02 CLI eval has the shape on tape: a worker
36
+ fixed one warning, the response came back carrying an `orphan_claim` that
37
+ had been true all along, and it spent four commands chasing a problem its
38
+ edit had not introduced.
39
+
40
+ Both states are labelled rather than only the surprising one. Marking just
41
+ the pre-existing ones would leave an unlabelled finding ambiguous between
42
+ "new" and "this surface does not know", which is the ambiguity the stamp
43
+ exists to remove. Surfaces with no baseline to compare against (the
44
+ read-only `reasoning lint`) send no stamp and render exactly as before.
45
+ """
46
+ pre_existing = finding.get("pre_existing")
47
+ if pre_existing is None:
48
+ return ""
49
+ return " (pre-existing)" if pre_existing else " (new)"
50
+
51
+
52
+ def cited_rules_footer(findings: Iterable[dict[str, Any]]) -> str | None:
53
+ """Return one line telling the reader how to read the rules just cited.
54
+
55
+ One footer rather than a pointer per finding: a forty-finding lint would
56
+ otherwise repeat the same instruction forty times, which trains the reader
57
+ to skip it. ``None`` when there is nothing to point at.
58
+
59
+ Two lines at most — the house rules cited, and how to explain a lint code.
60
+ The second is capped at three codes because the footer is an affordance,
61
+ not a second copy of the findings list.
62
+ """
63
+ seen: list[str] = []
64
+ codes: list[str] = []
65
+ for finding in findings:
66
+ rule_id = finding.get("house_rule")
67
+ if rule_id and rule_id not in seen:
68
+ seen.append(rule_id)
69
+ code = finding.get("rule")
70
+ if code and code not in codes:
71
+ codes.append(code)
72
+
73
+ parts: list[str] = []
74
+ if seen:
75
+ parts.append("Cited rules: " + " · ".join(f"deepcell rules {r}" for r in seen))
76
+ # Every finding has a code, and `deepcell ref lint/<code>` explains what it
77
+ # flags and the fix — so this line is useful even when no house rule was
78
+ # cited, which is the majority case.
79
+ if codes:
80
+ parts.append(
81
+ "Explain a code: " + " · ".join(f"deepcell ref lint/{c}" for c in codes[:3])
82
+ + (" …" if len(codes) > 3 else "")
83
+ )
84
+ return "\n".join(parts) if parts else None
@@ -0,0 +1,560 @@
1
+ """Authoritative cross-surface capability policy.
2
+
3
+ The Click tree owns command syntax and ``backend/openapi.json`` owns Jingwei
4
+ methods, paths, and schemas. This module owns the facts neither source can
5
+ express by itself: which operations a CLI command reaches, which transports
6
+ can run a command, and why an operation deliberately has no CLI route.
7
+
8
+ ``scripts/gen_cli_surface.py`` expands this policy against both authorities and
9
+ writes the resulting contract into every runtime that needs it. Keep entries
10
+ keyed by OpenAPI ``operationId`` rather than copying paths or request schemas;
11
+ codegen fails when an id disappears, a new operation is unclassified, or a
12
+ command pattern matches nothing.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ from functools import lru_cache
19
+ from pathlib import Path
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Transport policy
24
+ # ---------------------------------------------------------------------------
25
+
26
+ # Every top-level Click command is listed exactly once across these two
27
+ # collections. Default-allow would make a newly added destructive command
28
+ # remotely callable until somebody happened to audit it.
29
+ MCP_ALLOWED_COMMANDS = frozenset(
30
+ {
31
+ "assumption",
32
+ "cat",
33
+ "cell-meta",
34
+ "changes",
35
+ "claim",
36
+ "deck",
37
+ "defs",
38
+ "describe",
39
+ "diff",
40
+ "doc",
41
+ "doctor",
42
+ "download",
43
+ "edit",
44
+ "example",
45
+ "grep",
46
+ "guide",
47
+ "help",
48
+ "impact",
49
+ "import",
50
+ "ingest",
51
+ "log",
52
+ "ls",
53
+ "project",
54
+ "query",
55
+ "reasoning",
56
+ "reasoning-diff",
57
+ "ref",
58
+ "relationships",
59
+ "replace",
60
+ "restore",
61
+ "rm",
62
+ "rules",
63
+ "share",
64
+ "variant",
65
+ "viewer",
66
+ "whoami",
67
+ "write",
68
+ }
69
+ )
70
+
71
+ # Reason codes are stable data rendered into help, guide, MCP errors, and the
72
+ # for-agent page. The detail is for maintainers and structured clients; user
73
+ # prose remains translated at its own surface.
74
+ MCP_BLOCKED_COMMANDS: dict[str, dict[str, str]] = {
75
+ "account": {
76
+ "reason": "identity_destructive",
77
+ "detail": "exports or irreversibly deletes the caller's account",
78
+ },
79
+ "clone": {
80
+ "reason": "local_checkout",
81
+ "detail": "creates and updates a local Git working copy",
82
+ },
83
+ "commit": {
84
+ "reason": "local_checkout",
85
+ "detail": "reads local sync state and commits a working copy",
86
+ },
87
+ "login": {
88
+ "reason": "interactive_auth",
89
+ "detail": "opens or polls an interactive browser authentication flow",
90
+ },
91
+ "logout": {
92
+ "reason": "shared_credentials",
93
+ "detail": "mutates process-global CLI credentials on the MCP host",
94
+ },
95
+ "merge": {
96
+ "reason": "local_checkout",
97
+ "detail": "reads and mutates local sync and conflict state",
98
+ },
99
+ "pull": {
100
+ "reason": "local_checkout",
101
+ "detail": "updates files in a local Git working copy",
102
+ },
103
+ "push": {
104
+ "reason": "local_checkout",
105
+ "detail": "reads and uploads files from a local Git working copy",
106
+ },
107
+ "register": {
108
+ "reason": "interactive_auth",
109
+ "detail": "creates identity-bearing credentials through prompts",
110
+ },
111
+ "status": {
112
+ "reason": "local_checkout",
113
+ "detail": "compares the remote workspace with local sync state",
114
+ },
115
+ "to-docx": {
116
+ "reason": "binary_download",
117
+ "detail": "writes a binary export to the MCP server's filesystem",
118
+ },
119
+ "to-excel": {
120
+ "reason": "binary_download",
121
+ "detail": "writes a binary export to the MCP server's filesystem",
122
+ },
123
+ "to-pdf": {
124
+ "reason": "binary_download",
125
+ "detail": "writes a binary export to the MCP server's filesystem",
126
+ },
127
+ "to-pptx": {
128
+ "reason": "binary_download",
129
+ "detail": "writes a binary export to the MCP server's filesystem",
130
+ },
131
+ "upgrade": {
132
+ "reason": "shared_host_install",
133
+ "detail": "would inspect or replace the CLI installed on the MCP host",
134
+ },
135
+ "verify-email": {
136
+ "reason": "interactive_auth",
137
+ "detail": "prompts for a verification token sent out of band",
138
+ },
139
+ }
140
+
141
+ # The LangGraph in-process tool deliberately exposes a smaller service-layer
142
+ # subset. Its implementation table is checked against this declaration in
143
+ # both directions; this list is the cross-surface availability decision, while
144
+ # ``deepcell_tool._COMMANDS`` remains the parser/dispatcher implementation.
145
+ IN_PROCESS_COMMANDS = frozenset(
146
+ {
147
+ "cat",
148
+ "defs apply",
149
+ "describe",
150
+ "example",
151
+ "guide",
152
+ "help",
153
+ "ls",
154
+ "query",
155
+ "ref",
156
+ "rules",
157
+ }
158
+ )
159
+
160
+
161
+ # ---------------------------------------------------------------------------
162
+ # Jingwei operation -> CLI command mapping
163
+ # ---------------------------------------------------------------------------
164
+
165
+ # Values are fnmatch patterns over leaf command paths in the generated Click
166
+ # surface. The generator expands them to exact names and rejects a pattern
167
+ # that matches nothing. An operation may support several commands and a
168
+ # command may use several operations.
169
+ ENDPOINT_COMMANDS: dict[str, tuple[str, ...]] = {
170
+ # Service/auth lifecycle.
171
+ "health_check_health_get": ("doctor",),
172
+ "claim_demo_auth_claim_demo_post": ("login", "register"),
173
+ "device_code_auth_device_code_post": ("login",),
174
+ "device_token_auth_device_token_post": ("login",),
175
+ "login_auth_login_post": ("login",),
176
+ "register_auth_register_post": ("register",),
177
+ "revoke_token_auth_revoke_token_post": ("logout",),
178
+ "send_verification_auth_send_verification_post": ("verify-email",),
179
+ "verify_email_auth_verify_email_post": ("verify-email",),
180
+ "get_me_auth_me_get": ("whoami",),
181
+ "export_me_auth_me_export_get": ("account export",),
182
+ "delete_me_auth_me_delete": ("account delete",),
183
+
184
+ # Primary document/query/edit routes.
185
+ "apply_defs_ops_endpoint_apply_defs_ops_post": (
186
+ "defs add-*",
187
+ "defs update-*",
188
+ "defs delete-*",
189
+ "defs rename-*",
190
+ "defs reorder-*",
191
+ "defs set-*",
192
+ "defs header *",
193
+ "defs apply",
194
+ # The Deck surface's structural ops. Same endpoint as `defs` — a deck
195
+ # op is a defs op; `deepcell deck` is a front door onto the flag-shaped
196
+ # subset of them, not a second route.
197
+ "deck add",
198
+ "deck add-slide",
199
+ "deck rename",
200
+ "deck rename-slide",
201
+ "deck reorder",
202
+ "deck reorder-slide",
203
+ "deck delete",
204
+ "deck delete-slide",
205
+ "deck bind",
206
+ "deck unbind",
207
+ "deck brand",
208
+ "doc stamp-ids",
209
+ "doc replace-block",
210
+ "doc insert-block",
211
+ "doc move-block",
212
+ "doc delete-block",
213
+ "doc set-body",
214
+ "doc patch-body",
215
+ "doc set-notation",
216
+ ),
217
+ "batch_edit_values_batch_edit_post": ("edit",),
218
+ "get_cell_meta_cell_meta_post": ("cell-meta",),
219
+ "list_defs_endpoint_defs_list_post": ("defs list",),
220
+ "show_defs_endpoint_defs_show_post": ("defs show",),
221
+ "describe_document_describe_post": ("describe",),
222
+ "query_deepcell_query_post": ("query",),
223
+ "get_relationships_relationships_post": ("relationships",),
224
+ "document_backlinks_document_backlinks_post": ("doc backlinks",),
225
+ "impact_impact_post": ("impact show",),
226
+ "review_impact_review_post": ("impact review",),
227
+ "apply_impact_apply_post": ("impact apply",),
228
+ "reopen_impact_reopen_post": ("impact reopen",),
229
+ "document_blocks_document_blocks_post": ("doc blocks",),
230
+ "document_links_document_links_post": ("doc links",),
231
+ "document_lint_document_lint_post": ("doc lint",),
232
+ "list_documents_document_list_post": ("doc list",),
233
+ "document_outline_document_outline_post": ("doc outline",),
234
+ "show_document_document_show_post": ("doc show",),
235
+
236
+ # Reasoning graph.
237
+ "edit_reasoning_edit_post": (
238
+ "reasoning add-*",
239
+ "reasoning update-*",
240
+ "reasoning delete-*",
241
+ "reasoning supersede-*",
242
+ "reasoning set-conclusion",
243
+ ),
244
+ "reasoning_graph_reasoning_graph_post": ("reasoning graph",),
245
+ "reasoning_lint_reasoning_lint_post": ("reasoning lint",),
246
+ "query_reasoning_query_post": ("reasoning impact",),
247
+
248
+ # Generated reference surfaces.
249
+ "list_all_examples_examples_get": ("example", "example list"),
250
+ "get_example_layer_examples__name__layer__layer__get": (
251
+ "example show",
252
+ "example get",
253
+ ),
254
+ "list_guide_topics_guide_get": ("guide",),
255
+ "list_domain_packs_guide_packs_index_get": ("guide",),
256
+ "get_capability_index_guide_capabilities_index_get": ("guide",),
257
+ "get_topic_guide__topic__get": ("guide",),
258
+ "list_ref_namespaces_ref_get": ("ref",),
259
+ "resolve_id_ref_resolve_get": ("ref",),
260
+ "search_surfaces_ref_search_get": ("ref",),
261
+ "list_namespace_entries_ref__namespace__get": ("ref",),
262
+ "get_namespace_entry_ref__namespace___name__get": ("ref",),
263
+ "list_house_rules_rules_get": ("rules",),
264
+ "get_house_rule_rules__rule_id__get": ("rules",),
265
+
266
+ # Import, ingestion, and export.
267
+ "import_file_import_post": ("import",),
268
+ "cn_extract_ingest_cn_extract_get": ("ingest cn extract",),
269
+ "cn_filings_ingest_cn_filings_get": ("ingest cn filings",),
270
+ "cn_search_ingest_cn_search_get": ("ingest cn search",),
271
+ "cn_statements_ingest_cn_statements_get": ("ingest cn statements",),
272
+ "to_docx_to_docx_post": ("to-docx",),
273
+ "to_excel_to_excel_post": ("to-excel",),
274
+ "to_pdf_to_pdf_post": ("to-pdf",),
275
+ "to_pptx_to_pptx_post": ("to-pptx",),
276
+
277
+ # Workspace files, history, variants, sync, and sharing.
278
+ "list_workspaces_workspaces_get": ("project list",),
279
+ "create_workspace_workspaces_post": ("project create",),
280
+ "get_workspace_workspaces__slug__get": (
281
+ "project use",
282
+ "project info",
283
+ "clone",
284
+ "pull",
285
+ "push",
286
+ "status",
287
+ ),
288
+ "list_workspace_files_workspaces__slug__files_get": (
289
+ "ls",
290
+ "grep",
291
+ "clone",
292
+ "pull",
293
+ "push",
294
+ "status",
295
+ "doctor",
296
+ ),
297
+ "get_workspace_file_workspaces__slug__files__filename__get": (
298
+ "cat",
299
+ "download",
300
+ "grep",
301
+ "query",
302
+ "claim *",
303
+ "assumption *",
304
+ "reasoning *",
305
+ "merge *",
306
+ "clone",
307
+ "pull",
308
+ "push",
309
+ "status",
310
+ ),
311
+ "create_or_update_workspace_file_workspaces__slug__files__filename__post": (
312
+ "write",
313
+ "replace",
314
+ "reasoning add-*",
315
+ "reasoning update-*",
316
+ "reasoning delete-*",
317
+ "reasoning supersede-*",
318
+ "reasoning set-conclusion",
319
+ ),
320
+ "delete_workspace_file_workspaces__slug__files__filename__delete": ("rm",),
321
+ "batch_commit_workspace_files_workspaces__slug__files_batch_post": ("push",),
322
+ "replace_in_workspace_file_workspaces__slug__files_replace_post": ("replace",),
323
+ "list_workspace_versions_workspaces__slug__versions_get": (
324
+ "log",
325
+ "clone",
326
+ "pull",
327
+ "push",
328
+ "status",
329
+ ),
330
+ "workspace_commit_pending_workspaces__slug__versions_commit_post": ("commit",),
331
+ "workspace_version_diff_workspaces__slug__versions_diff_get": ("diff",),
332
+ "workspace_version_restore_workspaces__slug__versions_restore_post": ("restore",),
333
+ "list_workspace_changes_workspaces__slug__changes_get": ("changes list",),
334
+ "workspace_change_diff_workspaces__slug__changes_diff_get": ("changes diff",),
335
+ "revert_workspace_change_workspaces__slug__changes_revert_post": (
336
+ "changes revert",
337
+ ),
338
+ "list_workspace_variants_workspaces__slug__variants_get": ("variant list",),
339
+ "get_variant_workspaces__slug__variants__name__get": (
340
+ "variant checkout",
341
+ "clone",
342
+ "pull",
343
+ "push",
344
+ "status",
345
+ ),
346
+ "create_workspace_variant_workspaces__slug__variants__name__post": ("variant create",),
347
+ "get_variant_diff_workspaces__slug__variants__name__diff_get": ("variant diff",),
348
+ "list_variant_files_workspaces__slug__variants__name__files_get": (
349
+ "clone",
350
+ "pull",
351
+ "push",
352
+ "status",
353
+ ),
354
+ "get_variant_file_workspaces__slug__variants__name__files__filename__get": (
355
+ "clone",
356
+ "pull",
357
+ "push",
358
+ "status",
359
+ ),
360
+ "create_or_update_variant_file_workspaces__slug__variants__name__files__filename__post": ("push",),
361
+ "merge_variant_into_main_workspaces__slug__variants__name__merge_post": ("variant merge",),
362
+ "merge_preview_merge_preview_post": ("merge *", "pull"),
363
+ "resolve_conflicts_merge_resolve_post": ("merge resolve",),
364
+ "list_share_links_workspaces__slug__shares_get": ("share list",),
365
+ "create_share_link_workspaces__slug__shares_post": ("share create",),
366
+ "revoke_share_link_workspaces__slug__shares__share_id__delete": ("share revoke",),
367
+ }
368
+
369
+
370
+ # ---------------------------------------------------------------------------
371
+ # Explicit operations without a CLI route
372
+ # ---------------------------------------------------------------------------
373
+
374
+ # Every operation not in ENDPOINT_COMMANDS must appear here. Grouped values
375
+ # remain exact operation ids: adding a route under an existing tag still fails
376
+ # codegen until somebody classifies that route deliberately.
377
+ ENDPOINT_EXCEPTIONS: dict[str, tuple[str, str]] = {
378
+ # Service metadata, cache, telemetry, and server-owned lifecycle.
379
+ "root__get": ("service_metadata", "HTTP landing response, not an analytical capability"),
380
+ "version_info_version_get": ("service_metadata", "frontend/service version probe; CLI reports its installed version locally"),
381
+ "clear_cache_cache_clear_post": ("internal_ops", "operator-only cache maintenance"),
382
+ "get_cache_stats_cache_stats_get": ("internal_ops", "operator-only cache diagnostics"),
383
+ "record_unknown_command_cli_unknown_commands_post": ("implicit_client", "CLI telemetry emitted automatically, never invoked directly"),
384
+ "record_web_events_signals_web_post": ("frontend_only", "browser analytics collector"),
385
+ "create_demo_session_demo_session_post": ("implicit_client", "anonymous CLI session bootstrap performed by DeepCellClient"),
386
+ # The /demo gallery's preview panel. A CLI caller has the fixture itself —
387
+ # `deepcell example` and the shipped files — so a route whose whole job is
388
+ # to show a browser what is inside one before opening it has no verb.
389
+ "demo_example_preview_demo_examples__filename__preview_get": ("frontend_only", "gallery preview panel: which surfaces an example carries"),
390
+ "demo_example_thumbnail_demo_examples__filename__thumbnail_png_get": ("frontend_only", "gallery preview panel: the example's opening slide, rasterized"),
391
+ # The example as plain XML, for an agent that is NOT this CLI. A caller who
392
+ # has `deepcell` has the fixtures already (`deepcell example get`), so there
393
+ # is nothing here to wrap in a verb. What this route is for is the agent on
394
+ # the other side of a copied prompt — one that has never heard of DeepCell
395
+ # and needs a URL it can fetch to see the shape it is being asked to copy.
396
+ "demo_example_raw_demo_examples__filename__raw_get": ("frontend_only", "public example XML, for an outside agent handed a URL"),
397
+ "ensure_demo_workspace_demo_workspace_post": ("agent_runtime", "hosted-agent workspace bootstrap"),
398
+ "get_quota_agent_runs_quota_get": ("agent_runtime", "hosted chat run quota"),
399
+ "reserve_thread_agent_threads_reserve_post": ("agent_runtime", "hosted chat thread bookkeeping"),
400
+ # The anonymous run allowance, spent on every submit to the HOSTED agent.
401
+ # A CLI caller drives their own agent and their own model, so there is
402
+ # nothing here for a verb to wrap — the runs this counts are the ones the
403
+ # hosted chat makes on the caller's behalf, which the CLI never issues.
404
+ "reserve_run_agent_runs_reserve_post": ("agent_runtime", "hosted chat run reservation"),
405
+ # The same allowance, spent by a visitor on a share link. Doubly not a CLI
406
+ # surface: it is reached with a share token rather than a user identity.
407
+ "reserve_share_run_share__token__runs_reserve_post": ("agent_runtime", "hosted share-visitor run reservation"),
408
+
409
+ # The task record, which belongs to the workbench rail and its inspectors.
410
+ #
411
+ # A task is one CONVERSATION with the agent, and the CLI does not have
412
+ # conversations: its own writes are stamped with an operation id and a
413
+ # `DeepCell-Command` trailer, so `changes list` already says what was run
414
+ # and what it touched, without a thread to hang it from. A CLI mirror of
415
+ # these was built and removed — it could only report on work done in the
416
+ # browser, which is where the reader already is when they ask.
417
+ "list_workspace_tasks_workspaces__slug__tasks_get": ("frontend_only", "workbench task rail; a CLI session is not a task"),
418
+ "update_workspace_task_workspaces__slug__tasks__thread_id__patch": ("frontend_only", "task rename and scope, set from the rail and the composer shelf"),
419
+
420
+ # Browser/MCP authentication and account management not exposed as CLI verbs.
421
+ "claim_anonymous_auth_claim_anonymous_post": ("frontend_only", "browser anonymous-session claim flow"),
422
+ # Reads the HttpOnly ``browser_id`` cookie and nothing else, on purpose:
423
+ # accepting an identity from the request body would let anyone who guesses
424
+ # a visitor's browser_id probe their demo state from another machine. A CLI
425
+ # caller has no such cookie — its half of the claim flow is the
426
+ # ``device_id`` + ``anon_access_token`` body variant of /auth/claim-demo,
427
+ # which is already mapped above.
428
+ "pending_anonymous_work_auth_pending_anonymous_work_get": ("frontend_only", "browser claim prompt: does this browser still hold claimable demo work"),
429
+ "device_authorize_auth_device_authorize_post": ("frontend_only", "browser approval half of the device flow"),
430
+ "forgot_password_auth_forgot_password_post": ("frontend_only", "browser password recovery"),
431
+ "google_callback_auth_google_callback_get": ("frontend_only", "browser OAuth callback"),
432
+ "google_login_auth_google_login_get": ("frontend_only", "browser OAuth entry"),
433
+ "google_status_auth_google_status_get": ("frontend_only", "browser sign-in capability probe"),
434
+ "logout_auth_logout_post": ("frontend_only", "cookie logout; CLI uses the refresh-token revocation route"),
435
+ "update_me_auth_me_put": ("frontend_only", "account settings editor"),
436
+ "change_password_auth_me_password_put": ("frontend_only", "account settings editor"),
437
+ "get_my_quota_auth_me_quota_get": ("frontend_only", "account quota display"),
438
+ "password_requirements_auth_password_requirements_get": ("frontend_only", "registration/password form metadata"),
439
+ "refresh_auth_refresh_post": ("implicit_client", "DeepCellClient refreshes credentials automatically"),
440
+ "reset_password_auth_reset_password_post": ("frontend_only", "browser password recovery"),
441
+ "join_waitlist_auth_waitlist_post": ("frontend_only", "website waitlist form"),
442
+ "list_api_keys_auth_api_keys_get": ("frontend_only", "API keys are managed in account settings"),
443
+ "create_api_key_auth_api_keys_post": ("frontend_only", "API keys are managed in account settings"),
444
+ "revoke_api_key_auth_api_keys__key_id__delete": ("frontend_only", "API keys are managed in account settings"),
445
+ "verify_api_key_auth_api_keys_verify_post": ("mcp_auth", "MCP bearer verification performed by the server"),
446
+ # Frontend configuration, dashboards, uploads, and preview/share viewers.
447
+ "get_export_formats_config_export_formats_get": ("frontend_only", "download-menu capability metadata"),
448
+ "get_import_sources_config_import_sources_get": ("frontend_only", "browser import-menu metadata"),
449
+ "dashboard_snapshot_dashboard_snapshot_post": ("frontend_only", "project dashboard aggregation"),
450
+ "download_file_files_download__object_name__get": ("attachment_transport", "uploaded attachment download, distinct from workspace document files"),
451
+ "upload_file_files_upload_post": ("attachment_transport", "uploaded attachment storage, distinct from workspace document files"),
452
+ "get_download_url_files_url__object_name__get": ("attachment_transport", "presigned attachment URL for browser/agent tools"),
453
+ "import_from_url_import_from_url_post": ("agent_runtime", "hosted agent URL-ingestion helper"),
454
+ "cn_source_pdf_ingest_cn_source__source_id__pdf_get": ("frontend_only", "inline filing PDF viewer"),
455
+ "get_one_example_examples__name__get": ("published_reference", "published example envelope; CLI reads the index and individual layers"),
456
+ "render_plan_render_plan_post": ("frontend_only", "grid render-plan fetch"),
457
+ "edit_inline_edit_inline_post": ("deprecated_alias", "legacy frontend alias; CLI uses files:replace or batch-edit"),
458
+ "upload_workspace_file_workspaces__slug__files_upload_post": ("frontend_only", "browser attachment upload into a workspace"),
459
+
460
+ # Public preview is a cookie-free browser surface, not the authenticated CLI.
461
+ "preview_cell_meta_preview_cell_meta_post": ("public_preview", "public preview inspector"),
462
+ "preview_reasoning_enumerate_preview_reasoning_enumerate_post": ("public_preview", "public preview reasoning reader"),
463
+ "preview_reasoning_query_preview_reasoning_query_post": ("public_preview", "public preview reasoning reader"),
464
+ "preview_relationships_preview_relationships_post": ("public_preview", "public preview relationship graph"),
465
+ "preview_render_plan_preview_render_plan_post": ("public_preview", "public preview renderer"),
466
+ "preview_source_preview_source_post": ("public_preview", "public preview source reader"),
467
+ "preview_to_docx_preview_to_docx_post": ("public_preview", "public preview export"),
468
+ "preview_to_excel_preview_to_excel_post": ("public_preview", "public preview export"),
469
+ "preview_to_pptx_preview_to_pptx_post": ("public_preview", "public preview export"),
470
+
471
+ # Browser share capabilities are token-scoped mirrors of authenticated APIs.
472
+ "get_share_info_share__token__get": ("share_viewer", "browser share metadata"),
473
+ "create_share_agent_session_share__token__agent_session_post": ("share_viewer", "share-assistant bootstrap"),
474
+ "share_apply_defs_ops_share__token__apply_defs_ops_post": ("share_viewer", "token-scoped browser/assistant edit route"),
475
+ "share_batch_edit_share__token__batch_edit_post": ("share_viewer", "token-scoped browser/assistant edit route"),
476
+ "share_cell_meta_share__token__cell_meta_post": ("share_viewer", "token-scoped browser inspector"),
477
+ "get_share_content_share__token__content_get": ("share_viewer", "token-scoped browser document fetch"),
478
+ "edit_shared_file_share__token__edit_post": ("share_viewer", "token-scoped browser/assistant edit route"),
479
+ "share_edit_inline_share__token__edit_inline_post": ("deprecated_alias", "legacy token-scoped frontend edit alias"),
480
+ "get_share_membership_share__token__membership_get": ("share_viewer", "browser ownership banner"),
481
+ "share_document_meta_share__token__meta_get": ("share_viewer", "browser share metadata"),
482
+ "share_head_share__token__head_get": ("share_viewer", "browser revision poll"),
483
+ "share_latest_share__token__latest_get": ("share_viewer", "browser document refresh"),
484
+ "share_og_image_share__token__og_image_png_get": ("share_viewer", "social preview image"),
485
+ "share_query_share__token__query_post": ("share_viewer", "token-scoped browser query"),
486
+ "share_reasoning_edit_share__token__reasoning_edit_post": ("share_viewer", "token-scoped browser/assistant reasoning edit"),
487
+ "share_reasoning_enumerate_share__token__reasoning_enumerate_post": ("share_viewer", "token-scoped browser reasoning reader"),
488
+ "share_reasoning_query_share__token__reasoning_query_post": ("share_viewer", "token-scoped browser reasoning reader"),
489
+ "share_relationships_share__token__relationships_post": ("share_viewer", "token-scoped browser relationship graph"),
490
+ "share_render_plan_share__token__render_plan_post": ("share_viewer", "token-scoped browser renderer"),
491
+ "share_to_docx_share__token__to_docx_post": ("share_viewer", "token-scoped browser export"),
492
+ "share_to_excel_share__token__to_excel_post": ("share_viewer", "token-scoped browser export"),
493
+ "share_to_pptx_share__token__to_pptx_post": ("share_viewer", "token-scoped browser export"),
494
+ "verify_password_share__token__verify_password_post": ("share_viewer", "browser share-password session"),
495
+ "list_shared_file_versions_share__token__versions_get": ("share_viewer", "browser share history"),
496
+ "get_shared_file_at_version_share__token__versions_content_get": ("share_viewer", "browser share history"),
497
+ "shared_file_version_diff_share__token__versions_diff_get": ("share_viewer", "browser share history"),
498
+
499
+ # Reasoning routes used by the web viewer; the CLI has different or local readers.
500
+ "diff_reasoning_diff_post": ("frontend_only", "browser comparison; reasoning-diff is deliberately Git-local"),
501
+ "enumerate_reasoning_reasoning_enumerate_post": ("frontend_only", "browser reasoning panel enumeration"),
502
+
503
+ # Workspace collaboration and hosted-agent state have no CLI owner today.
504
+ "accept_invitation_invitations_accept_post": ("frontend_only", "browser invitation acceptance"),
505
+ "get_thread_workspace_threads__thread_id__workspace_get": ("agent_runtime", "hosted thread/workspace binding"),
506
+ "delete_workspace_workspaces__slug__delete": ("frontend_only", "project settings destructive action"),
507
+ "update_workspace_workspaces__slug__put": ("frontend_only", "project settings editor"),
508
+ "list_invitations_workspaces__slug__invitations_get": ("frontend_only", "project invitation settings"),
509
+ "create_invitation_workspaces__slug__invitations_post": ("frontend_only", "project invitation settings"),
510
+ "revoke_invitation_workspaces__slug__invitations__invitation_id__delete": ("frontend_only", "project invitation settings"),
511
+ "list_members_workspaces__slug__members_get": ("frontend_only", "project membership settings"),
512
+ "add_member_workspaces__slug__members_post": ("frontend_only", "project membership settings"),
513
+ "remove_member_workspaces__slug__members__user_id__delete": ("frontend_only", "project membership settings"),
514
+ "update_member_role_workspaces__slug__members__user_id__put": ("frontend_only", "project membership settings"),
515
+ "list_workspace_threads_workspaces__slug__threads_get": ("agent_runtime", "hosted workspace thread list"),
516
+ "move_thread_to_workspace_workspaces__slug__threads_post": ("agent_runtime", "hosted thread/workspace binding"),
517
+ "transfer_ownership_workspaces__slug__transfer_post": ("frontend_only", "project ownership settings"),
518
+ "workspace_pending_changes_workspaces__slug__versions_pending_get": ("frontend_only", "browser pending-change indicator"),
519
+ }
520
+
521
+
522
+ # Commands that intentionally do not own a Jingwei operation. Everything else
523
+ # in the Click tree must be reached by at least one ENDPOINT_COMMANDS pattern.
524
+ COMMAND_ENDPOINT_EXCEPTIONS: dict[str, tuple[str, str]] = {
525
+ "help": ("cli_local", "reads the local Click tree and generated contract"),
526
+ "reasoning-diff": ("cli_local", "compares a local working-tree file with Git HEAD"),
527
+ "upgrade*": ("external_service", "checks and installs a CLI release from the package service"),
528
+ "viewer": ("cli_local", "constructs the signed-in frontend URL without a Jingwei request"),
529
+ }
530
+
531
+
532
+ # ---------------------------------------------------------------------------
533
+ # Generated runtime contract
534
+ # ---------------------------------------------------------------------------
535
+
536
+ _ARTIFACT = Path(__file__).with_name("capability-contract.json")
537
+
538
+
539
+ @lru_cache(maxsize=1)
540
+ def load_capability_contract() -> dict:
541
+ """Return the generated contract shipped with the CLI package."""
542
+ return json.loads(_ARTIFACT.read_text(encoding="utf-8"))
543
+
544
+
545
+ def get_command_capability(name: str) -> dict | None:
546
+ """Return one exact leaf command's generated capability record."""
547
+ wanted = " ".join((name or "").split())
548
+ if not wanted:
549
+ return None
550
+ return (load_capability_contract().get("commands") or {}).get(wanted)
551
+
552
+
553
+ def blocked_mcp_commands() -> frozenset[str]:
554
+ """The top-level commands runtime MCP enforcement must reject."""
555
+ return frozenset(MCP_BLOCKED_COMMANDS)
556
+
557
+
558
+ def mcp_blocked_summary() -> str:
559
+ """Stable comma-separated block list for generated/user-facing prose."""
560
+ return ", ".join(sorted(MCP_BLOCKED_COMMANDS))