cassis-cli 3.1.0__tar.gz → 3.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: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Validate, test and evaluate your Cassis context from your terminal, then publish it
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -24,10 +24,10 @@ Description-Content-Type: text/markdown
24
24
 
25
25
  Validate, test and evaluate your context from your terminal, then publish it. The same commands gate your pull requests in CI:
26
26
 
27
- - `cassis context check` validates the context files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation), so you can gate merges in any CI system, not just GitHub. It then prints advisory **context quality warnings** for a tree that parsed: tables not assigned to any domain, joins/metrics pointing at unknown tables or columns, missing table/column descriptions (the same findings `context test` reports, without the agent run). In a checkout bound to a project (`project.yml`, `--project`, or `CASSIS_PROJECT_ID`), it also cross-checks the tree against the project's source schema: references to tables or columns the warehouse doesn't have print as **warnings** too, advisory only (the object may simply not be built or synced yet). Warnings never fail the check.
27
+ - `cassis context check` validates the context files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation), so you can gate merges in any CI system, not just GitHub. It then prints advisory **context quality warnings** for a tree that parsed: tables not assigned to any domain, joins/metrics pointing at unknown tables or columns, missing table/column descriptions, `enum_values` the column's type can't store (the same findings `context test` reports, without the agent run). In a checkout bound to a project (`project.yml`, `--project`, or `CASSIS_PROJECT_ID`), it also cross-checks the tree against the project's source schema: references to tables or columns the warehouse doesn't have print as **warnings** too, advisory only (the object may simply not be built or synced yet). Warnings never fail the check.
28
28
  - `cassis schema pull` downloads the data source's full source schema (as Cassis last introspected it) into `<base-path>/.schema.json`, a **gitignored** local snapshot (the command maintains the ignore entry) with a `pulled_at` stamp. The warehouse stays authoritative; the snapshot is a cache for offline/bulk work, e.g. a coding agent grepping table and column names during a modeling pass instead of paging through the MCP `get_source_schema` tool. Re-run to refresh.
29
29
  - `cassis context fmt` rewrites the context files in canonical form (think `black`/`gofmt` for your context), so hand or agent edits pass the round-trip check.
30
- - `cassis context upload` uploads the context files to a Cassis project (full replace) and, by default, publishes them immediately as a new version, so a merge to your main branch can go live in one CI step. It runs from a git checkout whose context files are committed, and the published version records that commit, so `cassis status` can tell whether a checkout matches what is live.
30
+ - `cassis context upload` uploads the context files to a Cassis project (full replace) and, by default, publishes them immediately as a new version, so a merge to your main branch can go live in one CI step. It runs from a git checkout whose context files are committed, and the published version records that commit, even when the upload changes nothing, so `cassis status` can tell whether a checkout matches what is live.
31
31
  - `cassis context pull` downloads the project's unpublished context into your repository checkout (full sync: stale local context files are pruned), so you can start editing from the current state, or bootstrap a repo that isn't git-synced (e.g. Bitbucket). Pruning only deletes files that are tracked and unmodified in git (i.e. restorable with `git checkout`); untracked or locally modified files are kept and listed, and every deleted path is printed.
32
32
  - `cassis context pull` and `cassis context fmt` also write `<base-path>/AGENTS.md`, the Cassis context design guide, into the checkout (default `cassis/AGENTS.md`). It is a managed file (generated banner; the CLI overwrites local edits) so a repo-aware coding agent loads current Cassis modeling doctrine by convention. It sits inside the context directory but is not part of the context tree (which is the YAML files plus the domain Markdown files `domains/**/README.md`), so it is never uploaded, validated, or pruned. Commit it alongside your context changes. The guide text ships inside the CLI package, so its version tracks the **installed cassis-cli version**: upgrade the CLI (`pip install -U cassis-cli`) and re-run `fmt` to pick up doctrine updates; an unpinned `pip install cassis-cli` in CI gets them automatically. The banner stamps a doctrine version, and the CLI never *downgrades* the file: if the checkout's `AGENTS.md` was written by a newer doctrine (a newer CLI, or Cassis itself on a publish), `fmt`/`pull` leave it in place, print an upgrade notice, and `fmt --check` still passes.
33
33
  - The CLI identifies itself to the API (`User-Agent: cassis-cli/<version>`), and successful API responses advertise the newest published version. When you are behind, commands print a one-line upgrade notice on stderr (purely informational; output and exit codes are unchanged).
@@ -55,7 +55,7 @@ The context tree under `<base-path>` (default `cassis/`) is:
55
55
 
56
56
  - **Project identity**: `project.yml`, the Cassis project id and format version. Written by `pull` and by server-side publish (the places that know the id); a local `fmt` won't create it.
57
57
  - **Domains**: Markdown files. Every domain is the `README.md` of its folder: `domains/README.md` for the root, `domains/<path>/README.md` for each sub-domain. Each has a small YAML frontmatter block (`type`, `title`, `description`) and a Markdown body carrying the domain's `context_md`; a generated section at the bottom links the domain's tables and metrics (kept current by `fmt`/`pull`: edit your prose above it, and the PR check fails if the links are stale, so re-run `fmt`). The layout is a Cassis profile inspired by [OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf): the files render on GitHub and read in any Markdown editor, but Cassis validates them strictly (unknown keys are flagged, not preserved).
58
- - **Tables, joins, metrics**: YAML, unchanged: `tables/<schema>/<table>.yml`, `joins.yml`, `metrics/<name>.yml`.
58
+ - **Tables, joins, metrics**: YAML, unchanged: `tables/<schema>/<table>.yml`, `joins.yml`, `metrics/<name>.yml`. A column can list the values it stores as `enum_values` (items `{value, description?}`, values quoted exactly as stored); see `cassis/AGENTS.md`.
59
59
 
60
60
  **Migrating an existing repo** (domains were YAML `_project.yml` / `_domain.yml` before cassis-cli 1.1.0): upgrade and run `cassis context fmt` (or `cassis context pull` if you have no local edits). It rewrites the domain files to Markdown and removes the old ones. Review the diff and commit. Cassis reads the old YAML domain files too, so an un-migrated repo keeps working until you convert it. **Uploading requires cassis-cli ≥ 1.1.0**: the server rejects an older CLI (which would drop the Markdown domain files) with a clear upgrade error.
61
61
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  Validate, test and evaluate your context from your terminal, then publish it. The same commands gate your pull requests in CI:
4
4
 
5
- - `cassis context check` validates the context files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation), so you can gate merges in any CI system, not just GitHub. It then prints advisory **context quality warnings** for a tree that parsed: tables not assigned to any domain, joins/metrics pointing at unknown tables or columns, missing table/column descriptions (the same findings `context test` reports, without the agent run). In a checkout bound to a project (`project.yml`, `--project`, or `CASSIS_PROJECT_ID`), it also cross-checks the tree against the project's source schema: references to tables or columns the warehouse doesn't have print as **warnings** too, advisory only (the object may simply not be built or synced yet). Warnings never fail the check.
5
+ - `cassis context check` validates the context files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation), so you can gate merges in any CI system, not just GitHub. It then prints advisory **context quality warnings** for a tree that parsed: tables not assigned to any domain, joins/metrics pointing at unknown tables or columns, missing table/column descriptions, `enum_values` the column's type can't store (the same findings `context test` reports, without the agent run). In a checkout bound to a project (`project.yml`, `--project`, or `CASSIS_PROJECT_ID`), it also cross-checks the tree against the project's source schema: references to tables or columns the warehouse doesn't have print as **warnings** too, advisory only (the object may simply not be built or synced yet). Warnings never fail the check.
6
6
  - `cassis schema pull` downloads the data source's full source schema (as Cassis last introspected it) into `<base-path>/.schema.json`, a **gitignored** local snapshot (the command maintains the ignore entry) with a `pulled_at` stamp. The warehouse stays authoritative; the snapshot is a cache for offline/bulk work, e.g. a coding agent grepping table and column names during a modeling pass instead of paging through the MCP `get_source_schema` tool. Re-run to refresh.
7
7
  - `cassis context fmt` rewrites the context files in canonical form (think `black`/`gofmt` for your context), so hand or agent edits pass the round-trip check.
8
- - `cassis context upload` uploads the context files to a Cassis project (full replace) and, by default, publishes them immediately as a new version, so a merge to your main branch can go live in one CI step. It runs from a git checkout whose context files are committed, and the published version records that commit, so `cassis status` can tell whether a checkout matches what is live.
8
+ - `cassis context upload` uploads the context files to a Cassis project (full replace) and, by default, publishes them immediately as a new version, so a merge to your main branch can go live in one CI step. It runs from a git checkout whose context files are committed, and the published version records that commit, even when the upload changes nothing, so `cassis status` can tell whether a checkout matches what is live.
9
9
  - `cassis context pull` downloads the project's unpublished context into your repository checkout (full sync: stale local context files are pruned), so you can start editing from the current state, or bootstrap a repo that isn't git-synced (e.g. Bitbucket). Pruning only deletes files that are tracked and unmodified in git (i.e. restorable with `git checkout`); untracked or locally modified files are kept and listed, and every deleted path is printed.
10
10
  - `cassis context pull` and `cassis context fmt` also write `<base-path>/AGENTS.md`, the Cassis context design guide, into the checkout (default `cassis/AGENTS.md`). It is a managed file (generated banner; the CLI overwrites local edits) so a repo-aware coding agent loads current Cassis modeling doctrine by convention. It sits inside the context directory but is not part of the context tree (which is the YAML files plus the domain Markdown files `domains/**/README.md`), so it is never uploaded, validated, or pruned. Commit it alongside your context changes. The guide text ships inside the CLI package, so its version tracks the **installed cassis-cli version**: upgrade the CLI (`pip install -U cassis-cli`) and re-run `fmt` to pick up doctrine updates; an unpinned `pip install cassis-cli` in CI gets them automatically. The banner stamps a doctrine version, and the CLI never *downgrades* the file: if the checkout's `AGENTS.md` was written by a newer doctrine (a newer CLI, or Cassis itself on a publish), `fmt`/`pull` leave it in place, print an upgrade notice, and `fmt --check` still passes.
11
11
  - The CLI identifies itself to the API (`User-Agent: cassis-cli/<version>`), and successful API responses advertise the newest published version. When you are behind, commands print a one-line upgrade notice on stderr (purely informational; output and exit codes are unchanged).
@@ -33,7 +33,7 @@ The context tree under `<base-path>` (default `cassis/`) is:
33
33
 
34
34
  - **Project identity**: `project.yml`, the Cassis project id and format version. Written by `pull` and by server-side publish (the places that know the id); a local `fmt` won't create it.
35
35
  - **Domains**: Markdown files. Every domain is the `README.md` of its folder: `domains/README.md` for the root, `domains/<path>/README.md` for each sub-domain. Each has a small YAML frontmatter block (`type`, `title`, `description`) and a Markdown body carrying the domain's `context_md`; a generated section at the bottom links the domain's tables and metrics (kept current by `fmt`/`pull`: edit your prose above it, and the PR check fails if the links are stale, so re-run `fmt`). The layout is a Cassis profile inspired by [OKF](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf): the files render on GitHub and read in any Markdown editor, but Cassis validates them strictly (unknown keys are flagged, not preserved).
36
- - **Tables, joins, metrics**: YAML, unchanged: `tables/<schema>/<table>.yml`, `joins.yml`, `metrics/<name>.yml`.
36
+ - **Tables, joins, metrics**: YAML, unchanged: `tables/<schema>/<table>.yml`, `joins.yml`, `metrics/<name>.yml`. A column can list the values it stores as `enum_values` (items `{value, description?}`, values quoted exactly as stored); see `cassis/AGENTS.md`.
37
37
 
38
38
  **Migrating an existing repo** (domains were YAML `_project.yml` / `_domain.yml` before cassis-cli 1.1.0): upgrade and run `cassis context fmt` (or `cassis context pull` if you have no local edits). It rewrites the domain files to Markdown and removes the old ones. Review the diff and commit. Cassis reads the old YAML domain files too, so an un-migrated repo keeps working until you convert it. **Uploading requires cassis-cli ≥ 1.1.0**: the server rejects an older CLI (which would drop the Markdown domain files) with a clear upgrade error.
39
39
 
@@ -31,7 +31,7 @@ GUIDE_FILENAME = "AGENTS.md"
31
31
  # Monotonic version of the doctrine text below. Bump it whenever
32
32
  # ontology_design_guide.md changes (a backend test enforces the pairing) — it
33
33
  # is what lets an older writer recognize a newer guide and leave it alone.
34
- DOCTRINE_VERSION = 9
34
+ DOCTRINE_VERSION = 10
35
35
 
36
36
  # Must stay byte-identical to backend/app/services/ontology_guide.py::_BANNER —
37
37
  # the server-side git export writes the same file, and differing banners would
@@ -375,7 +375,13 @@ def upload(
375
375
  f"{result['join_count']} joins, {result['metric_count']} metrics"
376
376
  )
377
377
  version = result.get("published_version")
378
- if version is not None:
378
+ if version is not None and result.get("unchanged"):
379
+ typer.secho(
380
+ f"✓ Context uploaded ({counts}). No change: v{version} stays published, "
381
+ f"and now records commit {head[:9]}.",
382
+ fg=typer.colors.GREEN,
383
+ )
384
+ elif version is not None:
379
385
  typer.secho(f"✓ Context uploaded and published as v{version} ({counts}).", fg=typer.colors.GREEN)
380
386
  else:
381
387
  typer.secho(
@@ -22,7 +22,7 @@ with domains as Markdown and everything else as YAML:
22
22
  | **Root context** (`domains/README.md` body) | One free-text block, always in the agent's prompt | markdown |
23
23
  | **Domains** | A navigable tree grouping the business by subject area | `path`, `display_name`, `description`, `context_md` |
24
24
  | **Tables** | A physical warehouse table (introspected) or a virtual one (SQL-defined) placed in a domain | `name` (`schema.TABLE`), `description`, `synonyms`, `grain`, columns |
25
- | **Columns** | Enrichment on a table's columns | `description`, `unit`, `synonyms` |
25
+ | **Columns** | Enrichment on a table's columns | `description`, `unit`, `synonyms`, `enum_values` |
26
26
  | **Joins** | A known way two tables relate | `from_schema`/`from_table`, `to_schema`/`to_table`, `column_pairs`, `condition_sql`, `cardinality`, `description` |
27
27
  | **Metrics** | A reusable business measure | `name`, `display_name`, `expression`, `table_schema`/`table_name`, `filters`, `unit`, `synonyms`, `notes`, `precomputed_in` |
28
28
 
@@ -50,8 +50,8 @@ edit surface: you edit the files, open a pull request, and merging syncs and
50
50
  publishes it.
51
51
 
52
52
  Physical tables and their columns come from schema introspection — you never
53
- invent them; you *enrich* them (place them in a domain, describe them, set units
54
- and synonyms). The one exception is a virtual table, which you define with a
53
+ invent them; you *enrich* them (place them in a domain, describe them, set units,
54
+ synonyms and stored values). The one exception is a virtual table, which you define with a
55
55
  `sql` body (see §5).
56
56
 
57
57
  The one governing principle everything below serves:
@@ -106,7 +106,7 @@ and must match the warehouse (see §7).
106
106
  Put each fact at the lowest layer that fully owns it:
107
107
 
108
108
  ```
109
- column synonym / column description single-column facts: meaning, encoding, unit, date format, nulls
109
+ column enum_values / synonym / description single-column facts: stored values, meaning, encoding, unit, date format, nulls
110
110
  → table description single-table facts: scope, mandatory filters, "use this not that"
111
111
  → domain context_md cross-table routing, disambiguation, vocabulary spanning tables
112
112
  → root context truly global rules only: corporate identity, entity hierarchy, global conventions
@@ -223,11 +223,31 @@ in the `sql` per §7.
223
223
 
224
224
  ## 6. Columns
225
225
 
226
- Column enrichment is `description`, `unit`, and `synonyms` — you cannot add or
227
- rename columns (they come from introspection). The agent sees a column's name,
228
- type, `unit` and `synonyms` structurally; the `description` carries what those
229
- don't: meaning, possible values, encoding, date format, null behavior, any
230
- mandatory filter.
226
+ Column enrichment is `description`, `unit`, `synonyms` and `enum_values` — you
227
+ cannot add or rename columns (they come from introspection). The agent sees a
228
+ column's name, type, `unit`, `synonyms` and `enum_values` structurally; the
229
+ `description` carries what those don't: meaning, encoding, date format, null
230
+ behavior, any mandatory filter.
231
+
232
+ - **Stored values go in `enum_values`, not in the description.** A categorical
233
+ column with a small, stable set (a status, type, tier, code) lists every value
234
+ it stores in `enum_values`, verbatim and case-exact, in a meaningful order
235
+ (ordinal sets keep their order). A value's meaning goes in that value's own
236
+ `description`; the column `description` says what the column *means* and never
237
+ lists values. Free text, identifiers and high-cardinality columns get no
238
+ `enum_values`; a few notable codes in a large column stay in the description.
239
+ How to get the list right, and what to do with value lists already written in
240
+ prose, is §10.
241
+
242
+ ```yaml
243
+ columns:
244
+ - description: Lifecycle stage of the subscription.
245
+ enum_values:
246
+ - value: ACTIVE
247
+ - value: CANCELLED
248
+ description: Cancelled by the customer; access runs to period end.
249
+ name: status
250
+ ```
231
251
 
232
252
  - **Prioritize the trap columns.** Disambiguate similarly-named columns
233
253
  (`created_at` vs `updated_at`, `amount` vs `net_amount`), numeric-encoded
@@ -370,7 +390,9 @@ warehouse's stored case**, or generated SQL fails with errors like
370
390
  `grain`, join endpoints (`from_schema`/`from_table`/…), `column_pairs` and
371
391
  `condition_sql`, and metric `table_schema`/`table_name`/`expression`/`filters`. Logical fields — domain paths, display names, descriptions,
372
392
  **synonyms**, `context_md` — stay natural language; the matching layer there is
373
- case-insensitive and should read naturally.
393
+ case-insensitive and should read naturally. A column's `enum_values` are data
394
+ literals, not identifiers, but they are matched exactly too: write each value
395
+ as stored, case included.
374
396
  - **Schema-qualify everything.** Table references in SQL and prose carry the
375
397
  `schema.TABLE` form (`dbt_core.fct_orders`), so a scope rule about `raw.users`
376
398
  never accidentally catches `core.users`, and a prefix that doesn't match what
@@ -383,12 +405,22 @@ warehouse's stored case**, or generated SQL fails with errors like
383
405
  An inaccurate example primes the agent **more strongly** than a correct prose
384
406
  description, so a wrong example is worse than none.
385
407
 
386
- - **Use real stored enum values verbatim.** Never hand-wave a list with "etc." —
387
- the agent reads `etc.` as license to invent values. If you don't have the exact
388
- values, omit the list rather than approximate it. A `DISTINCT` over the column
389
- *is* the list of stored values: write it plainly ("Stored values: 'EDITOR'"),
390
- with no "observed so far" hedge and no counts. When two columns supposedly
391
- share an enum, verify they're identical before encoding both.
408
+ - **`enum_values` is the complete set of stored values, verbatim.** The agent
409
+ treats it as closed: filter literals come from it, exact case, and a value it
410
+ doesn't list does not exist. Verify the set against the source before writing
411
+ it — a `SELECT DISTINCT` over the column, or a `CHECK ... IN (...)` constraint
412
+ the schema shows — and write it plainly, with no "observed so far" hedge and no
413
+ counts. If you don't have the exact set, leave `enum_values` out rather than
414
+ approximate it: a partial list is worse than none, because the agent stops
415
+ looking for other values. When two columns supposedly share an enum, verify
416
+ they're identical before encoding both.
417
+ - **Move value lists out of prose.** Older descriptions list values in text
418
+ ("Possible values: …", "Values:", "One of:", "Stored values: 'EDITOR'"). When
419
+ you touch a table and find one, move the list into that column's `enum_values`
420
+ and delete it from the description in the same change, verifying it against
421
+ the source first when you can. A hedged or open-ended prose list ("etc.",
422
+ "includes", "e.g.", "and legacy …") is **not** the complete set: verify it, or
423
+ drop it without promoting it. Never turn an approximation into `enum_values`.
392
424
  - **Be wary of analogies to external products** — they mislead the moment the
393
425
  real mechanic diverges.
394
426
  - **Don't describe things that don't exist.** Every table, column, and value you
@@ -430,10 +462,10 @@ description, so a wrong example is worse than none.
430
462
  definitions, not what the data shows today. Row counts, distinct counts, shares,
431
463
  distributions, funnel rates and "as of" / "currently" / "observed" statements go
432
464
  stale silently, and the answering agent repeats them as fact. Query the data to
433
- *verify* a stable fact (stored enum values, a date format, null behaviour) and
434
- write only the fact: "Stored values: 'EDITOR', 'ADMIN'", not "only 'EDITOR'
435
- observed (6 of 6, 100%)". Complete value lists and constants the business fixes
436
- (a 14-day trial) are facts, not measurements.
465
+ *verify* a stable fact (a column's stored values, a date format, null
466
+ behaviour) and write only the fact: the values in `enum_values`, not "only
467
+ 'EDITOR' observed (6 of 6, 100%)" in prose. Complete value sets and constants
468
+ the business fixes (a 14-day trial) are facts, not measurements.
437
469
  - **Ask for the business meaning; don't guess.** A term like "active user" or
438
470
  "last month" has a project-specific definition (time window, criteria, filters).
439
471
  Encode the business's actual definition, not a textbook default.
@@ -71,12 +71,25 @@ def _local_comparison(path: Path, head: Optional[str], published_sha: Optional[s
71
71
  return f"diverged from the published commit {published_sha[:9]}", False
72
72
 
73
73
 
74
+ def _published_commit(published: "dict[str, Any]") -> Optional[str]:
75
+ """The newest commit known to hold the published content.
76
+
77
+ An upload that changes nothing publishes no version but moves
78
+ `latest_git_commit_sha` forward. Servers older than that field only send
79
+ `git_commit_sha`, the commit the version was published from.
80
+ """
81
+ return published.get("latest_git_commit_sha") or published.get("git_commit_sha")
82
+
83
+
74
84
  def _render(status_record: "dict[str, Any]", comparison_text: str) -> None:
75
85
  published = status_record.get("published_version")
76
86
  if published:
77
87
  label = f" {published['label']!r}" if published.get("label") else ""
78
88
  sha = published.get("git_commit_sha")
89
+ latest = _published_commit(published)
79
90
  sha_text = f", commit {sha[:9]}" if sha else ""
91
+ if latest and latest != sha:
92
+ sha_text += f", unchanged at {latest[:9]}"
80
93
  typer.echo(f"Published: v{published['version']}{label} ({published.get('published_at')}{sha_text})")
81
94
  else:
82
95
  typer.echo("Published: nothing yet")
@@ -164,7 +177,7 @@ def status(
164
177
  """The status record plus the local comparison it implies: (record, comparison_text, in_sync)."""
165
178
  record = get_project_status(api_url=api_url, api_key=api_key, project_id=project_id)
166
179
  published = record.get("published_version") or {}
167
- comparison_text, in_sync = _local_comparison(path, head, published.get("git_commit_sha"))
180
+ comparison_text, in_sync = _local_comparison(path, head, _published_commit(published))
168
181
  return record, comparison_text, in_sync
169
182
 
170
183
  def report(snapshot: "Tuple[dict[str, Any], str, bool]") -> None:
@@ -175,7 +188,7 @@ def status(
175
188
  _render(record, comparison_text)
176
189
  else:
177
190
  published = record.get("published_version") or {}
178
- published_sha = published.get("git_commit_sha")
191
+ published_sha = _published_commit(published)
179
192
  version_text = f"v{published['version']}" if published else "nothing published"
180
193
  line = f"{version_text} (commit {published_sha[:9] if published_sha else 'none'}); local: {comparison_text}"
181
194
  if line != last_line["text"]:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cassis-cli"
3
- version = "3.1.0"
3
+ version = "3.2.0"
4
4
  description = "Validate, test and evaluate your Cassis context from your terminal, then publish it"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
File without changes
File without changes
File without changes