Structured Query Commands

Run and author repository-backed sqleton-style query commands for go-minitrace

Sections

Terminology & Glossary
📖 Documentation
Navigation
20 sectionsv0.1
📄 Structured Query Commands — glaze help structured-query-commands
structured-query-commands

Structured Query Commands

Run and author repository-backed sqleton-style query commands for go-minitrace

Topicminitraceduckdbsqletonglazedqueryquery commandsservequery-repositoryarchive-globdb-pathtable-name+1

Structured query commands add a metadata layer on top of raw SQL and JavaScript-backed analysis scripts. Instead of remembering a long --sql string or maintaining ad hoc files with no parameter schema, you define a scanner-first command source, let go-minitrace turn its fields into real CLI parameters and web-form fields, and then either render read-only SQL or invoke a JS handler that explicitly builds a normalized SQLite database with mt.db().

This matters when a query or analysis script becomes part of your team's repeatable workflow. A structured command gives you a stable name, typed inputs, alias support, discoverability in go-minitrace query commands --help, and a matching form in the /query web UI.

What gets loaded

The query commands subgroup loads a catalog from two places:

  1. the embedded go-minitrace catalog shipped in pkg/minitracecmd/core/
  2. any external repositories configured through flags, environment, or app config

Today the embedded catalog includes examples such as:

  • overview session-list
  • overview framework-summary
  • timing timing-analysis
  • nightly session-inventory
  • nightly workspace-summary
  • overview aliases codex-framework-summary (an alias)

You can inspect the currently loaded commands with standard Cobra help:

go-minitrace query commands --help
go-minitrace query commands overview session-list --help
go-minitrace query commands overview framework-summary --help

Running structured query commands from the CLI

The CLI surface is:

go-minitrace query commands <group...> <command-name> [command flags] [query runtime flags]

Repository subdirectories become nested Cobra groups. SQL files map directly to leaf commands, while JS files usually add one extra group level based on the file stem. For example:

  • pkg/minitracecmd/core/overview/session-list.sqlgo-minitrace query commands overview session-list
  • pkg/minitracecmd/core/overview/session-tools.js with name: session-listgo-minitrace query commands overview session-tools session-list
  • pkg/minitracecmd/core/nightly/session-inventory.sqlgo-minitrace query commands nightly session-inventory
  • pkg/minitracecmd/core/overview/aliases/codex-framework-summary.alias.yamlgo-minitrace query commands overview aliases codex-framework-summary

A special case avoids redundant doubled paths for self-named single-verb JS files. If hardware-research/research-summary.js defines exactly one verb named research-summary, the executable path is collapsed to:

  • go-minitrace query commands hardware-research research-summary

instead of hardware-research research-summary research-summary.

The command-specific flags come from the sqleton-style file metadata. The query-runtime flags are the same execution settings used by the DuckDB loader, such as --archive-glob, --db-path, --table-name, and --persist-loaded.

List sessions with the embedded command:

go-minitrace query commands overview session-list \
  --archive-glob './output/active/*/*.minitrace.json'

Filter the list by framework and limit:

go-minitrace query commands overview session-list \
  --archive-glob './output/active/*/*.minitrace.json' \
  --framework codex,pi \
  --limit 25

Run a summary command:

go-minitrace query commands overview framework-summary \
  --archive-glob './output/active/*/*.minitrace.json'

Run an alias command with baked-in defaults:

go-minitrace query commands overview aliases codex-framework-summary \
  --archive-glob './output/active/*/*.minitrace.json'

Use --output json, --output csv, --fields ..., and the other standard Glazed output flags exactly as you would with query duckdb, because the rendered SQL still flows through the normal query engine and processor stack.

Using structured commands through serve and the web UI

The same catalog is also available when you run the HTTP server:

go-minitrace serve --archive-glob './output/active/*/*.minitrace.json'

In serve mode, structured commands matter in three ways:

  • the /query page shows them in the Commands sidebar
  • the form is generated from the command's typed flag definitions
  • the backend exposes listing and execution endpoints under /api/v2/query-commands

That means one command definition can power:

  • CLI execution through go-minitrace query commands ...
  • interactive browser execution in the query editor
  • API-driven execution from other clients

The browser UI also exposes raw-template and rendered-SQL debug panels so you can see exactly what the command definition produced.

Repository discovery and override rules

External repositories are discovered with this precedence:

  1. repeated --query-repository flags
  2. GO_MINITRACE_QUERY_REPOSITORIES
  3. queryRepositories in app and local config files, resolved from lower to higher layers:
    • /etc/go-minitrace/config.yaml
    • ~/.go-minitrace/config.yaml
    • $XDG_CONFIG_HOME/go-minitrace/config.yaml
    • <git-root>/.go-minitrace.yml
    • <git-root>/.go-minitrace.override.yml
    • <cwd>/.go-minitrace.yml
    • <cwd>/.go-minitrace.override.yml
  4. the embedded catalog last

Higher-precedence repositories are mounted first so they can override embedded commands without changing loader behavior. If multiple config files contain queryRepositories, the later/higher layer replaces the earlier config-file list. CLI flags and environment repositories are then prepended ahead of the config-derived repositories.

CLI flag

Use one or more --query-repository flags:

go-minitrace query commands overview framework-summary \
  --query-repository ./query-commands/team \
  --query-repository ./query-commands/local \
  --archive-glob './output/active/*/*.minitrace.json'

The flag uses StringSlice semantics, so comma-separated values work too:

go-minitrace query commands overview framework-summary \
  --query-repository './query-commands/team,./query-commands/local' \
  --archive-glob './output/active/*/*.minitrace.json'

Environment variable

Use the platform path-list separator (: on Unix, ; on Windows):

export GO_MINITRACE_QUERY_REPOSITORIES=./query-commands/team:./query-commands/local

go-minitrace query commands overview session-list \
  --archive-glob './output/active/*/*.minitrace.json'

App config and local config

Add repository roots to the go-minitrace app config:

queryRepositories:
  - ./query-commands/team
  - ~/.config/go-minitrace/query-commands

App config files are read from /etc/go-minitrace/config.yaml, ~/.go-minitrace/config.yaml, and $XDG_CONFIG_HOME/go-minitrace/config.yaml. Relative paths inside config files are resolved relative to the config file directory.

For project-local query commands, add a git-root or current-working-directory config file:

# .go-minitrace.yml
queryRepositories:
  - ./query-commands

Use .go-minitrace.override.yml for machine-local or private overrides that should take precedence over .go-minitrace.yml in the same git root or current working directory:

# .go-minitrace.override.yml
queryRepositories:
  - ./private-query-commands

When both git-root and current-working-directory files exist, the current-working-directory files are higher precedence. If the current working directory is also the git root, duplicate paths are deduped.

Repository layout

A repository is just a directory tree containing scanner-first command files and optional alias YAML files.

A small example looks like this:

query-commands/
├── overview/
│   ├── session-list.sql
│   ├── session-tools.js
│   ├── framework-summary.sql
│   └── aliases/
│       └── codex-framework-summary.alias.yaml
├── timing/
│   └── timing-analysis.sql
└── tools/
    └── tool-failures.sql

Those folders become nested CLI groups. The source file extension decides how the command is executed:

  • .sql -> sqleton-style SQL preamble + SQL template body
  • .js / .cjs -> scanner-first JS metadata (__verb__, __section__, __package__) + JS handler body
  • .alias.yaml / .alias.yml -> alias definition that targets a previously loaded command by name

Those folders become nested CLI groups, and JS file stems usually become one more group level:

go-minitrace query commands overview session-list
go-minitrace query commands overview session-tools session-list
go-minitrace query commands overview framework-summary
go-minitrace query commands overview aliases codex-framework-summary
go-minitrace query commands timing timing-analysis
go-minitrace query commands tools tool-failures

The CLI leaf command name still comes from the file metadata name: field, not from the filename alone. For JS sources, the filename usually contributes a group and the scanned verb name contributes the final leaf command. The exception is a self-named single-verb JS file, where go-minitrace collapses the redundant extra path level.

Writing a sqleton-style SQL command file

A structured SQL command is a .sql file whose first block is a /* sqleton ... */ YAML preamble.

The preamble describes the command name, help text, and typed parameters. The rest of the file is the SQL template that will be rendered against the loaded table.

Here is a minimal but realistic example:

/* sqleton
name: session-list
short: List minitrace sessions
flags:
  - name: framework
    type: stringList
    help: Filter by agent framework
  - name: title_like
    type: string
    help: Filter titles with LIKE
  - name: limit
    type: int
    default: 100
    help: Limit the number of rows returned
*/
SELECT
  id,
  environment->>'agent_framework' AS framework,
  environment->>'model' AS model,
  title,
  CAST(metrics->>'turn_count' AS INT) AS turns,
  CAST(metrics->>'tool_call_count' AS INT) AS tools
FROM {{TABLE_NAME}}
WHERE 1=1
{{ if .framework -}}
AND (environment->>'agent_framework') IN ({{ .framework | sqlStringIn }})
{{ end -}}
{{ if .title_like -}}
AND title LIKE {{ .title_like | sqlLike }}
{{ end -}}
ORDER BY timing->>'started_at' DESC
LIMIT {{ .limit }};

Required fields

At minimum, a command file needs:

  • name: the CLI-visible command name
  • short: a short help description
  • a non-empty SQL body after the preamble

Optional fields

You can also include:

  • long: longer help text
  • flags: Glazed field definitions for named parameters
  • arguments: positional parameters
  • layout: Glazed layout metadata for form organization
  • tags: extra metadata for organization/search
  • metadata: free-form command metadata

Supported parameter types

Structured commands reuse Glazed field definitions. That means the CLI side gets real typed flags, and the web UI can render controls based on the field type.

The current web form supports these practical field types:

  • string
  • bool
  • choice
  • int
  • float
  • date
  • stringList
  • intList
  • floatList
  • choiceList

If you choose a field type outside that currently supported UI set, the CLI may still be able to expose it as a flag, but the browser form may not render it the way you expect yet.

Template variables and helper functions

The SQL body is rendered locally by go-minitrace before execution. Two inputs matter most:

  • {{TABLE_NAME}} expands to the loaded DuckDB table name
  • {{ .flag_name }} accesses one of your structured flag values

The current helper functions are:

One DuckDB parser rule is worth keeping in mind when authoring both SQL and JS-backed commands: JSON arrow operators (-> / ->>) have low precedence. Inside predicates, parenthesize the extraction so comparisons such as LIKE, =, and IN bind the way you intend.

  • sqlString
  • sqlStringIn
  • sqlIntIn
  • sqlLike

Use these helpers whenever user input becomes part of a predicate. They handle the quoting and escaping rules that the command renderer expects.

For example:

WHERE title LIKE {{ .title_like | sqlLike }}

or:

AND id IN ({{ .session_ids | sqlStringIn }})

Writing a scanner-first JS command file

A structured JS command is a .js or .cjs file that exposes command metadata through static scanner markers and executes only when the command is invoked.

A minimal example looks like this:

__section__("filters", {
  title: "Filters",
  fields: {
    framework: {
      type: "stringList",
      help: "Filter by agent framework",
    },
    limit: {
      type: "int",
      default: 25,
      help: "Maximum number of rows",
    },
  },
});

function sessionList(filters) {
  const mt = require("minitrace");
  const db = mt.db().RuntimeArchives().QueryCommandDefaults().Build();
  try {
    return db.query(`
      SELECT
        session_id,
        title,
        agent_framework AS framework
      FROM sessions
      WHERE 1=1
      ${filters.framework?.length ? `AND agent_framework IN (${mt.sql.stringIn(filters.framework)})` : ""}
      ORDER BY started_at DESC
      LIMIT ${filters.limit}
    `);
  } finally {
    db.close();
  }
}

__verb__("sessionList", {
  name: "session-list",
  short: "List minitrace sessions",
  fields: {
    filters: { bind: "filters" }
  }
});

If that file is stored as overview/session-tools.js, the resulting CLI path is:

go-minitrace query commands overview session-tools session-list

Important rules for JS command files:

  • metadata must stay static and scanner-friendly,
  • __verb__ must point at a top-level function,
  • one file may define multiple verbs,
  • the JS file stem usually becomes a command group and each scanned verb name becomes a leaf command,
  • if a JS file defines exactly one verb and that verb has the same name as the file stem, go-minitrace collapses the redundant extra path level,
  • helper modules can still be loaded with relative require() calls,
  • and text-output JS commands are currently deferred in go-minitrace query commands.

For more realistic examples, start with the checked-in showcase guide:

  • testdata/query-repositories/README.md

Then inspect the repositories under:

  • testdata/query-repositories/js-showcase/
  • testdata/query-repositories/mixed-sql-js-showcase/

Those testdata repositories demonstrate:

  • multi-verb files,
  • JS aliases targeting JS-backed commands,
  • relative helper modules,
  • pure synthetic row generation with no DB query,
  • async commands using require("timer"),
  • queryOne(...) plus JS-side reshaping,
  • query results post-processed in JavaScript before emission,
  • multiple SQL queries joined together in JS,
  • JS-side scoring and session classification logic,
  • side-by-side SQL leaves and JS file-group commands in one repository,
  • and real-data validation workflows using go-minitrace convert pi --source-dir ~/.pi/agent/sessions ....

Writing alias files

Alias files let you publish a shortcut command that points at another command and supplies default values.

A minimal alias looks like this:

name: codex-framework-summary
short: Summarize only codex sessions
aliasFor: framework-summary
flags:
  framework:
    - codex

This matters when you want a stable, memorable command for a common filter without copying the SQL template.

A few rules are worth remembering:

  • aliases are stored in .alias.yaml or .alias.yml files
  • aliasFor points at the target command name
  • caller-supplied values override alias defaults
  • aliases are first-class entries in the loaded catalog and in the UI sidebar

Authoring workflow

A practical authoring loop looks like this:

  1. create a repository directory
  2. add one sqleton-style .sql command file or one scanner-first .js command file
  3. run go-minitrace query commands <groups...> <leaf> --help to verify the parameter schema
  4. run the command against a real archive glob
  5. optionally open /query in serve mode and verify the form/debug panels
  6. add alias files for repeated filters only after the base command works

A concrete example:

mkdir -p ./query-commands/overview/aliases
$EDITOR ./query-commands/overview/session-tools.js

go-minitrace query commands overview session-tools session-list \
  --query-repository ./query-commands \
  --archive-glob './output/active/*/*.minitrace.json' \
  --help

go-minitrace query commands overview session-tools session-list \
  --query-repository ./query-commands \
  --archive-glob './output/active/*/*.minitrace.json' \
  --framework codex

If your JS file is self-named and defines exactly one same-named verb, use the collapsed path instead. For example:

go-minitrace query commands hardware-research research-summary \
  --query-repository ./query-commands \
  --archive-glob './output/active/*/*.minitrace.json'

When to use structured commands vs raw SQL

Use query commands when:

  • the query should be discoverable by name
  • the query has reusable typed parameters
  • you want the same definition to work in CLI, API, and browser UI
  • you want aliases for common defaults

Use query duckdb --sql or --sql-file when:

  • you are doing one-off exploration
  • the query is still changing rapidly
  • the query does not need a typed parameter schema yet

The two approaches are complementary. Raw query duckdb is the fastest ad hoc path over the DuckDB sessions_base table. Structured SQL commands make that style reusable, while structured JS commands can build normalized SQLite databases with mt.db() when the analysis needs multiple queries, post-processing, caching, or table-level schema discovery.

Troubleshooting

ProblemCauseSolution
unknown command under query commandsThe repository root was not loaded, or the command name: is different from the filename you expectedRun go-minitrace query commands --help, then check --query-repository, env/config sources, and the file's name: field
A repository command does not override the embedded oneThe higher-precedence repository was not mounted first, or the path/config source was wrongPrefer explicit --query-repository during debugging and confirm the directory exists
The browser form is missing a fieldThe field type is not currently rendered by the UIUse a currently supported practical type or run the command through the CLI instead
The rendered SQL fails read-only validationThe command template produced non-read-only SQLKeep the command limited to SELECT-style analysis queries
A JS command fails during executionThe handler threw, rejected a Promise, or called the host API with invalid SQLRe-run through the CLI first, then check the exact JS runtime error and the handler source file
unknown flag: --archive-glob on a JS command that should support runtime flagsYou probably stopped at an intermediate JS group instead of the executable leaf, or you assumed a doubled path for a self-named single-verb JS fileRun ... --help on the exact path you are invoking. Multi-verb JS files keep the extra file-stem group; self-named single-verb JS files collapse it
A JS command with output: "text" does not runText-output JS commands are currently deferred in go-minitrace query commandsReturn row-shaped data for now, or implement writer-mode support in a follow-up slice
An alias does not behave as expectedThe target command name is wrong, or the alias default shape does not match the target flag typeVerify aliasFor, then compare the alias flags: values to the target command's definitions
--query-repository with commas behaves strangelyShell quoting changed the argument before Cobra saw itWrap the comma-separated value in quotes or use repeated flags

See also

  • go-minitrace help js-api-reference — complete JS runtime API: mt.db(), db.query(), normalized SQLite tables, cache modes, mt.sql.*, mt.runtime, built-in modules, scanner markers, field types
  • go-minitrace help analysis-guide — end-to-end workflow with JS command guidance, authoring loop, and when to use JS vs SQL
  • go-minitrace help query-duckdb — worked examples for raw DuckDB exploration alongside structured commands
  • go-minitrace help duckdb-query-recipes — ready-to-use DuckDB examples that can often be translated to normalized mt.db() SQL
  • go-minitrace help writing-duckdb-queries — SQL patterns for the older sessions_base DuckDB JSON table: JSON access, UNNEST, casting
  • go-minitrace help query-commands — the raw DuckDB query group, presets, and custom SQL modes
  • go-minitrace help getting-started — end-to-end first-run tutorial
  • README.md — project overview and quick-start commands