Skip to content

Action Annotations, Quick Reference ​

A one-page digest of ACTIONSPEC.md. That document is the specification and always wins; this page is a reading aid and defines nothing.

The idea ​

A plain SQL or command file describes itself inside its own comments: lines of the form @name value declare how the file is presented, guarded and executed. To any other tool it stays an ordinary SQL or text file. Three commitments shape everything:

  1. No escaping. A value is the rest of its line, verbatim.
  2. No injection. Data travels as bound parameters or single argv elements, never concatenated into SQL or a command line.
  3. Nothing in silence. Anything unreadable is diagnosed with its location; a typo never quietly does nothing.

Two kinds of file ​

Query actionCommand action
Extension.sql (and unknown extensions).run
Comments-- lines and /* */ blocks# lines
BodySQL statements, split on top-level ;exactly ONE command, \ continues a line
First commandthe action itselfthe action itself
Later commandsrow buttons, each MUST carry @button or @opennot allowed, diagnosed
Runsagainst the connection, answers with a result setdirectly (never a shell), answers with its output

Syntax in five rules ​

  • An annotation's @name must be the first printable text of its comment line; - @description is prose, not an annotation.
  • Only the header is read: the comment run directly above a command. Comments anywhere else are prose (an annotation found below the last command is reported, never applied).
  • The value is the rest of the line, trimmed. No quoting, no escaping, no multi-line values.
  • Names and keyword values are case-insensitive.
  • Unknown names, wrong scope and wrong kind are diagnosed, and the bad line degrades as locally as possible (a broken @at keeps the button, a @button with no label is no button).

Two-parameter form (@confirm, @alert-on-*, and the declarations @file, @dir and @var): a value shaped exactly "Title", "text" names the dialog's title too (a prompt and a default, on a declaration). Anything not matching that whole shape is one parameter, verbatim.

The annotations ​

Generated from the registry (quickReference() in web/src/lib/actions.ts); a test holds the two to each other, so this table cannot say less than the registry. Each row is the annotation's syntax and one line about it; the specification's section has the rest.

Both kinds, action header ​

@description <text>what this action answers; shows under its row and in its header (alias @desc)
@toolbar [place] [icon] [label]a button in the title bar or a side strip: a place, an SVG icon, a name
@color <name>a color for every mark of this action: its row, its button, its tab
@face result|sourcewhat a click on the row opens: the file (source, default) or the answer (result)
@connection <id or name>run on this connection (its ID, or its name), from every door; nobody picks another
@for <object type> [, <object type> ...]attach the action to every object of these types; the object fills {schema}, {name}, {object},

Both kinds, either header (the declarations and the gate) ​

@file <name> ["<prompt>", "<default>"]ask for a file first; the picked path becomes {<name>}
@dir <name> ["<prompt>", "<default>"]ask for a directory first; the picked path becomes {<name>}
@var <name>=<value> or @var <name> ["<prompt>", "<default>"]name a value once: = declares it, without = it is asked for in a form
@confirm <text> or "<title>", "<text>"ask this first, before the action or the button runs
@pick <name> <query>list the values an asked @var may take: the query's rows are the choices

Query actions, action header ​

@refresh <interval>ask again on a timer, while its tab is the active one
@hide <column> [, <column> ...]keep a column out of the grid, still there for $column
@extension <name-or-path>[:<tab>] [as <title>] [, ...]apply a grid extension (.plumesql.js, by name or store path) to a query or the whole file
@view <name-or-path>[:<tab>] [as <title>] [only|beside]open the result on a result view (its tab first; only hides the grid, beside puts the view next to it)
@inputs <key>=<value> [, <key>=<value> ...]answer a view's inputs in the file (x=ts, series=a,b), so it draws with no dialog
@ask [<key> [, <key> ...]]always ask before the run or the view draws, the form prefilled; with keys, ask those alone
@noasknever ask: run and draw from the @inputs values and the defaults; a parameter without a value refuses the run
@format-keywords lower|upper|preservethis file's own keyword case; unset follows settings
@format-commas trailing|leadingthis file's own comma style; unset follows settings
@format-indent <spaces>this file's own indent width; unset follows settings
@format-width <columns>this file's own line width; unset follows settings
@readonlythis file's session runs read only, server enforced
@confirm-writesruns from this file confirm structure and data changes
@parse off|markers|outline|boththis file's own dynamic parser mode; unset follows settings

Query actions, either header ​

@inline <name> literal|identifierexpand this value into the statement, server-quoted, for statements that bind nothing

Query actions, button header (every command after the first) ​

@button <label>the command below ACTS on the row that was clicked (alias @row-action, @action)
@open <label>the command below ASKS; its answer replaces the grid
@on <column>put the button ON this column: its cells become the button
@at start|end|before <column>|after <column>where the button's own column sits in the grid

Command actions, action header ​

@env <key>=<value>an environment variable for the command; the only place {password} may go
@open-on-success <path>open this file when the command succeeds
@open-on-failure <path>open this file when the command fails
@open-on-done <path>open this file when the command finishes, if it exists
@alert-on-success <text> or "<title>", "<text>"show this in a dialog when the command succeeds
@alert-on-failure <text> or "<title>", "<text>"show this in a dialog when the command fails
@alert-on-done <text> or "<title>", "<text>"show this in a dialog when the command finishes

Values from outside the file ​

$column (row references, button commands only): the clicked row's value of that column. Always a bound parameter, never text in the SQL. In @confirm text it fills in as a message.

{name} (placeholders): values the app holds before the run.

Connection{host} {port} {db} {user} {sslmode} {conn} {server} {password}
Clock{year} {month} {day} {hour} {minute} {second} {date} {time} {timestamp}
Declaredwhatever @file / @dir / @var opened, in this file alone

Expansion by context: a bound parameter in SQL, ONE argv element in a command (nothing to quote, ever), plain text in messages and paths. An unknown {name} is diagnosed and left standing.

The secret: {password} is legal in a command action's @env value and nowhere else. Both parsers refuse it elsewhere, and the server refuses it again at the point of use; it never reaches the page, argv, SQL, a path or a message.

Rules that connect annotations ​

  • A second command without @button/@open does not run (diagnosed).
  • One header, one button: repeats and @button+@open together are diagnosed.
  • @on and @at exclude each other; @on wins. One button per column, one column per button. @on a hidden column is diagnosed at parse time; @on a missing column falls back to an own column.
  • @refresh never re-asks @confirm (the gesture already answered) and refreshes the step on screen, not the root.
  • In each hook family, -on-done cannot stand with -on-success or -on-failure; the specific ones win. At most one member of each family fires per exit.
  • A command using connection placeholders needs a connection; one using none must not demand one.

Examples ​

sql
-- @description Long-running queries
-- @refresh 5s
-- @hide pid
select pid, now() - query_start as runtime, query
from pg_catalog.pg_stat_activity
where state <> 'idle' and pid <> pg_backend_pid();

-- @button Cancel
-- @confirm "Cancel it?", "Cancel the query on backend $pid?"
select pg_catalog.pg_cancel_backend($pid);
# @description Back up this database, custom format
# @env PGPASSWORD={password}
# @alert-on-success Wrote {db}-{timestamp}.dump
pg_dump --host={host} --port={port} --username={user} --no-password \
        --format=custom --dbname={db} --file={db}-{timestamp}.dump

PlumeSQL is a hercstack product.