Skip to content

CLI Reference

tdsl is a command-line tool for compiling, validating, formatting, and rendering .tdsl files. This reference covers the global options, exit codes, and the arguments and options of the main subcommands.

tdsl [OPTIONS] <COMMAND>

--wikidata-timeout is a global option, so it can be placed either before or after the subcommand (e.g. tdsl --wikidata-timeout 60 build sample.tdsl).

Option Description Default
--wikidata-timeout <SECONDS> HTTP timeout (in seconds) for Wikidata requests 30
-h, --help Show help
-V, --version Show version
Code Meaning
0 Success
1 Error (parse failure, validation failure, IO error, etc.)

tdsl check and tdsl fmt --check can be used as CI gates. They return exit code 1 when there is a diff or an error, so a pipeline can rely on the exit code directly to decide pass or fail.

Subcommand Description
build Compile .tdsl to IR JSON
merge Merge multiple .tdsl files and output IR JSON
check Run a syntax and semantic error check
ast Dump the parsed AST (for debugging)
fetch Fetch and display data for a Wikidata entity
search Search for Wikidata entities by keyword
inspect Analyze a Wikidata entity in detail and suggest a mapping strategy
resolve Resolve a Wikipedia article URL to a Wikidata QID
scaffold Generate a .tdsl template from Wikidata entities
render Render .tdsl to a standalone HTML/SVG/PNG/PDF timeline
init Generate a minimal .tdsl template for manual editing
import-csv Import timeline items from CSV
export-csv Export IR to CSV (symmetric with import-csv)
fmt Format .tdsl files to the canonical style
lint Lint .tdsl files and apply automatic fixes
cache Manage the local Wikidata cache
decompile Decompile JSON IR back to .tdsl source
completions Generate shell completion scripts
lsp Start the LSP server over stdio (Diagnostics + Completion + Hover + Goto Definition + Code Action + Document Symbols + Find References + Rename + Formatting)

Compiles .tdsl files to IR JSON (intermediate representation JSON). When multiple files are given, they are merged before being compiled.

tdsl build [OPTIONS] [FILE]...
Argument Description
[FILE]... Path(s) to the input .tdsl file(s) (merged in order when multiple are given). Optional when --json-schema is used
Option Description Default
-o, --output <OUTPUT> Path to the output JSON file stdout
--pretty Pretty-print the JSON output
--offline Skip Wikidata fetching and process only statically defined items
--no-cache Bypass the local cache and call the API directly
--cache-ttl <CACHE_TTL> Cache expiry in seconds; 0 disables caching 86400 (24h)
--json-schema Write the JSON Schema for the IR to stdout (no input file required)
Terminal window
# Compile offline and print pretty-printed JSON
tdsl build examples/china_dynasties.tdsl --pretty
# Compile with Wikidata integration and save to a file
tdsl build examples/china_with_import.tdsl --output out.json --pretty
# Offline build (recommended during development)
tdsl build examples/china_with_import.tdsl --offline --pretty
# Merge multiple files and compile
tdsl build part1.tdsl part2.tdsl --output merged.json --pretty
# Print the JSON Schema for the IR (no input file required)
tdsl build --json-schema
# Pretty-print the JSON Schema and save it to a file
tdsl build --json-schema --pretty --output timeline-ir.schema.json

Checks a .tdsl file for syntax errors and semantic errors (undefined lane references, inconsistent date ranges, etc.). Returns exit code 0 when there are no errors.

tdsl check [OPTIONS] <FILE>
Argument Description
<FILE> Path to the input .tdsl file
Terminal window
# Run a syntax and semantic check
tdsl check examples/china_dynasties.tdsl
# Use it in CI (returns a non-zero exit code on error)
tdsl check my_timeline.tdsl && echo "OK"

Parses a .tdsl file and dumps its AST (abstract syntax tree) to stdout. This command is for grammar debugging and is not part of the normal workflow.

tdsl ast [OPTIONS] <FILE>
Argument Description
<FILE> Path to the input .tdsl file
Terminal window
# Dump the AST
tdsl ast examples/china_dynasties.tdsl
# View it through a pager
tdsl ast examples/china_with_import.tdsl | less

Reads multiple .tdsl files and outputs a merged IR JSON. Metadata (title, unit, range) from the first file takes precedence.

tdsl merge [OPTIONS] <FILE> <FILE>...
Argument Description
<FILE> <FILE>... Paths to the input .tdsl files (at least 2 required, merged in order)
Option Description Default
-o, --output <OUTPUT> Path to the output JSON file stdout
--pretty Pretty-print the JSON output
--offline Skip Wikidata fetching and process only statically defined items
--no-cache Bypass the local cache and call the API directly
--cache-ttl <CACHE_TTL> Cache expiry in seconds; 0 disables caching 86400 (24h)
Terminal window
# Merge two files and print pretty-printed output
tdsl merge china_dynasties.tdsl world_wars.tdsl --pretty
# Save to a file
tdsl merge base.tdsl extension.tdsl --output combined.json --pretty

Reverse-generates .tdsl source code from a JSON IR file. Useful when the JSON was produced by another tool, or when you want to recover source from an existing IR. Second precision and UTC offsets (Z / ±HH:MM) in start / end / time also round-trip without loss (v1.27.0+; earlier versions silently dropped them).

tdsl decompile [OPTIONS] [INPUT]
Argument Description
[INPUT] Path to the input JSON file (reads stdin if omitted)
Option Description Default
-o, --output <OUTPUT> Path to the output .tdsl file stdout
Terminal window
# Decompile a JSON IR to .tdsl
tdsl decompile out.json
# Save to a file
tdsl decompile out.json --output recovered.tdsl
# Via a pipeline (from stdin)
tdsl build examples/china_dynasties.tdsl --pretty | tdsl decompile --output recovered.tdsl

Limitation (comments are not preserved): Since decompile starts from a JSON IR, comments (// and /* */) that existed in the original .tdsl source cannot be restored. The IR does not carry any comment information, which is a permanent design constraint of treating the IR as the single source of truth.

Formats .tdsl files to the canonical style (2-space indentation, one blank line between blocks). By default, the formatted result is written to stdout. Use --write to overwrite the file in place, or --check to exit non-zero when there is a diff (for CI). --check and --write cannot be specified together.

Comments (// and /* */) are preserved. Top-level leading/trailing comments keep their position; comments inside a block keep their content but may be moved to the canonical position.

tdsl fmt [OPTIONS] <FILE>
Argument Description
<FILE> Path to the input .tdsl file
Option Description Default
--check Exit non-zero when formatting is needed (the file is left unchanged); for CI
--write Overwrite the file with the formatted result
Terminal window
# Print the formatted result to stdout
tdsl fmt examples/china_dynasties.tdsl
# Overwrite the file
tdsl fmt examples/china_dynasties.tdsl --write
# Check for formatting diffs in CI (exits 1 if there is a diff)
tdsl fmt examples/china_dynasties.tdsl --check

Runs a quality check on a .tdsl file and applies automatic fixes for fixable issues with --fix.

tdsl lint [OPTIONS] <FILE>
Argument Description
<FILE> Path to the input .tdsl file
Option Description Default
--fix Apply safe fixes directly to the file
--format <FORMAT> Output format (text / json) text
Terminal window
# Lint check only
tdsl lint examples/china_dynasties.tdsl
# Apply automatic fixes
tdsl lint examples/china_dynasties.tdsl --fix
# JSON output for CI
tdsl lint examples/china_dynasties.tdsl --format json

Fetches and displays the label, description, and properties of a Wikidata entity (given a QID). Use this to inspect an entity’s data before writing an import block.

tdsl fetch [OPTIONS] <QID>
Argument Description
<QID> A Wikidata QID (e.g. Q7209)
Option Description Default
-l, --lang <LANG> Language(s) to fetch labels for (comma-separated) ja,en
Terminal window
# Fetch information for the Han dynasty
tdsl fetch Q7209
# Fetch English and French labels
tdsl fetch Q7209 --lang en,fr

Searches for Wikidata entities by keyword and lists candidate QIDs. Use this to find a QID for use in import.

tdsl search [OPTIONS] <QUERY>
Argument Description
<QUERY> Search query (e.g. "Han dynasty")
Option Description Default
-l, --lang <LANG> Language to use for the Wikidata search ja
-n, --limit <LIMIT> Maximum number of results (1-50) 10
--json Output as JSON
Terminal window
# Search for "Han dynasty" in English
tdsl search "Han dynasty" --lang en
# Increase the result count
tdsl search "Han dynasty" --lang en --limit 20
# Get JSON output and pipe it to a script
tdsl search "samurai" --json | jq '.[] | .id'

Analyzes a Wikidata entity in detail and suggests a mapping strategy (which properties to use for start/end, etc.). Useful as a preliminary investigation before running scaffold.

tdsl inspect [OPTIONS] <QID>
Argument Description
<QID> A Wikidata QID (e.g. Q7209)
Option Description Default
-l, --lang <LANG> Fallback language(s) for labels (comma-separated) ja,en
--json Output as JSON
Terminal window
# Analyze the Tokugawa Ieyasu entity
tdsl inspect Q7243
# Output as JSON for scripted processing
tdsl inspect Q7243 --json | jq '.suggestions'

Resolves a Wikipedia article URL to a Wikidata QID. Use this when you have found an article but don’t know its QID.

tdsl resolve [OPTIONS] <URL>
Argument Description
<URL> A Wikipedia article URL
Option Description Default
-l, --lang <LANG> Fallback language(s) for labels (comma-separated) ja,en
--json Output as JSON
Terminal window
# Resolve a QID from an article URL
tdsl resolve "https://ja.wikipedia.org/wiki/%E6%BC%A2"
# Output as JSON
tdsl resolve "https://en.wikipedia.org/wiki/Han_dynasty" --json

Generates a .tdsl template from Wikidata entities. Specify the wikidata subcommand.

tdsl scaffold wikidata [OPTIONS] --qids <QIDS> --timeline <TIMELINE>
Option Description Default
--qids <QIDS> Comma-separated list of QIDs (e.g. Q7183,Q7209) required
--timeline <TIMELINE> Display title of the timeline required
-o, --output <OUTPUT> Path to the output .tdsl file stdout
-l, --lang <LANG> Fallback language(s) for labels (comma-separated) ja,en
--target <TARGET> Mapping target strategy (auto / span / event / event-range) auto
--lane-mode <LANE_MODE> Lane assignment strategy (single / per-entity / by-kind) per-entity
--single-lane-label <LABEL> Shared lane label used when --lane-mode single 項目
Terminal window
# Scaffold the Western Han and Eastern Han with automatic mapping (Wikidata integration)
tdsl scaffold wikidata \
--qids "Q7209,Q8209" \
--timeline "Han Dynasties" \
--output han_dynasties.tdsl
# Group all entities into a single lane
tdsl scaffold wikidata \
--qids "Q7209,Q8209" \
--timeline "Han Dynasties" \
--lane-mode single \
--single-lane-label "Dynasty"
# Force mapping as span
tdsl scaffold wikidata \
--qids "Q7183,Q7209,Q8209" \
--timeline "Han, Xin, and Eastern Han" \
--target span

Renders a .tdsl file to a standalone HTML / SVG / PNG / PDF timeline. See Commands for the basic usage.

tdsl render [OPTIONS] <FILE>
Argument Description
<FILE> Path to the input .tdsl file

See Commands for --format / --dpi / --png-scale / --orientation / --grid / --show-event-labels / --lane-height / --layout-style / --show-legend / --watch / --show-table. See Styling for --theme / --custom-css.

Option Value Default Introduced Details
-o, --output <OUTPUT> Path to the output file stdout
--format <FORMAT> html / svg / png / pdf html — (png/pdf since v1.10.0/v1.11.0) Commands
--scale <SCALE> Horizontal pixels per year 2 Below
--lane-height <LANE_HEIGHT> Height of each lane in pixels 60 Commands
--left-gutter <LEFT_GUTTER> Width of the left gutter for lane labels 120 Below
--top-margin <TOP_MARGIN> Top margin above the time axis 40 Below
--theme <THEME> default / dark / print / pastel default Styling
--custom-css <CUSTOM_CSS> Path to a custom CSS file injected after the theme CSS Styling
--dpi <DPI> PNG output resolution (mutually exclusive with --png-scale) 96 Commands
--png-scale <PNG_SCALE> Fixed pixel scale for PNG output (mutually exclusive with --dpi) Commands
--interactive Enable zoom, pan, search, legend, and detail panel since v1.1.0 Below
--color-map <COLOR_MAP> Tag-to-color mapping (e.g. war=#cc0000,dynasty=#3366cc) since v1.1.0 Below
--orientation <ORIENTATION> horizontal / vertical horizontal since v1.13.0 Commands
--grid <GRID> none / decade / year / month none since v1.14.0 Commands
--layout-style <LAYOUT_STYLE> timeline / group-bands / gantt / zigzag timeline Commands
--watch Re-render automatically on file change (html/svg only) Commands
--show-table Append a table listing all items Commands
--show-event-labels Always draw labels near event dots/bars Commands
--pdf-size <SIZE> a4 / a3 / letter (--format pdf only) a4 since v1.17.0 Below
--pdf-landscape Output the PDF in landscape orientation (--format pdf only) since v1.17.0 Below
--pdf-margin <MM> Paper margin in mm (--format pdf only) 10 since v1.17.0 Below
--pdf-title <TITLE> Override the PDF document Title metadata (--format pdf only) timeline title since v1.17.0 Below
--pdf-pagination Split the item table across multiple pages (requires --show-table, --format pdf only) disabled (single page) since v1.27.0 Below
--chart-pagination <N> Split the chart body into multiple pages by lane group, N lanes per page (requires --output, --format svg/pdf only) disabled (single page) since v1.28.0 Below

Adjust the layout dimensions of the timeline body. --scale controls the horizontal density (a larger value widens the pixels-per-year), --left-gutter sets the width of the lane label column, and --top-margin sets the top padding above the time axis ticks. Widen --left-gutter when long lane labels wrap.

Terminal window
# Widen to 4px per year and give long lane labels more room
tdsl render examples/china_dynasties.tdsl --scale 4 --left-gutter 160 --output china_wide.html

Generates interactive HTML with zoom, pan, item search, a legend, and a detail panel. Only meaningful with --format html (since v1.1.0).

Terminal window
tdsl render examples/china_dynasties.tdsl --interactive --output china_interactive.html

Overrides the color_map declaration inside a timeline block from the CLI, mapping tags to colors (since v1.1.0). Provide a comma-separated list of tag=color.

Terminal window
tdsl render examples/china_dynasties.tdsl \
--color-map "dynasty=#4b7bec,war=#e74c3c" \
--output china.html

--pdf-size / --pdf-landscape / --pdf-margin / --pdf-title are only meaningful with --format pdf, and control the paper size, orientation, margin, and metadata title (since v1.17.0).

Terminal window
# A3 landscape, 15mm margin, and an explicit title
tdsl render examples/china_dynasties.tdsl \
--format pdf --pdf-size a3 --pdf-landscape --pdf-margin 15 --pdf-title "Chinese Dynasties" \
--output china_a3.pdf

--pdf-pagination is only meaningful together with --show-table, and splits the item table across as many pages as fit the paper size and margins (since v1.27.0). The first page remains the timeline body only (scaled down), and the table is split across subsequent pages. Specifying --pdf-pagination without --show-table is an error.

Terminal window
tdsl render examples/china_dynasties.tdsl \
--format pdf --show-table --pdf-pagination \
--output china_paginated.pdf

--chart-pagination <N> splits the chart body (the timeline itself) into multiple pages by lane group (since v1.28.0). Lanes are sorted by (order, id) and then chunked into groups of N. The time axis (meta.range) stays common across all pages, and since every item belongs to exactly one lane, spans / event ranges are never clipped at a page boundary.

With --format svg, output is split into multiple files named <stem>.pageN.<ext> (N zero-padded to the digit width of the total page count). Combined with --show-table, a dedicated table page listing the entire IR is appended after the chart pages (its footer is fixed at 1 / 1; splitting the table itself across multiple pages is not supported in SVG output).

With --format pdf, output is not split into separate files. Instead, a single PDF contains multiple pages in the order “chart pages (by lane group) → table pages”. With --show-table alone, the entire IR is appended as one unsplit table page; combined with --pdf-pagination, the existing row-splitting logic produces the table pages instead. Either way, table page footers count only the table pages, not the preceding chart pages.

--show-legend is drawn independently on each chart page, so the legend content can differ from page to page (each page’s legend reflects that page’s lanes and items — this is intentional).

--output is required (stdout cannot represent multiple files or pages), and --chart-pagination cannot be combined with --watch. If a lane group is split across a page boundary, a warning is printed to stderr, but the output is still generated (see Troubleshooting for details). The Playground runs entirely on WASM in the browser and does not expose tdsl-render’s pagination logic, so --chart-pagination cannot be reproduced there — use the CLI (tdsl render) instead.

Terminal window
# Split the timeline body into multiple SVG pages by lane group
# Produces china.page1.svg / china.page2.svg ...
tdsl render examples/china_dynasties.tdsl --format svg --chart-pagination 2 --output china.svg
# Split the timeline body into multiple PDF pages by lane group
# Produces a single china_chart.pdf with multiple pages (no table)
tdsl render examples/china_dynasties.tdsl --format pdf --chart-pagination 2 --output china_chart.pdf
# Combine chart pagination with table pagination (chart pages, then table pages)
tdsl render examples/china_dynasties.tdsl \
--format pdf --chart-pagination 2 --show-table --pdf-pagination \
--output china_full_paginated.pdf

Generates a minimal .tdsl template for manual editing. Does not require a Wikidata connection.

tdsl init [OPTIONS]
Option Description Default
-o, --output <OUTPUT> Path to the output .tdsl file stdout
--timeline <TIMELINE> Display title of the timeline 新しい年表
--range-start <RANGE_START> Start year of the range 0
--range-end <RANGE_END> End year of the range 2000
--lanes <LANES> Lane labels (comma-separated, e.g. "kingdom,event,person") ""
Terminal window
# Generate a minimal template (stdout)
tdsl init
# Save to a file and specify lanes
tdsl init \
--output my_timeline.tdsl \
--timeline "Fantasy World Timeline" \
--range-start 1000 \
--range-end 1500 \
--lanes "kingdom,event,person"

Reads timeline items from a CSV file and converts them into a .tdsl snippet. See Commands for the CSV column spec (header row, time literals, and the source/origin columns).

tdsl import-csv [OPTIONS] <CSV>
Argument Description
<CSV> Path to the input CSV file (UTF-8, with a header row)
Option Description Default
-o, --output <OUTPUT> Path to the output .tdsl snippet stdout
--append <APPEND> Append the generated items to an existing .tdsl file
Terminal window
# Convert CSV to a .tdsl snippet (stdout)
tdsl import-csv items.csv
# Save to a file
tdsl import-csv items.csv --output items_snippet.tdsl
# Append to an existing file
tdsl import-csv new_items.csv --append my_timeline.tdsl

Exports IR to CSV. Intended for round-tripping with import-csv; see Commands for the CSV column spec.

tdsl export-csv [OPTIONS] <FILE>
Argument Description
<FILE> The input file: a .tdsl source (lowered to IR) or a .json file (IR loaded directly)
Option Description Default
-o, --output <OUTPUT> Path to the output CSV file stdout
--offline Skip Wikidata fetching (static items only); ignored for .json input false
--no-cache Bypass the local cache and refetch false
--cache-ttl <SECONDS> Cache TTL in seconds; 0 disables caching 86400
Terminal window
# Export .tdsl to CSV (stdout, static only)
tdsl export-csv my_timeline.tdsl --offline
# Save to a file
tdsl export-csv my_timeline.tdsl --offline --output items.csv
# Export from an IR JSON
tdsl build my_timeline.tdsl --offline --output ir.json
tdsl export-csv ir.json --output items.csv

Manages the local cache of Wikidata fetch results. Specify the status or clear subcommand.

tdsl cache <COMMAND>

Shows cache statistics (file count, total size, oldest/newest entry).

Terminal window
tdsl cache status

Deletes cache entries.

tdsl cache clear [OPTIONS]
Option Description Default
--older-than <DAYS> Only delete entries older than this many days — (deletes all)
Terminal window
# Show cache statistics
tdsl cache status
# Delete all cache entries
tdsl cache clear
# Delete entries older than 7 days
tdsl cache clear --older-than 7

Generates a shell completion script for the specified shell. Add the generated script to your shell’s configuration to enable Tab completion for tdsl subcommands and options.

tdsl completions [OPTIONS] <SHELL>
Argument Description
<SHELL> Target shell (bash / elvish / fish / powershell / zsh)
Terminal window
# Generate and install the bash completion script
tdsl completions bash >> ~/.bashrc
source ~/.bashrc
# Install the fish completion script
tdsl completions fish > ~/.config/fish/completions/tdsl.fish
# Install the zsh completion script
tdsl completions zsh > ~/.zfunc/_tdsl
echo 'fpath=(~/.zfunc $fpath)' >> ~/.zshrc
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
source ~/.zshrc

Starts the LSP (Language Server Protocol) server over stdio. Reads JSON-RPC 2.0 messages from stdin and writes responses to stdout.

tdsl lsp
Feature Description
textDocument/publishDiagnostics Reports parse errors, validation warnings, and static reference errors in real time
textDocument/didOpen Runs diagnostics when a document is opened
textDocument/didChange Re-runs diagnostics on document change (full sync)
textDocument/didClose Clears diagnostics when a document is closed
textDocument/completion Returns DSL keyword completion candidates (context-free, all keywords)
textDocument/hover lane ID → lane info / QID → cached entity info (offline) / time literal → precision and offset info (v1.27.0+)
textDocument/definition Jumps from a lane reference to its declaration
textDocument/codeAction Offers tdsl lint --fix-equivalent automatic fixes as quick fixes (full-text, offline)
textDocument/documentSymbol Returns a hierarchical symbol tree for timeline / lane / items
textDocument/references Returns all reference locations for a lane ID (includeDeclaration toggles the declaration)
textDocument/rename Renames a lane’s declaration and all references at once (explicit as <alias> only)
textDocument/prepareRename Validates whether a rename target is renameable (rejects lanes without as)
textDocument/formatting Returns a full-text TextEdit that formats the DSL source to the canonical style

import / map / apply blocks are diagnosed via static analysis (offline, no network access), so entity resolution that requires Wikidata fetching is not performed. See Installation for editor setup (VS Code / Neovim / Helix) and detailed usage of each feature.

Terminal window
# Start the LSP server (blocks waiting on stdin)
tdsl lsp
# Sanity-check with a minimal JSON-RPC request (Content-Length header required)
echo -e 'Content-Length: 2\r\n\r\n{}' | tdsl lsp