Skip to main content
agentx serve is what the desktop app runs in the background. It scans the machine once, then rescans whenever anything it inventories changes, and prints the snapshot again only when the inventory changed. You rarely need to run it yourself.

What counts as a change

Serve watches agentx itself, the library and every skills directory an agent client reads, including the ones clients keep under their own configuration such as ~/.claude/skills. An edit inside a skill counts, not only a skill added or removed. Edit a skill with any editor or another tool and the next snapshot shows it. A change outside the inventory, such as a skill in a directory no installed client reads, causes a rescan that prints nothing.

Run it

The first line is the whole snapshot. Every later snapshot line means the inventory changed; a change that leaves the inventory as it was prints nothing. Close the process’s stdin, or press Ctrl-C, to stop it. Either way serve prints its result and exits 0. Without --json it prints one line per event instead:

Drift

With --json, serve also tells you when a skill in your library changes state. After a snapshot in which a skill’s state or drift changed, serve prints one drift line for that skill:
state and drift are the skill’s states now, previous_state and previous_drift what the snapshot before showed, and scan_counter names the snapshot the change is in. Save an edit to a skill that was current and the drift line follows within a second, right after the snapshot that shows it; later edits to the same skill leave it modified and bring no new drift line. Saving a file git ignores, such as a .DS_Store or an editor’s swap file, brings no drift line: see Files that do not count. The states are those of agentx skill list. A drift line is a notification: the snapshot before it already holds the skill as it now is. The first snapshot, and a skill that is new in the library or gone from it, bring no drift line. If a managed skill’s directory is deleted from the library, the snapshot’s warnings names the skill, with the commands that install it again or stop managing it; see A skill whose library directory is gone.

Update checks

Serve checks your managed skills for updates as agentx skill check does: once when it starts, right after the first snapshot, and then every thirty minutes. Each check also fetches every source you added, including the ones you have not installed anything from, as agentx source fetch --all would, so that search results and updates follow what your sources hold now. With --json, each check prints one update_available line per skill that has an update, the same event skill check prints, with serve’s instance_id added:
A machine without any source has nothing to fetch, and serve runs no git for it. Every check lists every available update again, so the lines repeat on every check while the update is available. A check records what it found, so the snapshot that follows shows each skill’s candidate and any upstream removed state. Nothing is ever applied. A source that cannot be fetched, because you are offline or the repository moved, is a warning starting update check: that names it and any skills it left unchecked. Serve warns once, not on every check: it warns again only when the reason changes, and when the source can be fetched again it says so on a line of its own:
The checks in between log the failure as a debug line, which --verbose shows. Serve keeps running meanwhile, and the snapshot still shows each skill’s candidate. A skill whose new version agentx refuses to import, and a source that was fetched but could not be read, are warned about on every check. The other update check: warnings say what an update leaves out or renames, or that a whole check failed. Checks run in the background: scans and requests are answered while a check waits on the network, and a check still running when the next one is due is not started twice. To check more or less often, set AGENTX_CHECK_INTERVAL to a duration such as 10m or 2h before starting serve. Serve refuses to start with exit code 1 when it is not a positive duration. Without --json serve prints one line per update:

Ask for a fresh snapshot

Write one JSON line to its stdin:
Serve scans again and answers with a refresh_complete line carrying your request_id and the counter of the current snapshot. If the inventory changed, the new snapshot line comes first. A line serve does not understand gets an error line; serve keeps running. Serve takes two requests: refresh, and search below.

Search your sources

Write one JSON line to its stdin:
Serve answers with a search line carrying your request_id, the query and results: every skill of every source you added whose name or description contains the query, ignoring case. Each result names the source it is in by URL, the skill’s subpath in that repository, its name, its description and the tree id of the skill directory. Results are sorted by source, then name. Nothing matching is an empty results array. Pass a result’s source and subpath to an install to take that skill. Serve answers from memory, so a search is immediate: it reaches neither git nor the network, and it does not wait for a scan in progress. It lists the skills of each source when it starts and again whenever you add, re-fetch or remove one, so results follow agentx source add, agentx source fetch and agentx source remove on their own. Its update checks fetch every source too, so a skill added upstream shows up in results within one check interval. A source you have not fetched has no skills to search, and serve says so once as a warning, not again each time an update check brings another source new skills. Its next update check fetches it when it can, and otherwise warns about that too, once. It cannot fetch a source that agentx import brought until you add it again. Run agentx source add <url> to fetch it, or agentx source add <url>#<pin> for a pinned source so that it stays pinned. A source serve could not read is warned about on every scan until it can read it, since each scan tries it again, so it comes back without your having to restart serve. A search without a request_id or without a query gets an error line; serve keeps running. Without --json serve prints one line per answer:

Run one pass

Scans once, prints the snapshot and exits. It shows what the app receives on start; for an inventory to read, agentx scan prints the same snapshot. With nothing to compare against, it prints no drift line, and it runs no update check.

One serve per home

Only one serve runs per agentx home. Starting a second one exits with code 6 and names the lock file, serve.lock in agentx home. Stop the running one first.

When watching is not possible

If a directory cannot be watched, serve exits with code 6 and an error naming the directory and the cause. On Linux this usually means the inotify watch limit is reached; raise fs.inotify.max_user_watches. Serve never polls instead. On macOS the released binary watches through FSEvents, which handles a library of any size. A macOS binary built without cgo watches through kqueue instead, which needs one open file per watched file and reaches the default limit of 256 with a few dozen skills; raise the limit with ulimit -n, or use the released binary. A skills directory that does not exist yet is not watched. Serve picks it up at the next rescan after it appears.