Configuring a project with ubproject.toml

The ubproject.toml file is a TOML configuration file that allows you to specify the settings for your project.

You can find the schema at https://ubcode.useblocks.com/ubproject.schema.json, and it is recommended that you add this to the top of your file for use with the taplo toolkit:

"$schema" = "https://ubcode.useblocks.com/ubproject.schema.json"

Please also see the detailed ubproject.toml schema reference.

For a complete example, see the Sphinx-Needs Demo project.

Configuration file discovery

Hierarchical configuration is supported, such that the “closest” config file in the directory hierarchy is used for every individual file, with all paths in the config file being resolved relative to the directory containing that config file.

For example, given the following directory structure:

project/
├── docs1/
│   ├── doc1.rst
│   └── ubproject.toml
├── docs2/
│   └── doc2.rst
└── ubproject.toml

The configuration for doc1.rst will be read from project/docs1/ubproject.toml, whereas for doc2.rst it will be read from project/ubproject.toml.

See also the source configuration below for how file-discovery works for reStructuredText source files.

Sharing configuration

To share configuration between multiple projects, you can use the extend option to reference another ubproject.toml file.

# Extend the `ubproject.toml` file in the parent directory...
extend = "../ubproject.toml"

[needs]
# ...but use a different ID regex.
id_regex = "^[a-zA-Z0-9_]{5,}"

Configuration options

The ubproject.toml file supports a rich set of configuration options organized into logical sections. Each section controls different aspects of ubCode’s behavior:

  • Top-level options: Basic project settings and inheritance

  • needs: Configuration for need identification, types, links, and workflow

  • needs_json: Settings for the Needs Index tree view

  • parse: Extend reStructuredText parsing with custom directives and roles

  • project: Basic project metadata

  • lint: Control error reporting and validation

  • source: File discovery and inclusion patterns

  • scripts: Custom commands for your development workflow

  • server: Language server behavior and performance settings

  • format_rst: Comprehensive reStructuredText formatting options

Each section includes sensible defaults, so you only need to specify the options you want to customize.

Top-level options

The configuration file supports several top-level options that control the overall behavior:

# Extend another configuration file
extend = "path/to/ubproject.toml"
# Virtual configurations do not correspond to an actual project.
# They are used for providing shared configuration defaults.
virtual = true
extend

Type: string (optional)

The path to another ubproject.toml file to extend. This allows you to create a hierarchy of configuration files where child configurations can inherit settings from parent configurations. Paths are resolved relative to the current configuration file.

# Extend the configuration from the parent directory
extend = "../ubproject.toml"

# Or extend from a specific shared configuration
extend = "~/.config/ubcode/base.toml"
virtual

Type: boolean (default: false)

When set to true, this configuration is marked as virtual and does not correspond to an actual project. Virtual configurations are used for providing shared configuration defaults or for workspaces that are not themselves projects. This is particularly useful when creating base configurations that will be extended by other projects.

# In a shared base configuration file
virtual = true

[needs]
id_required = true
id_regex = "^[A-Z0-9_]{5,}"

needs

This provides configuration for ubCode to identify and extract needs in the source files.

[needs]
# whether an non-autogenerated ID is required for each need
id_required = true
# regex pattern to validate the ID against
id_regex = "^[A-Z0-9_]{5,}"
# options for auto-generating IDs
id_length = 5
# Base auto-generated IDs on the title.
id_from_title = false
# If True, a title is optional.
title_optional = false
# Base auto-generated titles on the content.
title_from_content = false
# If given, only the defined statuses are allowed.
statuses = [{name="open"}, {name="closed"}]
# If given, only the defined tags are allowed.
tags = [{name="tag1"}, {name="tag2"}]
# Mapping of keys that can be used as needimport arguments and replaced by the values
import_keys = {key1="/path/to/needs.json"}
# Path to a JSON file containing schema definitions
schema_definitions_from_json = "path/to/schema.json"
# Set of need fields that may contain variant functions
variant_options = ["status", "priority"]
# Additional context for filter expressions
filter_data = {environment = "production", team = "backend"}

# list of need types
[[needs.types]]
directive = "my-req"
title = "My Requirement"
prefix = "M_"
color = "#000000"
style = "node"

# list of link types
[[needs.extra_links]]
option = "tests"
incoming = "is tested by"
outgoing = "tests"
copy = true
color = "#00AA00"
style = "solid"
style_part = "dotted"
style_start = "-"
style_end = ">"
allow_dead_links = false

# list of extra options
extra_options = ["tests"]
# Alternative detailed definition
# extra_options = [{name = "priority", description = "Priority level"}]

# default options for all needs
[needs.global_options.status]
default = "open"
# Using predicates for conditional defaults
[needs.global_options.priority]
default = "medium"
predicates = [
  ["type == 'req'", "high"],
  ["status == 'closed'", "low"]
]

# External needs sources
[[needs.external_needs]]
json_path = "path/to/external_needs.json"
id_prefix = "EXT"
base_url = "https://example.com"
target_url = "needs/{{ id }}.html"
css_class = "external-need"
version = "1.0.0"
skip_in_ubcode = false

Core Options

id_required

Type: boolean (default: false)

If true, every need must have an explicit ID. If false, IDs will be auto-generated when not provided.

id_regex

Type: string (default: "^[A-Z0-9_]{5,}")

Regular expression pattern to validate need IDs against. Only applies when id_required is true.

id_length

Type: integer (default: 5)

Length of auto-generated IDs when not explicitly provided.

id_from_title

Type: boolean (default: false)

When true, auto-generated IDs will be based on the need title instead of being random.

title_optional

Type: boolean (default: false)

When true, allows needs to be created without a title.

title_from_content

Type: boolean (default: false)

When true, auto-generates titles from the first line of need content when no title is provided.

Need Types

The types array defines the available need directives in your project:

[[needs.types]]
directive = "requirement"  # The RST directive name
title = "Requirement"     # Human-readable title
prefix = "REQ_"          # Prefix for auto-generated IDs
color = "#BFD8D2"        # Color for diagrams
style = "node"           # Style for diagrams

Each type supports:

  • directive (required): The reStructuredText directive name

  • title (required): Human-readable title for the need type

  • prefix (required): Prefix used for auto-generated need IDs

  • color (optional, default: "#000000"): Color used in need diagrams

  • style (optional, default: "node"): Style used in need diagrams

Statuses and Tags

Define allowed values for need status and tags:

[[needs.statuses]]
name = "draft"
description = "Work in progress"

[[needs.statuses]]
name = "approved"
description = "Reviewed and approved"

[[needs.tags]]
name = "security"
description = "Security-related needs"

External Needs

Load needs from external sources (JSON files or URLs):

# From local file
[[needs.external_needs]]
json_path = "external/project_needs.json"
id_prefix = "EXT"
base_url = "https://external-project.com"
target_url = "docs/{{ id }}.html"

# From remote URL
[[needs.external_needs]]
json_url = "https://api.example.com/needs.json"
id_prefix = "REMOTE"

Extra Options

The extra_options configuration allows you to define custom fields for your needs beyond the standard fields (like id, title, status, etc.). You can specify these options in two ways:

Simple format (string list):

extra_options = ["priority", "effort", "assignee", "component"]

Detailed format (with descriptions and schemas):

extra_options = [
    {name = "priority", description = "Priority level"},
    {name = "effort", description = "Story points", schema = {type = "integer", minimum = 1, maximum = 13}},
    {name = "assignee", description = "Assigned team member", schema = {type = "string", pattern = "^[a-zA-Z.]+@company\\.com$"}},
    {name = "status", description = "Current status", schema = {type = "string", enum = ["draft", "review", "approved", "done"]}},
    {name = "due_date", description = "Due date", schema = {type = "string", format = "date"}},
    {name = "tags", description = "Associated tags", schema = {type = "array", items = {type = "string"}}},
    {name = "active", description = "Is currently active", schema = {type = "boolean"}},
]

Schema Validation

When using the detailed format, you can specify a schema property to validate the values entered for that field. The schema follows JSON Schema specification and supports various data types:

String Schemas:

needs.extra_options = [

# Basic string
{name = "title", schema = {type = "string"}},

# String with length constraints
{name = "summary", schema = {type = "string", minLength = 10, maxLength = 100}},

# String with pattern validation (email)
{name = "contact", schema = {type = "string", format = "email"}},

# String with pattern validation (custom regex)
{name = "id_pattern", schema = {type = "string", pattern = "^[A-Z]{3}-[0-9]{4}$"}},

# Enumerated string values
{name = "severity", schema = {type = "string", enum = ["low", "medium", "high", "critical"]}},

# Constant string value
{name = "type", schema = {type = "string", const = "requirement"}},

]

String Format Options:

The format property supports various predefined formats:

  • "date": RFC 3339 date format (e.g., 2023-10-22)

  • "date-time": RFC 3339 date-time format (e.g., 2023-10-22T14:30:00Z)

  • "time": RFC 3339 time format (e.g., 14:30:00Z)

  • "email": RFC 5322 email address (e.g., user@example.com)

  • "hostname": RFC 1123 hostname (e.g., example.com)

  • "uri": RFC 3986 URI (e.g., https://example.com/path)

  • "uuid": RFC 4122 UUID (e.g., 550e8400-e29b-41d4-a716-446655440000)

  • "regex": ECMAScript regular expression

Integer and Number Schemas:

needs.extra_options = [

  # Basic integer
  {name = "count", schema = {type = "integer"}},

  # Integer with range constraints
  {name = "priority", schema = {type = "integer", minimum = 1, maximum = 10}},

  # Integer with exclusive bounds
  {name = "score", schema = {type = "integer", exclusiveMinimum = 0, exclusiveMaximum = 100}},

  # Integer that must be a multiple of another number
  {name = "percentage", schema = {type = "integer", multipleOf = 5}},

  # Number (floating point)
  {name = "rating", schema = {type = "number", minimum = 0.0, maximum = 5.0}},

  # Enumerated integer values
  {name = "fibonacci", schema = {type = "integer", enum = [1, 2, 3, 5, 8, 13, 21]}},

]

Boolean Schemas:

needs.extra_options = [

  # Basic boolean
  {name = "approved", schema = {type = "boolean"}},

  # Boolean with specific allowed values
  {name = "enabled", schema = {type = "boolean", enum = [true]}},  # Only true allowed

  # Boolean with constant value
  {name = "published", schema = {type = "boolean", const = false}},

]

Array Schemas:

[[needs.extra_options]]
name = "tags"
schema = { type = "array", items = { type = "string" } }

[[needs.extra_options]]
name = "reviewers"
schema = { type = "array", items = { type = "string", format = "email" }, minItems = 1, maxItems = 3 }

[[needs.extra_options]]
name = "priorities"
schema = { type = "array", items = { type = "integer", enum = [1, 2, 3, 4, 5] } }

[[needs.extra_options]]
name = "categories"
schema = { type = "array", items = { type = "string" }, uniqueItems = true }

Complex Schema Examples:

[[needs.extra_options]]
name = "assignee"
description = "Assigned team member"

[needs.extra_options.schema]
type = "string"
format = "email"
pattern = "^[a-zA-Z0-9._%+-]+@(company|contractor)\\.com$"

[[needs.extra_options]]
name = "effort"
description = "Story points (Fibonacci sequence)"

[needs.extra_options.schema]
type = "integer"
enum = [1, 2, 3, 5, 8, 13, 21, 34]
description = "Must be a Fibonacci number"

[[needs.extra_options]]
name = "due_date"
description = "Target completion date"

[needs.extra_options.schema]
type = "string"
format = "date"

[[needs.extra_options]]
name = "component"
description = "System component affected"

[needs.extra_options.schema]
type = "array"
minItems = 1
uniqueItems = true

[needs.extra_options.schema.items]
type = "string"
enum = ["frontend", "backend", "database", "api", "auth", "ui"]

Usage in Need Directives:

Once defined, these extra options can be used in your need directives:

.. requirement:: User Authentication
    :priority: 5
    :effort: 8
    :assignee: developer@company.com
    :due_date: 2024-12-31
    :tags: security, authentication
    :component: auth, backend
    :active: true

    Users must be able to authenticate using their corporate credentials.

Advanced Options

schema_definitions_from_json

Type: string (default: "")

Path to a JSON file containing schema definitions for validating need content.

variant_options

Type: array (default: [])

List of need field names that may contain variant functions for conditional content.

variant_options = ["status", "priority", "assignee"]
import_keys

Type: object (default: {})

Mapping of keys for needimport directives, allowing short keys to reference longer file paths.

[needs.import_keys]
backend = "/path/to/backend_needs.json"
frontend = "/path/to/frontend_needs.json"
filter_data

Type: object (default: {})

Additional context variables that can be referenced in filter expressions throughout your needs configuration. This allows you to define custom variables that can be used in conditional logic in multiple contexts.

Where filter_data can be used:

  1. Predicate default filters - In global_options predicates to conditionally set default values

  2. needimport item filters - To filter which needs are imported from external sources

  3. needextend filters - To conditionally extend or modify needs based on context

[needs]
filter_data = { environment = "production", team = "backend", version = "2.0" }

# (1) Use in predicate defaults
[needs.global_options.priority]
default = "medium"
predicates = [
  ["environment == 'production'", "high"],
  ["team == 'backend'", "high"]
]

Example with needimport:

.. needimport:: external_needs.json
   :filter: environment == 'production' and version == '2.0'

Example with needextend:

.. needextend:: team == 'backend'
   :status: critical

Common use cases:

  • Setting environment-specific defaults (development, staging, production)

  • Filtering imported needs based on release version or team ownership

  • Applying team or project-specific configurations

  • Conditional formatting or validation based on build context

  • Dynamic behavior in CI/CD pipelines

needs_json

This provides configuration for the Needs Index tree view

[needs_json]
# Path to the needs.json file
path = "relative/path/to/needs.json"
# Optional path to resolve source locations against
src = "."

parse

This provides configuration for parsing of source files, allowing you to extend the built-in reStructuredText directives and roles.

[parse]
# A list of directive names that will not emit a warning if they are unknown.
ignore_directives = ["my-custom-directive", "experimental"]

# A map of directive names to extend the built-in directives with.
[parse.extend_directives.my-admonition]
argument = true  # Whether the directive can have an argument
options = true  # Whether the directive can have options
content = true  # Whether the directive can have content
content_required = false  # Whether a warning should be emitted if the directive has no content
parse_content = true  # Whether the directive content should be parsed as reStructuredText
description = "A custom admonition directive"  # A description of the directive
extension = "my-extension"  # The extension that provides the directive

# Named options for the directive
[parse.extend_directives.my-admonition.named_options]
class = { description = "CSS classes to apply" }
name = { description = "A name for the admonition" }

# A map of role names to extend the built-in roles with.
[parse.extend_roles.my-role]
description = "A custom role for special text"  # A description of the role
extension = "my-extension"  # The extension that provides the role

Core Options

ignore_directives

Type: array (default: [])

List of directive names that won’t trigger warnings when encountered but not recognized by the parser. Useful for custom directives from external extensions.

ignore_directives = [
    "my-custom-directive",
    "experimental-feature",
    "legacy-directive"
]

Extending Directives

The extend_directives section allows you to define custom directives or override built-in directive behavior. Each directive is configured as a subsection:

[parse.extend_directives.my-directive]
argument = true
options = true
content = true
content_required = false
parse_content = true
description = "My custom directive"
extension = "my-package"

# Define named options
[parse.extend_directives.my-directive.named_options]
title = { description = "The title of the element" }
class = { description = "CSS classes to apply" }

Directive Configuration Options:

argument

Type: boolean (default: false)

Whether the directive accepts an argument (text on the same line as the directive name).

.. my-directive:: This is the argument

   Content here.
options

Type: boolean (default: false)

Whether the directive accepts options (field list immediately after the directive line).

.. my-directive::
   :option1: value1
   :option2: value2

   Content here.
content

Type: boolean (default: false)

Whether the directive can have content (indented text block after options).

content_required

Type: boolean (default: false)

When true, emit a warning if the directive has no content when content is expected.

parse_content

Type: boolean (default: false)

When true, parse the directive content as reStructuredText instead of treating it as literal text.

description

Type: string (default: "")

Human-readable description of what the directive does.

extension

Type: string (default: "")

Name of the extension or package that provides this directive.

named_options

Type: object (default: {})

Map defining the specific options this directive accepts. Each option can have a description.

[parse.extend_directives.figure.named_options]
width = { description = "Width of the figure" }
height = { description = "Height of the figure" }
alt = { description = "Alternative text for accessibility" }

Extending Roles

The extend_roles section allows you to define custom inline roles:

[parse.extend_roles.api]
description = "Reference to an API endpoint"
extension = "my-api-docs"

[parse.extend_roles.issue]
description = "Reference to a GitHub issue"
extension = "github-integration"

Role Configuration Options:

description

Type: string (default: "")

Human-readable description of what the role does.

extension

Type: string (default: "")

Name of the extension or package that provides this role.

Example Usage

Once defined in configuration, you can use custom directives and roles in your RST files:

This is a paragraph with a :api:`/users/create` endpoint reference
and an :issue:`123` issue reference.

.. my-admonition:: Important Notice
   :class: warning highlight
   :name: security-note

   This is custom admonition content that will be parsed as RST.

Common Patterns

Sphinx Extension Integration: Define directives from Sphinx extensions you use:

[parse.extend_directives.automodule]
argument = true
options = true
description = "Automatically document a Python module"
extension = "sphinx.ext.autodoc"

[parse.extend_directives.automodule.named_options]
members = { description = "Include module members" }
undoc-members = { description = "Include undocumented members" }

Custom Documentation Patterns: Define project-specific directives:

[parse.extend_directives.api-endpoint]
argument = true
options = true
content = true
description = "Document an API endpoint"
extension = "project-docs"

[parse.extend_directives.api-endpoint.named_options]
method = { description = "HTTP method (GET, POST, etc.)" }
path = { description = "URL path pattern" }
deprecated = { description = "Mark as deprecated" }

project

This provides configuration for the project.

[project]
# Project name
name = "My Project"
# Project version
version = "1.0.0"
# Project description
description = "My project description"
# A directory, used for resolving paths in certain directives (defaults to config directory).
srcdir = "src"

lint

This provides configuration for linting of projects.

[lint]
# List of linting codes to ignore
ignore = ["block.title_line"]
# A list of linting codes to select in LSP file diagnostics
# This overrides the ignore list.
lsp_select = ["block.title_line"]
ignore

Type: array (default: [])

List of linting error codes to ignore globally across the project.

lsp_select

Type: array (default: [])

List of linting codes to specifically enable in Language Server Protocol (LSP) file diagnostics. This setting overrides the ignore list, allowing you to see specific errors in your editor even if they’re globally ignored.

Deprecated since version 0.18.0: The rst_lint configuration section has been deprecated. Use lint instead for all linting configuration.

source

This provides configuration for discovery of source files in your project. The source configuration determines which files are processed by ubCode.

[source]
# Whether to automatically exclude files ignored by git
respect_gitignore = true
# Files and patterns to include (default: *.rst)
include = ["*.rst"]
# Files and patterns to exclude (has sensible defaults)
exclude = [".git", "_build", "node_modules"]
# Additional patterns to extend the exclude list
extend_exclude = ["temp/**", "*.tmp"]
# Additional patterns to extend the include list
extend_include = ["*.md", "docs/**/*.txt"]

Core Options

respect_gitignore

Type: boolean (default: true)

When enabled, automatically excludes files that are ignored by:

  • .gitignore files

  • .git/info/exclude

  • Global gitignore configuration

  • .ignore files (used by tools like ripgrep)

include

Type: array (default: ["*.rst"])

Base list of glob patterns for files to include. By default, only reStructuredText files are processed.

exclude

Type: array (default: comprehensive list)

Base list of glob patterns for files and directories to exclude. The default list includes common directories that typically don’t contain source documentation:

exclude = [
    ".bzr", ".direnv", ".eggs", ".git", ".git-rewrite",
    ".hg", ".svn", ".venv", ".vscode", "_build", "build",
    "dist", "node_modules", "site-packages"
]
extend_exclude

Type: array (default: [])

Additional patterns to add to the exclude list without replacing the defaults.

extend_include

Type: array (default: [])

Additional patterns to add to the include list without replacing the defaults.

Glob Pattern Types

Single-path patterns

Match anywhere in the directory tree:

  • directory - matches any directory named “directory”

  • foo.rst - matches any file named “foo.rst”

  • foo_*.rst - matches files like “foo_test.rst”, “foo_doc.rst”

Relative patterns

Match specific paths relative to the project root:

  • ./foo.rst - matches “foo.rst” in project root only

  • directory/foo.rst - matches “foo.rst” in “directory” only

  • directory/*.rst - matches all RST files in “directory”

  • docs/**/*.rst - matches RST files anywhere under “docs”

Pattern Precedence

Exclude patterns take precedence over include patterns. If a file matches both an include and exclude pattern, it will be excluded.

Tip

To see which patterns are active: ubc config

To see all discovered files: ubc build list-documents

Common Configurations

Multi-format documentation:

[source]
extend_include = ["*.md", "*.txt"]
extend_exclude = ["README.md", "CHANGELOG.md"]

Excluding build artifacts:

[source]
extend_exclude = [
    "build/**",
    "_build/**",
    "*.tmp",
    "temp/**"
]

Including only specific directories:

[source]
include = ["docs/**/*.rst", "specifications/**/*.rst"]
exclude = ["**"]  # Exclude everything else

See also

For detailed glob syntax documentation, see the globset documentation.

scripts

This key can be used to register custom scripts to run. In VS Code they can be accessed via the command palette (Ctrl+Shift+P) by selecting “ubCode: Run Script in Terminal”.

Each key is a script, and each value is the configuration for that script. Normally the value is an object with different keys with the most important key being cmd which holds the command to execute. However if only cmd is set, then the object is optional.

[scripts]
sphinx1 = "sphinx-build -b html . _build/html {{filepath}}"
sphinx2 = { cmd = "sphinx-build -b html . _build/html {{filepath}}", env = { SPHINXOPTS = "-W" }, terminal = "name", jinja = true }

Additionally, the chain key can be used in place of cmd to run multiple commands in sequence:

[scripts]
rm_build = "rm -rf _build"
sphinx = "sphinx-build -b html . _build/html {{filepath}}"
"sphinx:clean" = { chain = ["rm_build", "sphinx"] }

The following keys are possible for a script:

  • cmd: The command to execute.

  • chain: A list of scripts to execute in sequence.

  • env: A map of environment variables to set.

  • terminal: What to name the terminal in VS Code. If not set, the terminal will be named after the script key. Terminal names are unique, so if a terminal with the same name already exists, it will be reused.

  • jinja: If True (the default), the command is treated as a jinja template, which can take the following variables:

    • {{ confdir }}: The directory containing the configuration file.

    • {{ filepath }}: The currently active file in VS Code

The current working directory for the script is set as the directory containing the configuration file.

server

This provides configuration for the language server that powers the VS Code extension and other editor integrations.

[server]
# Whether to enable hover images on image/figure directive arguments.
hover_images = true
# Whether to enable hover tips for need references.
hover_need_refs = true
# Whether to run background indexing of the project every time a source document is saved.
index_on_save = false
# File patterns considered as image files for autocompletion
image_file_patterns = [
    "*.jpg", "*.jpeg", "*.png", "*.gif", "*.svg",
    "*.webp", "*.bmp", "*.tiff", "*.tif", "*.ico"
]
hover_images

Type: boolean (default: true)

When enabled, hovering over image/figure directive arguments in the editor will show a preview of the image.

hover_need_refs

Type: boolean (default: true)

When enabled, hovering over need references (like :need:`REQ_001`) will show a tooltip with need information.

index_on_save

Type: boolean (default: false)

When enabled, the language server will automatically reindex the entire project whenever a source document is saved. This keeps need references and cross-links up to date but may impact performance on large projects.

Note

Enabling this feature ensures that need references are always current but may slow down save operations on large projects.

image_file_patterns

Type: array (default: extensive list of image formats)

List of glob patterns that define which files are considered images. Used for:

  • Autocompletion in image and figure directives

  • Image preview in hover tooltips

  • File discovery for image-related features

Default patterns include common formats like:

image_file_patterns = [
    "*.jpg", "*.jpeg", "*.png", "*.gif", "*.svg",
    "*.webp", "*.bmp", "*.tiff", "*.tif", "*.ico",
    "*.heif", "*.heic", "*.avif", "*.eps", "*.psd",
    "*.ai", "*.pdf"
]

Performance Considerations

For large projects, consider these settings:

[server]
# Disable automatic indexing for better performance
index_on_save = false

# Keep hover features for productivity
hover_images = true
hover_need_refs = true

# Limit image patterns to only used formats
image_file_patterns = ["*.png", "*.jpg", "*.svg"]

format_rst

This provides configuration for the reStructuredText formatter, which can automatically format and clean up your RST files.

[format_rst]
# Validation and error handling
fail_on_warning = ["rst.invalid_code_block"]
validate = true

# Indentation settings
blockquote_indent = 2
definition_list_indent = 2
directive_indent = 3
field_list_indent = 2
literal_indent = 2
substitution_indent = 3

# List formatting
enum_list_auto = "preserve"
enum_list_style = "preserve"

# Field list formatting
field_list_align_body = false
field_list_body_on_new_line = 20

# Paragraph formatting
paragraph_line_length = 88
paragraph_semantic_wrap = false

# Table formatting
simple_table_space = 2

# Transition formatting
transition_char = "-"
transition_length = 80

Core Options

fail_on_warning

Type: array (default: [])

List of linting codes that should cause formatting to fail. This is useful in CI/CD pipelines to enforce code quality.

fail_on_warning = ["rst.invalid_code_block", "rst.malformed_table"]
validate

Type: boolean (default: true)

When true, the formatter validates that the document structure hasn’t changed after formatting. This prevents accidental content corruption.

Indentation Settings

blockquote_indent

Type: integer (default: 2)

Number of spaces to indent blockquotes.

This is a paragraph.

  This is a blockquote, indented 2 spaces.
definition_list_indent

Type: integer (default: 2)

Number of spaces to indent definition bodies in definition lists.

Term
  Definition body indented 2 spaces.
directive_indent

Type: integer (default: 3)

Number of spaces to indent directive content.

.. note::
   This content is indented 3 spaces.
field_list_indent

Type: integer (default: 2)

Number of spaces to indent field list bodies.

:Author: John Doe
  Second line indented 2 spaces.
literal_indent

Type: integer (default: 2)

Number of spaces to indent literal blocks after paragraphs.

This is a paragraph::

  This literal block is indented 2 spaces.
substitution_indent

Type: integer (default: 3)

Number of spaces to indent substitution definition content.

.. |name| replace::
   Substitution content indented 3 spaces.

List Formatting

enum_list_auto

Type: string (default: "preserve")

Controls behavior of enumerated lists with auto # markers:

  • "preserve": Keep original auto markers from source

  • "always": Always use auto markers for subsequent items

  • "never": Never use auto markers for subsequent items

enum_list_style

Type: string (default: "preserve")

Controls the style of enumerated list markers:

  • "preserve": Keep original style from source

  • "period": Use periods (1., 2., 3.)

  • "rparen": Use right parenthesis (1), 2), 3))

  • "parens": Use parentheses ((1), (2), (3))

Field List Formatting

field_list_align_body

Type: boolean (default: false)

When true, align field list body continuation lines with the first line.

# When false (default):
:Field: This is the body
  and this continues on next line.

# When true:
:Field: This is the body
        and this aligns with first line.
field_list_body_on_new_line

Type: integer (default: 20)

Field name length threshold. When a field name exceeds this length, the body starts on a new line.

:Short: Body on same line.

:This is a very long field name:
  Body starts on new line due to length.

Paragraph Formatting

paragraph_line_length

Type: integer or null (default: null)

Target line length for paragraph wrapping. When set, paragraphs are wrapped at word boundaries near this length. Not a hard limit and won’t wrap paragraphs in tables.

paragraph_line_length = 88  # Wrap paragraphs around 88 characters
paragraph_semantic_wrap

Type: boolean (default: false)

When true, wrap paragraphs at semantic boundaries rather than arbitrary line lengths:

  • Always wrap at sentence boundaries ([.!?] [A-Z])

  • May also wrap at clause boundaries ([,;]) if paragraph_line_length is set

# With semantic wrapping:
This is a sentence.
This is another sentence with a clause,
and here's the rest.

Table Formatting

simple_table_space

Type: integer (default: 2)

Number of spaces between columns in simple tables.

====  ====  ====
A     B     C
====  ====  ====
1     2     3
====  ====  ====

Transition Formatting

transition_char

Type: string or null (default: null)

Character to use for transition lines. When null, preserves original characters.

Available options: "-", "=", "~", "^", ".", "+", "_", "#"

transition_length

Type: integer (default: 10)

Length of transition lines in characters.

--------  # 8 character transition with "-"

Complete Configuration Examples

This section provides complete, real-world configuration examples for different types of projects.

Basic Documentation Project

A minimal configuration for a simple documentation project:

"$schema" = "https://ubcode.useblocks.com/ubproject.schema.json"

[project]
name = "My Documentation"
version = "1.0.0"
description = "User documentation for our product"

[needs]
id_required = true
id_regex = "^[A-Z]{3}_[0-9]{3}$"  # Format: ABC_123

[[needs.types]]
directive = "requirement"
title = "Requirement"
prefix = "REQ_"
color = "#BFD8D2"

Software Development Project

Configuration for a software project with comprehensive need tracking:

"$schema" = "https://ubcode.useblocks.com/ubproject.schema.json"

[project]
name = "MyApp"
version = "2.1.0"
description = "Mobile application requirements"
srcdir = "docs"

[needs]
id_required = true
id_regex = "^[A-Z0-9_]{5,}$"
id_length = 8

# Define comprehensive need types
[[needs.types]]
directive = "epic"
title = "Epic"
prefix = "EPIC_"
color = "#FF6B6B"

[[needs.types]]
directive = "story"
title = "User Story"
prefix = "STORY_"
color = "#4ECDC4"

[[needs.types]]
directive = "task"
title = "Task"
prefix = "TASK_"
color = "#45B7D1"

[[needs.types]]
directive = "bug"
title = "Bug"
prefix = "BUG_"
color = "#F39C12"

# Define relationships
[[needs.extra_links]]
option = "implements"
incoming = "implemented by"
outgoing = "implements"
copy = false
color = "#2ECC71"

[[needs.extra_links]]
option = "blocks"
incoming = "blocked by"
outgoing = "blocks"
color = "#E74C3C"

# Define status workflow
[[needs.statuses]]
name = "draft"
description = "Initial draft"

[[needs.statuses]]
name = "review"
description = "Under review"

[[needs.statuses]]
name = "approved"
description = "Approved for implementation"

[[needs.statuses]]
name = "done"
description = "Completed"

# Custom fields
extra_options = [
    {name = "priority", description = "Priority level"},
    {name = "effort", description = "Story points"},
    {name = "assignee", description = "Assigned team member"}
]

# Default values with conditions
[needs.global_options.priority]
default = "medium"

[needs.global_options.status]
default = "draft"
predicates = [
    [["type", "bug"], "review"],
    [["priority", "critical"], "review"]
]

Multi-Team Enterprise Configuration

Configuration for large organizations with multiple teams and external dependencies:

"$schema" = "https://ubcode.useblocks.com/ubproject.schema.json"
extend = "../shared/base-config.toml"

[project]
name = "Platform API"
version = "3.2.0"
description = "Core platform API requirements"

[needs]
# Import from other team's exports
[needs.import_keys]
frontend = "../frontend-team/needs.json"
mobile = "../mobile-team/needs.json"

# Load external needs from other projects
[[needs.external_needs]]
json_path = "../security-team/security-requirements.json"
id_prefix = "SEC"
base_url = "https://security-docs.company.com"
target_url = "requirements/{{ id }}.html"

[[needs.external_needs]]
json_url = "https://compliance.company.com/api/needs.json"
id_prefix = "COMP"
base_url = "https://compliance.company.com"
css_class = "compliance-need"

# Advanced link types for traceability
[[needs.extra_links]]
option = "validates"
incoming = "validated by"
outgoing = "validates"
color = "#9B59B6"

[[needs.extra_links]]
option = "derives_from"
incoming = "parent of"
outgoing = "derives from"
color = "#34495E"

# Rich metadata with schema validation
extra_options = [
    {name = "component", description = "System component"},
    {name = "risk_level", description = "Security risk assessment"},
    {name = "test_coverage", description = "Test coverage percentage"}
]

# Variant options for conditional content
variant_options = ["component", "environment"]

# Additional context for conditional defaults
filter_data = {environment = "production", compliance_level = "strict"}

# Conditional defaults based on filter data
[needs.global_options.risk_level]
default = "low"
predicates = [
    ["compliance_level == 'strict'", "medium"],
    ["environment == 'production'", "high"]
]

[format_rst]
# Enforce consistent formatting
paragraph_line_length = 88
paragraph_semantic_wrap = true
validate = true
fail_on_warning = ["rst.malformed_table"]

[lint]
# Project-specific linting rules
ignore = ["needs.id_not_found"]  # Allow forward references
lsp_select = ["needs.title_missing", "rst.malformed_table"]

[server]
# Optimize for large project
index_on_save = false
hover_need_refs = true

[source]
# Multi-format support
extend_include = ["*.md"]
extend_exclude = ["archive/**", "drafts/**"]

[scripts]
# Team workflows
validate = "ubc lint --fail-on-error"
export = "ubc build needs-json --output needs.json"
"docs:build" = { chain = ["validate", "export"], terminal = "docs-build" }

Sphinx Integration Example

Configuration optimized for Sphinx documentation projects:

"$schema" = "https://ubcode.useblocks.com/ubproject.schema.json"

[project]
name = "API Documentation"
srcdir = "source"

[needs]
# Integrate with Sphinx-Needs
external_needs = []

[[needs.types]]
directive = "requirement"
title = "Requirement"
prefix = "R_"
color = "#BFD8D2"

[[needs.types]]
directive = "specification"
title = "Specification"
prefix = "S_"
color = "#FEDCD2"

# Link to API documentation
[[needs.extra_links]]
option = "api"
incoming = "documented by"
outgoing = "documents API"

[parse]
# Sphinx directive support
[parse.extend_directives.automodule]
argument = true
options = true
description = "Auto-document Python module"
extension = "sphinx.ext.autodoc"

[parse.extend_directives.automodule.named_options]
members = {description = "Include module members"}
undoc-members = {description = "Include undocumented members"}

[parse.extend_roles.py-func]
description = "Reference to Python function"
extension = "sphinx.domains.python"

[format_rst]
# Sphinx-friendly formatting
paragraph_line_length = 79  # PEP 8 line length
directive_indent = 3
field_list_indent = 2

[scripts]
# Sphinx build commands
html = "sphinx-build -b html {{confdir}} {{confdir}}/_build/html"
linkcheck = "sphinx-build -b linkcheck {{confdir}} {{confdir}}/_build/linkcheck"
"docs:live" = { cmd = "sphinx-autobuild {{confdir}} {{confdir}}/_build/html --host 0.0.0.0", terminal = "docs-server" }