Lua reference

Every function a Lua command can call in AutoDoc: the autodoc table, the editor object ed, and the daemon's verbs. Lua in AutoDoc says where scripts live and how they load; the tutorial uses all of this step by step.

The language is Lua 5.1 (gopher-lua), with the base, table, string and math libraries (the sandbox).

Positions

The autodoc table

Function Does Returns
autodoc.command{name, aliases, args, help, run, complete, id, title} Registers a : command, an entry of AutoDoc's command catalog (what it takes). Runs as the file loads. nothing
autodoc.run(id, …) Runs a command of the catalog by its id, AutoDoc's or yours, with the rest as its arguments (below). true, or false and why
autodoc.notify(message [, opts]) Shows a notification: a toast in the corner, kept in the history (SPC h). opts is {id = "sync", ongoing = true}: with ongoing, a toast that stays and is updated in place by each notify with that id; without, that toast ended (the history keeps its last message); the id posted ongoing again starts again. An id is at most 64 letters, digits, ., _ or -, ongoing needs one, and a message is at most 1024 bytes: a bad option raises an error. Every script shares one set of ids. nothing
autodoc.call(verb, params, cb) Calls any daemon verb by its name, with params a list of its params in order (nil or {} for none). cb is optional. nothing; the answer goes to cb
autodoc.current_workspace() The workspace in use. a string, or nil before one is entered
autodoc.document() The page in the editor, the buffer shown (below). a table
autodoc.buffers() Every open buffer, in opening order: {n, path, workspace, kind, modified, active} (buffers). a list of tables
autodoc.buffer(n) An editor object for buffer n, shown or not: ed's methods (buffers). the object, or nil and why
autodoc.widgets() The places the keyboard can go, in the order :go and F6 know them: the editor, every panel, each tab of the terminal and the agent panels; never the menu bar. autodoc.run("widget.go", name) goes to one, opening a closed panel (a card plugin's too). a list of {name, title, open, focused}
autodoc.map{mode, keys, command, title} Binds a key to a command line (keys and menus). nothing
autodoc.menu{where, title, command} Adds a menu row that runs a command line (keys and menus). nothing
autodoc.<domain>.<verb>(…, cb) Calls the daemon's <domain>.<verb>: its params in order, then cb. Made when AutoDoc connects (the daemon's verbs). nothing; the answer goes to cb

The engine's own functions, beside command and notify:

<!-- autodoc:lua-globals:start --> <!-- generated from the script package's own tables by go generate ./tui. Do not edit. -->
Function Returns Notes
autodoc.now() number The time, in seconds since 1970-01-01 UTC, with a fraction: os.time()'s, to the millisecond.
autodoc.date(format, t) string, or a table The time t (seconds, now() by default) written by format, as os.date does: %Y-%m-%d, %H:%M, …; %c by default. A format starting with ! is in UTC, else local time; "*t" gives a table: year, month, day, hour, min, sec, wday, yday, isdst.
autodoc.commands() list of tables Every command, in the order they were added: {id, name, aliases, args, help, title, source}. name is its : name, "" for one only a key or its id runs; source says whose it is ("lua", "plugin", "editor", …). A command that takes another's : name finds that one here, under its id and with no name, and runs it with autodoc.run.
autodoc.on(event, fn) nothing Calls fn(ed, info) each time event happens, from now until the scripts reload. The host says which events there are (events). A handler runs as a callback does, and its error is a notification.
<!-- autodoc:lua-globals:end -->
-- stamp.lua: :stamp inserts today's date, and :stamp! the time too
autodoc.command{
  name = "stamp",
  help = "insert the date (with !, the time too)",
  run = function(ed, args, line, cmd)
    ed:insert(autodoc.date(cmd.bang and "%Y-%m-%d %H:%M" or "%Y-%m-%d"))
  end,
}

autodoc.document()

The page in the editor, the buffer shown, as a table. It can be called anywhere, a file's top level too.

Field What it is
buffer the buffer's number, as :b and autodoc.buffers() give it
path its path in its workspace; nil for the untitled draft
workspace the workspace it is in; nil for the draft, or a file outside every workspace
kind how AutoDoc reads it: "markdown", "text", "yaml", "derived" or "registered" (source code)
derived a derived document's format, "PDF" or "DOCX": its text is read-only
frontmatter a Markdown page's frontmatter, as Lua values, read from the editor's text now: {title = "Plan", tags = {"a", "b"}}; nil without one
frontmatter_error why the frontmatter could not be read, when it is not YAML
-- tagged.lua: :tagged says the page's tags, from its frontmatter
autodoc.command{
  name = "tagged",
  help = "say the page's tags",
  run = function(ed)
    local doc = autodoc.document()
    local tags = doc.frontmatter and doc.frontmatter.tags
    if type(tags) ~= "table" or #tags == 0 then
      error((doc.path or "the draft") .. " has no tags", 0)
    end
    autodoc.notify((doc.path or "the draft") .. ": " .. table.concat(tags, ", "))
  end,
}

autodoc.command{…}

Field Type Required What it is
name string yes What you type after :. Letters, digits, - and _, not all digits.
run function yes run(ed, args, line, cmd): what the command does.
aliases list of strings no Other names for it, under the same rules as name.
args string no Its arguments, as Tab shows them: "<term>", "[depth]". A hint only: nothing checks it.
complete list of strings, or function no What Tab offers for an argument (below).
help string no What it does, in one line, as Tab shows it.
id string no Its id in the command catalog, which autodoc.run and F1 name it by: lua.<something>. lua.<name> when left out.
title string no What F1's COMMANDS calls it. Its help, or its name, when left out.

run gets:

Argument What it is
ed the editor object, below
args the words after the command, a list of strings: :hi Ada Lovelace gives {"Ada", "Lovelace"}
line the rest of the line, trimmed at its ends only: "Ada Lovelace"; "" with none
cmd how it was called: cmd.name, the name as typed (an alias, say), and cmd.bang, true for :hi!
-- shape.lua: :shape shows every field autodoc.command takes
autodoc.command{
  name = "shape",            -- letters, digits, - and _; not all digits
  aliases = {"sh"},          -- other names
  args = "[words]",          -- what Tab shows after the name
  help = "show what run gets", -- what Tab shows after that
  run = function(ed, args, line, cmd)
    autodoc.notify("shape: " .. #args .. " args, line '" .. line .. "' (as :" .. cmd.name
      .. (cmd.bang and "!" or "") .. ")")
  end,
}

Errors. autodoc.command raises, and the file fails to load, for:

What a command takes

The bang. :hello! runs hello with cmd.bang true; :hello with false. Run by a key or by autodoc.run, cmd.bang is false and cmd.name is the command's name.

One undo. Everything run edits is one undo step, ed:run's commands included.

Completing arguments

After the name and a space, Tab completes the word being typed from complete:

-- heading.lua: :heading <name> goes to a heading of the page; Tab offers them
local function headings(ed)
  local out = {}
  for n, text in ipairs(ed:lines()) do
    local title = text:match("^#+%s+(.-)%s*$")
    if title then out[#out + 1] = {name = (title:gsub("%s+", "-")), line = n} end
  end
  return out
end

autodoc.command{
  name = "heading",
  args = "<name>",
  help = "go to a heading of the page",
  complete = function(ed, word)
    local names = {}
    for _, h in ipairs(headings(ed)) do names[#names + 1] = h.name end
    return names
  end,
  run = function(ed, args)
    for _, h in ipairs(headings(ed)) do
      if h.name == args[1] then
        ed:set_cursor(h.line, 1)
        return
      end
    end
    error("heading: no " .. (args[1] or "name") .. " on the page", 0)
  end,
}

A heading's spaces become -, so each name is one word.

autodoc.run(id, …)

Runs a command of AutoDoc's command catalog by its id: AutoDoc's own (todos.open, file.save, terminal.toggle), a feature's, or a Lua command (lua.<name>). F1's COMMANDS lists every id, with its : names and keys. The arguments after the id are the command's words, as if typed after its name: strings, numbers or booleans.

-- again.lua: :again <id> [args] runs a command of the catalog by its id, and says how it went
autodoc.command{
  name = "again",
  args = "<id> [args]",
  help = "run a command by its id",
  run = function(ed, args)
    local ok, err = autodoc.run(unpack(args))
    if ok then
      autodoc.notify("again: ran " .. args[1])
    else
      autodoc.notify("again: " .. err)
    end
  end,
}

The editor object, ed

ed reaches the editor inside a command's run and inside a daemon callback. Elsewhere (at a file's top level, as it loads) its methods raise the editor is only reachable inside a command's run.

<!-- autodoc:lua-ed:start --> <!-- generated from the script package's own tables by go generate ./tui. Do not edit. -->
Method Returns Notes
ed:value() string The whole text, lines joined by "\n".
ed:set_value(text) nothing Replaces the whole text, as one edit: u brings the old text back.
ed:line_count() number How many lines. A text ending in "\n" has an empty last line.
ed:line(n) string Line n's text, without its line break. Raises no such line for n outside 1 to ed:line_count().
ed:lines() list of strings Every line, a copy.
ed:cursor() line, col Where the cursor is.
ed:set_cursor(line, col) nothing Moves the cursor, clamped, and scrolls to it.
ed:insert(text) line, col Inserts at the cursor, and returns the position just after the text. That position is exact even when Vim's Normal mode then rests the cursor on the last character (see Positions). "\n", "\r\n" and "\r" start lines. In Vim's Normal mode the text goes before the character under the cursor.
ed:insert_at(line, col, text) line, col ed:insert at a position.
ed:replace(l1, c1, l2, c2, text) line, col Puts text in place of the range; returns the position just after it.
ed:delete(l1, c1, l2, c2) nothing Removes the range. The cursor goes to its start.
ed:text(l1, c1, l2, c2) string The range's text, line breaks as "\n".
ed:selection() l1, c1, l2, c2, or nil The selection's range. A line-wise selection (V) runs from column 1 of its first line to the end of its last. nil outside Visual mode.
ed:select(l1, c1, l2, c2) nothing Selects the range, as Visual mode does, with the cursor on its last character: ed:mode() is then "visual", whatever the keys. An empty range leaves the selection as it was.
ed:unselect() nothing Ends the selection; the cursor stays where it is. Nothing without one.
ed:find(term, opts) l1, c1, l2, c2, or nil The next match of term after the cursor. It does not move the cursor. opts is optional: {ignore_case = true, whole_word = true, backward = true, wrap = true, at_cursor = true}.
ed:find_all(term, opts) list of {l1, c1, l2, c2} Every match of term, in order: {} with none. opts as ed:find's; only ignore_case and whole_word apply.
ed:replace_all(term, text, opts) number Puts text in place of every match of term, as one edit, and returns how many. opts as ed:find_all's.
ed:mode() string The editor's mode: "normal", "insert", "replace", "visual", "visual_line" or "visual_block". Without Vim's keys it is "insert", or "visual" with a selection.
ed:read_only() boolean Whether edits are refused: a PDF's or DOCX's derived text, say.
ed:revision() number Names the text as it is now: every edit and every new document gives a new one, and an undo gives back the one the text had. A callback compares the revision its command read with ed:revision() to know whether the page changed while it waited.
ed:modified() boolean Whether the text has changed since it was opened or last saved, as :x asks before it writes.
ed:run(line) nothing Runs a command line as the prompt does, without the :: "12", "find TODO", "table 2 3". Raises the command's error.
<!-- autodoc:lua-ed:end -->

Edits. ed:set_value, ed:insert, ed:insert_at, ed:replace, ed:delete and ed:replace_all edit.

ed:find.

ed:run. ed:run("x") raises widget: not an editor command: x for a command that does not exist. It runs AutoDoc's commands and the editor's alike, and each does what it does from the prompt. "find …" is AutoDoc's find in the page: its matches are marked, n and N step through them, and its jump to the first match comes just after your command ends.

Arguments. The method's own errors count ed as argument 1, so a method's first argument is #2: ed:line("3") raises bad argument #2 to line (number expected, got string). A position is a number; args are strings, so convert them with tonumber. A fraction is cut to a whole number. A text argument may be a string or a number.

Buffers

Every open file, and the untitled draft, is a buffer; the editor shows one. :b <n|name> and the Buffers picker switch, :bd closes the one shown.

-- todos.lua: :todos counts TODO in every open buffer
autodoc.command{
  name = "todos",
  help = "count TODO in every open buffer",
  run = function()
    for _, b in ipairs(autodoc.buffers()) do
      local n = #autodoc.buffer(b.n):find_all("TODO", {})
      autodoc.notify(b.n .. " " .. (b.path or "untitled") .. ": " .. n)
    end
  end,
}

Events

autodoc.on(event, fn) calls fn(ed, info) each time event happens:

Event When info
open a file was loaded into a buffer (shown or not), or a buffer became the draft {buffer, path, workspace}; path is nil for the draft
enter a buffer is shown in the editor: switched to, or opened {buffer, path, workspace}
leave the buffer shown is about to be replaced by another {buffer, path, workspace}
close a buffer is closed (after the unsaved question, if any) {buffer, path, workspace}
save a save wrote a buffer's text {buffer, path, workspace}
mode the editor's mode changed {mode, previous}, as ed:mode() names them
change a buffer's text changed, once typing pauses (300 ms): a burst of keys is one change, per buffer {buffer, revision}, as ed:revision()
-- lastsaved.lua: :lastsaved <path> says when a page was last saved in this run
local saved = {}
autodoc.on("save", function(ed, info)
  saved[info.path] = autodoc.date("%H:%M:%S")
end)

autodoc.command{
  name = "lastsaved",
  args = "<path>",
  help = "say when a page was last saved",
  run = function(ed, args)
    local p = args[1] or (autodoc.document().path or "")
    autodoc.notify("lastsaved: " .. p .. " " .. (saved[p] or "not saved yet"))
  end,
}

Keys and menus

autodoc.map binds a key, and autodoc.menu adds a menu row. Each runs its command line as : does: a command's name and its arguments, "table 2 3". Its error is a notification.

autodoc.map field What it is
mode "normal" or "visual": a key of the page in Vim's Normal or Visual mode; "leader": a letter of SPC u, your Lua keys' card
keys Normal and Visual: one character, "Q", or a control chord, "<C-k>". Leader: a letter or digit, not q (it closes the card)
command the command line it runs
title how SPC u's card names a leader key; the command line when left out
autodoc.menu field What it is
where "editor", the page's right-click menu, or "plugins", the Plugins menu
command the command line it runs
title its text, & before the mnemonic letter: "&Word count". In the editor's menu, also a function title(ctx), answered each time the menu opens: "Execute on " .. conn
menu a submenu to show it under: rows with the same menu share one, a plugin's rows included (they sit under the plugin's title)
files editor rows: globs over the open file's base name, {"*.sql"}; the row shows only for a match
when editor rows: a function when(ctx), asked as the menu opens; the row shows only when it returns true
id a name for the row: the same id from the same script again replaces the row in its place
-- keys.lua: :stamp on Q, on SPC u s, and in the right-click menu
autodoc.command{
  name = "stamp",
  help = "insert the date",
  run = function(ed) ed:insert(autodoc.date("%Y-%m-%d")) end,
}
autodoc.map{ mode = "normal", keys = "Q", command = "stamp" }
autodoc.map{ mode = "leader", keys = "s", command = "stamp", title = "today's date" }
autodoc.menu{ where = "editor", title = "Insert the &date", command = "stamp" }

-- under a "Notes" submenu, only in notes/ and only with a selection
local row = autodoc.menu{
  where = "editor", menu = "Notes", id = "quote",
  when = function(ctx) return ctx.selection and ctx.path:find("^notes/") ~= nil end,
  title = function(ctx) return "&Quote in " .. ctx.workspace end,
  command = "stamp",
}
-- row:remove() takes it out again

Characters: autodoc.text

Lua's #s and string.sub count bytes; the editor's columns count characters (grapheme clusters). autodoc.text counts as the editor does, so a column worked out from a string is right for ed:set_cursor and the ranges, whatever the script the text is in.

<!-- autodoc:lua-text:start --> <!-- generated from the script package's own tables by go generate ./tui. Do not edit. -->
Function Returns Notes
autodoc.text.columns(s) number How many characters (grapheme clusters) s has: the columns it takes in the editor. #s counts bytes.
autodoc.text.sub(s, i, j) string Characters i to j of s, both included, as string.sub takes bytes: j is the last by default, and a negative position counts from the end (-1 the last).
autodoc.text.chars(s) list of strings s's characters, in order: é written as e and an accent is one.
<!-- autodoc:lua-text:end -->
-- tail.lua: :tail <n> selects the last n characters of the cursor's line
autodoc.command{
  name = "tail",
  args = "<n>",
  help = "select the end of the line",
  run = function(ed, args)
    local l = ed:cursor()
    local cols = autodoc.text.columns(ed:line(l))
    local n = math.min(tonumber(args[1]) or 1, cols)
    if n < 1 then error("tail: an empty line", 0) end
    ed:select(l, cols - n + 1, l, cols + 1)
  end,
}

Callbacks

A daemon call never waits. autodoc.call and the autodoc.<domain>.<verb> functions return at once; the daemon's answer goes to the callback later, as cb(result, err):

Params.

Values cross as msgpack carries them:

Lua The daemon
nil, a boolean, a string nil, a boolean, a string
a whole number / any other number an integer / a float
a table with keys 1 to n a list
any other table a map, its keys as strings
{} an empty map, or an empty list where the verb's param is a list
a function, ed cannot cross: the call raises cannot cross between Lua and Go

Answers come back the same way: a list is a table with keys 1 to n, a map a table with string keys.

-- peek.lua: :peek <path> shows a document's first line, read through autodoc.call
autodoc.command{
  name = "peek",
  args = "<path>",
  help = "show the first line of a document of the workspace",
  run = function(ed, args)
    local workspace = autodoc.current_workspace()
    if not workspace or not args[1] then error("peek <path>, in a workspace", 0) end
    autodoc.call("doc.read", {workspace, args[1]}, function(res, err)
      if err then
        autodoc.notify("peek: " .. err)
        return
      end
      autodoc.notify("peek: " .. (res.content:match("[^\n]*")))
    end)
  end,
}

The program's own functions

Beside the daemon's verbs, AutoDoc gives scripts its clipboard, a picker, a card, its agents and what the app itself uses. They are there from the start, connected or not. As the daemon's do, each cb runs later on the loop as cb(result, err), never inside the call: a refusal known at once is a callback on the next turn.

Domain Functions
agents list, send, resume
app stats
clipboard read, write
plugin run
ui card, charts, pick, width
Call Answers
autodoc.clipboard.write(text) ok, err at once: the system clipboard set (a terminal's, by OSC 52)
autodoc.clipboard.read(cb) cb(text, err): "" for an empty clipboard or one holding no text; err "unsupported" where AutoDoc cannot read it (in a terminal, its own paste is the way in), "timeout", "closed"
autodoc.ui.pick({title, items}, cb) a picker over items, each a string or {label, detail, value} (detail is previewed, value given to cb, the item itself without one): cb(value); err "cancelled" (Esc), or "busy" while another script's picker is open
autodoc.ui.card{title, text, every, tick} at once, the card's handle: the card panel (at the right) shows text, Markdown, drawn as it reads in the window's Rendered view, where a ```vega-lite fence is a chart; a terminal shows the Markdown. tick(card) runs every every ms (250 at least; none without every) while the panel is open, its first on the next turn. One card at a time: a new one replaces the one shown. :lua-reload ends it and closes the panel
card:set(text) the panel shows text now; a replaced or ended card's does nothing
card:close() the panel closes and the card ends: its tick stops, and set and close do nothing after
card:open() true while this card is the panel's and the panel is open
autodoc.ui.charts() at once: true where the window draws a vega-lite fence as a chart (--gui), false in a terminal
autodoc.ui.width(s) at once: the terminal columns s takes, as AutoDoc measures its own tables (a wide character two, a combining mark none), to pad text so it lines up
autodoc.app.stats() at once: the app's own process, in the keys sys.stats answers for the daemon's: {pid, version, started_at, uptime, rss, peak_rss, cpu_seconds, go_memory, go_heap, goroutines, cpus}. Sizes are bytes (rss 0 where the system cannot say: macOS), cpu_seconds the CPU used so far: a rate is two readings apart
autodoc.agents.list() at once: {{codename, name, running, closing, instance, orphan}, …}, the saved profiles first; a closing one refuses a send
autodoc.plugin.run(id, args…) at once, ok, err: from a plugin's own Lua package only (its lua/, loaded as plugin:<name>, docs/plugins.md), one of its plugin's [[commands]] run with args (strings), the plugin started when it is not running. err "autodoc.plugin.run: not a plugin's package" for any other script, "unknown: <id>" for an id its manifest does not declare; arguments need the plugin's protocol = 7
autodoc.agents.resume([agent]) at once, ok, err: a SIGCONT to the agent's tab, agent as send's, none for the agents panel's tab now. An agent CLI stopped by a suspend runs again; a running one isn't disturbed. err "stale", "unavailable", or why
autodoc.agents.send(agent, text, cb) text pasted into the agent's prompt, no Enter added after it: agent a codename, or {codename, instance} for that exact run. A stopped agent is started once the user agrees. cb(true) once it is typed; err "cancelled", "stale" (that run is gone), "unavailable", "unbracketed", or why

The text goes as one bracketed paste, or not at all. An agent CLI that asked for bracketed paste gets it whole, held at its prompt for Enter. A program that didn't gets nothing, and err is "unbracketed": text you didn't type never arrives as keys, whose newlines would be Enters. An agent the send started is given up to 10 seconds to ask for it. AutoDoc's own usage.lua is :usage: a card of the daemon's and the app's memory and CPU and the workspace's index, ticking each second, with charts in the window. Its agents.lua uses the others: :agent-buffer sends the page, and :agent-forward the selection, else the clipboard, to the agent running (with several, or none, a picker asks which; one not running is started once you agree).

The daemon's verbs

Each verb the daemon offers scripts is a function autodoc.<domain>.<verb>. They are made from the daemon's own list (sys.contracts) each time AutoDoc connects, so they are this daemon's verbs; until it has connected, autodoc.<domain> is nil. autodoc --contracts prints each verb's params and result, and autodoc --contracts 'todos.*' --format md one domain's, as tables. The codes an err carries are listed at the end of --contracts.

Domain Verbs
doc outline, read, read_raw, remove, rename, validate, write, write_raw
embedding add, cancel_switch, catalog, estimate, log, model_context, models, providers, pull, pull_cancel, pull_status, remove, update, usage, use
file locate, read, read_raw, write, write_raw
host probe
graph backlinks, links, map, neighborhood, resolve, unresolved
index changes, documents, list, models, purge_model, reindex, status
preference list, set
ranker add, list, log, models, remove, update, usage, use, window
search query
sys capabilities, contracts, events, stats
todos ack, add, assign, changes, claim, get, list, migrate_refs, move_store, open, refresh, register, release, remove, remove_malformed, resolve, scaffold, set_dir, set_kb, set_var, status, stores, update, vars
workspace add, configure, embedding_policy, focus, list, of, remove, rename, scaffold, section_size, set_patterns, set_provider, set_schema, set_text_extensions, set_var, vars

The ones a command reaches for most:

Call Answers
autodoc.search.query(workspace, query, opts?, cb) {hits = {{path, breadcrumb, snippet, line_start, …}, …}, …}; opts such as {limit = 5}
autodoc.doc.read(workspace, path, cb) {content, version}
autodoc.doc.outline(workspace, path, cb) {version, headings = {{level, text, line, …}, …}}
autodoc.graph.backlinks(workspace, path, options?, cb) {{path, raw, kind, …}, …}: the documents that link to it
autodoc.workspace.list(cb) {{name, root, state, …}, …}
autodoc.todos.add(store, task, cb) the task filed, with its id (Help › Tutorial…, page 11)

Packages and capabilities

A package (lua/<name>/ with a package.toml, Lua in AutoDoc) runs gated: each kind of call below needs its capability, granted by you once. Without it, the call answers nil and needs <capability>: not granted, and does nothing.

Capability Lets a package
workspace.read call the daemon's reading verbs: autodoc.search, autodoc.doc.read, autodoc.graph, …
workspace.write call its writing verbs: write, rename and remove documents and tasks
settings change AutoDoc's settings: workspaces, models, rankers, preferences
clipboard autodoc.clipboard.read and .write
agents autodoc.agents.list, .send and .resume
commands run commands: autodoc.run, ed:run, and the keys and menu rows it binds (autodoc.map, autodoc.menu: they register either way, and say they need it when pressed)

The sandbox

There Not there
string, table, math os, io, coroutine, debug, package
assert, error, pcall, xpcall, select, type, tostring, tonumber require, module
pairs, ipairs, next, unpack, rawget, rawset, rawequal load, loadstring, dofile, loadfile
setmetatable, getmetatable, collectgarbage getfenv, setfenv, newproxy, _printregs
print: a notification, its values joined by tabs, as autodoc.notify os.time, os.date: autodoc.now and autodoc.date instead
_G, _VERSION

The deadline. A command's run, a callback and a file's loading each have 2 seconds. Past them the script is stopped: <command>: the script ran past 2s and was stopped.

Errors. error("text") puts the file and line first (wc.lua:7: text); error("text", 0) does not. A command's error shows in the prompt as <command>: <error>, and a file's that failed to load in a notification as <file>: <error>, neither with Lua's stack trace.