Commit Graph
254 Commits
Author SHA1 Message Date
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
james.maesandClaude Opus 5.5 13c020f62e feat(tui): F9 menu in two levels, areas then entries
The flat three-column list of every entry did not fit and was hard to scan.
Level 1 lists the areas with a count and a summary; Enter or the right arrow
opens one, a letter jumps to it, left or Esc goes back, Tab steps areas. Long
groups (Recent, Apps, Favorites, or any entry's group) show five and more….
Two or more typed characters still search every entry, now listed by area.
Only the structure and rendering changed: the entry catalog in commands.ts
is untouched apart from the optional group field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 05:54:17 -06:00
james.maesandClaude Opus 5.5 d9317f5f59 feat(core,cli): profile short name, description and sign-in mode; qqq profile verbs
Profiles gain short (2-6 chars, for compact places), description, authMode
(auto, browser, device for OAuth2 servers), and lastUsedAt/user recorded by
the TUI. Every field is additive: a 1.0.0-rc.1 config.json loads unchanged and
its note shows where a description is missing. probeServer asks /version and
the authentication metadata with a short timeout so the CLI and the TUI's
Servers dialog report reachability the same way.

qqq profile list|add|edit|use|remove (also under qqq config profile) matches
the dialog field for field: --short, --description, --auth-mode, --check,
edit --server/--rename, list --probe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 05:54:16 -06:00
james.maesandClaude Fable 5.1 fc47cb8d47 docs(printing): document printing and add a virtual print agent script
scripts/print-agent.sh runs qqq-print-agent's virtual file printer against
a local server with the printing qbit (the enterprise server on 8078 by
default): it enrolls and approves itself the first time and writes each
job to a directory. README and CHANGELOG (1.0.0, Added) describe qqq print,
qqq printer and the TUI's Print action.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:16 -06:00
james.maesandClaude Fable 5.1 05f467c605 feat(conformance): check the printing extension when a server lists it
GET /qbit/printing/session must answer 200 with principalId and tenantId
(or 403 with the error schema when the user lacks printing access), and the
printing printer, device and job tables must query and validate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:16 -06:00
james.maesandClaude Fable 5.1 acfb090024 feat(tui): print records and selections, and follow print jobs
p (Print...) on a record, or on a grid's Space selection, opens a printer
picker: printable printers first, the others listed with the reason, the
last used one preselected and remembered per profile. The jobs are followed
in the title bar and status line (print 1/3 RECEIVED) with a flash when they
end, and :printjobs (F9 > Work) lists recent jobs. Without the printing
extension no Print action is offered and :printjobs says what is missing.
The qbit's own Print process is not listed again under Run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:16 -06:00
james.maesandClaude Fable 5.1 c1e7bf9cfa feat(cli): add qqq print and qqq printer
qqq print <table> <id...> prints records as record sheets and follows the
jobs until they have printed (--printer, --copies, --no-wait, --timeout;
exit 1 when a job failed, 4 when still running at the timeout). qqq print
job list|status [--watch]|cancel follows and cancels jobs; qqq printer
list|use|show|forget|devices shows printers and print agents and manages
the printer remembered for the profile. Without the printing extension the
commands exit 5 with the installed list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:03 -06:00
james.maesandClaude Fable 5.1 d632987e43 feat(core): add the Printing module for printers, jobs and Print
Printing wraps what a person does with the printing qbit: printers (with
approval, exposure and formats), print devices and their health, jobs by id
or state, Print on a table's records through its printingPrintRecords_{table}
process (returning the queued job ids), cancel, and watch until every job
is final. resolvePrinter and whyNotPrintable serve both heads, and the
printer chosen last is remembered per profile in config.json. Every call
needs the extension and throws ExtensionNotInstalledError before any
request otherwise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:03 -06:00
james.maesandClaude Fable 5.1 5cf62430aa feat(client): carry the printing 2.0.0 extension document
Appends qbit-printing's committed extension document (printing 2.0.0,
17 operations under /qbit/printing) to the client document with
merge-spec.mjs, keeping the other documents untouched, and regenerates the
schema and operation index (219 operations). The registry knows printing
with minimum 2.0.0: the realtime channel became the agent's prerequisite
there, so a 1.x server counts as not installed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:49:03 -06:00
james.maesandClaude Fable 5.1 f149a4753a fix(tui): smoke.sh skips the sample-only record steps against another server
Steps 13b-13d open person 12 and its ESB events, which only the sample
server has; with --server they are skipped (SAMPLE=1 keeps them), so the
smoke test passes against the enterprise server too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:47:20 -06:00
james.maesandClaude Fable 5.1 c35def57ff fix(tui): server links are content, never commands
Review of the widget links: parseLink turned any `:…` link into a command
that Enter ran, and links come from server content (widget payloads,
HTML, table rows with __href), so a buggy or malicious server could make
Enter run `:process run …` or `:table delete …`.

- Server links are read against the metadata: `/app/{table}[/{id}]` and
  `/app/{process}` through the developer view's linkTarget, the web's
  app paths only when every app on the way is an app; record keys must be
  plain segments, filters must have a QueryFilter's shape. `:` text,
  unknown names and other sites are inert notices that show the address
  (no browser).
- Only TUI-built links are trusted: ESB destination rows carry a
  symbol-keyed link that JSON cannot contain. Commands they run are shown
  in the status line first, server names are quoted as single arguments,
  and mutating commands confirm.
- Extension output is now laid out by the kernel's formatDocument (the
  CLI's layout since 5e34433) instead of a second renderer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:43:50 -06:00
james.maesandClaude Fable 5.1 e8577daf3b docs(tui): search and help, collaboration, widgets and smoke against any server
CHANGELOG 1.0.0 (changed, added and fixed since rc.1), the TUI README keys
and sections, and TRY-IT: `?` is the keyboard reference, `/` the one
search (with :search), the record's collaboration keys, J for raw JSON.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:41 -06:00
james.maesandClaude Fable 5.1 6950e9d811 fix(tui): smoke.sh takes --server URL and --profile NAME
Against another server the smoke test launched the TUI without a
profile for it and landed on the profile picker. With --server it writes
an isolated config (config, cache, state, file keyring) with one profile
for the server, so a MOCK or anonymous server signs in straight away and
the user's own config is untouched. The positional profile still works.
The help step checks the new keyboard reference.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:20 -06:00
james.maesandClaude Fable 5.1 b782812108 feat(tui): record collaboration: comments, attachments, tasks, timeline, follow, share preview, presence
The record view offered only tags from the collaboration extension. It
now shows a line with the record's comments, attachments and open tasks
and the follow state (keys C P K T W), and below it who is viewing the
record now: everyone the server reports, this user marked "(you)" (the
MOCK and same-user case merges into one person, so others alone could
never show). `a` and F9 list comments (list, add, reply), follow and stop
following, attachments (list, download through the file navigator,
upload, delete), the timeline, tasks (list, add, done) and the share
preview (find people, or type user ids when the directory is empty; see
who could read the record, then send). Without the extension the line
is not drawn and F9 says "needs the collaboration extension".

Comments, following and people go through the kernel's Collaboration;
attachments, timeline, tasks and shares are the extension's operations.
An attachment's temporary URL gets credentials only when it is on the
server's own origin.

Also: a choice list sizes itself for disabled reasons too (they were cut).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:20 -06:00
james.maesandClaude Fable 5.1 e13e7bf0ff fix(tui): leave quietly when the terminal is already gone
Closing the tmux pane or window before quitting printed "setRawMode
failed with errno: 2" and a native session OutputFailed error, each with
a stack trace. The renderer's restore steps cannot succeed then and the
messages say nothing to the person who closed the window; they are
dropped from console.error/warn and from the crash handler. Other errors
still print.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:19 -06:00
james.maesandClaude Fable 5.1 0691e6fd13 fix(tui): app cards show the app's icon as a glyph, not its Material name
Cards printed "[notifications_active]". Common Material icon names map to
single-width glyphs (bell, chart, database, people, settings, search,
star, folder, terminal, shield, hub, clock, ...), with ASCII stand-ins
under the ASCII-glyphs setting; unknown names fall back to the app mark.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:19 -06:00
james.maesandClaude Fable 5.1 70c064eabe feat(tui): one search on /, a keyboard-only ?, and :search
Help and go-to overlapped: `?` listed actions and searched help, `/`
listed recents and commands, F9 listed every app and metadata match.
Each thing now has one home.

- `/` (F2, `.`, Ctrl+K, `:search <text>`) searches apps, tables,
  processes, widgets, reports, records, commands and help, grouped under
  headers with the best match first. Records come from quick search (or
  the server's search), debounced; the group reads "quick search: N
  matches" with the table and record label per hit, and says once,
  dimmed, "records: install the quick search extension" without it.
  Hits open by `recordId` (they opened with an empty key) and the label
  falls back to the highlighted text when it is only the key.
- `?` is the keyboard reference of the screen: keys grouped by what they
  do (the screen's own shortcuts from its actions, keys only), the
  screen's help text, and "/ search anything · F9 menu · : command line".
- F9 keeps its sections and drops the per-app, recent and index entries.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:40:19 -06:00
james.maesandClaude Fable 5.1 19cc3ec16b fix(tui): draw the ESB, jobs and alerts widgets and extension JSON
The enterprise sweep found widgets the terminal could not draw: the ESB
app's overview printed "(ESB_OVERVIEW: no terminal rendering yet)", the
jobs statistics printed their web addresses, the alerts setup checklist
ran label, status and detail together, and extension output dumped
nested arrays as one-line JSON.

- ESB_OVERVIEW is drawn from the esb extension as `:esb overview` does;
  Enter on a destination offers the ESB of its table and processes, its
  messages and dead letters.
- Widget lines carry links (a statistic's url, a table row, an HTML
  row's first link); Enter reads the table, filter, record, process or
  app out of the web path and opens the TUI's own screen.
- Row-shaped HTML becomes aligned columns, the last wrapping under
  itself; the kernel decodes the common named and numeric entities.
- Extension operations on `:` and payloads without a block draw as
  key/value rows and clipped sub-tables; J shows the raw JSON.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:39:52 -06:00
james.maesandClaude Fable 5.1 e8f075addf feat(tui): copy CLI equivalents to the clipboard
The CLI equivalent dialog could only be read. y, c or Enter now copies the command
(Esc closes without copying), the filter dialog gets Ctrl+Y for its command line, and
F9 > Settings has "Copy CLI commands to the clipboard automatically" (ui.copyCliCommands,
default off). The footer points at Shift+drag for terminal selection; mouse tracking is
left to the TUI-wide mouse setting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:38:53 -06:00
james.maesandClaude Fable 5.1 536d0b37cd docs: record the enterprise sweep fixes and the -o convention for 1.0.0
CHANGELOG 1.0.0 gains the -o/--order convention with its migration note
and the extension table output under "Changed since 1.0.0-rc.1", and the
ESB trigger, login profile, health fallback and conformance fixes under a
new "Fixed since 1.0.0-rc.1". README, the cli and conformance package
READMEs, TRY-IT, the 1.0.0 release notes and the parity matrix (and its
generator) use --order for the sort and -o/--out for files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:39 -06:00
james.maesandClaude Fable 5.1 248f30f458 fix(cli): name ESB triggers by process and fall back to an unadvertised /health
esb messages on a topic without --trigger only passed on the server's
"name one of the triggers", and trigger names look like
syncOrder.orderEvents, so the process name syncOrder answered 404. On a
400 or 404 the command now reads the ESB overview: a process name (or
label) with one trigger on the topic reads that trigger, several are
listed, and otherwise the error lists the valid trigger names
(resolveEsbTrigger in the kernel).

health get-summary said "not installed" on servers that serve /health
without listing the health extension in GET /version (qqq-all). The
kernel's probeHealth reads /health, shapes it like the extension's summary
and marks it unadvertised; the CLI shows it with a note when the
extension is not listed and the endpoint answers.

Tests cover the sub-table golden output, --columns, the -o convention,
ESB trigger resolution, the login warning and the health fallback against
a fake enterprise server.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:39 -06:00
james.maesandClaude Fable 5.1 5e34433a86 feat(cli): print extension results as key/value rows and sub-tables
The generated QBit commands (alerts get-inbox, sql-workbench
get-capabilities, collab get-..., api) printed lists as one line of JSON
in a key/value table (`items [{"id":5,...`). The kernel's render model
gains a document layout (formatDocument): scalars as aligned key/value
rows with nested objects as dotted keys, each list of objects as a
sub-table under its key with columns from the union of the row keys in
first-seen order, nested objects flattened one level, lists in a cell
joined, fitted to the terminal width (cells cut with an ellipsis, columns
that do not fit named under the table). render() uses it for nested
results and for any result given a layout; json, yaml, csv and text do not
change, and a --query reshape drops the column choice.

The inbox, notebooks, SQL history, comments, tasks, favorites and recents
get default columns (DEFAULT_LIST_COLUMNS); --columns (or the global
--fields) chooses, on the generated commands and on the first-class collab
comments list, favorites list, recents, tags list and people.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:39 -06:00
james.maesandClaude Fable 5.1 0687083c56 fix(cli): make -o the output file everywhere and the sort --order
table export and table query used -o for --order while the extension and
download commands used it for the output file, so `table export customer
-o file.csv` sorted by a field named after the path (and the server
answered 403). One convention now: -o/--out is always the output file, the
sort is --order (no short form; -s is --set). table export takes
-o/--out. On the commands that write no file (table query, saved-view
save, saved-report preview and set) -o still sorts for one release, hidden
from help, with a deprecation note. A sort field that looks like a file
path is warned about. Completion offers fields after --order only and
skips hidden options; the flat query alias forwards short-only options.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:38 -06:00
james.maesandClaude Fable 5.1 a10c8344d4 fix(cli): ask before login --server points an existing profile elsewhere
With QQQ_PROFILE set, `qqq login --server URL` silently moved that profile
to another server. resolveProfile now reports movedFrom when an existing
profile is given a different server; login warns, needs --yes (or a yes at
the question on a terminal) and says --profile <new-name> adds a profile
instead. saveLogin only moves a profile when login asked for it, so a token
refresh under a one-off --server no longer moves it either.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:38 -06:00
james.maesandClaude Fable 5.1 e7108ad319 fix(conformance): authenticate every check the same way, as the CLI does
GET /jobs and the realtime check built their own credentials while the
other checks used the token as a bearer only, so a token could report "no
jobs permission" where a session worked. One Authentication now serves
every authenticated check: a JWT as a bearer token, a session UUID also as
the sessionUUID cookie, and on MOCK and FULLY_ANONYMOUS servers without a
token a server session from manageSession (what qqq login does there). The
metaData check names the authentication used, and a jobs 403 says it was
the same one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:27:38 -06:00
james.maesandClaude Fable 5.1 f236c7b833 feat(tui): developer view in sections, with a scrollable pane and real JSON
The record and table developer views were one wall of text: panel blocks
flattened to prose, tables with ad-hoc headers, web paths as links, and the
ESB sample event broken into fragments that looked duplicated. That came
from the prose word-wrap: the minified event was split at the spaces inside
its strings ("Clone of: Blair", in both record and oldRecord) and every
longer piece was cut at the width.

Both views are now a section bar (left/right or 1-9) over a scrollable pane
with a scroll bar. Record: Security, Events, Scripts (revisions, code, logs,
test runs, store from $EDITOR), JSON (endpoints, display and raw values),
Metadata, Audits, APIs, then any further panel. Table: Events (ESB counters),
JSON, Metadata, APIs. Grids use the main grid's column layout with a dash
for no value and a note on why a column is empty; JSON is pretty-printed,
tinted and cut per line, with v for the full value viewer and y to copy.
Web links map to TUI screens or are shown as browser only. F3 goes to the
record or the grid; the help overlay lists the developer keys.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 05:24:09 -06:00
james.maesandClaude Fable 5.1 213c605d95 fix(tui): read escaped quotes in the filter dialog like core's grammar
Core now prints quoted filter values with \" and \\ escapes and its
tokenizers skip an escaped quote. mixedJoinAt, which points at the first
mixed AND/OR in the Advanced text, toggled on every quote, so a value such
as "x \" AND y" was read as ending early and its AND flagged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:51:16 -06:00
james.maesandClaude Fable 5.1 c52220a413 docs(changelog): record the CodeQL security fixes for 1.0.0
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:57 -06:00
james.maesandClaude Fable 5.1 d749f144a8 fix(release): escape every sed metacharacter in the notes command
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:56 -06:00
james.maesandClaude Fable 5.1 c5b4b47888 fix(tui): strip process help markup with the core stripTags
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:56 -06:00
james.maesandClaude Fable 5.1 9609acfc08 fix(conformance): dev IdP end-session redirects only to loopback URIs
post_logout_redirect_uri is rebuilt from a constant loopback host, a numeric
port and its path, and any other value is answered with 400 (it showed the
signed-out page before), matching the authorization redirect_uri rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:56 -06:00
james.maesandClaude Fable 5.1 4d13ba98d2 fix(cli): keep the edit file path out of the editor's shell command
editText ran $EDITOR through a shell with a file name built from the table,
primary key and field, so a primary key such as $(...) executed. The path is
now the shell's $1 on POSIX (as git runs its editor) or a quoted variable on
Windows, and the name keeps only letters, digits and ._-.

Also: --api-path trimming and bulk help text use the linear core helpers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:56 -06:00
james.maesandClaude Fable 5.1 e1694d4fe7 fix(core): linear text scanning and correct escaping for CodeQL alerts
Server metadata and user input reached regular expressions that backtrack
quadratically (slash and = trimming, ${name} and ${input.x} references,
filter error tails, tmux list-keys lines). They are replaced by linear scans
in render/text.ts, with the same matches for well-formed input.

htmlToText and the upload-template names decoded entities in sequence, so
&amp;lt; became <; they now strip tags in one pass and decode once. Filter
values escape backslashes as well as quotes, and the tokenizers count
preceding backslashes, so a value ending in a backslash keeps its closing
quote.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:47:56 -06:00
james.maesandClaude Fable 5.1 067d97ccb3 fix(tui): rework the F6 filter dialog into rows of field, operator, value
The guided dialog mixed three models: quick rows that reused one
editor call (a true/false quick field became the unary TRUE and its row
edit re-applied TRUE, so it could not be changed there), condition lines
edited through a menu, and letter shortcuts next to cells that took
typing. People could not tell which row did what or how to change a
value.

Now every condition is one row of three cells, edited the way its type
asks: text and numbers in place, choices from a searchable list that
toggles values, Space cycles true/false (t/f set it), dates take presets
or typed values, and the operator cell lists the operators that fit the
field. A row without a value shows (any) and is not applied; Backspace
clears any cell. Quick fields are the same rows. AND/OR and groups are
drawn as structure, the help and key lines follow the focus, and the
footer shows a live, debounced match count. Commands moved to Ctrl and
F keys so letters always type; Esc asks before discarding changes.
Advanced gains a cheat sheet, a caret that follows the scrolled text
(and points at the joiner when AND and OR are mixed), and says why it
cannot switch back to Guided.

Design: packages/tui/docs/filter-dialog.md. The smoke script switches
modes with F2 (Tab now moves between cells) and the soak's reset accepts
discarding a half-edited filter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:44:39 -06:00
james.maesandClaude Fable 5.1 5dd5e2d7f1 test: run the package tests without shell globs, for the Windows lane
The cli-windows-x64 lane failed in every package with "Could not find
...\dist\test\*.test.js": npm runs scripts with cmd.exe there, which
does not expand globs, and Node 20 does not glob its own arguments.
Plain "node --test" is no fix on Node 20 either, because its default
patterns run every file under a test directory, helpers such as
harness.js and stub.js included.

scripts/run-tests.mjs lists the *.test.js files directly in dist/test and
spawns node --test with them, so all four packages run the same files on
every platform. Counts on macOS are unchanged: 18, 167, 3 and 100.

Two cheap Windows fixes in the CLI test harness: the sandbox sets
USERPROFILE as well as HOME (os.homedir() reads USERPROFILE on Windows,
so defaults would have escaped into the real profile) and QQQ_STATE_DIR,
and the golden-file normaliser turns backslashes in sandbox paths into /
so export-download-dir.txt matches there too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 04:39:55 -06:00