Your Config
The TUI is yours to shape. Add commands, bind keys, build your own palettes, hook the lifecycle — in TypeScript, fully typed, with no build step and no imports.
Configuration lives with you, not with your agents. An agent is portable: it carries its own modules, prompts and tools wherever it runs. How your terminal behaves is a different thing entirely — it belongs to the person at the keyboard, and it follows you across every agent you run.
Where it lives
Your profile is a real directory, and everything in it is a real file.
~/.axon/profiles/<your-email>/
├── main.ts # your config — commands, keys, palettes
├── profile.config.ts # which extensions load
├── plugins/ # lifecycle hooks; every file here loads automatically
├── extensions/ # appears when you install one
├── agents/ # your agent projects
├── .axon/ # generated types — never edit
└── store/ # credentials, history, settings — written by Axon
The split is deliberate. Everything at the top level is written by you. Everything
under store/ is written by Axon and is not meant to be edited by hand. .axon/ is
regenerated on every launch.
Axon creates this on first run. If a file is missing it is written; if you have edited one, it is left exactly as you wrote it.
Your first command
Open main.ts and add:
commands.register("hello", async () => {
tui.info("hello from my config")
}, { description: "Say hello" })
Restart Axon, press :, and type hello. Your command sits in the palette beside the
built-in ones, with your description, tab-completion and all.
That is the whole loop. No imports, no wrapper, no build step — commands is a global,
the file runs on launch, and registering is a side effect of it running.
Everything is typed
Seven globals are available in every file in your profile:
| Global | What it controls |
|---|---|
tui | The terminal — navigation, messages, lifecycle hooks, quit |
palette | Your own palettes, and asking the user a question |
commands | The : command tree |
keys | Key chords |
mode | Mode switching |
input | The message box |
agents | Agent instances — spawn, focus, stop, send |
They are ambient: there is nothing to import, and your editor autocompletes all of them.
Type commands. and see. That is the intended way to explore the API — the types are the
reference, and they are always current.
Axon writes those declarations into .axon/types/ every time it launches, so an upgrade
that adds a verb makes it available in your editor without you doing anything.
Splitting it up
A config grows. Split it whenever you like — an import is all it takes:
// main.ts
import "./keybindings"
import "./commands"
// ...anything else you want here
// keybindings.ts — no export, no wrapper
keys.register("ctrl+p", async () => {
await palette.open("files")
})
Importing the file is loading it. The registrations happen as the module body runs,
exactly as they do in main.ts.
Files load in the order you import them, which matters when two things want the same name — see Commands & Keys.
Reloading
Save the file. That is the whole loop — Axon watches your profile and reloads on write, so the moment your editor saves, the config is live: new commands appear in the palette, rebound keys fire, renamed things stop existing, a changed theme repaints.
Installing and uninstalling work the same way. axon install @cody/ember-theme
writes to your profile, and a running terminal picks it up without being told.
:reload does the same thing on demand, for when you want it explicitly.
Every registration from the previous load is undone first, so a command you renamed disappears rather than lingering beside its replacement. If a palette or a question is open when you reload, it closes: the definition behind it is about to be replaced.
:reload is your CONFIG. :reboot is the running agent. They are different
things, and neither disturbs the other.
When something breaks
Your config is code, so it can throw. When it does, Axon tells you: the error appears in the chat view with a code, the file, and your own error message.
AX-EXT-008: main.ts Failed to Load
Your profile's main.ts threw while being evaluated. Anything it registered before
the error survives; everything after it did not run. Plugins and extensions still load.
A broken config never costs you the terminal — you get the working terminal you need in order to go fix it. Containment is per file: one bad plugin disables itself, its siblings keep working, and everything a file registered before it threw stays registered.
What this is not
This layer is for workflow design — the shape of your terminal. It is deliberately not a place to build agent behaviour.
You can drive a conversation from here: spawn an agent, send it a message, stop it. You
cannot read what it says back. agents.send() returns nothing, on purpose — reacting to
what a model produced is an agent's job, and Axon has a first-class way to build that.
See Cognets.
What's next
Commands & Keys — the : tree, key chords,
and what happens when two things want the same name.
Palettes — your own filterable, tabbed lists, and asking the user a question.
Plugins & Hooks — running code when something happens.
Extensions — packaging your config so someone else can install it.