Commit Graph
288 Commits
Author SHA1 Message Date
james.maesandClaude Fable 5.1 79ba68eea3 fix(tui): record view settings take typed values
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / gradle-0 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-1 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-2 (push) Skipped
QRun-IO/ci-policy: ci / node-3 (push) Skipped
QRun-IO/ci-policy: ci / source-config-5 (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: Shared Node CI / verify (push) Successful in 3m48s
QRun-IO/ci-policy: Shared Node CI / secrets (push) Successful in 29s
QRun-IO/ci-policy: Shared Node CI / gate (push) Successful in 10s
QRun-IO/ci-policy: ci / node-4 (push) Successful in 24m15s
QRun-IO/ci-policy: ci / ci-result (push) Successful in 4s
The record view's v dialog was a flat filterable list of 15 pre-baked
choices (label width +-4, five child row counts, the collaboration panel
listed three times). It is now a settings form like the grid's View
settings: Columns cycles auto/1/2, Label width (8-60) and Child list rows
(1-50) take typed digits or +/- with Backspace back to the default, one
Collaboration panel row, and Reset to defaults with a confirm. Persistence
keys are unchanged, so saved settings still load.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cli-v1.0.0 v1.8.0
2026-11-01 12:42:11 -06:00
james.maesandClaude Fable 5.1 c670509b09 chore: drop an accidentally tracked empty log file and ignore log/
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:24:31 -06:00
james.maesandClaude Fable 5.1 969f3c139a docs: add the assistant to the 1.0.0 entry, release notes, README and TRY-IT
CLI qqq assistant ask/config, the TUI Assistant panel (Ctrl+G, :ask), the kernel Assistant module, the
base64 link filter fix and the enterprise server's ANTHROPIC_API_KEY handling are folded into the
structured entry with commit ids; the release notes get an Assistant section saying what the server needs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:24:09 -06:00
james.maesandClaude Fable 5.1 e5327b245d fix(links): read base64 filter parameters, as the web UI and the assistant write them
The assistant's links (and the web UI's) carry `filter` as base64 JSON,
which the TUI's link reader and the kernel's parseAppHref dropped, so a
filtered link opened the whole table. Both now decode it (JSON still
works), and a filter without criteria counts as an empty one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:18:19 -06:00
james.maesandClaude Fable 5.1 29f35595d1 feat(scripts): enterprise server passes ANTHROPIC_API_KEY and reports the assistant
The enterprise profile now picks Claude when ANTHROPIC_API_KEY is set,
else a local Ollama. The key is read by name from the private assistant
env file (~/.local/state/qqq-enterprise/assistant.env: owned by you, not a
symlink, mode 0600; never sourced, never printed) and exported only into
the server's environment, not onto a command line. --status gains an
assistant line (and --assistant-status prints just that): the model that
answers, or the reason and what to configure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:16:12 -06:00
james.maesandClaude Fable 5.1 2c70c3c2d4 feat(tui): Assistant panel (Ctrl+G, :ask, F9 > Work > Assistant)
A chat pane on the right (the whole body below 110 columns): question
line at the bottom, the conversation above with each answer's numbers and
links; Tab and Enter open a link as a record or grid through the existing
server-link checks. Questions carry the record or grid under the panel as
context like the Next UI (Ctrl+D drops it), history is kept per profile.
Ctrl+A was not taken because the grid uses it for its selection menu.
Without the extension the entry is disabled and Ctrl+G says what is
missing; when the assistant is off the panel shows the server's reason.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:16:12 -06:00
james.maesandClaude Fable 5.1 a5e0cf4268 feat(assistant): qqq assistant ask and config, on a kernel Assistant module
The assistant qbit was reachable only through its generated operation
subcommands, which cannot read a streamed turn. The kernel now has an
Assistant module (feature probe, ask with the page context the way the
Next UI panel sends it, an incremental SSE parser and an answer fold with
numbers and links), and the CLI builds two first-class verbs on it:
`assistant ask` streams the answer and prints its numbers and links with
the command that opens each; `assistant config` says whether it answers
and with which model, or the server's reason (NOT_CONFIGURED names what
to configure).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:16:01 -06:00
james.maesandClaude Fable 5.1 981c0ca858 fix(core): linear-time history redaction; activity log reads on Windows
CodeQL flagged js/polynomial-redos (high) in the activity log's
redaction. Two regexes could backtrack polynomially on long arguments:
the header pattern (`Name:\s*(.+)`, slow on "A:" plus many spaces) and
the filter-criterion pattern (slow on long runs of name characters).
Redaction now uses linear scans:
- flags are parsed with indexOf at the first `=`
- headers and name=value pairs check the name before the first `:` or
  `=`
- filter text goes through a one-pass tokenizer (spaces, names, symbol
  operators, quoted strings, other runs), and the value after
  `<secret name> <operator>` is replaced
The remaining regexes each have a single, unambiguous quantifier.

Behaviour is unchanged on every existing redaction test. A differential
fuzz against the old code (300k random inputs, not committed) shows
three kinds of difference, none of which exposes a secret:
- a value now stops at a `)` that closes a group
- a quoted value is hidden whole
- a header whose value is only whitespace is left as it is
The rest of the differences are malformed mixed quotes.
New tests run 18 adversarial 100k-character inputs, each well under
500 ms.

The Windows lane failed "events are appended one JSON line each". The
test asserted POSIX mode 0600, and Windows has no mode bits. That check
now runs only off Windows. The reader splits lines on /\r?\n/, and a
new test reads back a CRLF file. The offset test now checks the parsed
time and the offset that getTimezoneOffset gives for that instant,
whatever the zone's name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:11:47 -06:00
james.maesandClaude Fable 5.1 981e442e29 docs: fold history, saver, export names, widgets and collaboration into the 1.0.0 entry
The CHANGELOG bullets added by accretion since bedbf69 are merged into the structured entry (CLI history
and table stats, TUI history screen, one saver bullet instead of three fixes, Search coverage hint,
collaboration at the bottom, popups and widgets) with commit ids. Release notes, README and TRY-IT mention
history, stamped export names and the new keys.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:05:12 -06:00
james.maesandClaude Fable 5.1 3e80043bd9 fix(tui): collaboration lives at the bottom of the record view
The record view opened on three collaboration rows (tags, the counts line,
"Viewing now") between the title and the record's sections, and the cursor
landed on the tags row. The owner wants collaboration to sit with the
controls at the bottom and take no space at the top.

The record now starts right under its title, the cursor on its first field.
A dim strip directly above the key line carries tags, comment/file/task
counts, timeline, follow state and who is viewing; others viewing or editing
replace "(you)" (with their state, from presence's mode and field) and give
the strip an accent. Segments drop whole when the width is short, the
viewers and tags last. From 140 terminal columns a 36-column Collaboration
panel on the right shows tags, the latest two comments, open tasks with the
next due date, files, followers and each viewer's state, and the strip
shortens to the keys; a record view setting (collaboration panel
auto/on/off) controls it. The keys are unchanged; strip segments and panel
rows are click zones that press them. The summary now also fetches the
followers and keeps the latest comments and next due date for the panel.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:00:17 -06:00
james.maesandClaude Fable 5.1 98d2076d14 fix(tui): keep each saver group compact and the header and lock line beside it
Big numbers and Flip clock are one group at most about 90 columns wide
(tiles in one or two adjacent columns of at most 43 cells, the caption
capped), however wide the terminal; the guard drifts it a tenth of the
width at most. The clock line and the lock line now sit just above and
below the group (swapping every 30 s, a new offset each time) instead of
at the opposite screen edges. Tests cover six sizes including 200x50.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:57:00 -06:00
james.maesandClaude Fable 5.1 aff2b00529 fix(tui): lay the screen saver styles out as one group each
On a tall narrow terminal Big numbers squashed the number and Query into
a corner and left its only tile floating alone at the far left. Now the
metric name, number, Query (beside it, or below on a narrow screen),
caption, dots and the other tiles are one centred stack, and the guard
moves that stack as a unit between spots near the centre, always fully on
screen. A single tile sits centred under the number. Flip clock keeps its
tiles and Query with the clock as one group; Bounce stacks the tile panel
under Query when the terminal is too narrow for side by side. The drifting
header and lock line keep a one-cell left margin.

Layout tests cover five sizes (75x95, 200x30, 80x24, 120x40, 160x50) and
check every style stays within 40% of the group's centre.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:57:00 -06:00
james.maesandClaude Fable 5.1 7d754bfbbf test(core): de-flake the PIN storage test
The test asserted that the digits "2468" never appear in config.json,
but the stored salt and scrypt hash are random hex and can contain that
substring by chance, so the test failed now and then. It now checks the
stored values: the lock holds only pinSalt (32 hex) and pinHash (64 hex),
neither is the PIN, and no JSON value equals the PIN.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:51:35 -06:00
james.maesandClaude Fable 5.1 0bcc735ee9 feat: history of everything done in the CLI and the TUI
The owner asked for a "history" command that shows everything they have
done, like a bash history. Both heads now write to one activity log, an
append-only JSONL file per profile in the state directory
(~/.local/state/qqq/activity/<profile>.jsonl). Each event records the
time with the local offset, profile, server, client, kind, a one-line
summary and the command that does it again. The file is rotated once to
.1 at 5 MB. Secrets are written as ***: secret flags, name=value pairs,
filter criteria on secret or PASSWORD fields, Authorization headers and
bearer tokens. QQQ_HISTORY=off turns the log off.

- core: session/activity-log.ts (append, rotate, read, filter, redact,
  CLI kinds, mutating-command check).
- cli: every invocation is recorded on exit with its exit code and
  duration (shell completion is skipped). `qqq history` lists entries
  with --limit, --since, --grep, --kind, --all-profiles, --absolute,
  --commands and --format. `history rerun <n>` asks first for commands
  that change data and records the command it ran. `history clear`
  asks first.
- tui: one history.record(...) call at each site: `:` lines, opened
  apps, tables, records and processes, queries with filters, record
  create, edit and delete (no values), process outcomes, exports,
  prints, server switches, sign-in and sign-out. The History screen
  (`:history`, F9 > Work > History) filters as you type, Enter does an
  entry again, y copies and c or F8 clears. Ctrl+R on the `:` line is
  now a bash-style reverse search over the log.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:50:58 -06:00
james.maesandClaude Fable 5.1 7c66b16b6f fix(cli): table stats computes from matching records when the server offers none; quiet exit on a closed pipe
table stats refused tables without QUERY_STATS and pointed at --local. It now falls back
to the matching records itself (also when the columnStats process answers 403 or 404),
using the kernel computeLoadedColumnStats, says where the numbers come from, and exits 0.
--local forces it, --no-local keeps the old error, --max-records (default 10,000) caps it.

A reader that leaves early (qqq table query person | head -1) crashed the CLI with an
unhandled write EPIPE. stdout and stderr errors with EPIPE now exit 0 quietly, and a
stream pipeline rejected with EPIPE is not reported, so exports, process logs and
assistant ask behave the same.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:50:31 -06:00
james.maesandClaude Fable 5.1 70b6fa1908 fix(tui): completion popups readable: the width they need, two columns, wrapped descriptions
The owner on the `:` popup: "this needs to be easier to read, text cut
off?" On a 160-column terminal the popup was about 50 columns wide, the
descriptions were right-aligned and cut with an ellipsis, and nothing
separated the name from the description.

The popup now takes the width its matches need, at least 60 and at most
120 columns, never past the screen's inner width. Rows are two
left-aligned columns, `name  ·  description`; the name column fits the
longest name and gives way (down to 40%) only to descriptions that need the
room. A description that still does not fit wraps onto a second line under
the description column instead of being cut. The selected row is
highlighted across the whole popup. A dim header line carries the column
titles (command/argument/name and description or type/columns) and the
match count (on the border when the titles fill the line). The window
counts lines, so two-line rows keep the cursor visible, and the `:` list
stays above the command line with its height capped so the line and its
key help stay visible. The SQL editor popup gets the same layout (name ·
type / columns), below the cursor line or above it when there is more room
there.

Tests: the layout (width bounds, name column, wrapping, line-counting
window) and the `:` line end to end (header, separator, a wrapped
description).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:47:07 -06:00
james.maesandClaude Fable 5.1 81a044f68d fix(tui): explain quick search coverage when Search finds nothing
A search with no matches said "no records match", which reads as "the
record does not exist" when the table is simply not indexed. The empty
state now says what quick search covers (N of M tables, up to five names
then "and N more", records, last reindex), that other tables cannot be
found there, where to add them (Quick Search Admin › Quick Search
Index), and that `o` searches the table Search came from by name (its
text fields, through the query API). The status line adds the indexed
table count ("index 57 records in 17 tables"). The kernel's
searchCoverageText builds the sentence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:45:28 -06:00
james.maesandClaude Fable 5.1 d7c6e6575a fix(tui): give text and HTML widgets headings, badges, aligned lists and paragraphs
The owner on Quick Search Admin's Search Diagnostics: "the header text
needs to have more layout or spacing". The widget's HTML went through
htmlToText into one block: sub-headings looked like body text, status
words were inline, label/value lines unaligned, paragraphs ran the full
pane with no blank lines, ISO timestamps had microseconds, and the Tools
paragraph was a run-on list.

Text and HTML widgets are now parsed into a small document
(src/text-doc.ts) and drawn with the hierarchy the web gives the same
HTML: headings (h1-h4, markdown `#`, or a short line before a block) in
the accent colour with a blank line before and their block indented;
a leading OK/WARN/FAIL/DEGRADED/DOWN as a fixed-width coloured badge so
lines align; `Label: value` items as an aligned two-column list, with
none/0 under a failure label in green; instants in the profile's date
format; body text at a ~100-cell measure with blank lines between
paragraphs; a sentence enumerating three or more capitalised names as
bullets; stat cards kept together; rows of button links as items Enter
follows (through the safe link parser). Lines carry coloured segments,
kept by the clipping and the list cursor; ASCII uses [OK] and *.
Row-shaped HTML (the alerts setup checklist) keeps its aligned columns.

Tests use the Search Diagnostics and SQL Workbench payloads captured
from the enterprise server, before and after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:42:34 -06:00
james.maesandClaude Fable 5.1 c201f92f2b feat: stamp default export names with the local date and time
Why: two exports of the same table overwrote nothing but piled up as person.csv, person-1.csv,
person-2.csv, which say nothing about when they were taken. A default name now carries the local
date and time (person_2026-11-01_102316.csv), so exports are unique and sort by age.

One kernel helper, exportFileName(base, ext, now), is used by every surface that chooses a name:
table export, widget --csv and report route downloads, field saves from a record, the OpenAPI
document, and in the TUI the grid export, SQL Runner results, saved reports, widget exports and the
screen-text print fallback. A name the user gives (-o, a typed name in the file dialog) is left as
given; the dialog's name field is pre-filled with the stamped name. A name that comes from the
server keeps it unless it is taken, when the stamp goes before the extension (then -1, -2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:34:13 -06:00
james.maesandClaude Fable 5.1 899326a1c7 fix(tui): screen saver moves every element to prevent burn-in
The Bounce tiles panel, the clock/server/profile row and the lock line sat
still on an OLED/CRT. Now (burnin on, the default): the panel rides beside
the drifting logo on the side with room (alternating 20 s panel / 20 s
Query when both do not fit), the header and lock line change place every
30 s, the flip clock and its tiles drift, Big numbers changes corners, and
everything dims after 10 minutes (:saver burnin on|off, :saver dim
<minutes|off>, F9 Settings). The block-letter logo says the connected
profile's name (short name when 6 characters or fewer) with the server
host beneath, and the block font covers letters, digits - _ . with a
plain-text fallback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 10:26:44 -06:00
james.maesandClaude Fable 5.1 bedbf69452 docs: say which printing operations are qqq printing subcommands
The client document's 17 printing 2.0.0 operations are the generated qqq printing subcommands (print
agent side). printTableList and printText are processes the kernel runs, reached as qqq print list and
qqq print text, not as qqq printing subcommands. Every operation count in the docs is 219, the length of
OPERATIONS in @qrun-io/qqq-core (generated from the merged document); no 202 remains outside the rc.1 notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:42:39 -06:00
james.maesandClaude Fable 5.1 4f95a76d50 docs: restructure the 1.0.0 changelog and rewrite the release notes
The 1.0.0 entry had grown by accretion (Added/Changed/Fixed since rc.1 beside older sections). It is now
one entry with Added, Changed, Fixed, Security and Known limitations, grouped by area (CLI, terminal UI,
kernel and client, packaging and CI), deduplicated, with the commit of each post-rc.1 change. The release
notes describe what qqq is, the UI keys, the CLI highlights, compatibility, install, known limitations and
the enterprise test server, and the command sequence matches the real tags, orb 0.8.33 and the tap and
bucket repositories. The client README and runbook counts and checklist are brought up to date.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:40:15 -06:00
james.maesandClaude Fable 5.1 5d55f40127 docs: fix stale keys in the READMEs and let the readiness gate see default subcommands
F2 is Search, / is the find-anything palette, Ctrl+O switches servers and Ctrl+P prints; Ctrl+P no
longer steps to the previous record. The CLI README lists print, printer and profile. The readiness
gate reported qqq print person 1 2 as a missing verb although record is print's default command.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:40:04 -06:00
james.maesandClaude Fable 5.1 3bc2179928 fix(tui): message dialog footer drops whole keys; sweep F2 Search
The mouse footer of the message dialog ("any key or a click here closes ·
...") was cut mid-token in narrow dialogs; it now goes through hintLine. The
sweep's / scenario follows the new Search dialog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:56 -06:00
james.maesandClaude Fable 5.1 c2a9e98496 fix(tui): date picker keys use fitKeyLine; sweep the date picker
At most three lines of keys, broken between keys and dropping the least
important first, like the other dialogs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:56 -06:00
james.maesandClaude Fable 5.1 da76a35d30 fix(tui): F9, Servers and SQL Runner key lines use the shared fitting; sweep them
The F9 menu, the Servers dialog and the SQL Runner dialogs had their own key
line helpers measuring string length; they now call util/text fitKeyLine, and
the SQL Runner's segment fitting uses fitSegments (terminal cells). The overflow
sweep covers F9 levels 1 and 2, the Servers dialog and its add form, and, where
the sql-workbench extension is installed, the SQL Runner (SELECT, a table,
an error, a plan, the schema and the history).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:56 -06:00
james.maesandClaude Fable 5.1 6937a652d0 docs(changelog): overflow entry matches the rebased help and record view
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:55 -06:00
james.maesandClaude Fable 5.1 0e4716a85e fix(tui): sweep the redesigned developer screens; drop the old screen's wrap hook
The developer view now sizes itself at render time (f236c7b), so the LinesScreen
wrap-width hook added for the old screen is removed again. The overflow sweep
covers the record and table developer views, their sections, the JSON section
and a value opened from it. CHANGELOG follows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:55 -06:00
james.maesandClaude Fable 5.1 c94499c336 fix(tui): record view key line drops whole keys; V shows a field's whole value
The record view's key line now goes through hintLine with drop priorities
(A and D go first, Esc back is kept), so it never cuts a key at 80 columns.
V on a field row of the record view shows the whole value of its field(s) in
a dialog, as V does on the grid. The help's key table keeps two columns when
all but a couple of descriptions fit. The sweep adds a 30+ column grid
(db_perf_snapshot on the enterprise server, else fieldLab), the F9 menu with a
search, and waits longer for a large instance to start; the checker accepts a
box cut by the bottom of its scrolling pane even when a dialog covers that
pane's corner.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:55 -06:00
james.maesandClaude Fable 5.1 4abfd8d4f1 fix(tui): no text outside its container; view settings help line and key wrapping
The owner saw the View settings dialog run text and its highlight bar past the
right border, and the key help cut in the middle of a token. Make clipping the
default at the container level instead of trusting each screen:

- util/text: trunc/pad/padLeft/center measure terminal cells (Bun.stringWidth)
  so wide characters cannot push a border; fitSegments for styled lines;
  fitKeyLine/hintLine lay key help out between tokens (" · "), dropping the
  least important first (explicit `drop` priority; Esc/Enter/Ctrl+S kept).
- overlays/frame: Frame is held inside the screen and gives Line/KeyLines its
  inner width through context; Line cuts with an ellipsis instead of slicing.
- View settings: the value cell holds only the value; the selected row is
  explained on a help line ("columns that stay left while scrolling · +/-
  changes"); keys in at most three lines; +/- really change Pinned columns.
- Sweep fixes: footers drop whole keys, help key table goes to one column
  when two would cut it, developer panels and process errors wrap to the
  pane, home header lines end in an ellipsis, saved column widths are held to
  the grid, bulk edit intro fits 80 columns, wrap() keeps long words.
- V on the grid shows a cell's whole value.
- scripts/overflow-sweep.sh drives every screen and dialog at 80/100/120/200
  x 24/50 against a running server; scripts/overflow-check.ts fails on a line
  wider than the terminal or a broken box border. smoke.sh runs the check on
  its captures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:34:55 -06:00
james.maesandClaude Fable 5.1 16be5c2a6f test: make presence tests wait on state, stop handles in finally, add test deadlines
Windows timers fire in ~15 ms steps, so the fixed tick(30) in the joinPresence
stream test ended before the three stub events (tick(2) each) were applied, and
the failed assertion skipped handle.stop(). The live handle's heartbeat interval
and endless stub stream then kept the test process alive, which CI saw as ten
silent minutes. Tests now poll for the state they assert and stop every handle
in finally. run-tests.mjs gives each test a 2 minute deadline and kills a run
that outlives 8 minutes with a message.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:30:10 -06:00
james.maesandClaude Fable 5.1 9535e3a972 fix: linear scanning for the search snippet and date parser regexes
Two more CodeQL js/polynomial-redos alerts on the release pull request: the
search screen's status-line clock (/^.* (\d\d:\d\d).*$/) and the date parser's
date/time split (/^(\S+?)(?:[T ](.+))?$/i). Both backtracked quadratically on
long repeated input. lastClockTime and splitDateAndTime in core's text helpers
return the same results by plain scanning; each has a 100k-character test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:09:02 -06:00
james.maesandClaude Fable 5.1 3fa81bf428 fix(dev): keep the enterprise server's secrets in a private state dir and never source them
The enterprise script generated its MinIO credentials in the shared temp dir and sourced them, so
another local user could pre-create or symlink the file to run code or read the credentials. State
now lives in a 0700 per-user directory; existing files are used only if owned by us, not symlinks
(credentials also 0600); the credentials and pid files are parsed and validated, never sourced.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:04:26 -06:00
james.maesandClaude Fable 5.1 6018cdf706 feat(dev): start a MinIO container for the enterprise server's object storage
The enterprise profile installs qbit-object-storage when S3_ENDPOINT is
set, so collaboration attachments work. enterprise-server.sh now starts
qqq-enterprise-minio on 127.0.0.1:9310 alongside Redis and OpenSearch,
creates the attachments bucket, and passes S3_* to the server. The
credentials are generated once into $TMPDIR/qqq-enterprise-minio.env (0600;
QQQ_ENTERPRISE_MINIO_ENV overrides) and never written to the repository.
container_up takes a container command after "--".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:01:16 -06:00
james.maesandClaude Fable 5.1 cf0e610bc1 feat(dev): add a local enterprise test server and a CLI sweep of its qbits
scripts/enterprise-server.sh builds and runs qqq-all's enterprise profile
(branch feat/enterprise-terminal-test in .worktrees/qqq-all-enterprise)
on port 8078 against the UI API 1.9.0 fix-branch build: MOCK auth, every
locally runnable qbit, the qqq sample app, Redis and OpenSearch in Docker
for redis and quick search. --status and --stop only touch the pid it
started and the two containers it names; it never touches the 8077 sample
server.

scripts/enterprise-smoke.sh runs the CLI through each qbit surface (ESB,
alerts, assistant, collaboration, data analysis, DB performance, QA
capture, quick search, release notes, secured data, SQL workbench, jobs,
audits, saved views and reports, exports, webhooks, processes) and records
exit codes, so client and server gaps show as non-zero lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 07:01:16 -06:00
james.maesandClaude Fable 5.1 3ccb9731b2 docs(printing): document print anything and Ctrl+P
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:53:46 -06:00
james.maesandClaude Fable 5.1 555c4346be feat(tui): Ctrl+P prints what you see on every screen
A record view prints its record sheet, a grid a table list with its
columns, widths, filter and sort (all rows, this page, or the selection),
and any other screen its text as a monospace PDF, then the printer picker
and the job progress line. Also p on records and grids, F8 where a screen
has no Delete, :print [printer], a and F9, and Print Screen when the
terminal passes it on. Without printing Ctrl+P saves instead: the grid's
export, else the screen text. Ctrl+P no longer steps to the previous
record; PgUp and [ still do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:53:37 -06:00
james.maesandClaude Fable 5.1 4bd62b7c1f feat(cli): add qqq print list and qqq print text
print list prints a table list of the whole result of the criteria (up to
10,000 rows) or one page, in the column order given, with paper and
orientation options; print text prints a file or stdin as a monospace
document. Both pick and remember the printer as print does and follow the
job.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:53:28 -06:00
james.maesandClaude Fable 5.1 d81c05352b feat(core): print table lists and text through the printing qbit
printTableList sends a grid's table, columns, widths, filter and sort with
the scope (all rows, this page, or selected rows) to printingPrintTableList
and passes the server's row-limit note on; printText prints text such as a
screen capture. listScopeChoices orders a grid's print choices: all rows
first when the grid is filtered or small, this page first otherwise, the
10,000-row limit named, and the selection as a list or as record sheets.
The client document's printing entry moves to 2.1.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:53:28 -06:00
james.maesandClaude Fable 5.1 d76fb03a27 docs(tui): Search on F2, qqq search and the attachments message
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:41:35 -06:00
james.maesandClaude Fable 5.1 437ed3c258 fix(tui): say plainly when the server cannot store attachments
Uploading to a server without the Object Storage QBit showed the raw
"IllegalStateException (The Object Storage qbit is not installed)". A
server failure (5xx) naming a missing QBit or extension now reads as one
line, for attachments "This server cannot store attachments: the Object
Storage QBit is not installed." (the kernel's missingQBitMessage, used by
the TUI's errors and the CLI's error output, so `collab
post-records-attachments` says it too). Once known for a server, the
UI's "+ Upload a file" row and the record's upload action are dimmed
with that reason instead of starting another upload.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:41:35 -06:00
james.maesandClaude Fable 5.1 ef2b554e73 feat(tui): F2 is Search, a full screen for finding records with quick search
The owner: quick search is the main way to find a record and get into
the data, so it gets a function key and a screen of its own. F2 (it was
"Go to", which `/` covers) opens Search everywhere: a search line with
"search records across N tables (quick search)", results as you type
grouped by table with label and count, each hit's label, key and
highlight snippet with the field. ←→/Tab cycle the tables, Enter opens
the record and Esc returns to the hit, `o` opens the table's grid
filtered to the hits, Space marks hits for `a` (grid, export, print,
bulk, processes), `f` narrows to one table, `h` recent searches kept per
profile, `?` the syntax quick search really has. The status line shows
matches, tables, time, index size and the last reindex.

Home starts with the search line: typing there starts a search. Without
the extension F2 still opens Search and finds tables, apps and processes
by name and the records of the table it came from, saying "quick search
not installed: searching names only". `:search <text>` opens Search;
`:find <text>` and `/` stay the find-anything palette.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:41:35 -06:00
james.maesandClaude Fable 5.1 0e07f8aefa feat(cli): qqq search shows the matched field, takes several tables and explains the syntax
The kernel gains record search (query/record-search): POST /search
grouped by table with counts, each hit's label, key and highlighted
snippet with the field, what the quick search query language supports
(AND of words matched from their start; no phrases, field:value,
wildcards or OR), notes for text that uses those, and the quick search
status line. The CLI's `qqq search` uses it: a `match` column, `--table`
repeated or comma-separated, `--limit` per table, a footer with the
tables and the total, `field:value` searching the value with a note, the
syntax in --help; `--json` prints the response.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:41:34 -06:00
james.maesandClaude Fable 5.1 e5d6c929d5 fix(tui): column statistics from loaded rows when the server offers none
The S dialog was a dead end on tables without QUERY_STATS: it only said so. It now
computes count, nulls, distinct, min and max from the rows the grid has loaded, plus
sum, mean and median for numbers, shortest, longest and the top 5 values for text, and
earliest, latest and the top 5 days for dates, and says where the numbers come from.
A server that offers the process but answers 403 or 404 gets the same fallback. 'a'
pages through the existing query API (current filter and sort, up to 10,000 rows) with
a progress line; Esc stops and keeps the rows so far. The computation is a pure kernel
function (loaded-stats.ts) with unit tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:36:05 -06:00
james.maesandClaude Fable 5.1 cbabc31b5f feat(tui): mouse support across the whole TUI
The owner asked why the mouse did not work everywhere. Mouse tracking is now
on by default (button-event tracking with SGR 1006, no motion, so hovering
does nothing), switched by F9 Settings "Mouse: on/off" and :mouse (saved in
ui.mouse) or --no-mouse / --mouse for one run.

One transparent box over the terminal takes every event, so OpenTUI's own
text selection and focus never start; a registry of zones (rectangles
measured from the drawn elements, a layer per dialog) says what is under the
pointer. Clicks select, double-clicks activate, right clicks open the
actions at that row, the wheel scrolls 3 lines (Shift sideways in the grid),
header drags resize or move grid columns (saved as view settings), the
F-key bar presses keys and the footer opens the Servers dialog. Clicks
beside a dialog do nothing. Help and copyable-text dialogs say Shift+drag
selects text. The two-level F9 menu, the Servers dialog and the SQL Runner
panes are wired too.

The layer lives in src/mouse/*; screens add a few Zone wrappers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:31:45 -06:00
james.maesandClaude Fable 5.1 efc202a0d0 feat(tui): SQL Runner results like the main grid, export and copy, completion popups
Owner feedback on SELECT * FROM QQQTABLE: the results need to show better,
the export should be complete and discoverable, and Tab should list what
fits even before anything is typed.

Results: columns stay in the server's column list order (for QQQTABLE that
is the table's own H2 order, LABEL, CREATEDATE, MODIFYDATE, NAME, ID, as the
execute response sends it). Cells use type-aware formatting: dates and date-times in the profile's
display format (the kernel's formatDateValue and the TUI's date settings:
day order, 12/24 h clock), seconds only when the value has them, never the
fraction, zoned instants in the local zone; numbers
right-aligned with thousands separators except key-like columns, booleans,
NULL as a dim dash. The grid gets a row-number gutter, a header rule, zebra
per the sql:results view setting, a selection bar that ends at the last
column, horizontal scroll with Shift+Left/Right and edge markers. The status
line sits directly under the results ("1 row · 7 ms · h2 · read-only ·
limit 10,000 · sorted by —") with the key line under it, and F8 Export
joins the F-key bar.

Export and copy: F8 and x open the main grid's export dialog with csv, tsv,
xlsx, json (every row: the server writes csv/xlsx/json from its cached
result, tsv is read page by page) and copy to the clipboard for small
results, through the file navigator and the download-folder preference.
y copies the cell, Y the row as TSV, Ctrl+Y the whole result. Export and
copy show in the a actions menu, F9 Context and F1.

Completion: Tab in the SQL editor opens a popup also with nothing typed
(after FROM/JOIN every table with its column count, after SELECT/WHERE/ON
or a dot the statement's columns with types, at the start the statement
keywords and recent statements), filtered as you type. The : command line
uses the same popup: every command with a one-line description on an empty
line (TUI commands plus the CLI's own descriptions), then a command's
arguments. The popup clips to its width and follows the ASCII setting.

Execution UUIDs and cell keys now always come from crypto.randomUUID()
(the old Math.random fallback was flagged by CodeQL, and the id goes to the
server).

Tests: column order, cell formatting, delimited output, export choices,
reading every row, the popup model, empty-prefix completion, and the screen
and : line end to end. smoke-sql.sh runs the owner's query, both popups and
a real csv export whose row count must match the status line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:28:21 -06:00
james.maesandClaude Fable 5.1 c22ae75d0e feat(tui): a live screen saver starring Query, the qrun.io mascot
The saver was a drifting clock. It now shows what is happening on the
server: people online (collaboration presence), jobs, health, database
sessions, ESB depth and dead letters, unread alerts, realtime events per
minute, ping, and the profile's favourite table counts. Each tile hides
when its extension is absent (or answers 403/404) so the plain sample
server works too; failures keep the last value dimmed as stale and back
off. Four styles (Bounce, Starfield, Flip clock, Big numbers, or auto)
animate Query, traced from the line drawing on qrun.io/query, at no more
than 4 fps, and only the saver repaints. The lock's key handling is
reused unchanged: with a PIN the prompt opens over the animating saver.
`:saver [style]` previews; F9 > Settings holds style and refresh.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:22:18 -06:00
james.maesandClaude Fable 5.1 ad4001dba4 feat(tui): one date control: typed entry in the display format and a calendar picker
Dates and date-times were typed as bare ISO text with no help; the owner asked
that they can be entered by hand or picked. One control now serves every place a
DATE or DATE_TIME is edited: create/edit forms, process forms, bulk edit and the
filter dialog's value cell (its row model unchanged).

Kernel (forms/dates.ts): parseDateInput reads ISO, the display format, partial
dates, t/today, n/now and the filter grammar's now(...)/startOf(...), refusing
with the exact reason; slash dates only when the profile's date order says which
part is the month. formatDateValue/formatHint, monthGrid and week numbers, and
ui.dates settings (order, clock, week start, week numbers, picker on Enter).
Values go out as the kernel already sends them.

TUI (controls/): DateEditor (a LineEditor showing the display format, Backspace
clears a whole value when nullable), DatePickerOverlay anchored to the field
(F2/Ctrl+Space; time row with zone, UTC sent and server zone), the bulk prompt
(now sending DATE_TIME as the UTC instant like forms), the filter wiring, and
:dates. Screens claim F2 through Screen.claimsKey so it is not the palette there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:11:29 -06:00
james.maesandClaude Fable 5.1 cc7839639f feat(tui): SQL Runner over the sql-workbench extension
The owner asked whether anyone was getting the SQL Runner working in the
terminal. The Next UI's Runner (a SQL_WORKBENCH widget on its own app page)
had no TUI screen: its app opened an empty app home.

Add a classic database-TUI screen over the sql-workbench UI API extension:
- editor on top: multi-line buffer, syntax tint (server keywords included),
  F5 / Ctrl+Enter (Ctrl+J) runs the statement under the cursor, Tab completes
  tables and columns (alias.columns) from the schema endpoint, Ctrl+Up/Down
  steps through the on-disk run history; :name parameters are asked for,
  bound as values and remembered
- result grid below: server-paged and server-sorted over the cached result,
  column widths from the column kinds, NULL dimmed, V whole value, x export
  (csv/xlsx/json through the file flow), status line with rows, elapsed,
  backend, read-only and the row limit (cursor tokens dropped first)
- errors inline under the editor with the SQL position marked (H2 [*],
  PostgreSQL Position, line/column forms)
- F6 EXPLAIN as an indented, foldable plan tree with costs and a relative
  cost bar (raw text with r); quick text bar charts (c)
- F3 schema browser (backends, tables, columns; filter; Enter inserts)
- F2 notebooks (mine/shared, open, save as a new version, save as,
  duplicate, delete, publish when allowed), F4 cells (SQL and markdown,
  markdown rendered as text, move, title, backend, run all), F7 audited
  history with open and re-run
- reachable from the Runner app in the tree, `/` (the SQL Workbench widget
  hit opens the Runner instead of `widget get`), F9 > Work and :sql
  (:sql <statement>, :sql open <id>); without the extension the Runner apps
  and their SQL_WORKBENCH widgets leave the metadata and :sql says the
  extension is missing

Registration is kept to a few lines: store.setMeta hides the Runner apps
(extensions now load before the metadata), a screen may claim a global key
(F2 here; its keys reach F1 through the existing helpKeys), openApp routes
Runner apps, and commands gain :sql, the Work entry and the widget route. Write mode is not offered (the Next UI keeps
the gated write flow).

Tests: editor model, completion, statements, parameters, error positions,
history, notebooks, absent-extension metadata, result paging and sort,
column widths, plan tree, bars, and the screen end to end against a fake
server (80x24 clipping checked). scripts/smoke-sql.sh drives it in tmux at
80x24 and 120x36 against a server with the extension.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 06:00:35 -06:00
james.maesandClaude Opus 5.5 3c45c8ee2f feat(tui): Servers dialog (Ctrl+O) for saved profiles and one-key switching
Switching servers took the profile chooser and gave no sign of which servers
were up. The dialog lists every profile with short name, server, background
probe status, last use, signed-in user and description; Enter switches
(reusing a valid stored sign-in), n/e/d/* add, edit, remove and set the
default, typing filters. Home and the footer show the profile's short name,
the switch fetches fresh metadata, and the footer clock and status are reset
for the new server instead of freezing on the old one.

With the flat app and recent entries gone from F9 (places are found with /),
Go to lists the apps and the recent records as groups (five and more…), so
those lists stay one level down in the menu; Ctrl+O joins the key reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 05:55:32 -06:00