Skip to content

mammoth-cli 2.3.0

Choose a tag to compare

@ankitkpandey1 ankitkpandey1 released this 26 Oct 09:30
· 183 commits to main since this release
0e7c395

Added (mammoth-cli 2.3.0)

  • CLI: typed output schemas (#315). Every command (346 reads and 505 writes) declares the shape of its data as output_schema in the manifest, and schema get --full returns it as a self-contained JSON Schema 2020-12. A command declares openapi (its data is the API's own 200 body), a named schema from spec/manifests/output-schemas.yaml ({ref: NAME}, shared between commands), or an inline schema. Writes share WriteResult: the verify block every API-backed write returns, the state read-back where the command has one, and the API's own fields. Transforms, exports, bulk deletes, agent turns and runs, and skill writes build on it (TransformResult, ExportResult, DatabaseExportResult, BulkDeleteResult, AgentTurnResult, AgentRunResult, SkillWriteResult and others); a delete with no response body is null (WriteResultOrNull). output_schema is always the real run's data. --dry-run of an API-backed write returns a report instead, and schema get --full returns its schema as dry_run_output_schema (null for a read). A contract test checks that each of the 851 commands declares one and that it resolves, and a fixture test runs each command's example (and each write's --dry-run) through the real handler and validates the result. It found two wrong read schemas, fixed here: view data compare returns data, not rows, and a folder's created_by can be text.
  • CLI: ETag'd bodies are kept on disk between commands, so repeated project list, dataset list or view get calls download only what changed. There is one owner-only directory per credential, profile, server and workspace, under the user cache dir. A write through that credential empties it, and auth login/auth logout remove all of them. MAMMOTH_CLI_ETAG_CACHE=0 keeps the bodies for one command only. Embedded calls (embed.invoke(runtime=...)) never touch the disk. The host's EmbedRuntime keeps one in-memory cache, capped at 4096 bodies and 128 MB, and each caller uses only their own scope of it, named by a digest of their credential. Two users therefore never share a body, even when the server gives them the same tag for differently filtered lists.
  • CLI: every API request names the command that sent it in its User-Agent, which the server's request log already stores: mammoth-cli/<version> (cmd=view.data.get; inv=3f9c2a1b7d4e; surface=run; agent=claude-code) mammoth-io/<version>. inv is one id per command invocation, shared by all its requests and equal to the local run log's run_id. surface is terminal, run (a mammoth run line) or embed. agent is set from MAMMOTH_AGENT, or from the variable a coding agent sets (CLAUDECODE, CURSOR_AGENT, GEMINI_CLI, CODEX_SANDBOX), and never for an embedded call. Only identifiers are sent: no argument values, paths or data.
  • CLI: steps in mammoth run. A line with "name" keeps its envelope, and a later line reads it in args, input or flags as "{{steps.<name>.data...}}". "when" skips a line on a JSON condition (eq, in, not, all, any, present), and "for_each" runs a line once per item of a list, with {{item}} and {{index}}. A reference that does not resolve fails the line with unresolved_reference (exit 2); under --dry-run the line is planned instead. It is plain JSON: no code runs. A $-key other than a value operator ($schema, $ref) in a line is data. A when that is a bare string (no braces) is refused, as it would always be true. Under --dry-run, only a reference into the result of an earlier line plans the line; a malformed expression or a step no line makes fails it. A for_each over an empty list prints a skipped envelope with data: [], and with --continue it runs the items after a failed one.
  • CLI: mammoth history list, history show ID and history undo ID. Every write is journaled on this machine (owner-only, under the log directory, rotated at 10 MB) with the mammoth run lines that reverse it: a trash is undone by a restore, an object a write made goes to the trash, and a pipeline edit returns the view to the pipeline version it had before. history undo takes one write's inv or a whole mammoth run's group, runs the lines newest first through the normal command path, stops at the first failure (outcome: partial, exit 3) and needs a prompt or --yes; --dry-run previews them. A permanent delete, or a write with an effect outside Mammoth, is listed with the reason it cannot be undone. MAMMOTH_CLI_JOURNAL=0 turns the journal off; embedded calls are never journaled. An append (file upload with append_to_ds_id) or an export into an existing dataset is never created, so its undo does not trash the dataset (rows cannot be taken out again, and the undo says so). Undo records only the --profile that was typed, not the default, so an env-token write is not undone as another account. An undo of a pipeline edit refuses when the view's pipeline changed since the edit (--input '{"force": true}' overrides), a stopped undo goes on from the line that failed, and two undos of one write cannot run together. The journal takes a file lock for each append, writes each record in one write, and keeps a run's plan in its own file (pruned after 60 days) so a rotation never drops it. Plans and named results are stored with credential-looking values replaced. history list --input '{"limit": 0}' is refused. An undo runs as the account the write used: a write made under a stored profile is undone under that profile even after profile use changed, and one made with MAMMOTH_API_TOKEN is refused with undo_needs_token when no token is set. The fingerprint that detects a changed pipeline redacts like the CLI output. When another process holds a write's undo, the writes already undone stay in the result (exit 3, not_undone lists the rest), and an undo's lock file is removed when it ends. A journal that cannot be written warns once on stderr.
  • CLI: every write command declares how history undo reverses it in its manifest, as undo: {strategy: inverse, command} (run the named command with the write's own arguments, e.g. agent run pause and resume, dataset file-settings update and undo), pipeline_version (apply the view's version from before the edit), trash_created (trash what the write made), manual (a command reverses it but needs an id the write made, so it is named, not run) or irreversible with a specific reason (a permanent delete, an export to a named system, an email, a refund). journal.compensation reads the declaration instead of working it out from mutation_class. scripts/build_manifests.py derives the default; spec/manifests/commands/<group>.yaml overrides it, and a contract test fails a write with no undo. #311 An inverse undo line carries the --confirm target and the --session the original write named, so a high-impact reversal runs; an inverse that needs input the write lacks (support partner client cancel-detach) is manual. A manual entry says why it is not run, including for pairs that take the same arguments (addon, project user, dashboard filter). file upload-folder and dataset create-from-pdf are undone by trashing what they made; view variants create lists every view it made; a repeated dashboard live-chart create is not journaled as created.
  • CLI: every success envelope carries meta.run_id, the invocation id: the same id as the run log's run_id (log tail), the journal's inv (history show) and the inv in the request's User-Agent. embed.invoke(..., schema_version=1) pins the envelope version a host was written against; mammoth-cli/docs/envelope-compatibility.md says what may change in a minor release and what needs a new schema_version (still 1: nothing in 2.3 breaks it).
  • CLI: the servers that honour Idempotency-Key are remembered between commands (one owner-only file of API base URLs in the user cache dir), so a command's first write can be replayed safely too. Embedded calls never write it.
  • CLI: mammoth recipe list and mammoth recipe run ID --input '{"inputs": {...}}' run a common task as one command. All 32 recipes in discovery/recipes.json are executable: append-rows, upload-dashboard, export-csv (takes view_id and dataset_id), share-dashboard, refresh-daily, blend-by-key, stuck-upload, export-powerbi, clean-data, cleanup-mine, upload-clean, join-files, lookup-column, add-calculated-column, extract-date-part, split-column, find-replace, fill-blanks-with-value, rename-columns, remove-duplicates, pivot-summary, top-n, filter-export-csv, export-xlsx, export-to-s3, email-view, dashboard-from-view, summarize-by, compare-views, refresh-now, undo-last-change and revert-view-version. The tasks that would replace a view's rows (pivot-summary, top-n, filter-export-csv) work on a copy. A recipe input can be a string, an integer, an array, an object or any. A recipe's steps run through the mammoth run path in one process, printing one envelope per step and a summary (recipe, inputs, partial_lines). A recipe that writes asks once (prompt or --yes), and --dry-run previews every step. mammoth how returns the recipe's run_command; schema find lists matching recipes (recipes), and schema list and capability find list tasks, before single commands. scripts/eval_task_calls.py counts the calls: 32 with recipes against 113 step by step for the 32 tasks (mammoth-cli/docs/task-call-eval.md). New error codes: recipe_not_found, recipe_input_invalid. cleanup-mine moves things to the trash (nothing is deleted for good), after a name check and a dependents dry run. A recipe never confirms a destructive line for the caller: it needs an explicit --yes, and the one prompt names those lines. An any input takes a list or a boolean. upload-clean, upload-dashboard and join-files skip the lines that need the upload's view when it has none, and end with a dataset get that shows the dataset's state. In --dry-run a when that reads a preview is still planned, and a step skipped by when is not taken for a preview. A binding with $first (or another operator) beside other keys is refused. Embedded in a host (mammoth_cli.embed.invoke), recipe run runs each step as a step of the host's call, with the host's login, project and session, and returns one envelope: the summary, with each step's envelope in lines_run. A recipe with a line the host cannot run (file upload, history undo) is refused before any line runs (not_available_embedded).
  • CLI: mammoth history resume GROUP goes on with a mammoth run or recipe run that stopped (a failed line, Ctrl-C, a crash). It reruns from the first line that did not finish, and later lines still read the earlier lines' results ({{steps...}}). The run's lines and those results are kept in the journal; a result larger than 256 KB is not kept. If the run stopped while a write was being sent, resume refuses (resume_outcome_unknown): check the write, then pass --input '{"from_line": N}'. Needs a prompt or --yes; --dry-run previews. A failed named line is run again without an already named error. A write that was interrupted, timed out or lost its connection after it was sent makes resume refuse (resume_outcome_unknown) until from_line names the line to start from; a partial line of recipe run is not run again. A resume is recorded under the run it resumed, so a second resume and history undo GROUP see what the first did, and a for_each line goes on from its first unfinished item. A line that needs a secret the history does not keep is refused (resume_secret_needed). A for_each item result over 256 KB is journaled as omitted: a resume runs a read item again, refuses to repeat a write item (resume_result_too_large), and names the from_line that makes a result a later line reads. A resume also refuses a line that reads redacted data in an unfinished for_each.

Changed (mammoth-cli 2.3.0)

  • CLI: needs mammoth-io 0.8.52 or later: the response cache (mammoth.etag) and the safe write retries are not in 0.8.51.
  • CLI: embedded in a host (mammoth_cli.embed.invoke), the discovery commands leave out what the host refuses: schema list, schema find, capability find and capability list drop the host-machine commands (history, auth, config, log, skill...) and those that read a local file (file upload); recipe list, the tasks and recipes of the index commands, and how drop the recipes that use them (append-rows, upload-clean, upload-dashboard, join-files, undo-last-change). When the best recipe for a goal is one of them, how says so (matched: false, a hint) instead of answering with a weaker recipe, and a several-part goal keeps the parts the host can run. A terminal run is unchanged.
  • CLI: this is mammoth-cli 2.3.0, the ver3 architecture (#307): one command path with interceptors, a write journal with undo and resume, steps and recipes in mammoth run. Nothing breaks: no command, flag or input was removed or changed, and every envelope field was reviewed and kept, so schema_version stays 1 (the field review is in mammoth-cli/docs/envelope-compatibility.md). A host or agent written against 2.2 needs no change.

Fixed (mammoth-cli 2.3.0)

  • CLI: two embedded calls that start at the same moment in a fresh process no longer fail with internal_error ("partially initialized module 'mammoth.view'"). One call could read the SDK's view module while the other was still importing it.
  • CLI: dataset refresh on a dataset made from an uploaded file returns dataset_not_refreshable with the ways forward (append the new file, or connect a cloud source), as automation create already did. Before, it was a generic api_error ("Invalid source type [4DSET048]").
  • CLI: numbers_stored_as_text (and dates_stored_as_text) now catch a column whose missing values are written as a placeholder. Values like N/A, NA, #N/A, -, --, null, none, nil, ? and nan no longer count against the column's share of numbers. Before, 100, 250, 75, N/A (3 of 4, under the 80% bar) uploaded as TEXT with no warning. The finding still lists the placeholders, which the conversion makes empty.
  • CLI: the dashboard v3 generate agent example now includes the required intent. Without it the backend refused the example with 4GENR007 ("intent must not be empty").
  • CLI: view version apply waits for the pipeline to be rebuilt before it returns, so the view shows the applied steps. Before, it returned the job handle and a read straight after it still showed the old steps.
  • CLI: view conditional-format create and replace list the allowed color swatches (red, amber, green, blue, purple, teal, coral, grey, text-*) in schema get and schema find, and refuse another word before the request with the list. Before, color was an open string and the list was only in the backend's 400.
  • CLI: schema get takes ids separated by spaces (schema get A B) as well as by , or ;. Before, it refused the second id with unexpected_argument.
  • CLI: a file upload lists the datasets it made in meta.opens (created: true), as other creating writes already did.
  • CLI: an append (file upload --input '{"append_to_ds_id": N}') waits for the target dataset to take the rows before it reports. Before, it could read the target while it was still processing: rows_added: 0 and a dataset health of unknown, though the rows did land. If the target is still processing when the wait ends, the result says append_pending: true with a warning.