cassis-cli 2.1.0__tar.gz → 2.2.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cassis-cli
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Validate, test and evaluate your Cassis ontology from your terminal, then publish it
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -35,10 +35,10 @@ Validate, test and evaluate your ontology from your terminal, then publish it. T
35
35
  - `cassis ontology test` runs individual questions through the text-to-SQL agent using your local ontology files, so you can check that a change actually works (e.g. a new column gets picked) — where `eval run` only checks for regressions on existing eval cases.
36
36
  - `cassis eval add-case` adds a gold question/SQL case to the project's eval suite — after fixing an ontology issue, add the question users were failing on so `eval run` guards it from regressing.
37
37
  - `cassis eval list-cases` and `cassis eval delete-case` maintain the suite: list the current cases with their ids, and prune one that is stale or wrong (e.g. its gold SQL encodes a definition the ontology has since changed).
38
- - `cassis schema plan <ddl>` (or `--warehouse` on a project connected to a warehouse) previews what a schema update would change before anything is applied: the source schema diff, the ontology changes Cassis will make (every change on a table placed in the ontology, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting ontology files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local ontology to the app (`--yes` in CI). The file speaks only for the schemas it contains — pass `--complete` when it is the project's complete source schema so schemas absent from it are treated as dropped. With `--warehouse` the server introspects the connected warehouse instead of parsing a file; the plan is always whole-source.
38
+ - `cassis schema plan <ddl>` (or `--warehouse` on a project connected to a warehouse) previews what a schema update would change before anything is applied: the source schema diff, the ontology changes Cassis will make (every change on a table placed in the ontology, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting ontology files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local ontology to the app (`--yes` in CI). The file speaks only for the schemas it contains — pass `--complete` when it is the project's complete source schema so schemas absent from it are treated as dropped. With `--warehouse` the server introspects the connected warehouse instead of parsing a file; the plan is always whole-source. `cassis schema plan <ddl> --dry-run` is the prepare-ahead variant: the plan is computed synchronously and nothing is kept in Cassis (no plan to apply or resume, the current plan untouched), so a dbt model or migration still in a PR can be planned against safely; `--write-checkout` writes the ontology files it would produce into the checkout, to commit alongside the schema change.
39
39
  - `cassis projects list` lists the projects your API key can reach — id (what `--project` and `CASSIS_PROJECT_ID` take), name, published ontology version, and data-source dialect — so a pipeline or agent can discover the project id from the terminal instead of fishing it out of a webapp URL.
40
40
  - `cassis status` shows the project's published version (number, label, git commit), whether unpublished changes await publication, the git-sync binding, a schema plan waiting to be applied, and how your local git HEAD relates to the published commit (in sync / N commits ahead / diverged). `cassis status --watch` polls until the published commit matches your local HEAD — e.g. right after merging a PR whose CI publishes the ontology — instead of watching the GitHub Actions tab.
41
- - `cassis issues` triages the issues Cassis raised on the project — what it found wrong while answering questions (an ontology gap, missing data) — without leaving the checkout: `issues list` (filterable by status, impact and cause), `issues show <id>` for the diagnosis, suggested action and the occurrences behind it, `issues evidence <id> <occurrence-id>` for what the agent actually saw, and `issues resolve` / `dismiss` / `reopen` once you've acted on it.
41
+ - `cassis issues` triages the issues Cassis raised on the project — what it found wrong while answering questions (an ontology gap, missing data) — without leaving the checkout: `issues list` (filterable by status, impact, cause and ontology domain, and showing each issue's domain so you can work through one domain at a time), `issues show <id>` for the diagnosis, suggested action and the occurrences behind it, `issues evidence <id> <occurrence-id>` for what the agent actually saw, and `issues resolve` / `dismiss` / `reopen` once you've acted on it.
42
42
  - `cassis verify` runs the full local gate in one verb — `ontology fmt --check`, `ontology check`, `eval run` — stopping at the first failure. One command in a checkout ("is this change safe to merge?"), one job in CI. `--no-eval` skips the eval suite.
43
43
 
44
44
  ## Install
@@ -140,6 +140,7 @@ cassis schema plan schema.sql --complete
140
140
  cassis schema apply schema.sql --complete # writes cassis/ locally
141
141
  cassis schema push schema.sql --complete --yes # schema + ontology to the app
142
142
  cassis schema plan --warehouse # warehouse-connected projects: introspect instead
143
+ cassis schema plan future.sql --dry-run --write-checkout # plan a not-yet-deployed DDL, keep nothing server-side
143
144
 
144
145
  # List the projects the API key can reach (id, name, published version, dialect):
145
146
  cassis projects list
@@ -153,6 +154,9 @@ cassis issues analyze
153
154
  cassis issues list --status open
154
155
  cassis issues show 019f0000-0000-7000-8000-0000000000e1
155
156
 
157
+ # Work one ontology domain at a time (nested domains included):
158
+ cassis issues list --domain sales
159
+
156
160
  # Read what the agent saw for one occurrence (ids from `issues show`):
157
161
  cassis issues evidence 019f0000-0000-7000-8000-0000000000e1 019f0000-0000-7000-8000-0000000000c1
158
162
 
@@ -13,10 +13,10 @@ Validate, test and evaluate your ontology from your terminal, then publish it. T
13
13
  - `cassis ontology test` runs individual questions through the text-to-SQL agent using your local ontology files, so you can check that a change actually works (e.g. a new column gets picked) — where `eval run` only checks for regressions on existing eval cases.
14
14
  - `cassis eval add-case` adds a gold question/SQL case to the project's eval suite — after fixing an ontology issue, add the question users were failing on so `eval run` guards it from regressing.
15
15
  - `cassis eval list-cases` and `cassis eval delete-case` maintain the suite: list the current cases with their ids, and prune one that is stale or wrong (e.g. its gold SQL encodes a definition the ontology has since changed).
16
- - `cassis schema plan <ddl>` (or `--warehouse` on a project connected to a warehouse) previews what a schema update would change before anything is applied: the source schema diff, the ontology changes Cassis will make (every change on a table placed in the ontology, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting ontology files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local ontology to the app (`--yes` in CI). The file speaks only for the schemas it contains — pass `--complete` when it is the project's complete source schema so schemas absent from it are treated as dropped. With `--warehouse` the server introspects the connected warehouse instead of parsing a file; the plan is always whole-source.
16
+ - `cassis schema plan <ddl>` (or `--warehouse` on a project connected to a warehouse) previews what a schema update would change before anything is applied: the source schema diff, the ontology changes Cassis will make (every change on a table placed in the ontology, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting ontology files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local ontology to the app (`--yes` in CI). The file speaks only for the schemas it contains — pass `--complete` when it is the project's complete source schema so schemas absent from it are treated as dropped. With `--warehouse` the server introspects the connected warehouse instead of parsing a file; the plan is always whole-source. `cassis schema plan <ddl> --dry-run` is the prepare-ahead variant: the plan is computed synchronously and nothing is kept in Cassis (no plan to apply or resume, the current plan untouched), so a dbt model or migration still in a PR can be planned against safely; `--write-checkout` writes the ontology files it would produce into the checkout, to commit alongside the schema change.
17
17
  - `cassis projects list` lists the projects your API key can reach — id (what `--project` and `CASSIS_PROJECT_ID` take), name, published ontology version, and data-source dialect — so a pipeline or agent can discover the project id from the terminal instead of fishing it out of a webapp URL.
18
18
  - `cassis status` shows the project's published version (number, label, git commit), whether unpublished changes await publication, the git-sync binding, a schema plan waiting to be applied, and how your local git HEAD relates to the published commit (in sync / N commits ahead / diverged). `cassis status --watch` polls until the published commit matches your local HEAD — e.g. right after merging a PR whose CI publishes the ontology — instead of watching the GitHub Actions tab.
19
- - `cassis issues` triages the issues Cassis raised on the project — what it found wrong while answering questions (an ontology gap, missing data) — without leaving the checkout: `issues list` (filterable by status, impact and cause), `issues show <id>` for the diagnosis, suggested action and the occurrences behind it, `issues evidence <id> <occurrence-id>` for what the agent actually saw, and `issues resolve` / `dismiss` / `reopen` once you've acted on it.
19
+ - `cassis issues` triages the issues Cassis raised on the project — what it found wrong while answering questions (an ontology gap, missing data) — without leaving the checkout: `issues list` (filterable by status, impact, cause and ontology domain, and showing each issue's domain so you can work through one domain at a time), `issues show <id>` for the diagnosis, suggested action and the occurrences behind it, `issues evidence <id> <occurrence-id>` for what the agent actually saw, and `issues resolve` / `dismiss` / `reopen` once you've acted on it.
20
20
  - `cassis verify` runs the full local gate in one verb — `ontology fmt --check`, `ontology check`, `eval run` — stopping at the first failure. One command in a checkout ("is this change safe to merge?"), one job in CI. `--no-eval` skips the eval suite.
21
21
 
22
22
  ## Install
@@ -118,6 +118,7 @@ cassis schema plan schema.sql --complete
118
118
  cassis schema apply schema.sql --complete # writes cassis/ locally
119
119
  cassis schema push schema.sql --complete --yes # schema + ontology to the app
120
120
  cassis schema plan --warehouse # warehouse-connected projects: introspect instead
121
+ cassis schema plan future.sql --dry-run --write-checkout # plan a not-yet-deployed DDL, keep nothing server-side
121
122
 
122
123
  # List the projects the API key can reach (id, name, published version, dialect):
123
124
  cassis projects list
@@ -131,6 +132,9 @@ cassis issues analyze
131
132
  cassis issues list --status open
132
133
  cassis issues show 019f0000-0000-7000-8000-0000000000e1
133
134
 
135
+ # Work one ontology domain at a time (nested domains included):
136
+ cassis issues list --domain sales
137
+
134
138
  # Read what the agent saw for one occurrence (ids from `issues show`):
135
139
  cassis issues evidence 019f0000-0000-7000-8000-0000000000e1 019f0000-0000-7000-8000-0000000000c1
136
140
 
@@ -372,6 +372,10 @@ class SchemaPlanConflictError(ApiError):
372
372
  """409 from the schema-plan routes: a plan is already active, the project is connected, the plan is stale / expired / not ready."""
373
373
 
374
374
 
375
+ class SchemaPlanRejectedError(ApiError):
376
+ """400 from the preview route: the DDL does not parse or reads as truncated (the user's file, not transport)."""
377
+
378
+
375
379
  class SchemaPlanNotFoundError(ApiError):
376
380
  """404 with the server's exact detail `"Schema plan not found"` (a project-scope 404 is a different error)."""
377
381
 
@@ -462,6 +466,61 @@ def post_schema_plan_warehouse(
462
466
  return _schema_plan_response(response, url)
463
467
 
464
468
 
469
+ def _schema_plan_preview_response(response: httpx.Response, url: str) -> dict[str, Any]:
470
+ if response.status_code == 400:
471
+ raise SchemaPlanRejectedError(str(_detail_or_text(response)))
472
+ _raise_for_schema_plan_status(response)
473
+ result = _parse_json_response(response, url)
474
+ if not isinstance(result, dict) or "document" not in result or not isinstance(result.get("files"), dict):
475
+ raise ApiError(f"Unexpected response shape from the Cassis API at {url}.")
476
+ return result
477
+
478
+
479
+ def post_schema_plan_preview(
480
+ *,
481
+ api_url: str,
482
+ api_key: str,
483
+ project_id: str,
484
+ ddl: str,
485
+ complete_source: bool = False,
486
+ transport: Optional[httpx.BaseTransport] = None,
487
+ ) -> dict[str, Any]:
488
+ """POST /api/ci/projects/{project_id}/schema/plans/preview: plan + rendered tree, nothing persisted.
489
+
490
+ Synchronous: the server computes the plan in the request (no plan row, no
491
+ job), so the tree timeout applies. A DDL the server cannot parse, or that
492
+ reads as truncated, is a 400 → `SchemaPlanRejectedError`.
493
+ """
494
+ url = api_url.rstrip("/") + f"/api/ci/projects/{project_id}/schema/plans/preview"
495
+ try:
496
+ with _client(timeout=ONTOLOGY_TREE_TIMEOUT_SECONDS, transport=transport) as client:
497
+ response = client.post(
498
+ url,
499
+ json={"ddl": ddl, "complete_source": complete_source},
500
+ headers={"Authorization": f"Bearer {api_key}"},
501
+ )
502
+ except httpx.HTTPError as exc:
503
+ raise ApiError(f"Could not reach the Cassis API at {url}: {exc}") from exc
504
+ return _schema_plan_preview_response(response, url)
505
+
506
+
507
+ def post_schema_plan_preview_warehouse(
508
+ *,
509
+ api_url: str,
510
+ api_key: str,
511
+ project_id: str,
512
+ transport: Optional[httpx.BaseTransport] = None,
513
+ ) -> dict[str, Any]:
514
+ """POST /api/ci/projects/{project_id}/schema/plans/preview/warehouse: the warehouse twin of the preview."""
515
+ url = api_url.rstrip("/") + f"/api/ci/projects/{project_id}/schema/plans/preview/warehouse"
516
+ try:
517
+ with _client(timeout=ONTOLOGY_TREE_TIMEOUT_SECONDS, transport=transport) as client:
518
+ response = client.post(url, headers={"Authorization": f"Bearer {api_key}"})
519
+ except httpx.HTTPError as exc:
520
+ raise ApiError(f"Could not reach the Cassis API at {url}: {exc}") from exc
521
+ return _schema_plan_preview_response(response, url)
522
+
523
+
465
524
  def get_schema_plan(
466
525
  *,
467
526
  api_url: str,
@@ -790,11 +849,16 @@ def get_issues(
790
849
  status: Optional[str] = None,
791
850
  impact: Optional[str] = None,
792
851
  cause: Optional[str] = None,
852
+ domain: Optional[str] = None,
793
853
  transport: Optional[httpx.BaseTransport] = None,
794
854
  ) -> list[dict[str, Any]]:
795
855
  """GET /api/ci/projects/{project_id}/issues and return the issue list."""
796
856
  url = api_url.rstrip("/") + f"/api/ci/projects/{project_id}/issues"
797
- params = {key: value for key, value in (("status", status), ("impact", impact), ("cause", cause)) if value}
857
+ params = {
858
+ key: value
859
+ for key, value in (("status", status), ("impact", impact), ("cause", cause), ("domain", domain))
860
+ if value
861
+ }
798
862
  result = _get_issue_json(url, api_key, transport, params=params)
799
863
  if not isinstance(result, list) or not all(isinstance(issue, dict) and "id" in issue for issue in result):
800
864
  raise ApiError(f"Unexpected response shape from the Cassis API at {url}.")
@@ -117,6 +117,9 @@ def list_issues(
117
117
  status: Optional[str] = typer.Option(None, "--status", help=f"Filter by status ({', '.join(STATUSES)})."),
118
118
  impact: Optional[str] = typer.Option(None, "--impact", help=f"Filter by impact ({', '.join(IMPACTS)})."),
119
119
  cause: Optional[str] = typer.Option(None, "--cause", help=f"Filter by cause ({', '.join(CAUSES)})."),
120
+ domain: Optional[str] = typer.Option(
121
+ None, "--domain", help="Filter by ontology domain path; nested domains included."
122
+ ),
120
123
  path: Path = _PATH_OPTION,
121
124
  project_id: Optional[str] = _PROJECT_OPTION,
122
125
  api_key: Optional[str] = _API_KEY_OPTION,
@@ -126,9 +129,11 @@ def list_issues(
126
129
  ) -> None:
127
130
  """List the project's issues, prioritized by impact then recurrence.
128
131
 
129
- Prints each issue's id, impact, occurrence count, status and title; the id
130
- is what `cassis issues show`, `resolve`, `dismiss` and `reopen` take. Exits
131
- 0 on success, 2 on usage errors, 3 on transport/API errors.
132
+ Prints each issue's id, impact, occurrence count, status, primary ontology
133
+ domain (`-` when Cassis could not attach one) and title; the id is what
134
+ `cassis issues show`, `resolve`, `dismiss` and `reopen` take. Triage one
135
+ domain at a time with `--domain`, which covers its nested domains too.
136
+ Exits 0 on success, 2 on usage errors, 3 on transport/API errors.
132
137
  """
133
138
  status = _validate_choice(status, STATUSES, "--status")
134
139
  impact = _validate_choice(impact, IMPACTS, "--impact")
@@ -144,6 +149,7 @@ def list_issues(
144
149
  status=status,
145
150
  impact=impact,
146
151
  cause=cause,
152
+ domain=domain,
147
153
  )
148
154
  except (AuthError, ApiError) as exc:
149
155
  raise api_failure(exc) from exc
@@ -153,14 +159,16 @@ def list_issues(
153
159
  raise typer.Exit(EXIT_OK)
154
160
 
155
161
  if not issues:
156
- typer.echo("No issues match." if (status or impact or cause) else "No issues on this project.")
162
+ typer.echo("No issues match." if (status or impact or cause or domain) else "No issues on this project.")
157
163
  raise typer.Exit(EXIT_OK)
158
164
 
159
165
  for issue in issues:
160
166
  occurrences = issue.get("occurrence_count_cache") or 0
167
+ # Primary domain only; --json carries the whole list.
168
+ domains = issue.get("domains") or []
161
169
  typer.echo(
162
170
  f"{issue.get('id')} {issue.get('impact')} x{occurrences} "
163
- f"{issue.get('status')} {one_line(issue.get('title'))}"
171
+ f"{issue.get('status')} {domains[0] if domains else '-'} {one_line(issue.get('title'))}"
164
172
  )
165
173
  raise typer.Exit(EXIT_OK)
166
174
 
@@ -201,6 +209,7 @@ def show(
201
209
  f"{issue.get('status')} {issue.get('impact')} {issue.get('cause')} "
202
210
  f"{issue.get('occurrence_count_cache', len(occurrences))} occurrence(s)"
203
211
  )
212
+ _field("Domains", ", ".join(issue.get("domains") or []) or None)
204
213
  _field("Description", issue.get("description"), blank_line=True)
205
214
  _field("Suggested action", issue.get("suggested_action"), blank_line=True)
206
215
  typer.echo("")
@@ -21,6 +21,7 @@ from cassis_cli.api import (
21
21
  ApiError,
22
22
  AuthError,
23
23
  SchemaPlanConflictError,
24
+ SchemaPlanRejectedError,
24
25
  UploadValidationError,
25
26
  get_ontology_export,
26
27
  get_schema_export,
@@ -29,6 +30,8 @@ from cassis_cli.api import (
29
30
  post_ontology_import,
30
31
  post_schema_plan,
31
32
  post_schema_plan_apply,
33
+ post_schema_plan_preview,
34
+ post_schema_plan_preview_warehouse,
32
35
  post_schema_plan_warehouse,
33
36
  )
34
37
  from cassis_cli.common import (
@@ -229,6 +232,17 @@ def plan(
229
232
  timeout: float = _TIMEOUT_OPTION,
230
233
  json_output: bool = _JSON_OPTION,
231
234
  out: Optional[Path] = _OUT_OPTION,
235
+ dry_run: bool = typer.Option(
236
+ False,
237
+ "--dry-run",
238
+ help="Compute the plan in the request and keep nothing server-side: no plan to apply or resume, "
239
+ "the project's current plan untouched. For a DDL not deployed to the warehouse yet.",
240
+ ),
241
+ write_checkout: bool = typer.Option(
242
+ False,
243
+ "--write-checkout",
244
+ help="With --dry-run: also write the ontology files the plan would produce under <path>/<base-path>.",
245
+ ),
232
246
  ) -> None:
233
247
  """Preview what a schema update would change. Nothing is applied.
234
248
 
@@ -241,11 +255,46 @@ def plan(
241
255
  whole-source. Exits 0 when the plan is ready (even when it is empty), 1
242
256
  when the plan failed (unparseable or truncated DDL, unreachable
243
257
  warehouse), 2 on usage errors, 3 on transport errors or a timeout.
258
+
259
+ --dry-run is the prepare-ahead gesture: the plan is computed synchronously
260
+ and nothing is kept server-side, so it works for a schema change that is
261
+ still a PR (a dbt model, a migration) and leaves the project's current plan
262
+ alone. --write-checkout then writes the resulting ontology files into the
263
+ checkout, to commit next to the schema change; nothing is pushed.
244
264
  """
245
265
  api_key = require_api_key(api_key)
246
266
  _require_one_source(ddl_file, warehouse)
267
+ if write_checkout and not dry_run:
268
+ typer.secho(
269
+ "--write-checkout needs --dry-run (use `cassis schema apply` otherwise).", fg=typer.colors.RED, err=True
270
+ )
271
+ raise typer.Exit(EXIT_USAGE)
247
272
  resolved_project = resolve_project_id(project_id, path / base_path, quiet=json_output)
248
273
  assert resolved_project is not None
274
+ if dry_run:
275
+ preview = _preview(
276
+ ddl_file,
277
+ warehouse=warehouse,
278
+ api_url=api_url,
279
+ api_key=api_key,
280
+ project_id=resolved_project,
281
+ complete_source=complete_source,
282
+ json_output=json_output,
283
+ out=out,
284
+ )
285
+ if write_checkout:
286
+ ontology_dir = path / base_path.strip().strip("/")
287
+ written, deleted, _kept = _write_checkout(ontology_dir, preview["files"], json_output=json_output)
288
+ typer.secho(
289
+ f"✓ Wrote {len(written)} ontology file(s) into {ontology_dir}"
290
+ + (f", deleted {len(deleted)} stale file(s)" if deleted else "")
291
+ + ". The app is unchanged.",
292
+ fg=typer.colors.GREEN,
293
+ err=json_output,
294
+ )
295
+ if json_output:
296
+ typer.echo(json.dumps(preview, indent=2))
297
+ raise typer.Exit(EXIT_OK)
249
298
  record = _plan(
250
299
  ddl_file,
251
300
  warehouse=warehouse,
@@ -724,6 +773,54 @@ def _plan(
724
773
  return record
725
774
 
726
775
 
776
+ def _preview(
777
+ ddl_file: Optional[Path],
778
+ *,
779
+ warehouse: bool,
780
+ api_url: str,
781
+ api_key: str,
782
+ project_id: str,
783
+ complete_source: bool,
784
+ json_output: bool,
785
+ out: Optional[Path],
786
+ ) -> "dict[str, Any]":
787
+ """Compute a dry-run plan for `ddl_file` (or the warehouse), render it. Return the preview record."""
788
+ try:
789
+ if warehouse:
790
+ preview = post_schema_plan_preview_warehouse(api_url=api_url, api_key=api_key, project_id=project_id)
791
+ else:
792
+ assert ddl_file is not None
793
+ preview = post_schema_plan_preview(
794
+ api_url=api_url,
795
+ api_key=api_key,
796
+ project_id=project_id,
797
+ ddl=_read_ddl(ddl_file),
798
+ complete_source=complete_source,
799
+ )
800
+ except (SchemaPlanRejectedError, SchemaPlanConflictError) as exc:
801
+ typer.secho(f"Plan failed: {exc}", fg=typer.colors.RED, err=True)
802
+ raise typer.Exit(EXIT_VALIDATION_FAILED) from exc
803
+ except (AuthError, ApiError) as exc:
804
+ typer.secho(str(exc), fg=typer.colors.RED, err=True)
805
+ raise typer.Exit(EXIT_TRANSPORT) from exc
806
+ if out is not None:
807
+ try:
808
+ out.write_text(json.dumps(preview, indent=2) + "\n", encoding="utf-8")
809
+ except OSError as exc:
810
+ typer.secho(f"Could not write {out}: {exc}", fg=typer.colors.RED, err=True)
811
+ raise typer.Exit(EXIT_USAGE) from exc
812
+ # The renderer reads a plan record; a preview is one without an id.
813
+ record = {"status": "ready", "document": preview.get("document"), "summary": preview.get("summary")}
814
+ render_plan(record, err=json_output)
815
+ for warning in preview.get("warnings") or []:
816
+ typer.secho(f" warning: {warning}", fg=typer.colors.YELLOW, err=True)
817
+ if plan_is_empty(record):
818
+ typer.secho("✓ Schema is up to date (dry run, nothing kept).", fg=typer.colors.GREEN, err=json_output)
819
+ else:
820
+ typer.secho("✓ Plan computed (dry run, nothing kept).", fg=typer.colors.GREEN, err=json_output)
821
+ return preview
822
+
823
+
727
824
  def _explain_not_ready(record: "dict[str, Any]") -> None:
728
825
  status = record.get("status")
729
826
  error = record.get("error") or ""
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cassis-cli"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "Validate, test and evaluate your Cassis ontology from your terminal, then publish it"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
File without changes
File without changes