Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Flag Reference

Audience: CLI users who want one page that lists every flag and every environment variable in scope across the binary.

The per-command pages list each command’s full flag set; this page is the cross-cutting reference for flags that appear on multiple commands and for the environment variables Pulse reads.

LLM agents using MCP: there is no LLM-facing skill for the CLI surface. Agents go via MCP tools (pulse_process, pulse_inspect, …) — see pulse mcp and Adding an MCP tool.

Global flags

Available on the bare pulse invocation:

FlagEffect
--jsonPrint the root manifest as JSON (envelope-wrapped)
--slimWith --json, drop prose descriptions for size-sensitive clients
--intent IDWith --json, scope the manifest to one intent-taxonomy ID (e.g. compare_groups): only the operators serving it and its skills, the fixed sections dropped and listed in elided. An unknown ID is a PULSE_RECOMMEND_INTENT_UNKNOWN error envelope

All default to off. pulse --json is the discovery entry point — it emits the manifest documented at pulse manifest.

Persistent on every leaf (accepted before or after the leaf name):

FlagEffect
--log-level off|debug|info|warn|errorStructured log records on stderr at or above the level (default off: nothing). debug adds the per-operation plan record (execution arm, workers, shards, projection)
--log-format text|jsonLog record encoding (default text); ignored while --log-level is off

Logs never reach stdout, so --json output and the pulse mcp JSON-RPC transport stay byte-identical with or without them. Records carry identifiers, counts, timings, enums and error codes only — never row data, filter values or error messages. An unknown value is CLI_INPUT. There are no environment-variable forms. Leaves that never build a Pulse instance (import, convert, skills, errors, version, features) accept the flags but emit nothing. pulse mcp --metrics-addr is documented under pulse mcp; see also Observability.

Environment variables

VariableUsed byRequiredPurpose
PULSE_DATA_DIRAll commands when no path override is given; required by pulse mcpconditionallyBase directory for cohort files. Relative cohort paths resolve against it
PULSE_IMPORTS_DIRpulse import auto / list / dropnoManaged-imports subdir under the data root. Defaults to imports
PULSE_IMPORT_TTLpulse import autonoDefault TTL for managed imports. Go duration (24h, 30m), day form (7d, 30d), or pin. Defaults to 7d
PULSE_LABEL_TABLES_DIRpulse api sample --labels, pulse api facet --labelsnoDirectory of JSON files auto-loaded as label tables at pulse.New time; each *.json becomes one table keyed by its filename
PULSE_MCP_NO_COHORT_SCANpulse mcpnoSkip the startup walk that enumerates .pulse files as pulse:// resources. Same as --no-cohort-scan; cohorts stay readable by URI, only resources/list loses them
PULSE_FEATURE_PROFILEpulse mcp onlynoOS path (cwd-relative or absolute) to a feature profile JSON file. Same as --feature-profile, which wins when both are set. Every other leaf ignores it

PULSE_DATA_DIR is the only required PULSE_* environment variable. The Makefile auto-loads a repo-root .env file so you can keep these (and any future env vars) there for development.

When embedding the library, you can bypass the env vars entirely by passing pulse.Options{DataDir: "/path"}, pulse.Options{ImportsDir, ImportTTL, LabelTablesDir, FS: myFs} etc. — see pulse.New & Options.

--json envelope

Almost every leaf command accepts --json, which switches output from human prose to a structured envelope. The envelope shape is fixed and documented in CLAUDE.md → Output Format Contract:

{
  "format_version": "1.1",
  "data":     { /* operation-specific result */ },
  "request":  { /* normalized request, omitted unless --echo-request was passed */ },
  "errors":   [ /* {"code": "...", "message": "...", "details": {...}} */ ],
  "warnings": [ /* same shape */ ]
}

format_version is currently "1.1". errors and warnings are always arrays (never null) so JSON consumers can index without nullable-check overhead. request is opt-in (see --echo-request below); data.components is the additive Response.Components slot documented per leaf — see api process → --json.

Shared per-command flags

Several flags appear on multiple commands with identical semantics.

--no-defaults

Available on: api process, api compose.

Disable the runtime smart-defaults pass that infers operator Type from the named field’s schema type when the caller omits it. Forces the request to be source-of-truth. See pulse.New & Options for the underlying library option.

--stream

Available on: api process, api compose.

Stream result rows as NDJSON (one row per line) instead of buffering the full result. For compose, each line carries an {"index": N, "row": {...}} shape so consumers know which sub-request produced each row. See Streaming & ProcessStream.

--strict

Available on: api process, api predict.

Treat request-validation warnings (e.g. numeric aggregation on a categorical field, low-quality field description) as errors. On api predict this fails validation; on api process it refuses to execute. Useful in CI gates that want the strictest possible validation.

import <format> also takes --strict, with a narrower meaning: it escalates the parent-group viability warnings only — see --strict (import).

--echo-request

Available on: api process, api process-chain, api compose, api predict, api sample, api facet.

Include the normalized request — smart defaults resolved, label bindings expanded — on envelope.request. Absent (and omitted from JSON) by default so the envelope shape is unchanged for callers that do not need it. Streaming output (--stream) skips the echo because NDJSON has no envelope.

--full-dict

Available on: cohort inspect.

Print full categorical dictionaries instead of truncating after 100 entries. Pair with --json for programmatic consumption.

--strict / --seed / --rows

synth from-schema and synth from-profile use --seed (for deterministic RNG) and --rows (override the spec’s row count). See the per-command pages.

--emit-spec / --rules

Available on: synth from-profile.

--emit-spec <path> writes the profile-derived spec (after any --rules merge) as indented JSON. It is the spec that actually generated, so feeding it to synth from-schema at the same --seed reproduces the same rows — and it is the only way to see which captured models survived translation, which distribution each field reconstructed to, and which conditional pairs were retired.

--rules <path> loads structural rules from a standalone JSON file — a bare array of rule objects, the same shape as a spec’s rules key — and replaces the derived spec’s rules with them. Refusals carry E1’s own PULSE_SYNTH_RULE_* codes with the file path in details.path. See synth from-profile.

--suggest-rules

Available on: profile create.

--suggest-rules <path> detects structural rules on the same scan the profile already makes — no additional cohort read — and writes them to <path> as the bare rules array synth from-profile --rules consumes unmodified. Three detectors, one file: GATING relationships (a low-cardinality field whose levels split a target’s null rate into ~1 and ~0, proposed as set_null), CO-MISSING blocks (fields null on exactly the same rows, proposed as null_together) and EXACT DEPENDENCIES (a field that IS a function of one other, proposed as set_expr). An identical null RATE is never enough for a block — two unrelated fields can share one — so admission is identical null PATTERN; near misses, almost-determined pairs, always-null columns and constant columns are reported in warnings rather than proposed.

Dependency detection is bounded, deliberately, and the bounds are published — in every candidate’s _evidence.note, on stderr and in profile create. TARGETS are packed_bool and u4 only; SOURCES are categorical_* / packed_bool / u4 with at most 16 observed levels; the dependency is on exactly ONE source. A field missing from the file was not cleared, it was not examined.

PROPOSED, never applied. Detection finds the STATISTICAL gate; a human knows the SEMANTIC one, and the two are routinely different fields that move together. Each candidate carries its own measurement on _evidence — the one inert slot of a rule, which generation never reads — so a candidate the analyst deletes takes its evidence with it. Gating candidates are emitted first and dependency candidates LAST, because declaration order is applied order: a block that FOLLOWS a hand-narrowed set_null repairs it, and a set_expr derivation must re-resolve its own block after every null-state rule has run. The profile document itself gains no section: absent the flag it is byte-identical, and with it only warnings moves. See profile create.

--run-continuation

Available on: profile create.

--run-continuation measures, on the same scan the profile already makes, each field’s run-continuation: the fraction of adjacent row pairs whose on-wire bytes (and, for a nullable field, null bit) repeat. That is exactly the hit rate of the run-skip decode, which rewrites only the fields that changed since the previous row — so a cohort that was sorted by its parent key upstream scans faster, and one re-imported without that ORDER BY silently loses the win. The additive run_continuation section carries the per-field rates, an overall figure, the high-continuation fields and a one-line advice; pairs never span a shard boundary. Absent the flag the document is byte-identical. See profile create.

--schema / --ignore-extra

Available on: import csv, import tsv, import ndjson, import jsonarray, import parquet, import arrow, import excel, import spss, import predict, convert.

--schema FILE supplies an explicit schema (a JSON array of {name, type, description, nullable}, plus precision / scale on a decimal128 field; import schema-template INPUT emits an editable one, decimal precision and scale included). The file carries no byte offsets — the layout is derived from field order and types exactly as inference derives it, so a file mixing u4 / packed_bool with byte-aligned fields writes the same schema block an inferred import of the same fields does. Its fields are matched to the source’s columns by exact name — case and whitespace included — never by position, so the schema may list them in any order and that order is the cohort’s field order. For NDJSON and JSON-array sources the columns are the keys of the first object. Arrow, Parquet and SPSS sources, whose own schema an explicit one overrides, are matched the same way. Every mismatch is refused before a row is read, under its own code:

CodeWhenDetails
PULSE_IMPORT_SCHEMA_FIELD_MISSINGa schema field has no column of that namefield, columns
PULSE_IMPORT_SCHEMA_COLUMN_UNMAPPEDa source column no schema field namescolumn, fields
PULSE_IMPORT_SCHEMA_DUPLICATEa field name declared twice, or a repeated header namefield or column, side
PULSE_IMPORT_SCHEMA_KEY_LATEan NDJSON / JSON-array key first seen after the first objectcolumn, row, columns
PULSE_IMPORT_SCHEMA_TYPE_UNKNOWNa field’s type is not a registered field type (never read as f64)field, type, valid
PULSE_IMPORT_SCHEMA_INVALIDthe file is not a JSON array of field objects, or lists no fieldreason, error

--ignore-extra drops source columns, and late keys, that the schema does not name instead of refusing (library: ImportJob.IgnoreExtra / ConvertJob.IgnoreExtra). A schema field with no column is still refused. Without --schema the flag is inert and inference is unchanged.

The MCP pulse_import tool takes the same document inline as its schema argument, with ignore_extra and float beside it, and refuses with the same codes (plus PULSE_IMPORT_SCHEMA_INVALID for an unknown entry key or a schema on a .pulse source).

--float

Available on: import csv, import tsv, import ndjson, import jsonarray, import parquet, import arrow, import excel, import spss, import predict, import schema-template, convert.

The width schema inference gives an all-float column: f64 (the default) keeps every decimal the source wrote exact, so 0.1 and 123456.789012 round-trip and a money column’s sum matches the f64 reference; f32 halves the column’s bytes but rounds every value to about 7 significant digits. Under f32 a column whose sample holds a value past f32’s range still infers f64, and a later such value promotes the field (PULSE_IMPORT_WIDTH_PROMOTED, below). Any other value refuses the run with PULSE_IMPORT_FLOAT_WIDTH_INVALID (value, valid), with or without --schema. Inert with --schema or a source that carries its own schema (Arrow, Parquet, SPSS); a managed import’s column_type_overrides still forces a column’s type either way. Library: ImportJob.FloatWidth / ConvertJob.FloatWidth ("f64", "f32"; "" is f64). import auto and pulse_import take no float knob and infer f64.

--elide-constants

Available on: import csv, import tsv, import ndjson, import jsonarray, import parquet, import arrow, import excel, import spss, import predict (reports what would be elided and the bytes saved; writes nothing).

--elide-constants stores every field that holds exactly one value — or is null — on every imported row once, in the schema block, and drops it from each record. Constancy is measured over the full row pass, never the inference sample. The output is a format 0x02 cohort that binaries predating 0x02 cannot open, which is why it is opt-in. Nothing is elided (and the cohort stays byte-identical 0x01) for fewer than two rows or when the saving would not repay the schema-block growth; when every field is constant the lowest-index field stays in the row. The elided fields are printed, and reported as ElidedConstants under --json. See parent groups.

--group

Available on: import csv, import tsv, import ndjson, import jsonarray, import parquet, import arrow, import excel, import spss, import predict, import auto. Repeatable — one parent group per flag. On import auto the group is judged at the default ratio floor and its findings are always warnings (--json puts them in warnings, the per-group figures under data.groups); the floor and --strict stay on the per-format leaves, since neither changes the bytes written. On import predict the declaration is evaluated, not applied: see --suggest-groups.

--group KEY[,KEY...]:MEMBER[,MEMBER...] declares a parent group: the key fields identify a parent (a customer ID, say) and the members are the fields that key determines (its name, region, tier). Each distinct tuple is stored once in the schema block and every record carries a 4-byte index instead of the members, so a denormalised join shrinks by its repeated parent block. --group F1,F2,... (no colon) is a plain tuple group: each distinct combination is one entry, with no key check.

pulse import csv -i lines.csv -o lines.pulse \
  --group cust_id:cust_name,cust_region \
  --group prod_id:prod_cat

A keyed declaration is checked, not trusted: if two rows share a key tuple but disagree on a member (value or null state), the import fails with PULSE_GROUP_MEMBER_NOT_CONSTANT naming the member, the record index and the source row — a wrong declaration is refused, never silently turned into a larger dictionary. A field in two groups is PULSE_GROUP_FIELD_CONFLICT (naming both groups), an unknown field PULSE_GROUP_FIELD_UNKNOWN, a malformed value PULSE_GROUP_DECLARATION_INVALID; all three fail before the row pass. More than 2^32 distinct tuples is PULSE_GROUP_ENTRIES_EXHAUSTED.

Every declared group passes a per-group viability gate. A group whose members are no wider than the 4-byte index can never save space: it is dropped (its members stay in the row) with a PULSE_GROUP_TOO_NARROW warning naming both widths. A group whose dedup ratio (rows per distinct tuple) is below --dedup-ratio-floor, or that makes the file no smaller, is still written, with a PULSE_DEDUP_LOW_RATIO warning carrying the ratio, the resident dictionary bytes and the byte delta. Other groups on the same import are unaffected. The text output prints each group’s ratio, resident dictionary bytes and byte delta; --json reports them per group under Groups and the findings in warnings.

--suggest-groups

Available on: import predict.

Finds candidate parent groups before you import, and measures each one. A candidate is a single key field plus every field it determines (the same value on every row sharing a key value). Each is reported with its fields, verdict, distinct-tuple count, ratio, resident dictionary bytes, byte delta and the exact file size with that group declared, next to the flat cohort’s size. You also get a ready-to-paste --group value and the structured {key, members} form. A candidate below the width or ratio floor is listed with its verdict and reason, not hidden. suggested lists only the admitted candidates that do not share a field with a better one. A source with no viable group suggests none.

pulse import predict -i lines.csv --suggest-groups
pulse import predict -i lines.csv --group cust_id:cust_name,cust_tier

It suggests only. Nothing is declared until you pass --group to the import. Candidates are nominated over the first rows, up to 10,000 (fewer on a wide schema; window_rows / window_bound). The 500-row inference sample is too small at typical fanouts: it holds a few dozen parents, and a column that happens to be constant across so few looks like a parent attribute. Candidates are then confirmed and measured over every row. A member that varies within its key later in the file is dropped and listed under rejected_members. The figures therefore come from the same conversion and the same internal/encoding.DedupGate arithmetic the import uses, so they are not a sample estimate. They equal what import --group reports and what cohort inspect shows afterwards. A dependency can still break in a later re-export of the same source; the import catches that with PULSE_GROUP_MEMBER_NOT_CONSTANT.

Detection has these limits:

  • Keys are single fields; composite keys are not detected. Evaluate one with --group A,B:MEMBERS.
  • Up to 64 keys are evaluated, highest cardinality first (keys_over_bound), and 16 candidates are confirmed (candidates_over_bound).
  • A field constant across the window, or declared in a --group, is never nominated.
  • Candidate dictionaries share a 512 MiB budget. A candidate that outgrows its share is reported unmeasured.

Cost: import predict normally reads rows without converting them. With --suggest-groups, --group or --elide-constants it converts every row as the import would. That measured roughly 1.2x-1.4x the import’s own time on synthetic 12- and 102-column sources, against about 0.15x for plain predict. It does not spool the rows, so memory is bounded by the window and the candidate dictionaries, not the file. When a field outgrows its sample-inferred width mid-file (see PULSE_IMPORT_WIDTH_PROMOTED below) the measured pass stops measuring, finishes the file to find every promotion, then rewinds and measures again at the final widths, so such a source is read twice. Only the measured pass sees the full-file widths: plain import predict reports the sample-inferred schema. --json adds Projection, Groups, GroupWarnings, ElidedConstants, GroupCandidates and WidthWarnings to data; format_version stays "1.1".

Width promotion (inferred imports and converts)

Not a flag: every inferred import <format>, import auto, pulse_import and pulse convert does it. Inference sizes categorical_* rungs and integer widths from the first --sample-rows rows. A later value that outgrows them promotes the field to the narrowest type that holds it instead of dropping the row: categorical_u8 → u16 → u32; u4 → u8 → u16 → u32 → u64 for a larger non-negative integer; u4..u32 → f64 for a negative, fractional or signed number; f32 → f64 for a value outside f32’s range — past MaxFloat32, or a non-zero magnitude f32 would flush to zero. Floats only infer f32 under --float f32 (see below), and by that same range test, so a value f32 merely rounds (0.1) never promotes. Every step is lossless for every value already read. Each promoted field draws one PULSE_IMPORT_WIDTH_PROMOTED warning (field, from, to, source_row) in the envelope warnings and data.WidthWarnings (width_warnings on import auto / pulse_import). A --schema or an authoritative source schema (SPSS, Arrow, Parquet) never promotes: its overflow stays a PULSE_IMPORT_ROW_ERROR. A column_type_overrides column never promotes either, and any value it cannot hold refuses the whole import with PULSE_IMPORT_OVERRIDE_INVALID. So does a non-number, a non-integer in a u64 column, a non-boolean in a packed_bool column (no type holds both losslessly), and a dictionary past categorical_u32.

pulse convert copies cell text, so a promotion never changes what the target receives. It reports the promoted types in data.Schema and the warnings in data.WidthWarnings and the envelope warnings, and a --keep-pulse intermediate is byte-identical to a plain import of the source. On a declared schema (--schema, or an SPSS / Arrow / Parquet source’s own) a full categorical rung still stops the convert with the fatal PULSE_IMPORT_CATEGORICAL_OVERFLOW, and no intermediate is written.

--dedup-ratio-floor

Available on: every import <format> leaf, and import predict. Default 2.

The rows-per-distinct-tuple floor below which a --group draws PULSE_DEDUP_LOW_RATIO. A floor of 1 disables it, leaving only the check that the group actually makes the file smaller. The group is written either way unless --strict is set.

--strict (import)

Available on: every import <format> leaf, and import predict (which then fails where the import would).

Turns the parent-group viability warnings (PULSE_GROUP_TOO_NARROW, PULSE_DEDUP_LOW_RATIO) into errors: the import fails with that code (errors[0].code under --json) and writes nothing. Other import warnings are unaffected. Groups are named in errors and output by position and key (group 1 [key: cust_id]) — the format stores no group name. Each group is printed with its distinct-tuple count, and reported under Groups with --json (entry count, entry width, member bytes per row, index width). Declared members are never constant-elided, so --group composes with --elide-constants. Field names containing , or : cannot be declared here; use io.ImportJob.Groups. The output is a format 0x02 cohort that binaries predating 0x02 cannot open; with no --group the import is unchanged. pulse_import (MCP) takes the same declarations as structured groups: [{key, members}] entries, plus suggest_groups to return measured candidates in the same shape. See parent groups.

--source-tz / --dst-policy

Available on: every import <format> leaf, import auto, import predict, convert and convert predict.

--source-tz Zone reads every naive datetime value (no Z, no ±HH:MM) as a wall clock in Zone and stores the UTC instant it names; storage is UTC epoch seconds either way, so the .pulse format is unchanged. Zone is an IANA name (America/New_York), UTC, or — for imports only — a fixed offset +HH:MM / -HH:MM. Repeatable --source-tz col=Zone sets one column and wins over the bare form; it is CLI_INPUT when col is not a column, or is not datetime (a date is a calendar day and has no zone). The bare form skips date columns. A value carrying its own Z or offset names that instant and ignores the zone. Without --source-tz the cohort is byte-identical to before.

--dst-policy decides a naive value a DST transition makes ambiguous (a fall-back repeats the hour) or nonexistent (a spring-forward skips it): error (default) fails the import at the first such row with PULSE_IMPORT_DST_AMBIGUOUS / PULSE_IMPORT_DST_NONEXISTENT, naming row, column, value and zone, and writes nothing; earlier uses the pre-transition offset (an overlap’s first occurrence; Berlin 2026-03-29 02:30 → 01:30Z), later the post-transition offset (second occurrence; → 00:30Z). A resolved value is never silent: one PULSE_IMPORT_DST_RESOLVED warning carries ambiguous_n / nonexistent_n (warnings and data.ZoneWarnings under --json). The policy is inert without a zone and under a fixed offset.

On import auto the zone, per-column map and policy are persisted onto the managed sidecar (source_tz, column_source_tz, dst_policy); a handle is never reused across zones — re-importing in another zone needs --overwrite. import predict and convert predict raise the refusal the real run would and report the resolved count. convert is a text pass, so a zoned naive value is written to the target as the canonical UTC literal of the instant it names (2026-07-01T16:00:00Z); a value with its own Z / offset, an unparseable cell and every other column pass through verbatim, and a --keep-pulse cohort is imported in the same zone. pulse_import (MCP) takes source_tz, column_source_tz ({column: zone}) and dst_policy.

pulse import csv -i orders.csv -o orders.pulse \
  --source-tz America/New_York --source-tz shipped_at=Europe/London \
  --dst-policy later

--tz (export)

Available on: every export <format> leaf and export predict.

--tz Zone renders every datetime cell on Zone’s wall clock with its numeric offset — an RFC 3339 literal such as 2026-03-29T08:00:00+05:30 under Asia/Kolkata, +01:00 / +02:00 either side of a Europe/Berlin transition (a fall-back hour’s two readings differ only by offset) — instead of the canonical UTC 2026-03-29T02:30:00Z. The literal names the same instant, so re-importing the export (offset literals keep their instant) round-trips every value exactly. date columns are calendar days and never move. Zone is an IANA name or UTC — no fixed offsets, abbreviations or Local (PULSE_TIMEZONE_UNKNOWN, from export predict too). Absent, UTC or a UTC-equivalent zone (Etc/UTC) is byte-identical to today’s output. An instant whose historical offset is not a whole minute (a pre-1900 local mean time) keeps the …Z form: RFC 3339 cannot spell it.

Text formats carry the literal as text, and so do arrow / parquet, whose datetime columns are UTF8 strings. export spss refuses a non-UTC zone (PULSE_SPSS_EXPORT_UNSUPPORTED, details.option = --tz), and export predict --format spss --tz ... predicts that: a .sav DATETIME is a naive wall-clock count with no offset slot. The library knob is io.ExportJob.TimeZone; the manifest’s export.formats[].time_zone says per format (local_offset / refused).

pulse export csv -i orders.pulse -o orders.csv --tz Asia/Kolkata

Command index

Every runnable leaf the binary exposes, with the page that documents it in depth. A leaf whose “Documented in” column names no dedicated page is covered by the surrounding topic page or by --help.

The list itself is the contract, and it is enforced in both directions by TestSkillsCoverAllCliLeaves: a leaf added to buildApp() without a row here fails the gate, and a row here that names a command the binary does not actually mount fails it too. The second direction is the one that matters when a registration is deleted — the documented command would otherwise stay listed while answering “command not found”. Group nodes that carry no action of their own (pulse api, pulse shard, pulse index) are not leaves and are deliberately absent.

LeafPurposeDocumented in
pulse api composeExecute multiple processing requests in batchapi compose
pulse api facetDistinct values for a field, or a rich multi-field summaryapi facet
pulse api lookupKey-exact row read against a prebuilt sidecar indexapi lookup
pulse api materializeWrite a request’s derived columns (features, attributes, windows) as fields of a new cohort: -r REQ.json -o OUT.pulse [--overwrite] [--strict] [--json]; refusals keep their own codeMaterialize
pulse api predictValidate a request against a cohort without executingapi predict
pulse api predict-chainValidate a process-chain request without executingapi predict
pulse api predict-composeValidate a Compose batch without executingapi predict
pulse api predict-facetValidate a facet request without executingapi predict
pulse api processExecute a processing request against a cohortapi process
pulse api process-chainSource-rooted linear chain of mergeable stagesapi process
pulse api sampleReturn sample rows from a cohortapi sample
pulse cohort filterFilter a .pulse file to a new .pulse file--help
pulse cohort inspectInspect a .pulse header and schemacohort inspect
pulse convertConvert between tabular formats, auto-detected from extensionsimport spss, export spss
pulse convert predictValidate a conversion without writing outputexport spss
pulse dedupDeduplicate an existing single-file cohort’s repeated parent blocks into parent groups (format 0x02) — the existing-cohort twin of pulse import --group, taking the same --group, --elide-constants, --dedup-ratio-floor and --strict. Rewrites in place (destructive, non-interactive, atomic: temp file, fsync, rename, so a refusal or failure leaves the cohort byte-identical) or, with --out PATH, writes a new file that must not exist. --suggest-groups detects candidates over the cohort’s records; alone it writes nothing. An already-grouped cohort is regrouped from scratch. Refuses a shard archive (SERVICE_VALIDATION). An in-place rewrite names each invalidated sidecar and its rebuild command (data.invalidated_sidecars) and rebuilds nothingparent groups
pulse recommendTurn a question kind into ranked draft requests: --intent ID (required; an intent-taxonomy ID, pulse skills show intents), --cohort PATH to bind the drafts to the cohort’s fields and predict-validate each one (omit for <placeholder> skeletons), --field F (repeatable, cohort only) to pin a field to the first role it fits, --level basic|intermediate|advanced to rank that level first, --limit N (default 10). Reads the cohort’s header, schema and sidecar only. Refusals keep their own code: PULSE_RECOMMEND_INTENT_UNKNOWN, SERVICE_VALIDATION (bad hint, level or limit), DATA_FILE--help; skill tool-recommend
pulse explainSay in plain words what a request will do, or what a result found. --request FILE alone describes the request before it runs (never run; predict-checked over its cohort — header, schema and sidecar only — when it names one). --response FILE reads a result into findings (closed verdicts at the result’s own alpha, strength bands with their convention, caveats); a --json envelope is unwrapped to its data, and a null figure stays undefined. --request beside --response is the request that produced it (without it an aggregation is described by count only). --root request|composed|chain|facet|sample (default request) names which root the files hold; sample has no response. --detail terse|full (full adds narrative sentences, glossary refs, follow-ups and assumptions). Refusals keep their own code: CLI_INPUT (no file, bad --root, unreadable JSON), SERVICE_VALIDATION (bad --detail), DATA_FILE--help; skill tool-explain
pulse docs exportWrite the analysis reference — operator catalog, “Reading your results” pages, glossary, skills and an mdBook SUMMARY.md fragment — as deterministic Markdown under --out DIR (required). Needs no data directory; --feature-profile PATH (host OS path; PULSE_FEATURE_PROFILE is not read) scopes it to a feature profile, --no-skills leaves the skill pages out. An unmarked non-empty --out is refused with PULSE_DOCS_EXPORT_DIR_NOT_EMPTYguided analysis
pulse errors listList error codes by domain and/or substring--help
pulse errors lookupMessage + fixups for one error code--help
pulse examples searchSearch the embedded request-example library--help
pulse examples showPrint a named example’s runnable request JSON--help
pulse export arrowExport .pulse to Arrow IPC--help
pulse export csvExport .pulse to CSV--help
pulse export excelExport .pulse to Excel--help
pulse export jsonarrayExport .pulse to a JSON array--help
pulse export ndjsonExport .pulse to NDJSON--help
pulse export parquetExport .pulse to Parquet--help
pulse export predictValidate an export without writing outputexport spss
pulse export spssExport .pulse to SPSS .savexport spss
pulse export transferCompress a cohort or shard archive (any format version) into a zstd transfer artifact, <input>.zst by default; --level 1..22 (default 3). Transport only — the artifact is never opened as a cohorttransfer compression
pulse export tsvExport .pulse to TSV--help
pulse features checkValidate a feature profile file (host OS path) against this build exactly as pulse.New would; offline, so an unregistered extension-like name is a PULSE_FEATURE_PROFILE_UNKNOWN warning (unverified_extension), and any failure exits non-zerofeature profiles
pulse features diffList the features this build offers that a feature profile does not list (flagged new past its written_with) and the names it does not resolvefeature profiles
pulse features initPrint strict feature-profile JSON listing every feature this build offers, or --from NAME seeded from an example feature profilefeature profiles
pulse features showDescribe every feature a feature profile lists: kind, category, source, since and dependenciesfeature profiles
pulse import arrowImport Arrow IPC into .pulse--help
pulse import autoAuto-detect a source format into the managed pool; carries the per-format read knobs --sheet (Excel) and --charset (SPSS), --group parent-group declarations, --source-tz / --dst-policy, and deliberately not --spss-missingimport spss
pulse import csvImport CSV into .pulse--help
pulse import dropRemove a managed-import handle--help
pulse import excelImport Excel into .pulse--help
pulse import jsonarrayImport a JSON array into .pulse--help
pulse import listList managed-import handles and TTL status--help
pulse import ndjsonImport NDJSON into .pulse--help
pulse import parquetImport Parquet into .pulse--help
pulse import predictValidate an import without writing outputimport spss
pulse import schema-templateEmit an editable schema template from input dataimport spss
pulse import spssImport SPSS .sav / .zsav into .pulseimport spss
pulse import transferDecompress a pulse export transfer artifact into a byte-identical .pulse at rest (temp file, fsync, rename); refuses an existing --output without --overwritetransfer compression
pulse import tsvImport TSV into .pulse--help
pulse index buildBuild a point-lookup sidecar indexindex
pulse index dropRemove a cohort’s sidecar index (destructive, no prompt)index
pulse index listList every sidecar index built for a cohortindex
pulse index verifyReport whether a cohort’s sidecar index is freshindex
pulse mcpRun the MCP server over stdio; --metrics-addr HOST:PORT serves Prometheus metrics on GET /metrics (unset opens no port); --return PRESET sets the response preset for a tool request without its own return (default standard); repeatable --limit NAME=VALUE sets an instance resource limit (unlimited / Go duration for request_timeout; a bad key or value is CLI_INPUT)mcp
pulse profile createCreate a profile JSON for an existing cohort; carries the capture flags --include-correlations, --conditional, --fit-shape, --fit-models (one linear model per numeric field, so several categoricals can condition the same field) and --residual-correlations (the correlation submatrix among those models’ residuals; requires --fit-models), plus --suggest-rules <path>, which detects structural gating relationships, co-missing question blocks and exact single-source dependencies on the same scan and writes them as a standalone rules file for review, and --run-continuation, which measures per-field run-continuation (the run-skip decode’s hit rate) to show whether the cohort still has the sort order that makes scans fast. Profiles a single-file cohort or a whole shard archiveprofile create
pulse schemaPrint the payload JSON Schema (raw, not envelope-wrapped)schema
pulse shard addAppend a shard to an existing archive--help
pulse shard compactRewrite an archive to reclaim orphan bytes--help
pulse shard createCreate a shard archive from single-file shards--help
pulse shard extractWrite a shard’s standalone .pulse bytes to stdout--help
pulse shard listList shards inside an archive--help
pulse shard removeRemove a shard from an archive by basename--help
pulse shard verifyRe-validate every shard against the canonical schema--help
pulse skills listList every embedded skill--help
pulse skills showPrint one skill’s markdown--help
pulse synth from-profileGenerate a synthetic cohort from a captured profile; --emit-spec writes the derived spec (the one that actually generated) and --rules applies a standalone structural-rules file to it; --fidelity-report additionally scores how much of the captured structure survived, including the models and model_residual_correlations sections a --fit-models profile earnssynth from-profile
pulse synth from-schemaGenerate a cohort from a JSON schema/specsynth from-schema
pulse versionPrint the build version as pulse <version>; --json emits the standard envelope whose data carries the build version, Go version, VCS commit and commit time (both omitted when the binary carries no VCS metadata) and the envelope format_version. Same build version as pulse --version and the root manifest--help
pulse widenWiden a set column of a single-file cohort to a wider set rung (--field, --to), rewriting the cohort in place. Destructive and non-interactive, but atomic — temp file, fsync, rename — so a refusal or a failure leaves the cohort byte-identical. Refuses a narrower or equal target, a non-set column, a column already at set_u256, and a shard archive (whose widen spans the canonical schema plus every shard payload, so it is not this leaf’s operation); each refusal carries its own error code, usable with pulse errors lookup. On success it names any sidecar the rewrite invalidated — the point-lookup index, the SPSS metadata sidecar — and the exact command that rebuilds it (data.invalidated_sidecars under --json); it rebuilds nothing, and prints nothing when there are none--help

Help

Every command supports --help:

pulse --help
pulse api --help
pulse api process --help
pulse mcp --help

--help output is the urfave/cli v3 default — a usage block, description, flag list, and an examples block where applicable.

Cross-references

If you need…Go to
Per-command synopsis & examplesCLI Tour and each cli/ page
Library-side equivalentsLibrary Embedding
MCP-side equivalentsHow LLMs Use Pulse
Envelope and error code semanticsTroubleshooting and the pulse_errors_lookup MCP tool / pulse errors lookup CODE CLI