// ZSH-LEARN — ENGINEERING REPORT

MySQL / MariaDB-backed knowledge plugin · le to save · se / see / seee to search · qu / qua to quiz · Ctrl-K ZLE widget

>_EXECUTIVE SUMMARY

zsh-learn is a MySQL/MariaDB-backed flashcard plugin for zsh. Every entry is a row in a $ZPWR_SCHEMA_NAME.$ZPWR_TABLE_NAME table (default root.LearningCollection); the plugin ships save / search / random-recall / quiz / SQL-passthrough / in-place-edit / delete operations as autoloaded zsh functions. le "git rebase -i HEAD~3" writes the row, se rebase searches it, qu 50 pipes 50 random entries into fzf for spaced-recall drilling. The plugin's database surface is the same one consumed by the LearningCollectionAPI Spring Boot service — the zsh CLI and the web/REST API operate on the same authoritative table. Source: 833 zsh lines across 1 plugin entry + 26 autoload functions + 1 completion. Pinned by 453 @test blocks across 6 zunit files.

833
Zsh Lines
453
@test Blocks (zunit)
6
Test Files
26
Autoload Fns
16
Aliases (typo-proof verbs)
3
ZLE Bindkeys (viins/vicmd/emacs)
1
Compdef (_se)
6
fzf Completer Hooks

~ARCHITECTURE

Single entry point that conditionally registers ZLE + zstyle + bindkeys, then autoloads every function under autoload/ via autoload -Uz "${0:h}/autoload/"*(.:t). Each verb is its own one-file function — lazy-loaded on first use.

File / GroupLinesRole
zsh-learn.plugin.zsh 137 Plugin entry. Declares ZPWR_VARS association; sets defaults for ZPWR_SCHEMA_NAME / ZPWR_TABLE_NAME / ZPWR_LEARN_COMMAND / temp paths; declares 16 alias-as-verb pinned shortcuts (le, se, see, seee, re, rsql, editl, delid, plus typo variants es/ees/ses/ese/sse/ssee/rer/er); populates ZPWR_VERBS association when zpwr is loaded; registers per-completion zstyle (group-order, list-colors, sort-disabled for numeric IDs); binds Ctrl-K across viins / vicmd / emacs keymaps; prepends ${0:h}/src + ${0:h}/autoload to fpath; autoloads all 26 functions under autoload/.
autoload/zsh-learn-Learn 40 Core ZLE widget. Prompts via the current line buffer, formats the entry, dispatches to zsh-learn-Savel. Bound to Ctrl-K.
autoload/zsh-learn-Savel 57 The insert path. Escapes apostrophes, length-checks against ZPWR_LEARN_MAX_SIZE (default 3000), composes the SQL INSERT, dispatches via $ZPWR_LEARN_COMMAND $ZPWR_SCHEMA_NAME (default mysql root).
autoload/zsh-learn-Searchl · Searchle · Searchlee 71 / 72 / 21 Three-tier search functions, fronted by se / see / seee respectively. Each tier widens the projected column set (learning → learning+category → learning+category+date). All three accept filter arguments and apply them via SQL WHERE ... LIKE ....
autoload/zsh-learn-Redo · Redosql 21 / 29 SQL passthrough. re ID prints the matched row plus a ready-to-edit UPDATE statement; rsql ID opens the same in $EDITOR.
autoload/zsh-learn-Editl · DeleteId · del 35 / 11 / 9 In-place edit via $EDITOR; targeted delete by ID; bulk delete of last N rows.
autoload/qu · qua · ser · sera · sef 14 / 8 / 13 / 8 / 12 Random-recall family. ser N = N random rows; sera = all randomized; qu N = same as ser but piped to fzf for interactive quiz; qua = all randomized into fzf; sef = recency-ordered fzf browse.
autoload/zsh-learn-CreateLearningCollection · DropLearningCollection 65 / 61 DDL admin. Create emits the schema (id PK + auto-increment, learning TEXT, category VARCHAR, timestamp), Drop tears it down. Largest single autoload files because they carry the literal DDL strings.
autoload/zsh-learn-Get · GetItems · GetLastItem 20 / 42 / 12 Read primitives used by the search/redo verbs internally.
autoload/_fzf_complete_{se,redo,rsql} + _post 11 + 8 (× 3) fzf-tab integration hooks. When fzf-tab or fzf's shell-completer is loaded, these widgets feed candidate IDs / learning rows directly into fzf.
src/_se 18 compdef completion shared by se / see / seee / re / rsql / zsh-learn-Redo / zsh-learn-Redosql / zsh-learn-Searchl / zsh-learn-Searchle / zsh-learn-Searchlee.
Total source 833 1 entry + 26 autoloads + 1 completion · pure zsh

#TEST COVERAGE

453 @test blocks across 6 zunit files. t-unit.zsh carries the bulk (432) — behavioral pins for every verb against a fixture $ZPWR_LEARN_COMMAND stub. The 4 contract files pin install-path / fpath / autoload shape.

FileTestsPins
t-unit.zsh432Every verb (le, se, see, seee, ser, sera, qu, qua, sef, re, rsql, editl, del, delid, Create/Drop) emits the documented SQL shape against a mocked $ZPWR_LEARN_COMMAND; apostrophe-escape behavior; length-cap behavior; verb-typo aliases resolve to the canonical function.
t-syntax.zsh2zsh -n over plugin entry + autoload tree.
t-contract.zsh3Entrypoint stem matches dir; entrypoint parses; #compdef on every _* file.
t-contract2.zsh6fpath augmentation includes both src/ AND autoload/; autoload glob picks up every file under autoload/ with (.:t).
t-contract3.zsh5Zinit-style install path stability; ZPWR_LEARN=false opt-out skips bindkey/zstyle registration.
t-contract4.zsh5OMZ-style install path; ZPWR_VARS initialized as association on first source; ZLE widget registered exactly once.
Total4536 zunit files · verb-surface + install-contract

/INTEGRATION

Database backend

Default: mysql command, schema root, table LearningCollection. Override via ZPWR_LEARN_COMMAND (e.g. 'sudo mysql' for MariaDB unix-auth) and ZPWR_SCHEMA_NAME / ZPWR_TABLE_NAME. zsh-learn-CreateLearningCollection emits the DDL the first time.

LearningCollectionAPI cross-stack

The same $ZPWR_SCHEMA_NAME.$ZPWR_TABLE_NAME rows are consumed by the LearningCollectionAPI Spring Boot REST service — entries saved from zsh are immediately readable from the web app, and vice versa. One source of truth, two front-ends.

ZLE widget binding

zle -N zsh-learn-Learn registers the widget; the file then binds Ctrl-K in three keymaps (viins, vicmd, emacs) so the binding survives any $KEYMAP setting in the user's .zshrc. Vim-emulation users press Ctrl-K in insert OR command mode; emacs-mode users press Ctrl-K directly.

Completion: numeric IDs, ordered, color-coded

The plugin sets three zstyle contracts on the _se completion: sort false (preserve numeric-DESC server order rather than lexicographic), group-order to put IDs before learning text, and per-group list-colors for ID vs text contrast.

fzf integration

If fzf's shell-completer is loaded, the six _fzf_complete_se/redo/rsql hooks intercept the **TAB trigger and feed live database rows into fzf — same backend as the inline search, different presentation.

Lazy autoload + Zsh Plugin Standard

Each verb file under autoload/ is one function. The plugin entry registers them via autoload -Uz — nothing is parsed until first call. Honors the Zsh Plugin Standard 0= header so ${0:h} resolves correctly under zinit / oh-my-zsh / antibody / antigen / bare source.


!DESIGN DECISIONS

SQL backend, not an embedded SQLite

Embedded SQLite would isolate the data to one shell host. MySQL/MariaDB lets the same table be served by the LearningCollectionAPI Spring Boot service — the user's notes are accessible from any browser, any cron job, any sibling process. Cost: a running mysqld. Gain: a personal knowledge graph that isn't bound to one terminal.

Typo-proof verb aliases

16 aliases mapped to 7 canonical functions. es / ees / ses / ese / sse / ssee all dispatch to zsh-learn-Searchl; re / rer / er all dispatch to zsh-learn-Redo. Muscle-memory typos still work — the operator never has to remember the exact spelling.

3-tier search by projected columns

Naive plugins would have one search with a --verbose flag. zsh-learn instead ships three named verbs (se < see < seee) corresponding to projected-column count. Saves a flag, makes the cardinality explicit in the verb itself, and pre-renders the zstyle group-order per verb.

Three keymaps for one widget

bindkey applies to one keymap at a time. The plugin doesn't try to detect the user's mode — it just registers Ctrl-K against all three of viins, vicmd, emacs. Total cost: three bindkey calls. Total gain: never lose access to Learn after a mode switch.

noglob on every alias

Every alias is noglob zsh-learn-*. Search filters contain *, ?, [] — without noglob the shell would glob-expand them against the local filesystem before the function ever saw them. noglob at alias time is one keystroke for the operator and zero behavioral surprises.

Verb-as-file, not verb-as-block

26 separate files under autoload/ instead of one mega-file. Each function lazy-loads on first call, so cold-startup cost is just the entry-point parse (~137 lines). Editing one verb does not invalidate any compiled .zwc for the others.


@FOOTPRINT