Run and author repository-backed sqleton-style query commands for go-minitrace
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.
The query commands subgroup loads a catalog from two places:
pkg/minitracecmd/core/Today the embedded catalog includes examples such as:
overview session-listoverview framework-summarytiming timing-analysisnightly session-inventorynightly workspace-summaryoverview 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
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.sql → go-minitrace query commands overview session-listpkg/minitracecmd/core/overview/session-tools.js with name: session-list → go-minitrace query commands overview session-tools session-listpkg/minitracecmd/core/nightly/session-inventory.sql → go-minitrace query commands nightly session-inventorypkg/minitracecmd/core/overview/aliases/codex-framework-summary.alias.yaml → go-minitrace query commands overview aliases codex-framework-summaryA 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-summaryinstead 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.
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:
/query page shows them in the Commands sidebar/api/v2/query-commandsThat means one command definition can power:
go-minitrace query commands ...The browser UI also exposes raw-template and rendered-SQL debug panels so you can see exactly what the command definition produced.
External repositories are discovered with this precedence:
--query-repository flagsGO_MINITRACE_QUERY_REPOSITORIESqueryRepositories 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.ymlHigher-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.
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'
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'
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.
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 nameThose 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.
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 }};
At minimum, a command file needs:
name: the CLI-visible command nameshort: a short help descriptionYou can also include:
long: longer help textflags: Glazed field definitions for named parametersarguments: positional parameterslayout: Glazed layout metadata for form organizationtags: extra metadata for organization/searchmetadata: free-form command metadataStructured 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:
stringboolchoiceintfloatdatestringListintListfloatListchoiceListIf 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.
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 valuesThe 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.
sqlStringsqlStringInsqlIntInsqlLikeUse 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 }})
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:
__verb__ must point at a top-level function,require() calls,go-minitrace query commands.For more realistic examples, start with the checked-in showcase guide:
testdata/query-repositories/README.mdThen inspect the repositories under:
testdata/query-repositories/js-showcase/testdata/query-repositories/mixed-sql-js-showcase/Those testdata repositories demonstrate:
require("timer"),queryOne(...) plus JS-side reshaping,go-minitrace convert pi --source-dir ~/.pi/agent/sessions ....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:
.alias.yaml or .alias.yml filesaliasFor points at the target command nameA practical authoring loop looks like this:
.sql command file or one scanner-first .js command filego-minitrace query commands <groups...> <leaf> --help to verify the parameter schema/query in serve mode and verify the form/debug panelsA 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'
Use query commands when:
Use query duckdb --sql or --sql-file when:
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.
| Problem | Cause | Solution |
|---|---|---|
unknown command under query commands | The repository root was not loaded, or the command name: is different from the filename you expected | Run 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 one | The higher-precedence repository was not mounted first, or the path/config source was wrong | Prefer explicit --query-repository during debugging and confirm the directory exists |
| The browser form is missing a field | The field type is not currently rendered by the UI | Use a currently supported practical type or run the command through the CLI instead |
| The rendered SQL fails read-only validation | The command template produced non-read-only SQL | Keep the command limited to SELECT-style analysis queries |
| A JS command fails during execution | The handler threw, rejected a Promise, or called the host API with invalid SQL | Re-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 flags | You 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 file | Run ... --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 run | Text-output JS commands are currently deferred in go-minitrace query commands | Return row-shaped data for now, or implement writer-mode support in a follow-up slice |
| An alias does not behave as expected | The target command name is wrong, or the alias default shape does not match the target flag type | Verify aliasFor, then compare the alias flags: values to the target command's definitions |
--query-repository with commas behaves strangely | Shell quoting changed the argument before Cobra saw it | Wrap the comma-separated value in quotes or use repeated flags |
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 typesgo-minitrace help analysis-guide — end-to-end workflow with JS command guidance, authoring loop, and when to use JS vs SQLgo-minitrace help query-duckdb — worked examples for raw DuckDB exploration alongside structured commandsgo-minitrace help duckdb-query-recipes — ready-to-use DuckDB examples that can often be translated to normalized mt.db() SQLgo-minitrace help writing-duckdb-queries — SQL patterns for the older sessions_base DuckDB JSON table: JSON access, UNNEST, castinggo-minitrace help query-commands — the raw DuckDB query group, presets, and custom SQL modesgo-minitrace help getting-started — end-to-end first-run tutorialREADME.md — project overview and quick-start commands