blong-dev: the development CLI
blong-dev is the command line the repository is worked with — by people at a terminal and by
coding agents through the same binary. It exists because the loop around the framework has a dozen
small jobs that each used to be a paragraph of shell in someone's notes: query the development
database, read back a log from a run that has finished, open a Playwright trace, put plain JSON in
front of an encrypted gateway, run the tests, turn their output into the one report CI consumes,
check the generated documentation, keep the dictionary sorted, keep the vocabulary in order and keep
the memory files.
node tools/blong-dev/bin/blong-dev.ts <command> [args]
It is a workspace package (@feasibleone/blong-dev, binary blong-dev) and not a published tool:
the commands assume they are running inside a checkout, and several of them read the repository's
own configuration (.blong_devrc, rush.json, cspell.config.yaml, the memory files).
The commands
| Command | The problem it removes |
|---|---|
lint [files…] | Which of tsc, cspell and eslint to run, with which config, over which files. |
lint-staged | The same, fanned out over every package the staged files belong to. |
test | A TAP wall and a report format nobody remembers. |
playwright [args] | Running the browser suite and then assembling its Allure report and CI contract by hand. |
report <runner> | Turning another runner's raw output (vitest) into the same CI contract. |
ci-report | Aggregating every package's report into the one file CI publishes, with metrics and a bundle. |
ci <verb> | Diagnosing a red run: which operations failed, why, and whether the run before was green. |
proxy | The gateway speaks MLE; curl does not. |
trace <trace.zip> | Unzipping a Playwright trace and reading JSON lines to find out which step failed. |
log [ulid] | Log lines that only existed live, in a terminal that has been closed. |
sql [query] | Reaching into a pod with a MySQL client to look at development data. |
cspell <verb> | A word list that has to be sorted by hand, and the insertion point that goes with it. |
glossary <verb> | A vocabulary that drifts out of order, or a link that points at a page that moved. |
memory <verb> | Agents hand-editing the memory format, and forgetting its index and wrapping. |
docs <verb> | Refreshing a generated diagram or screenshot and proving it is not stale. |
Linting and tests
blong-dev lint --files $(git diff --name-only)
blong-dev lint-staged # what the pre-commit hook runs
blong-dev test # tap, in the current package
blong-dev playwright --coverage
lint bundles its own cspell, eslint and tsc, so it works from any package without that package
declaring them. Passed files narrow the spell and lint work; the type check still covers the
package, because a file cannot be type-checked in isolation. lint-staged is the same command
applied per-package over git diff --cached.
test runs tap in the current package, keeps the TAP in .ci-report/ and prints a compact failure
view — enough that a failing suite is readable without reaching for --reporter. playwright wraps
the browser runner and builds the Allure report; report vitest exists so a package that runs
vitest (such as blong-browser) feeds the same .ci-report/ contract, which is what lets
ci-report aggregate a monorepo whose packages do not share a runner. report jest does the same
for a leg that is jest underneath — the Storybook test runner beside tap in tools/blong-log, whose
--json output is the document vitest already writes.
Diagnosing a CI run
blong-dev ci failures # the release-please PR, newest completed run
blong-dev ci failures --pr 200 # another pull request
blong-dev ci failures --run 622 # one run by number
blong-dev ci failures --full # + the reasons, read from the reports artifact
blong-dev ci runs --limit 10 # recent runs and what failed in each
blong-dev ci history blong-cli # one package's verdict and test time, run by run
ci-report writes the documents; ci failures reads them back and answers the question a red run
raises. Three sources are merged, because each is the only witness to something: the run's failure
bundle (which packages and tests failed, and how they stand against the base branch), the job log's
operation summary (an operation that failed while publishing no report of its own — the leg-gap of
build #622), and, behind --full, the reports artifact (the raw TAP stream a timeout is only
readable from, and a leg's own jest log).
With no flag it diagnoses the release-please pull request, which is the run that decides whether a
release can go out, and it says which run it chose and what it passed over — an in-progress or
cancelled run published nothing. It stays read-only (a diagnosis is not a fix), and it is
deliberately hypothesis-shaped: a verdict names the numbers it rests on, and a failure nothing
explains is left as unknown with the note saying what would settle it. See the blong-ci skill
for the vocabulary it reads.
The dictionary
blong-dev cspell add kukum mxbai # words, inserted where they belong
blong-dev cspell add --section ignorePaths "core/**/fixture/**"
blong-dev cspell check # non-zero when a list is not sorted
blong-dev cspell sort # put a drifted list back in order
blong-dev cspell list --section words | grep -x kukum
cspell.config.yaml holds two lists a person appends to — words and ignorePaths — and cspell
reads them in any order, so the order is only ever maintained by hand. That makes the insertion
point the whole cost of the edit, and an insertion one word off invisible in review.
cspell owns that decision. add places the value where it belongs, reports one the list already
has instead of repeating it, and quotes a glob the way YAML needs it; sort normalises a list that
has already drifted and drops an exact duplicate; check reports one that is not sorted and exits
non-zero, which is the gate for a hand edit. Everything else in the file — comments, blank lines,
the keys around the lists — is left byte for byte as it was, and a run that changes nothing does not
write the file at all.
The glossary
blong-dev glossary add adapter --definition "A component that exposes an external system as a high-level handler API and hides the protocol it speaks. See [adapter](./adapter.md)."
blong-dev glossary list
blong-dev glossary show adapter
blong-dev glossary check # non-zero on an unsorted entry or a dead link
The glossary is the repository's vocabulary: one term, one sentence, one
link to the page that explains it. A hand edit gets two things wrong — where the term belongs in an
alphabetical list, and how long a "short" definition may grow — so glossary owns both. add
inserts the term in sorted position and wraps the sentence to the repository width, replacing the
definition when the term is already present; list and show read it back, with --json for a
caller. check is the gate: it fails on a repeated or unsorted term, a definition over forty words,
a paragraph inside the managed region that is not an entry, a missing marker pair, or a link that
points at a file which does not exist. The region it owns is delimited by <!-- BEGIN GLOSSARY -->
and <!-- END GLOSSARY -->, so the intro above it is never touched.
The proxy: curl cannot speak MLE
The gateway's RPC endpoint is encrypted end to end, which is a good property and a bad development
loop. blong-dev proxy sits between plain HTTP and that endpoint: it performs the MLE handshake on
startup, optionally logs in, then forwards each request encrypted and returns the decrypted result
as plain JSON.
# Pre-authenticated: log in on startup (or from MLE_USERNAME / MLE_PASSWORD)
blong-dev proxy --port 8099 --target http://localhost:8080 \
--username testAdmin --password testPassword
# Manual login: the handshake happens through the proxy
blong-dev proxy --port 8099 --target http://localhost:8080 --no-login
curl -s -X POST http://localhost:8099/login/token/create \
-H 'content-type: application/json' -d '{}'
The method comes from the path (/rpc/gateway/bundle/find → gateway.bundle.find) or from a dotted
method in the body, so anything that can send HTTP can now call the gateway:
curl -s -X POST http://localhost:8099/gateway/bundle/find \
-H 'content-type: application/json' -d '{"params":{"paging":{}}}'
Traces and logs
blong-dev trace .playwright/results/my-test/trace.zip
blong-dev log --level error --name gateway --limit 20
blong-dev log 01J8Z6… # one entry, as JSON
A Playwright trace is a zip of JSON lines, and a failed test often captures no screenshot, so
trace prints the action timeline, the requests that failed and a console count. It reads every
numbered *-trace.trace chunk rather than the first one, because a run long enough to split its
trace keeps the interesting events in the later chunks.
log reads the entries the pino-cacache transport wrote to disk (default ~/.blong/log-cache,
$BLONG_LOG_CACHE to move it), which is what makes a log line from a run that has already finished
recoverable. It filters by level, service name, trace id, method or free text, and a ULID prints one
entry in full.
The development database
blong-dev sql "SELECT * FROM access_role"
blong-dev sql "SELECT 1" --output json
sql opens a MySQL connection from .blong_devrc (found in the current directory or its parents,
or in the home directory; the key defaults to srv.db.knex.connection) and falls back to the
development defaults, deriving the database name as <suite>-<user> when none is configured.
Multiple statements separated by ; run in one round trip. --output json exists for the non-TTY
case, which is what makes the command usable from an agent as well as from a terminal.
Docs and memory
blong-dev docs list # the register of generated artefacts
blong-dev docs check # regenerate, compare, restore — non-zero when stale
blong-dev docs verify # build/serve the site and load every page in Chromium
blong-dev memory add friction --title "…" --area core/blong-browser --body "…"
blong-dev memory search "why do agents edit memory files by hand"
blong-dev memory check
docs reads docs/blong/docs-artifacts.json, the register of everything in the documentation site
that is generated rather than written. check snapshots the destinations, runs the generators and
restores them, so a stale diagram is a non-zero exit rather than a silent difference in review;
verify starts its own static server (a Docusaurus build does not resolve absolute asset paths over
file://), loads every page and every blog post in Chromium, and reports the diagrams and images it
expected against those that rendered.
memory is the writer and validator of the agent memory files: it owns the identifiers, the section
an entry belongs to, the wrapping and the generated index, and check is the gate — it is wired
into the staged-file lint, so a malformed memory file fails a commit rather than a review comment.
Every write also pushes the entry it touched into a local Hindsight index, which is what
memory search reads; the markdown stays the source of truth, so an unreachable server costs a
warning and memory index --semantic rebuilds. See the
memory pattern for the settings and the backfill.
Why agents and people use the same commands
The reason this is one CLI rather than a set of convenience scripts is that the development loop and
the agent loop have the same shape. An agent asked to fix a failing test needs the same four things
a person needs: the test's output, the persisted log, the query against the development database,
and the memory file that records what was learned. When those are commands, both callers can run
them — and the repositories' own instructions say so, pointing agents at blong-dev memory,
blong-dev sql, blong-dev cspell and blong-dev lint by name.
Two small conventions keep that honest. Anything with output gets a machine mode (--output json,
list --json) beside its human one. And anything that would otherwise be a one-off shell
incantation becomes a command, so it can be tested and documented.
See also
- The log and the log viewer — what
logreads. - Playwright — the suite
playwrightruns andtraceexplains. - Memory — the format
memorywrites. - Glossary — the vocabulary
glossarymaintains. - Integration tests in CI — what
reportandci-reportproduce.