wyrd/tools
esc

    ↑↓ Move ↵ Open toha results first Search by Pagefind
    Releases

    Asking questions

    A template asks questions through the interview list in template.yml. Toha asks them in the order you write them. It saves each answer under the question’s id, so later questions and generated files can use that answer.

    Start with one question:

    interview:
      - id: title
        type: text
        prompt: Note title

    Here title is the answer’s name, text says what kind of answer to collect, and prompt is what the person sees. Every question needs all three fields. An id must start with a lowercase letter or underscore and then contain only lowercase letters, digits, or underscores. For example, note_title is valid.

    Choose an answer type

    text: one line

    Use text for a short string, such as a title or file name.

    - id: title
      type: text
      prompt: Note title

    The answer is a string. In a file, {{ title }} inserts that string. If you need several lines, use multiline instead. To collect several one-line answers as a list, use loop.

    multiline: several lines

    Use multiline for a paragraph or another answer that may contain newlines. The terminal driver opens an editor when it can; its help explains the fallback when an editor is unavailable.

    - id: summary
      type: multiline
      prompt: Summary

    The answer is still one string. Toha keeps its newlines when it renders a file.

    document: a JSON file

    Passing a JSON document to an interview

    Use document to accept a complete JSON object, such as a catalog. Name the schema URI and a default file relative to the target:

    name: catalog-view
    schemas: schemas
    interview:
      - id: catalog
        type: document
        prompt: Catalog file
        schema: https://schemas.example.test/catalog/1
        input: catalog.json

    Vendor the schema as schemas/catalog.yml (JSON works too):

    $id: https://schemas.example.test/catalog/1
    type: object
    required: [title]
    properties:
      title:
        type: string

    The input document can be:

    {"$schema":"https://schemas.example.test/catalog/1","title":"Sample catalog"}

    A generated file can use {{ catalog.title }}. The answer keeps the entire object. For an unanswered question, Toha reads catalog.json from the target at the start of the run. The path can reach a parent directory inside the target repository; without Git it must stay inside the target. A missing file leaves the question unanswered. An invalid default file stops the run before it writes output.

    Use toha apply ./catalog-view ./output --input ./another-catalog.json to select a different file for one run. Repeat --input for different schemas. The $schema string routes the file to every question that accepts that URI. It overrides a value for catalog in an answers document, which overrides the default file when the question is unanswered. stage --async --input FILE and continue PATH --input FILE accept the same document. Once a staged interview records an answer, it keeps that document for the session. A later default file cannot replace it. To change a recorded answer, abort and stage again. A missing $schema, an unaccepted URI, or two inputs for one question is a usage error (exit 2).

    If you type an unreadable path or select a file that is not a JSON object, Toha reports the error and asks for a path again. It keeps your earlier answers.

    A question can name a list of accepted schema URIs. The document’s $schema selects one by exact string equality; it can be absent only when the question accepts one schema. Toha indexes YAML and JSON schemas by $id, including nested directories, and resolves schema references from that index first. Missing or duplicate $id values stop template loading.

    Vendoring is the working path. An unvendored URI requires an HTTPS fetch; that fetch must succeed before the interview starts. Hosted schema URIs such as https://intentional.foo/schemas/release-model/1 do not resolve until their hosting exists. The example URI above needs its vendored schema.

    The runnable example is in docs/examples/document. The integration tests in tests/document_questions.rs cover these examples, precedence, batches, and invalid-file refusals.

    git: a repository for the target

    Use git when the generated project should be a git repository. The person enters the URL of its remote, or leaves the answer empty for none.

    - id: remote
      type: git
      prompt: Remote URL (empty for none)

    When the interview reaches this question, Toha initializes the target as a git repository after it writes the files and before any hook runs, so hooks can use git. The branch is your configured init.defaultBranch; if that is unset or master, Toha uses main. A URL answer becomes the origin remote.

    Toha checks the remote before it writes anything. If the remote already has history, Toha stops and tells the person to clone it first, then apply the template into the clone. If Toha cannot reach the remote, it adds the remote, shows a warning, and continues.

    A target that is already inside a git repository stays as it is. If a when skips the question, Toha does not initialize a repository. A template can have only one git question. Toha checks that git is on PATH before the interview starts, as if the template listed it in requires.

    confirm: yes or no

    Use confirm for a decision. Its answer is a boolean: true or false.

    - id: include_summary
      type: confirm
      prompt: Include a summary?
      default: false

    You can use that answer in a later condition, such as when: include_summary.

    select: one choice

    Use select when the person must choose one item from options.

    - id: priority
      type: select
      prompt: Priority
      options: [ low, normal, high ]
      default: normal

    The answer is the selected string. options is required. You can write a literal list, as above, or an expression that returns a list of strings:

    options: "priorities | map(attribute='name') | list"

    In that example, priorities must already be available from data or an earlier answer. Toha evaluates the expression before asking the question.

    multiselect: several choices

    Use multiselect when the person can choose multiple items from options. The answer is a list of strings, even when only one item is selected.

    - id: labels
      type: multiselect
      prompt: Labels
      options: [ personal, shared, archived ]
      default: []

    As with select, options is required and can be a literal list or an expression that returns a list of strings. The default must be a list; [] means no selections.

    Help the person answer

    description adds explanatory text to a question. placeholder shows an example or hint in the input field. Both can contain Jinja substitutions from earlier answers.

    - id: slug
      type: text
      prompt: File name
      description: Used for the note's file name
      placeholder: "{{ title | kebab }}"

    The prompt asks for the answer. The description explains its use. The placeholder suggests a value but does not supply one; use default for that.

    Supply a default

    default preselects an answer that the person can accept or change. For text, multiline, and select, it is a string template. It can include an earlier answer:

    - id: slug
      type: text
      prompt: File name
      default: "{{ title | kebab }}"

    For confirm, the default is a boolean. For multiselect and a repeated text question, it is a list of strings. These non-string defaults can be literals or Jinja expressions that return the required type:

    - id: include_summary
      type: confirm
      prompt: Include a summary?
      default: "title | length > 20"

    Here the quoted value is evaluated as a boolean expression; Toha does not render it as text. Configuration can replace a question’s default only through a template-defaults mapping for the selected template’s formal name. The mapping can use an inline value or refer to a named preset. Toha reads the live mapping and preset again when a staged interview resumes. See Configuration.

    A template default must satisfy the same options and constraints as an entered answer. If it does not, Toha reports a template error and stops. It does not ask the person to repair the template’s value. When a question is skipped, Toha checks the default against constraints whose inputs are already available. It does not render the skipped question’s prompt, description, or placeholder.

    A configured default belongs to the caller. If its answer type is correct but a constraint rejects it, the question has no preselected value, the error names the winning configuration entry, and an entered answer can replace it.

    Require and validate answers

    Set required: true to reject an empty answer. You can also use an expression that returns a boolean when the requirement depends on an earlier answer.

    - id: title
      type: text
      prompt: Note title
      required: true
      validate:
        min: 3
        max: 80

    validate.min and validate.max set the allowed length of a string. For multiselect, they set the number of selected items. They accept a nonnegative integer or an expression that returns one. A text question can also use validate.regex to require a pattern:

    - id: slug
      type: text
      prompt: File name
      validate:
        regex: '^[a-z0-9-]+$'

    validate.regex also works on other string answers. Toha checks an answer before storing it. A choice question also checks that every chosen item is in its options.

    Change an answer with format

    Use format when Toha should transform a valid answer before storing it. The expression receives the submitted answer as value:

    - id: slug
      type: text
      prompt: File name
      format: value | kebab

    If the person enters Meeting Notes, later questions and generated files see meeting-notes. Toha validates the submitted value first, then applies format. For a repeated text question, it validates and formats each item.

    Ask for a list with loop

    A text question with loop asks for one item at a time. The person enters an empty line to finish. Toha stores the answer as a list of strings.

    - id: tags
      type: text
      prompt: Tag (empty to finish)
      loop:
        min: 1
        max: 5
      format: value | lower

    loop.min requires at least that many items. loop.max ends the loop when that number is reached. Each bound accepts a nonnegative integer or an expression. validate and format apply to each item; loop.min and loop.max apply to the whole list. Only text questions support loop.

    Ask a question only when it applies

    Set when to an expression that returns a boolean. Toha asks the question only when it is true:

    - id: include_summary
      type: confirm
      prompt: Include a summary?
    
    - id: summary
      type: multiline
      prompt: Summary
      when: include_summary

    If include_summary is false, Toha skips summary. A skipped question uses its default when it has one. Without a default, Toha records an empty list for a repeated text or multiselect question, and none for the other types. Generated files can use that value to decide what to render. A question skipped by a flow { skip: rest } or { skip: group } takes the same value.

    For groups, calculated values, and flow control, continue with Controlling the interview. For the expression syntax used by when, default, and other fields, see Jinja and values.