Skip to content

CLI

The atif-sql console script (packages/atif-cli/pyproject.toml:42) is the repo’s public contract: one cyclopts router (packages/atif-cli/src/atif_cli/app.py:52) dispatches the ten subcommands below, and a root-level --version resolves the installed atif-sql distribution (:60).

Three conventions hold across every subcommand. --format takes auto, table, json, or csv from the OutputFormat StrEnum at packages/atif-cli/src/atif_cli/output.py:58, and auto resolves to table when stdout is a TTY and json otherwise (packages/atif-cli/src/atif_cli/output.py:74). Exit codes come from one table, EXIT_CODES at packages/atif-cli/src/atif_cli/errors.py:28: 0 ok, 2 empty-session / no-embeddings, 64 invalid input or SQL parse error, 65 catalog error / validation error / embedding mismatch, 70 runtime error, 78 terminal state or suspicious scan, 127 harbor missing. A number in that table is a wire contract: keys may be added, never renumbered. And stderr carries WARNING and above only, so a piped read emits data on stdout and nothing on stderr unless something is wrong; ATIF_SQL_LOG_LEVEL widens it (INFO, DEBUG) and is the only way to reach the INFO surface, because the entry point replaces loguru’s default handler and with it the LOGURU_LEVEL that would have parameterized it (packages/atif-cli/src/atif_cli/app.py:1141-1148).

analyze, embed, and search call Amazon Bedrock and spend money; the other seven are offline.

atif-sql convert [OPTIONS] SESSION-JSONL

Convert one Claude Code session JSONL to ATIF plus a loss report and edges. packages/atif-cli/src/atif_cli/app.py:226

Flags:

  • SESSION-JSONL / --session-jsonl — required path to the session transcript, ~/.claude/projects/<proj>/<session>.jsonl. :226
  • --include-subagents / --no-subagents — stage <session>/subagents/**.jsonl side-files alongside the main chain; default True, with the negative form named explicitly. :228
  • --trajectory-out — write the trajectory JSON here and edges.jsonl beside it, instead of stdout. :229

Exit codes: 0 ok, 2 empty session, 64 invalid input, 65 validation, 70 conversion, 127 the pinned private harbor method is gone. :250

atif-sql materialize [OPTIONS]

Sync the materialized corpus with the raw transcript corpus in one scan-plan-convert-write pass. packages/atif-cli/src/atif_cli/app.py:352

Flags:

  • --force / --no-force — re-materialize every quiescent session regardless of the watermark; default False. :341
  • --quiesce-seconds — source-silence threshold; defaults from settings (contract: 300). :342
  • --source-root — override the raw transcript root, otherwise ATIF_SQL_SOURCE_ROOT or <CLAUDE_CONFIG_DIR>/projects. :343
  • --corpus-root — override the materialized corpus root, otherwise env or ~/.atif-sql/corpus/<slug>. :344
  • --sessions — comma-separated session-id filter; only these sessions are planned this pass. :345
  • --format — report format. :346

Exit codes: 0 ok, 78 suspicious scan — the source scan found zero sessions while the corpus holds materialized ones, so ghost removal was refused and nothing was deleted. Check --source-root; a retry over the same root cannot succeed. packages/atif-cli/src/atif_cli/app.py:429-443

atif-sql status [OPTIONS]

Report corpus freshness: watermark age, counts, bytes, staleness. packages/atif-cli/src/atif_cli/app.py:444

Flags:

  • --source-root — override the raw transcript root. :414
  • --corpus-root — override the materialized corpus root. :415
  • --quiesce-seconds — source-silence threshold used to replay materialize’s planning decision. :416
  • --format — report format. :417

Both roots resolve through _corpus_settings (:433), so a --source-root given without --corpus-root re-derives the corpus root from the overridden source’s slug unless ATIF_SQL_CORPUS_ROOT is set (:206).

atif-sql query [OPTIONS] [ARGS]

Run one SQL statement against the atif-duck catalog and emit results. packages/atif-cli/src/atif_cli/app.py:529

Flags:

  • SQL — positional-only statement; omitting it without --examples is a parse error. :498
  • --examples — short-circuit to the examples listing, honoring --category and --requires, without opening DuckDB. :501
  • --category — forwarded to the examples listing. :502
  • --requires — forwarded to the examples listing. :503
  • --corpus-root — override the materialized corpus root. :504
  • --formattable on a TTY, a JSON array of row objects on a pipe. :505

The statement runs against a hardened connection: reads reach the registered views and nothing else, and the only writable path is the query engine’s own spill directory <corpus_root>/.duckdb_tmp. :601

Exit codes: 64 parse error, 65 catalog error, 65 embedding mismatch, 70 runtime error. :550

atif-sql analyze [OPTIONS]

Run the analytics pipelines — cluster, terms, community, plus the LLM classify, trajectory, conflicts, friction, and perceived stages. packages/atif-cli/src/atif_cli/app.py:659

Flags:

  • --since-days — restrict LLM stages to sessions whose last step is within N days; default 30, and structural stages always run over the full store. :629
  • --limit — cap the number of sessions, newest-first, per LLM stage. :630
  • --max-sessions — hard per-run session ceiling per LLM pipeline; overrides ATIF_SQL_LLM_MAX_SESSIONS_PER_RUN, default 50. :631
  • --max-cost-usd — hard per-run dollar ceiling across all LLM pipelines, checked against running actual usage; overrides ATIF_SQL_LLM_MAX_COST_USD_PER_RUN, default 25.0. :632
  • --no-dry-run — execute the LLM stages for real, which costs money; the default is a dry run emitting plan dicts and cost estimates. :633
  • --structural-only — run only cluster, terms, and community, the hourly cron lane that fires at minute 17. :634
  • --llm-only — run only classify, trajectory, conflicts, friction, and perceived, the nightly lane. :635
  • --skip-cluster — opt out of the cluster stage. :636
  • --skip-terms — opt out of the terms stage. :637
  • --skip-community — opt out of the community stage. :638
  • --skip-classify — opt out of the classify stage. :639
  • --skip-trajectory — opt out of the trajectory stage. :640
  • --skip-conflicts — opt out of the conflicts stage. :641
  • --skip-friction — opt out of the friction stage. :642
  • --skip-perceived — opt out of the perceived stage. :643
  • --force-cluster — recompute clustering even when the mtime sidecar says the input is unchanged. :644
  • --force-community — recompute community detection even when the mtime sidecar says the input is unchanged. :645
  • --corpus-root — override the materialized corpus root. :646
  • --format — summary format. :647
atif-sql embed [OPTIONS]

Embed unembedded corpus steps with Cohere Embed v4 and append them to LanceDB. packages/atif-cli/src/atif_cli/app.py:766

Flags:

  • --limit — cap the number of steps embedded this run. :736
  • --all — explicitly embed every unembedded step, a full backfill. :737
  • --dry-run — preview only; emit the plan JSON with keys pipeline, candidates, batches, batch_size, concurrency, model, limit, dry_run and make no embedding calls. :738
  • --corpus-root — override the materialized corpus root. :739
  • --format — output format. :740

A real run requires an explicit scope: a bare atif-sql embed exits 64 with a hint rather than starting an unbounded backfill. :778

Exit codes: 0 success, 64 missing --limit or --all, 70 runtime (Bedrock, DuckDB, or Lance failure — transient, safe to retry), 78 terminal state, where the store or its config needs operator action and unattended lanes suppress retries. :767

atif-sql search [OPTIONS] QUERY_TEXT

Semantic top-k nearest-neighbor search over step embeddings. packages/atif-cli/src/atif_cli/app.py:860

Flags:

  • QUERY_TEXT — required positional-only text, embedded with Cohere Embed v4 in search_query mode. :829
  • -k / --k — top-k; default 10. This is the CLI’s only short flag. :832
  • --session-id — confine the kNN to one session. :833
  • --corpus-root — override the materialized corpus root. :834
  • --format — output format. :835

Output columns are uuid, session_id, snippet, and sim (:927), ranked by cosine distance ascending so the highest similarity comes first (:935).

Exit codes: 0 success, 2 no embeddings yet, 65 embedding mismatch when the store was written by another provider, 70 runtime. :864

atif-sql examples [OPTIONS]

List tested example queries for every view and macro, derived from the static atif-duck catalog. packages/atif-cli/src/atif_cli/app.py:995

Flags:

  • --category — filter to one of view, table-macro, scalar-macro, validated against CATEGORY_VALUES. :965
  • --requires — filter to one of core, analytics, vss, validated against REQUIRES_VALUES. :966
  • --format — a TTY table grouped by requires, or a JSON object carrying note and examples. :967

Both value sets are Literal aliases in the producer package: Requires at packages/atif-duck/src/atif_duck/domain/examples.py:48 and Category at :53, exported as tuples at :55 and :56.

Exit codes: 0 ok, 64 unknown --category or --requires value. packages/atif-cli/src/atif_cli/app.py:1020

atif-sql schema [OPTIONS]

List every registered view with its columns and every macro signature. packages/atif-cli/src/atif_cli/app.py:1087

Flags:

  • --format — a TTY listing, or a JSON object carrying views, macros, and examples_hint. :1057

The answer comes from the static VIEW_SCHEMA and MACRO_SIGNATURES dicts with no DuckDB import and no view registration. :1067

atif-sql cron COMMAND

Inspect and manually install the atif-sql refresh cron lanes. packages/atif-cli/src/atif_cli/cron.py:38

The group is attached to the root router by app.command(cron_app) at packages/atif-cli/src/atif_cli/app.py:66, and its three lanes — materialize on */10 * * * *, structural on 17 * * * *, llm on 20 10 * * * — are declared once in LANES at packages/atif-cli/src/atif_cli/cron.py:47.

atif-sql cron install [OPTIONS]

Print the crontab block for the three refresh lanes and never write it. packages/atif-cli/src/atif_cli/cron.py:180

Flags:

Without the flag the script is located by walking up from this module (:147); a tree where scripts/atif-sql-refresh.sh is unreachable exits 64 demanding --script (:175).

atif-sql cron status [OPTIONS]

Report each lane’s lock holder plus the last run and skip parsed from the refresh log. packages/atif-cli/src/atif_cli/cron.py:199

Flags:

  • --script — path to atif-sql-refresh.sh, whose .run/ sibling holds the locks and the log. packages/atif-cli/src/atif_cli/cron.py:201
  • --tail — how many trailing log lines to include; 0 disables, default 10. :202
  • --format — human lines on a TTY, JSON on a pipe. :203

The lock probe acquires and releases nonblocking, so the command perturbs no running lane. :127