Repository navigation
Development
python3 -m pytest -q -n 16 # everything, the browser suite included: under 2 minutes
python3 -m pytest -q -n 16 -m "not browser" # the quick loop: Python only
npm test # the first line
npm run test:quick # the second
npm run lint # ESLint on static/
npm run relink # regenerate the frontend modules' import blocks
npm ci # once: ESLint and the parser the checks use-n is pytest-xdist (in requirements.txt): each
worker starts its own Argus, port, temp HOME, tmux socket and Chromium, so the browser suite runs
in parallel safely — 12 minutes on one worker, under 2 on sixteen. CI uses four.
The count changes most weeks; pytest is the truth. Node is used for checks only — the
app itself has no build step and no dependencies, and without npm ci those checks skip rather
than fail (CI sets ARGUS_BROWSER_REQUIRED=1, which turns a missing browser or linter into a
failure, so a runner that lost one cannot pass by testing nothing).
The tests never touch tmux's default socket. Neither should anything else you run against this code: a tmux server that dies takes every session on it with it, which is how this repository learned the rule the hard way.
The tooling, since a folder of unexplained scripts is worse than no folder:
scripts/openapi.py |
rewrites docs/openapi.json from the code. The test suite runs it with --check and fails if the published description has drifted, so this one is not optional. |
scripts/demo.py |
builds the instance every screenshot and clip is made from — fabricated sessions, fabricated files, on its own tmux socket. It exists so the pictures can be remade when the interface moves, and so that "none of this is a real machine" is something you can verify rather than take on trust. |
scripts/orchestra.py, scripts/referee.py, scripts/fullstack.py
|
three worked orchestrators, each run against real tmux before being committed — see Orchestrating several agents |
tools/argus_client.py, tools/argus-say
|
the Python client and a command-line front door onto it: the transport. Standard library only, one file, no package — see An agent driving Argus. |
tools/argus_orchestra.py |
the orchestration framework on top of it — the contract, the waiting, the names, the worktrees. It is why the three examples are twenty lines each: see Writing an orchestrator. |
scripts/surface.py |
lists every public name in tools/ and fails if one has no docstring, or — given a checkout of this wiki — if one is named nowhere on it. The test suite runs both; CI clones the wiki so the second one is real there rather than skipped. It exists because the client was once documented eight methods out of fourteen and nothing noticed. |
scripts/record.mjs |
drives a Chromium over the debugging protocol to record those clips, from the demo instance. |
scripts/modules.mjs |
npm run relink: regenerates the import block at the top of every frontend module from what the module actually uses. Also moves code between modules without breaking it — see below. |
scripts/apiref.py |
writes this wiki's API reference from docs/openapi.json (itself from scripts/openapi.py): python3 scripts/apiref.py ~/argus.wiki/API-reference.md. tests/test_wiki.py fails when it is not current, when a wiki link or #anchor goes nowhere, or an image points at a branch that does not exist — with the wiki cloned at ~/argus.wiki; skipped otherwise. |
tests/browser/ |
the browser suite: a real Argus driven by a real headless Chromium. See below. |
Plain ES modules in static/js/, no
bundler: edit, reload, done. It was one 16,600-line app.js until September 2026, split one
section per module with the behaviour unchanged and the browser suite green at every step.
app.js is still the entry point — one import — because the page, the service worker and
installed PWAs all know it by name; js/main.js is the start-up and imported by nothing else.
Three rules, each learnt during the split, and each checked by a test:
-
The imports are generated. Every module starts with a
// <imports>block listing what it uses from the others. Do not edit it: write the code, runnpm run relink. ESLint says when a name is used but not imported (no-undef), or when an imported variable is being assigned (no-import-assign— an ES import is read-only, so shared state has a setter in the module that owns it). A stale block fails the tests. -
Load order is not file order. The sections use each other in circles, so a module's
top-level code can run before a module it reads from is ready — "Cannot access 'prefs'
before initialization", and a page that never starts. Code that runs at load may only read
another module's variables if that module is in no import cycle (
state.js,dom.js,words.js,icons.js…).tests/js/loadorder.mjsfollows the calls, across modules, and names the whole chain when it finds one. - Every module is in the service worker's list, or an installed app breaks offline; and a module that is there for what it does at load (taking the token from the address) needs an import of its own, since nothing uses its exports.
tests/browser/ starts Argus as a
person would, with a config of its own, a folder of sample files, its own HOME and a tmux
socket named argus-t-… — never the default one — and drives it with headless Chromium over the
debugging protocol, at desktop and phone size. Every screen, and the things people do: a shell
from an empty session list and typing into it, rename and kill with and without confirming, a
markdown report with its figure (and its <script> neutralised), hidden files, theme, language,
desks, labelling a process.
Any problem fails the test by itself — an uncaught exception, a console error, a request of ours answering 4xx or 5xx, a module answered with HTML because the file is missing — so a test written to check one button also catches the error it set off somewhere else. It was checked against itself: bugs planted on purpose (a misspelt name in a rare path, a screen that throws, kill without confirmation, a broken figure URL…) all fail it. A terminal is checked by what a typed command does, never by reading the screen back.
Chromium is found in $ARGUS_CHROMIUM, then Playwright's cache, then the PATH.
Vendored under static/vendor/, unmodified, each keeping its own copyright header:
- xterm.js 6.0.0 — MIT
- marked 18.0.7 — MIT
- qrcode-generator 2.0.4 — MIT
- highlight.js 11.12.0 — BSD-3-Clause, the ESM build: the core and 27 languages, imported only when a file of code is opened
- pdf.js 4.10.38 — Apache-2.0, the build Argus draws every PDF with
-
swagger-ui 5.32.14 — Apache-2.0, the bundle and
its stylesheet, served at
/api/docs; FastAPI's own one-liner would fetch it from a CDN -
three.js 0.185.1 — MIT, the module build with
OrbitControlsandSTLLoader, for the 3D viewer -
occt-import-js 0.0.23 — LGPL-2.1, the OpenCascade
kernel compiled to WebAssembly, loaded only to read a STEP file. It has its own
NOTICE.mdexplaining the licence - mermaid 11.16.1 — MIT, the minified ESM build with its chunks and without its source maps: 3.7MB on disk and none of it fetched until a document with a ```mermaid fence in it is opened, at which point the entry and the chunks for that one kind of diagram are what load
-
diff2html 3.4.56 — MIT, the UI bundle without its
highlighter (Argus's highlight.js is handed to it) and its stylesheet: the built-in
diffviewer pack, which draws.diff/.patchfiles and the Changes window -
Material Icon Theme 5.39.0
— MIT, the file icons only (633 SVGs, 512 KB) and their extension and file-name map, cut from
the package by
scripts/fileicons_vendor.py; each loaded when a file of its kind is listed. The genomics ones (sequences, alignments, variants, trees, annotations, pipelines) are Argus's own, instatic/img/fileicons/
Optional at runtime: pandoc for Word documents, and pdftotext
(poppler) to search inside PDFs. Without either, those files still open — they are just
plainer.
Using it
- Starting an agent
- Sessions and the terminal
- Files
- Documents
- Viewers of your own
- Mounting a folder on your computer
- Syncing a folder with your computer
- Desks and windows
- Teams
- Two agents (older)
- The machine
- The journal
- Notifications
- Claude, Codex, Gemini compared
- Keyboard shortcuts
- On a phone
- What an agent can do
- An agent driving Argus
- The Argus plugin
- Orchestrating several agents
- Writing an orchestrator
Setting it up
When something is wrong
All of it