Skip to main content

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​

CommandThe problem it removes
lint [files…]Which of tsc, cspell and eslint to run, with which config, over which files.
lint-stagedThe same, fanned out over every package the staged files belong to.
testA 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-reportAggregating 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.
proxyThe 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​