cassis-cli 2.4.0__tar.gz → 3.1.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.
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: cassis-cli
3
+ Version: 3.1.0
4
+ Summary: Validate, test and evaluate your Cassis context from your terminal, then publish it
5
+ License: Apache-2.0
6
+ License-File: LICENSE
7
+ License-File: NOTICE
8
+ Keywords: cassis,context,context-layer,ontology,cli,ci,text-to-sql
9
+ Author: Cassis
10
+ Author-email: tech.admin@getcassis.com
11
+ Requires-Python: >=3.10,<4.0
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Database
16
+ Requires-Dist: httpx (>=0.24,<1.0)
17
+ Requires-Dist: typer (>=0.12,<1.0)
18
+ Project-URL: Documentation, https://github.com/GetCassis/cassis-cli#readme
19
+ Project-URL: Homepage, https://getcassis.com
20
+ Project-URL: Repository, https://github.com/GetCassis/cassis-cli
21
+ Description-Content-Type: text/markdown
22
+
23
+ # Cassis CLI
24
+
25
+ Validate, test and evaluate your context from your terminal, then publish it. The same commands gate your pull requests in CI:
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.
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
+ - `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.
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
+ - `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
+ - 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).
34
+ - `cassis eval run` runs the project's eval suite against your local context files (scored in-memory, nothing is pushed to Cassis) and prints per-question results, so you can test the changes on your git branch before merging.
35
+ - `cassis context test` runs individual questions through the text-to-SQL agent using your local context 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
+ - `cassis eval add-case` adds a gold question/SQL case to the project's eval suite: after fixing a context issue, add the question users were failing on so `eval run` guards it from regressing.
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 context 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 context changes Cassis will make (every change on a table placed in the context, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting context files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local context 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 the schema snapshot for a change still in a PR can be planned against safely; `--write-checkout` writes the context files it would produce into the checkout, to commit alongside the schema change.
39
+ - `cassis projects list` lists the projects your API key can reach: id (what `--project` and `CASSIS_PROJECT_ID` take), name, published context version, and data-source dialect. A pipeline or agent can discover the project id from the terminal instead of fishing it out of a webapp URL.
40
+ - DDL imports describe one schema snapshot/export file, including ordinary and materialized views; they do not replay incremental migrations. Extraction diagnostics include object names and statement locations. If extraction is incomplete, `schema plan` and `--dry-run` show the extracted inventory and exit 1; `apply`, `push`, and `--write-checkout` cannot save that result. Unknown column types are warnings when all output names are known. Plans also show object-kind, view-definition, comment, and constraint changes. `--json` preserves structured diagnostics and the server-capped inventory.
41
+
42
+ - `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 context) instead of watching the GitHub Actions tab.
43
+ - `cassis issues` triages the issues Cassis raised on the project (what it found wrong while answering questions: a context gap, missing data) without leaving the checkout: `issues list` (filterable by status, impact, cause and context 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. When the fix ships through a pull request, write the `PR mention:` line `issues show` prints (`Resolves <id>`) in the PR description instead: Cassis resolves the issue when the PR merges, and `issues show` then reports how it was closed and through which PR.
44
+ - `cassis verify` runs the full local gate in one verb (`context fmt --check`, `context 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.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install cassis-cli
50
+ ```
51
+
52
+ ## Context file format
53
+
54
+ The context tree under `<base-path>` (default `cassis/`) is:
55
+
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
+ - **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`.
59
+
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
+
62
+ ## Setup
63
+
64
+ 1. Create an API key in Cassis under **Organization settings → API keys** (keys start with `sk-k6-`).
65
+ 2. Store it as a CI secret and expose it as `CASSIS_API_KEY`.
66
+ 3. For `pull`, `upload`, `schema pull`, `eval run`, `context test`, the `eval` case commands (`add-case`, `list-cases`, `delete-case`), and the `issues` commands: the project ID (UUID) is taken from `<base-path>/project.yml` in the checkout (written by `pull` and by publishing), so once a repo is pulled you don't need to pass it. To override, or before the first pull, set `CASSIS_PROJECT_ID` or pass `--project` (find the UUID with `cassis projects list`, or in the project's URL). `context check` uses the same resolution but treats it as optional: unbound checkouts get the project-less validation (no schema reference warnings).
67
+
68
+ ## Usage
69
+
70
+ ```bash
71
+ # From the root of a repository synced with Cassis (contains the context export directory, cassis/ by default):
72
+ cassis context check
73
+
74
+ # Or point at the checkout explicitly:
75
+ cassis context check /path/to/checkout
76
+
77
+ # Download the project's unpublished context into the checkout (full sync;
78
+ # review with git diff. Untracked/modified files are never deleted, and
79
+ # --no-prune keeps even the tracked stale files it would otherwise delete):
80
+ cassis context pull --project 019f0000-0000-7000-8000-000000000000
81
+
82
+ # Upload the context to a project and publish it immediately (commit the
83
+ # changes under cassis/ first: uploads refuse uncommitted context files):
84
+ cassis context upload --project 019f0000-0000-7000-8000-000000000000
85
+
86
+ # Upload without publishing (the tree becomes the project's unpublished context, to review in Cassis):
87
+ cassis context upload --project ... --no-publish
88
+
89
+ # Label the published version:
90
+ cassis context upload --project ... --label "release 1.2"
91
+
92
+ # Machine-readable output:
93
+ cassis context check --json
94
+ cassis context pull --project ... --json
95
+ cassis context upload --project ... --json
96
+ cassis eval run --project ... --json
97
+
98
+ # Run the eval suite against the local context files and wait for results
99
+ # (the run is labelled with your git branch name in the Evals page):
100
+ cassis eval run --project ...
101
+
102
+ # Run against an existing Cassis context branch, or the unpublished context:
103
+ cassis eval run --project ... --branch feature-x
104
+
105
+ # Run only specific cases (repeatable), e.g. prove a fresh add-case in seconds:
106
+ cassis eval run --project ... --case 019f0000-0000-7000-8000-0000000000ca
107
+
108
+ # Start the run and return immediately (poll in the webapp):
109
+ cassis eval run --project ... --no-wait
110
+ ```
111
+
112
+ Under each failed case `eval run` prints what is needed to diagnose it: the SQL the agent
113
+ generated, the gold SQL it was compared against, how many rows each side returned, any concepts
114
+ the agent found missing, and the judge's reasoning when a judge graded the case. The expected and
115
+ actual row *values* are deliberately not printed: they are in `--json` and in the Cassis app.
116
+ Note that the **generated SQL is printed as the agent wrote it**, and an agent that read your data
117
+ while planning can carry a value it saw into a literal in that SQL. Treat `eval run` output as
118
+ carrying the same sensitivity as the queries themselves when you decide who can read your CI logs.
119
+
120
+ ```bash
121
+
122
+ # Probe questions through the text-to-SQL agent using the local context files
123
+ # (one full agent run per question, expect ~30-90s each; repeat -q for several):
124
+ cassis context test --project ... -q "How much was refunded last month?" -q "Net revenue in Q1?"
125
+
126
+ # Add a gold case to the eval suite (rejected if the exact question already exists):
127
+ cassis eval add-case --project ... -q "How much was refunded last month?" \
128
+ --gold-sql "SELECT SUM(refunded_cents) / 100.0 FROM public.orders WHERE ..."
129
+
130
+ # Multi-line gold SQL: read it from a file instead (no shell quoting pitfalls):
131
+ cassis eval add-case --project ... -q "How much was refunded last month?" \
132
+ --gold-sql-file refunds.sql
133
+
134
+ # List the suite's cases (id + question; --json adds the gold SQL), then prune one:
135
+ cassis eval list-cases --project ...
136
+ cassis eval delete-case 019f0000-0000-7000-8000-0000000000ca --project ...
137
+
138
+ # Pull the source schema into <base-path>/.schema.json (gitignored local snapshot):
139
+ cassis schema pull
140
+
141
+ # Preview, apply locally and push a schema update from a DDL file (DDL-only projects):
142
+ cassis schema plan schema.sql --complete
143
+ cassis schema apply schema.sql --complete # writes cassis/ locally
144
+ git add cassis && git commit -m "Apply schema update" # push needs the tree committed
145
+ cassis schema push schema.sql --complete --yes # schema + context to the app
146
+ cassis schema plan --warehouse # warehouse-connected projects: introspect instead
147
+ cassis schema plan future.sql --dry-run --write-checkout # plan a not-yet-deployed DDL, keep nothing server-side
148
+
149
+ # List the projects the API key can reach (id, name, published version, dialect):
150
+ cassis projects list
151
+
152
+ # Refresh the issues from the conversations nobody has analyzed yet (the same pass as the
153
+ # webapp's "Analyze conversations" button; waits for the result, --no-wait returns at once,
154
+ # and Ctrl-C cancels the run server-side and exits 130):
155
+ cassis issues analyze
156
+
157
+ # Triage the issues Cassis raised (filter by --status/--impact/--cause; --json for raw output):
158
+ cassis issues list # open issues by default
159
+ cassis issues list --status all # include resolved and dismissed issues
160
+ cassis issues show 019f0000-0000-7000-8000-0000000000e1
161
+
162
+ # Work one context domain at a time (nested domains included):
163
+ cassis issues list --domain sales
164
+
165
+ # Read what the agent saw for one occurrence (ids from `issues show`):
166
+ cassis issues evidence 019f0000-0000-7000-8000-0000000000e1 019f0000-0000-7000-8000-0000000000c1
167
+
168
+ # Close the loop once the fix is published (or reopen). When the fix ships in a pull
169
+ # request, put `Resolves <id>` in its description instead and the merge closes the issue:
170
+ cassis issues resolve 019f0000-0000-7000-8000-0000000000e1 --published
171
+ cassis issues dismiss 019f0000-0000-7000-8000-0000000000e1 --reason irrelevant --detail "Outside our scope"
172
+ cassis issues reopen 019f0000-0000-7000-8000-0000000000e1
173
+
174
+ # Published version vs local checkout (add --watch to poll until your merge is published):
175
+ cassis status
176
+ cassis status --watch --timeout 600
177
+
178
+ # The full local gate in one verb (fmt --check, check, eval run; stops at the first failure):
179
+ cassis verify
180
+ cassis verify --no-eval
181
+ ```
182
+
183
+ Configuration (flags take precedence over env vars):
184
+
185
+ | Flag | Env var | Default |
186
+ | ----------- | ---------------- | --------------------------- |
187
+ | `--api-key` | `CASSIS_API_KEY` | none (required) |
188
+ | `--api-url` | `CASSIS_API_URL` | `https://app.getcassis.com` |
189
+ | `--base-path` | `CASSIS_BASE_PATH` | `cassis`, must match the project's git-sync "Path" setting |
190
+ | `--project` (check, pull, upload, schema pull, eval run, eval add-case, eval list-cases, eval delete-case, test, issues) | `CASSIS_PROJECT_ID` | the id in `<base-path>/project.yml` (required before the first pull; `check` alone falls back to the project-less validation when unbound) |
191
+
192
+ `cassis eval run` also accepts `--case <id>` (repeatable; run only the named
193
+ cases, ids from `eval list-cases` or `add-case`), `--label` (run label in the Evals page; defaults
194
+ to the branch name from the CI environment or the local git checkout; rejected
195
+ with `--branch`, whose runs are labelled with the branch name), `--wait/--no-wait`, `--poll-interval` (5 s),
196
+ `--timeout` (30 min; the run keeps going server-side if the CLI stops waiting),
197
+ and Ctrl-C cancels the run (exit 130). It prints a deep link to the run's page
198
+ in the Evals UI; `--app-url` / `CASSIS_APP_URL` overrides the link's base URL
199
+ when the webapp is not served from the API host (defaults to `--api-url`).
200
+
201
+ ### Formatting
202
+
203
+ ```bash
204
+ # Rewrite the context files in canonical form (in place)
205
+ cassis context fmt
206
+
207
+ # CI mode: fail (exit 1) if any file is not canonical, write nothing
208
+ cassis context fmt --check
209
+ ```
210
+
211
+ `fmt` uses the exact serializer the validation round-trip compares against, so a formatted tree cannot fail that stage. Formatting does not run import validation: `check` remains the pass/fail gate for semantic problems (dangling references, incomplete metrics).
212
+
213
+ **Review the diff before committing**: canonical form keeps exactly the fields Cassis understands. Unknown fields (typos) are dropped, and the rewrite makes them visible in `git diff` instead of losing them silently at sync time. Files with duplicate YAML keys are rejected (fix them by hand: the formatter can't know which value you meant).
214
+
215
+ ### Exit codes
216
+
217
+ | Code | Meaning |
218
+ | ---- | ------------------------------------------------------------------------------ |
219
+ | 0 | Context is valid (check) / pulled (pull) / uploaded (upload) / eval run completed all-passed (eval run) / every probe completed (test, whatever its outcome; probes are informational, don't gate CI on them) |
220
+ | 1 | Validation failed (check: findings printed; upload: nothing imported; eval run: invalid tree, failed cases, or failed/cancelled run; test: invalid tree or a probe failed; add-case: duplicate question or gold SQL that does not run; delete-case: no such case in the project; issues: no such issue or occurrence in the project; issues analyze: failed or cancelled analysis run; schema plan/apply: extraction is incomplete, the plan failed (unparseable or truncated DDL), is stale or expired, the apply failed, or the project won't accept it (a plan is being applied, a DDL was given for a warehouse-connected project, or --warehouse for a DDL-only one)) |
221
+ | 2 | Usage error (missing API key or project, no context directory, unreadable file, tree over the size limits, `eval run --branch` naming a context branch the project does not have, `upload` or `schema push` outside a git checkout or with uncommitted context files) |
222
+ | 3 | Transport/API error (unreachable API, invalid key, inaccessible project, unexpected response), another eval run or issue analysis already active, out of credits, or `--timeout` reached |
223
+
224
+ Commands that send the local tree (`check`, `fmt`, `upload`, `eval run`, `test`) accept up to
225
+ 20,000 context files / 100 MB total (path + content bytes), sized for a context of
226
+ roughly 10,000 modeled tables. Beyond that the CLI fails fast with exit 2 before
227
+ uploading anything; double-check `--base-path` if you hit it.
228
+
229
+ `upload` replaces the project's entire context with the uploaded tree. A
230
+ never-published project always goes live immediately on first upload (even
231
+ with `--no-publish`), matching imports from the Cassis app. Publishing is
232
+ idempotent: re-uploading content identical to the published version reports
233
+ that version instead of creating a new one, so re-running the CI job on
234
+ unchanged files is a no-op.
235
+
236
+ ### GitHub Actions example
237
+
238
+ ```yaml
239
+ jobs:
240
+ context-check:
241
+ runs-on: ubuntu-latest
242
+ steps:
243
+ - uses: actions/checkout@v4
244
+ - uses: actions/setup-python@v5
245
+ with:
246
+ python-version: "3.12"
247
+ - run: pip install cassis-cli
248
+ - run: cassis context check
249
+ env:
250
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
251
+
252
+ context-eval:
253
+ runs-on: ubuntu-latest
254
+ if: github.event_name == 'pull_request'
255
+ steps:
256
+ - uses: actions/checkout@v4
257
+ - uses: actions/setup-python@v5
258
+ with:
259
+ python-version: "3.12"
260
+ - run: pip install cassis-cli
261
+ - run: cassis eval run
262
+ env:
263
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
264
+ CASSIS_PROJECT_ID: ${{ vars.CASSIS_PROJECT_ID }}
265
+
266
+ context-publish:
267
+ runs-on: ubuntu-latest
268
+ if: github.ref == 'refs/heads/main'
269
+ steps:
270
+ - uses: actions/checkout@v4
271
+ - uses: actions/setup-python@v5
272
+ with:
273
+ python-version: "3.12"
274
+ - run: pip install cassis-cli
275
+ - run: cassis context upload
276
+ env:
277
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
278
+ CASSIS_PROJECT_ID: ${{ vars.CASSIS_PROJECT_ID }}
279
+ ```
280
+
281
+ ### GitLab CI example
282
+
283
+ ```yaml
284
+ context-check:
285
+ image: python:3.12-slim
286
+ script:
287
+ - pip install cassis-cli
288
+ - cassis context check
289
+ variables:
290
+ CASSIS_API_KEY: $CASSIS_API_KEY
291
+
292
+ context-eval:
293
+ image: python:3.12-slim
294
+ rules:
295
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
296
+ script:
297
+ - pip install cassis-cli
298
+ - cassis eval run
299
+ variables:
300
+ CASSIS_API_KEY: $CASSIS_API_KEY
301
+ CASSIS_PROJECT_ID: $CASSIS_PROJECT_ID
302
+
303
+ context-publish:
304
+ image: python:3.12 # not -slim: the upload needs git to record the commit
305
+ rules:
306
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
307
+ script:
308
+ - pip install cassis-cli
309
+ - cassis context upload
310
+ variables:
311
+ CASSIS_API_KEY: $CASSIS_API_KEY
312
+ CASSIS_PROJECT_ID: $CASSIS_PROJECT_ID
313
+ ```
314
+
315
+ ## About this repository
316
+
317
+ [github.com/GetCassis/cassis-cli](https://github.com/GetCassis/cassis-cli) is a
318
+ read-only mirror, synced automatically from the Cassis monorepo where the CLI is
319
+ developed. Issues are welcome and watched; pull requests can't be merged here, so
320
+ open an issue (or mail tech.admin@getcassis.com) and we'll port the patch upstream
321
+ with credit.
322
+
323
+ Only the CLI is open source. The Cassis backend it talks to is proprietary and
324
+ requires an account.
325
+
326
+ ## License
327
+
328
+ Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
329
+
@@ -0,0 +1,306 @@
1
+ # Cassis CLI
2
+
3
+ Validate, test and evaluate your context from your terminal, then publish it. The same commands gate your pull requests in CI:
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.
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
+ - `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.
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
+ - `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
+ - 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).
12
+ - `cassis eval run` runs the project's eval suite against your local context files (scored in-memory, nothing is pushed to Cassis) and prints per-question results, so you can test the changes on your git branch before merging.
13
+ - `cassis context test` runs individual questions through the text-to-SQL agent using your local context 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
+ - `cassis eval add-case` adds a gold question/SQL case to the project's eval suite: after fixing a context issue, add the question users were failing on so `eval run` guards it from regressing.
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 context 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 context changes Cassis will make (every change on a table placed in the context, with everything a drop takes with it) and warnings, terraform-style. `cassis schema apply <ddl>` (or `--plan <id>`) writes the resulting context files into the local checkout, app untouched, for review with `git diff`. `cassis schema push <ddl> [--publish]` pushes the new schema and the local context 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 the schema snapshot for a change still in a PR can be planned against safely; `--write-checkout` writes the context files it would produce into the checkout, to commit alongside the schema change.
17
+ - `cassis projects list` lists the projects your API key can reach: id (what `--project` and `CASSIS_PROJECT_ID` take), name, published context version, and data-source dialect. A pipeline or agent can discover the project id from the terminal instead of fishing it out of a webapp URL.
18
+ - DDL imports describe one schema snapshot/export file, including ordinary and materialized views; they do not replay incremental migrations. Extraction diagnostics include object names and statement locations. If extraction is incomplete, `schema plan` and `--dry-run` show the extracted inventory and exit 1; `apply`, `push`, and `--write-checkout` cannot save that result. Unknown column types are warnings when all output names are known. Plans also show object-kind, view-definition, comment, and constraint changes. `--json` preserves structured diagnostics and the server-capped inventory.
19
+
20
+ - `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 context) instead of watching the GitHub Actions tab.
21
+ - `cassis issues` triages the issues Cassis raised on the project (what it found wrong while answering questions: a context gap, missing data) without leaving the checkout: `issues list` (filterable by status, impact, cause and context 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. When the fix ships through a pull request, write the `PR mention:` line `issues show` prints (`Resolves <id>`) in the PR description instead: Cassis resolves the issue when the PR merges, and `issues show` then reports how it was closed and through which PR.
22
+ - `cassis verify` runs the full local gate in one verb (`context fmt --check`, `context 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.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install cassis-cli
28
+ ```
29
+
30
+ ## Context file format
31
+
32
+ The context tree under `<base-path>` (default `cassis/`) is:
33
+
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
+ - **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`.
37
+
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
+
40
+ ## Setup
41
+
42
+ 1. Create an API key in Cassis under **Organization settings → API keys** (keys start with `sk-k6-`).
43
+ 2. Store it as a CI secret and expose it as `CASSIS_API_KEY`.
44
+ 3. For `pull`, `upload`, `schema pull`, `eval run`, `context test`, the `eval` case commands (`add-case`, `list-cases`, `delete-case`), and the `issues` commands: the project ID (UUID) is taken from `<base-path>/project.yml` in the checkout (written by `pull` and by publishing), so once a repo is pulled you don't need to pass it. To override, or before the first pull, set `CASSIS_PROJECT_ID` or pass `--project` (find the UUID with `cassis projects list`, or in the project's URL). `context check` uses the same resolution but treats it as optional: unbound checkouts get the project-less validation (no schema reference warnings).
45
+
46
+ ## Usage
47
+
48
+ ```bash
49
+ # From the root of a repository synced with Cassis (contains the context export directory, cassis/ by default):
50
+ cassis context check
51
+
52
+ # Or point at the checkout explicitly:
53
+ cassis context check /path/to/checkout
54
+
55
+ # Download the project's unpublished context into the checkout (full sync;
56
+ # review with git diff. Untracked/modified files are never deleted, and
57
+ # --no-prune keeps even the tracked stale files it would otherwise delete):
58
+ cassis context pull --project 019f0000-0000-7000-8000-000000000000
59
+
60
+ # Upload the context to a project and publish it immediately (commit the
61
+ # changes under cassis/ first: uploads refuse uncommitted context files):
62
+ cassis context upload --project 019f0000-0000-7000-8000-000000000000
63
+
64
+ # Upload without publishing (the tree becomes the project's unpublished context, to review in Cassis):
65
+ cassis context upload --project ... --no-publish
66
+
67
+ # Label the published version:
68
+ cassis context upload --project ... --label "release 1.2"
69
+
70
+ # Machine-readable output:
71
+ cassis context check --json
72
+ cassis context pull --project ... --json
73
+ cassis context upload --project ... --json
74
+ cassis eval run --project ... --json
75
+
76
+ # Run the eval suite against the local context files and wait for results
77
+ # (the run is labelled with your git branch name in the Evals page):
78
+ cassis eval run --project ...
79
+
80
+ # Run against an existing Cassis context branch, or the unpublished context:
81
+ cassis eval run --project ... --branch feature-x
82
+
83
+ # Run only specific cases (repeatable), e.g. prove a fresh add-case in seconds:
84
+ cassis eval run --project ... --case 019f0000-0000-7000-8000-0000000000ca
85
+
86
+ # Start the run and return immediately (poll in the webapp):
87
+ cassis eval run --project ... --no-wait
88
+ ```
89
+
90
+ Under each failed case `eval run` prints what is needed to diagnose it: the SQL the agent
91
+ generated, the gold SQL it was compared against, how many rows each side returned, any concepts
92
+ the agent found missing, and the judge's reasoning when a judge graded the case. The expected and
93
+ actual row *values* are deliberately not printed: they are in `--json` and in the Cassis app.
94
+ Note that the **generated SQL is printed as the agent wrote it**, and an agent that read your data
95
+ while planning can carry a value it saw into a literal in that SQL. Treat `eval run` output as
96
+ carrying the same sensitivity as the queries themselves when you decide who can read your CI logs.
97
+
98
+ ```bash
99
+
100
+ # Probe questions through the text-to-SQL agent using the local context files
101
+ # (one full agent run per question, expect ~30-90s each; repeat -q for several):
102
+ cassis context test --project ... -q "How much was refunded last month?" -q "Net revenue in Q1?"
103
+
104
+ # Add a gold case to the eval suite (rejected if the exact question already exists):
105
+ cassis eval add-case --project ... -q "How much was refunded last month?" \
106
+ --gold-sql "SELECT SUM(refunded_cents) / 100.0 FROM public.orders WHERE ..."
107
+
108
+ # Multi-line gold SQL: read it from a file instead (no shell quoting pitfalls):
109
+ cassis eval add-case --project ... -q "How much was refunded last month?" \
110
+ --gold-sql-file refunds.sql
111
+
112
+ # List the suite's cases (id + question; --json adds the gold SQL), then prune one:
113
+ cassis eval list-cases --project ...
114
+ cassis eval delete-case 019f0000-0000-7000-8000-0000000000ca --project ...
115
+
116
+ # Pull the source schema into <base-path>/.schema.json (gitignored local snapshot):
117
+ cassis schema pull
118
+
119
+ # Preview, apply locally and push a schema update from a DDL file (DDL-only projects):
120
+ cassis schema plan schema.sql --complete
121
+ cassis schema apply schema.sql --complete # writes cassis/ locally
122
+ git add cassis && git commit -m "Apply schema update" # push needs the tree committed
123
+ cassis schema push schema.sql --complete --yes # schema + context to the app
124
+ cassis schema plan --warehouse # warehouse-connected projects: introspect instead
125
+ cassis schema plan future.sql --dry-run --write-checkout # plan a not-yet-deployed DDL, keep nothing server-side
126
+
127
+ # List the projects the API key can reach (id, name, published version, dialect):
128
+ cassis projects list
129
+
130
+ # Refresh the issues from the conversations nobody has analyzed yet (the same pass as the
131
+ # webapp's "Analyze conversations" button; waits for the result, --no-wait returns at once,
132
+ # and Ctrl-C cancels the run server-side and exits 130):
133
+ cassis issues analyze
134
+
135
+ # Triage the issues Cassis raised (filter by --status/--impact/--cause; --json for raw output):
136
+ cassis issues list # open issues by default
137
+ cassis issues list --status all # include resolved and dismissed issues
138
+ cassis issues show 019f0000-0000-7000-8000-0000000000e1
139
+
140
+ # Work one context domain at a time (nested domains included):
141
+ cassis issues list --domain sales
142
+
143
+ # Read what the agent saw for one occurrence (ids from `issues show`):
144
+ cassis issues evidence 019f0000-0000-7000-8000-0000000000e1 019f0000-0000-7000-8000-0000000000c1
145
+
146
+ # Close the loop once the fix is published (or reopen). When the fix ships in a pull
147
+ # request, put `Resolves <id>` in its description instead and the merge closes the issue:
148
+ cassis issues resolve 019f0000-0000-7000-8000-0000000000e1 --published
149
+ cassis issues dismiss 019f0000-0000-7000-8000-0000000000e1 --reason irrelevant --detail "Outside our scope"
150
+ cassis issues reopen 019f0000-0000-7000-8000-0000000000e1
151
+
152
+ # Published version vs local checkout (add --watch to poll until your merge is published):
153
+ cassis status
154
+ cassis status --watch --timeout 600
155
+
156
+ # The full local gate in one verb (fmt --check, check, eval run; stops at the first failure):
157
+ cassis verify
158
+ cassis verify --no-eval
159
+ ```
160
+
161
+ Configuration (flags take precedence over env vars):
162
+
163
+ | Flag | Env var | Default |
164
+ | ----------- | ---------------- | --------------------------- |
165
+ | `--api-key` | `CASSIS_API_KEY` | none (required) |
166
+ | `--api-url` | `CASSIS_API_URL` | `https://app.getcassis.com` |
167
+ | `--base-path` | `CASSIS_BASE_PATH` | `cassis`, must match the project's git-sync "Path" setting |
168
+ | `--project` (check, pull, upload, schema pull, eval run, eval add-case, eval list-cases, eval delete-case, test, issues) | `CASSIS_PROJECT_ID` | the id in `<base-path>/project.yml` (required before the first pull; `check` alone falls back to the project-less validation when unbound) |
169
+
170
+ `cassis eval run` also accepts `--case <id>` (repeatable; run only the named
171
+ cases, ids from `eval list-cases` or `add-case`), `--label` (run label in the Evals page; defaults
172
+ to the branch name from the CI environment or the local git checkout; rejected
173
+ with `--branch`, whose runs are labelled with the branch name), `--wait/--no-wait`, `--poll-interval` (5 s),
174
+ `--timeout` (30 min; the run keeps going server-side if the CLI stops waiting),
175
+ and Ctrl-C cancels the run (exit 130). It prints a deep link to the run's page
176
+ in the Evals UI; `--app-url` / `CASSIS_APP_URL` overrides the link's base URL
177
+ when the webapp is not served from the API host (defaults to `--api-url`).
178
+
179
+ ### Formatting
180
+
181
+ ```bash
182
+ # Rewrite the context files in canonical form (in place)
183
+ cassis context fmt
184
+
185
+ # CI mode: fail (exit 1) if any file is not canonical, write nothing
186
+ cassis context fmt --check
187
+ ```
188
+
189
+ `fmt` uses the exact serializer the validation round-trip compares against, so a formatted tree cannot fail that stage. Formatting does not run import validation: `check` remains the pass/fail gate for semantic problems (dangling references, incomplete metrics).
190
+
191
+ **Review the diff before committing**: canonical form keeps exactly the fields Cassis understands. Unknown fields (typos) are dropped, and the rewrite makes them visible in `git diff` instead of losing them silently at sync time. Files with duplicate YAML keys are rejected (fix them by hand: the formatter can't know which value you meant).
192
+
193
+ ### Exit codes
194
+
195
+ | Code | Meaning |
196
+ | ---- | ------------------------------------------------------------------------------ |
197
+ | 0 | Context is valid (check) / pulled (pull) / uploaded (upload) / eval run completed all-passed (eval run) / every probe completed (test, whatever its outcome; probes are informational, don't gate CI on them) |
198
+ | 1 | Validation failed (check: findings printed; upload: nothing imported; eval run: invalid tree, failed cases, or failed/cancelled run; test: invalid tree or a probe failed; add-case: duplicate question or gold SQL that does not run; delete-case: no such case in the project; issues: no such issue or occurrence in the project; issues analyze: failed or cancelled analysis run; schema plan/apply: extraction is incomplete, the plan failed (unparseable or truncated DDL), is stale or expired, the apply failed, or the project won't accept it (a plan is being applied, a DDL was given for a warehouse-connected project, or --warehouse for a DDL-only one)) |
199
+ | 2 | Usage error (missing API key or project, no context directory, unreadable file, tree over the size limits, `eval run --branch` naming a context branch the project does not have, `upload` or `schema push` outside a git checkout or with uncommitted context files) |
200
+ | 3 | Transport/API error (unreachable API, invalid key, inaccessible project, unexpected response), another eval run or issue analysis already active, out of credits, or `--timeout` reached |
201
+
202
+ Commands that send the local tree (`check`, `fmt`, `upload`, `eval run`, `test`) accept up to
203
+ 20,000 context files / 100 MB total (path + content bytes), sized for a context of
204
+ roughly 10,000 modeled tables. Beyond that the CLI fails fast with exit 2 before
205
+ uploading anything; double-check `--base-path` if you hit it.
206
+
207
+ `upload` replaces the project's entire context with the uploaded tree. A
208
+ never-published project always goes live immediately on first upload (even
209
+ with `--no-publish`), matching imports from the Cassis app. Publishing is
210
+ idempotent: re-uploading content identical to the published version reports
211
+ that version instead of creating a new one, so re-running the CI job on
212
+ unchanged files is a no-op.
213
+
214
+ ### GitHub Actions example
215
+
216
+ ```yaml
217
+ jobs:
218
+ context-check:
219
+ runs-on: ubuntu-latest
220
+ steps:
221
+ - uses: actions/checkout@v4
222
+ - uses: actions/setup-python@v5
223
+ with:
224
+ python-version: "3.12"
225
+ - run: pip install cassis-cli
226
+ - run: cassis context check
227
+ env:
228
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
229
+
230
+ context-eval:
231
+ runs-on: ubuntu-latest
232
+ if: github.event_name == 'pull_request'
233
+ steps:
234
+ - uses: actions/checkout@v4
235
+ - uses: actions/setup-python@v5
236
+ with:
237
+ python-version: "3.12"
238
+ - run: pip install cassis-cli
239
+ - run: cassis eval run
240
+ env:
241
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
242
+ CASSIS_PROJECT_ID: ${{ vars.CASSIS_PROJECT_ID }}
243
+
244
+ context-publish:
245
+ runs-on: ubuntu-latest
246
+ if: github.ref == 'refs/heads/main'
247
+ steps:
248
+ - uses: actions/checkout@v4
249
+ - uses: actions/setup-python@v5
250
+ with:
251
+ python-version: "3.12"
252
+ - run: pip install cassis-cli
253
+ - run: cassis context upload
254
+ env:
255
+ CASSIS_API_KEY: ${{ secrets.CASSIS_API_KEY }}
256
+ CASSIS_PROJECT_ID: ${{ vars.CASSIS_PROJECT_ID }}
257
+ ```
258
+
259
+ ### GitLab CI example
260
+
261
+ ```yaml
262
+ context-check:
263
+ image: python:3.12-slim
264
+ script:
265
+ - pip install cassis-cli
266
+ - cassis context check
267
+ variables:
268
+ CASSIS_API_KEY: $CASSIS_API_KEY
269
+
270
+ context-eval:
271
+ image: python:3.12-slim
272
+ rules:
273
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
274
+ script:
275
+ - pip install cassis-cli
276
+ - cassis eval run
277
+ variables:
278
+ CASSIS_API_KEY: $CASSIS_API_KEY
279
+ CASSIS_PROJECT_ID: $CASSIS_PROJECT_ID
280
+
281
+ context-publish:
282
+ image: python:3.12 # not -slim: the upload needs git to record the commit
283
+ rules:
284
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
285
+ script:
286
+ - pip install cassis-cli
287
+ - cassis context upload
288
+ variables:
289
+ CASSIS_API_KEY: $CASSIS_API_KEY
290
+ CASSIS_PROJECT_ID: $CASSIS_PROJECT_ID
291
+ ```
292
+
293
+ ## About this repository
294
+
295
+ [github.com/GetCassis/cassis-cli](https://github.com/GetCassis/cassis-cli) is a
296
+ read-only mirror, synced automatically from the Cassis monorepo where the CLI is
297
+ developed. Issues are welcome and watched; pull requests can't be merged here, so
298
+ open an issue (or mail tech.admin@getcassis.com) and we'll port the patch upstream
299
+ with credit.
300
+
301
+ Only the CLI is open source. The Cassis backend it talks to is proprietary and
302
+ requires an account.
303
+
304
+ ## License
305
+
306
+ Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).