CLI Design
3. CLI Design¶
Depends on: sec1 (scope, personas, command taxonomy), sec2 (architecture, backend protocol, command tree outline) Feeds into: sec6 (NFR — startup time, responsiveness targets), Implementation
3.1 Design Goals¶
The bass CLI is the primary user surface for the BASS platform in v0.1. The design is guided
by four principles:
- Approachability — A bioinformatician with no prior BASS experience can run
bass list Sampleand get results without reading documentation. - Scriptability — All commands produce machine-readable output (
--format json). Exit codes are stable and documented. No interactive prompts on stdout. - Discoverability —
--helpat every level shows concise examples. Unknown inputs produce actionable error messages, not stack traces. - Consistency — All commands follow the same flag conventions, output structure, and error behavior. Learning one command transfers to all others.
3.2 Command Taxonomy¶
The bass command tree for v0.1 organises all operations into seven top-level groups:
| Command | Group | Summary |
|---|---|---|
bass list |
Entity | List entities of a given type |
bass get |
Entity | Fetch a single entity by ID |
bass create |
Entity | Create a new entity |
bass update |
Entity | Update fields on an existing entity |
bass set-availability |
Entity | Mark an entity available or unavailable |
bass search |
Entity | Full-text / fuzzy search across entities |
bass history |
Provenance | Show provenance events for an entity |
bass schema list |
Schema | List all registered entity types |
bass schema show |
Schema | Show field definitions for an entity type |
bass ingest |
Ingestion | Trigger a batch ingestion from a flat file |
bass status |
System | Show connectivity and deployment health |
bass config get |
Config | Read a config key |
bass config set |
Config | Write a config key |
bass config show |
Config | Print the fully resolved configuration |
Commands deferred to v0.2: bass resolve, bass reconcile, bass login, bass logout.
3.3 Global Flags¶
These flags are accepted by every bass command:
| Flag | Short | Default | Description |
|---|---|---|---|
--format |
-f |
table |
Output format: table, json, csv |
--no-color |
off | Disable ANSI color codes | |
--no-pager |
off | Never page output (useful in scripts) | |
--quiet |
-q |
off | Suppress all non-essential output; errors still printed to stderr |
--config |
./aperture.yaml |
Path to aperture config file | |
--hippo-url |
(from config) | Override Hippo REST URL for this invocation | |
--version |
Print bass version and exit |
||
--help |
-h |
Show help for the command |
Format and color flags may also be set via environment variables:
- BASS_FORMAT=json
- BASS_NO_COLOR=1
- BASS_NO_PAGER=1
3.4 Entity Commands¶
3.4.1 bass list <entity_type>¶
Lists entities of the specified type. Returns paginated results sorted by creation time (descending by default).
bass list <entity_type> [flags]
Flags:
--filter, -F key=value Filter by field value. Repeatable. Multiple filters are ANDed.
--limit, -n N Max results to return (default: 50, max: 500)
--offset N Pagination offset (default: 0)
--sort-by <field> Sort field (default: created_at)
--desc Sort descending (default: true)
--asc Sort ascending
--include-unavailable Include entities where is_available=false
--columns col1,col2 Comma-separated list of columns to display (table/csv only)
--format table|json|csv Output format (default: table)
Examples:
# List all Samples (first 50, table view)
bass list Sample
# List Samples from a specific tissue, JSON output
bass list Sample --filter tissue_type=DLPFC --format json
# List Samples with a specific donor, unavailable included, CSV
bass list Sample --filter donor_id=D001 --include-unavailable --format csv
# Custom columns
bass list Sample --columns id,name,tissue_type,created_at --limit 20
# Paginated iteration
bass list Sample --limit 100 --offset 0 # page 1
bass list Sample --limit 100 --offset 100 # page 2
Table output (default):
Column headers are derived from entity type schema. System fields (id, is_available) are
always included unless --columns overrides. String fields are truncated to 40 characters
with … suffix if needed.
JSON output:
Returns a JSON array of entity objects. Each object includes all stored fields plus system
fields (id, is_available, entity_type).
CSV output: Header row matches the column selection. All values are quoted. Suitable for import into spreadsheet tools or pipeline input.
3.4.2 bass get <entity_type> <id>¶
Fetches a single entity by its UUID.
Examples:
Table output: Renders as a two-column key/value table (field name | value). Relationship
references are displayed as URIs (e.g. donor:D001). Rich syntax highlighting for JSON-valued
fields.
If the entity does not exist, prints to stderr:
Exit code 1.3.4.3 bass create <entity_type>¶
Creates a new entity. Data can be provided inline (JSON string), from a file, or interactively
(if stdin is a TTY and neither --data nor --file is supplied).
bass create <entity_type> [flags]
Flags:
--data '{"field": "value"}' Inline JSON payload
--file <path> JSON or YAML file with entity data
--actor <identity> Actor for provenance (default: $USER)
--format table|json|csv Output format for the created entity (default: table)
--dry-run Validate and print what would be created; do not write
Examples:
# From inline JSON
bass create Sample --data '{"name": "S-001", "tissue_type": "DLPFC", "donor_id": "D001"}' --actor alice
# From file
bass create Sample --file new_sample.json --actor alice
# Dry-run validation
bass create Sample --data '{"name": "S-001"}' --dry-run
Interactive mode (when stdin is a TTY and no --data/--file provided):
Creating Sample. Enter values for each required field. Press Enter to accept defaults.
Leave optional fields blank to skip.
name (string, required): █
tissue_type (string, required): █
donor_id (string, optional): █
Submit? [y/N]:
Interactive mode is disabled (exits with error) when --quiet is set or stdin is not a TTY,
so scripts never hang waiting for input.
On success: Prints the created entity (using --format). Exits with code 0.
On validation failure: Prints validation errors as a bulleted list to stderr. Exit code 1.
3.4.4 bass update <entity_type> <id>¶
Updates fields on an existing entity. Only the supplied fields are modified (partial update).
bass update <entity_type> <id> [flags]
Flags:
--data '{"field": "value"}' Fields to update (partial JSON)
--file <path> JSON/YAML patch file
--actor <identity> Actor for provenance (default: $USER)
--format table|json|csv
--dry-run
Examples:
bass update Sample abc123 --data '{"tissue_type": "frontal_cortex"}' --actor bob
bass update Sample abc123 --file patch.json --actor bob
Behaviour notes:
- Fields not in --data / --file are unchanged.
- Relationship fields (ref type) accept a target entity URI or UUID.
- System fields (id, is_available) cannot be updated via this command.
- On success, prints the updated entity.
3.4.5 bass set-availability <entity_type> <id> <true|false>¶
Toggles entity availability. Uses Hippo's supersession model — this creates a provenance
event and updates is_available.
bass set-availability <entity_type> <id> <true|false> [flags]
Flags:
--actor <identity> Actor for provenance (default: $USER)
--reason <text> Optional reason string (written to provenance event)
Examples:
bass set-availability Sample abc123 false --actor alice --reason "sample failed QC"
bass set-availability Sample abc123 true --actor alice
Output: Confirmation message on stdout. On failure, error to stderr with exit code 1.
3.4.6 bass search <entity_type> <query>¶
Runs a full-text or fuzzy search across entities of the specified type. Delegates to Hippo's FTS5 search capability.
bass search <entity_type> <query> [flags]
Flags:
--field <field_name> Restrict search to a specific field (default: all indexed fields)
--limit N Max results (default: 10, max: 100)
--format table|json|csv
Examples:
bass search Sample "frontal lobe" --limit 20
bass search Sample "D001" --field donor_id
bass search Sample "DLPFC" --format json
Output: Table/JSON/CSV of matching entities with a relevance score column (table view only).
If the entity type has no searchable fields (search: fts), prints an informational warning to
stderr and exits with code 0 (empty results).
3.5 Provenance Command¶
3.5.1 bass history <entity_type> <id>¶
Shows the provenance event log for an entity.
bass history <entity_type> <id> [flags]
Flags:
--limit N Max events to show (default: 20)
--format table|json|csv
Examples:
Table output columns: event_type, actor, timestamp, changes (brief summary of
what changed), schema_version.
Events are shown newest-first (default). The --format json output includes the full
previous_value / new_value fields for each changed attribute.
3.6 Schema Commands¶
3.6.1 bass schema list¶
Lists all entity types defined in the loaded schema.
Table output columns: entity_type, field_count, relationship_count, searchable
(yes/no), validator_count.
3.6.2 bass schema show <entity_type>¶
Shows detailed field and relationship definitions for a single entity type.
Table output:
Entity type: Sample
Fields (6):
Field Type Required Indexed Searchable Notes
─────────────────────────────────────────────────────────────
name string yes yes fts Human-readable identifier
tissue_type string yes yes —
donor_id string no yes —
collection_date date no no —
notes text no no fts
batch_id string no yes —
Relationships (1):
Field Target type Cardinality Notes
──────────────────────────────────────────
donor Donor many-to-one Foreign key via donor_id
Validators (1):
tissue_type must be one of: DLPFC, frontal_cortex, temporal_lobe, cerebellum
3.7 Ingestion Command¶
3.7.1 bass ingest <entity_type> <file>¶
Triggers a batch ingestion from a flat file (CSV or JSON Lines). Delegates to Hippo's
IngestionPipeline.
bass ingest <entity_type> <file> [flags]
Flags:
--actor <identity> Actor for provenance records (default: $USER)
--dry-run Validate all records; do not write
--on-conflict skip|update|error Conflict resolution strategy (default: error)
--format table|json Summary output format (default: table)
Examples:
# Ingest from CSV
bass ingest Sample samples.csv --actor alice
# Dry-run to check for validation errors
bass ingest Sample samples.csv --dry-run
# Upsert mode: update existing entities if ID matches
bass ingest Sample samples.csv --on-conflict update --actor alice
Progress reporting: For large files, a Rich progress bar shows
[████████████░░░░] 800/1000 records | created: 750 updated: 48 errors: 2.
Summary output (table):
Ingestion complete — 1000 records processed in 28.4s
Result Count
──────────────────
Created 750
Updated 48
Unchanged 200
Errors 2
2 errors written to: samples_errors.csv
Error rows are written to a sidecar CSV (<input_file>_errors.csv) alongside the original
file, not to stdout. This allows the caller to inspect and resubmit failed rows without
parsing mixed output.
Exit codes:
- 0 — all records ingested without errors
- 1 — some records failed validation (partial success; see error CSV)
- 2 — ingestion aborted (connection failure, unreadable file, etc.)
3.8 System Status Command¶
3.8.1 bass status¶
Checks connectivity to configured backends and prints deployment health.
Output (table):
BASS Platform Status
Component Mode URL / Path Status Version Entities
──────────────────────────────────────────────────────────────────────────
Hippo sdk ./hippo.yaml ✓ OK 0.4.1 12,341
Cappella — (not configured) — N/A
Canon — (not configured) — N/A
Bridge — (not configured) — N/A
Schema: omics_v2.yaml (version: a3f7b2c1) | Entity types: 6
Exit codes:
- 0 — all configured backends healthy
- 1 — at least one configured backend is unreachable
- 2 — configuration is invalid or unreadable
3.9 Config Commands¶
3.9.1 bass config show¶
Prints the fully resolved configuration, showing the source for each key (default, user config, project config, env var, or CLI flag).
Output:
hippo:
mode: sdk # source: project config (./aperture.yaml)
config: ./hippo.yaml # source: project config
output:
format: table # source: default
pager: auto # source: default
color: auto # source: default
3.9.2 bass config get <key>¶
Reads a single resolved config value.
3.9.3 bass config set <key> <value>¶
Writes a key to the user config file (~/.bass/aperture.yaml). Project config (./aperture.yaml)
is not modified by bass config set.
3.10 Shell Completion¶
Typer generates shell completions automatically. Installation:
# Bash
bass --install-completion bash
# output: source ~/.bass-completion.bash
# Zsh
bass --install-completion zsh
# Fish
bass --install-completion fish
Dynamic completions:
| Context | Completable values |
|---|---|
<entity_type> argument |
All entity types from loaded schema |
--filter key=... |
Field names for the current entity type |
bass config get/set <key> |
Known config keys |
--format flag |
table, json, csv |
Dynamic completions require a working Hippo connection. If the backend is unavailable during tab-completion, static fallback completions are returned with no error output (completion silently falls back to no-op rather than printing an error to the terminal).
3.11 Interactive Flows¶
3.11.1 Create Interactive Mode¶
When bass create is run in a TTY with no --data/--file, it enters a guided interactive
field-entry flow:
Creating Sample
Required fields:
name (string): █
tissue_type (string) [DLPFC, frontal_cortex, temporal_lobe, cerebellum]: █
Optional fields (press Enter to skip):
donor_id (string): █
collection_date (YYYY-MM-DD): █
notes (text): █
Preview:
{
"name": "S-042",
"tissue_type": "DLPFC",
"donor_id": "D001"
}
Submit? [y/N]: █
Rules for interactive mode:
- Required fields are prompted before optional fields.
- Fields with enum constraints show valid values in brackets.
- Ctrl-C at any prompt cancels without creating an entity.
- --dry-run in interactive mode shows the preview but never submits.
3.11.2 Conflict Confirmation¶
When bass ingest runs with --on-conflict error (default) and conflicts are detected, it
prints a summary and prompts for confirmation before aborting or proceeding:
Warning: 3 conflict(s) detected.
ID Current value Incoming value
────────────────────────────────────────
abc123 tissue=DLPFC tissue=frontal_cortex
def456 name=S-001 name=S-001a
ghi789 ... ...
Proceed? [y/N/skip]: █
In non-TTY mode (piped stdin), this prompt is skipped and the command exits with code 1 so the caller can handle conflicts programmatically.
3.12 Error Messages¶
Errors always go to stderr. The design standard:
| Situation | Message format |
|---|---|
| Entity not found | Error: Sample 'abc123' not found. |
| Validation failure (single) | Error: Validation failed for 'tissue_type': 'XYZ' is not a valid value. Expected one of: DLPFC, frontal_cortex, ... |
| Validation failure (multiple) | Bulleted list; each bullet is one violation |
| Connection failure | Error: Cannot connect to Hippo at http://localhost:8000. Check 'bass config show' and ensure the server is running. |
| Unknown entity type | Error: Unknown entity type 'Sampel'. Did you mean 'Sample'? Run 'bass schema list' to see all types. |
| Missing required flag | Typer's built-in message, e.g. Missing argument 'ENTITY_TYPE'. |
| Config file not found | Warning: No aperture.yaml found. Using defaults. Run 'bass config show' to inspect current settings. (stderr, not an error — continues with defaults) |
Fuzzy-match suggestions (e.g., "Did you mean 'Sample'?") are applied to entity type names. The suggestion threshold is Levenshtein distance ≤ 2.
3.13 Exit Code Reference¶
| Code | Meaning |
|---|---|
0 |
Success |
1 |
User / data error: entity not found, validation failure, partial ingest failure |
2 |
System error: connection failure, backend error, unreadable config |
3 |
Auth error (v0.2) |
Exit codes are stable across minor versions. Scripts may rely on them.
3.14 Open Questions¶
| Question | Priority | Status |
|---|---|---|
Should bass list support server-side cursor pagination in addition to offset? |
Medium | Open — depends on Hippo REST API design (sec4 §4.x) |
Should interactive mode support $EDITOR for multiline text fields? |
Low | Deferred to post-v0.1 |
Should bass search support cross-type search (all entity types)? |
Low | Open |
| Fuzzy match threshold for "did you mean" suggestions: LD ≤ 2 or ≤ 3? | Low | Open |