Scripts and agents
Everything Skwir does is a named command, and scripts and AI agents can run the same commands in your open window. What they would change comes to you as a plan, and nothing runs until you apply it.
Turn it on
Open Settings › Plugins and turn on Agents and scripts. The open window then listens on a channel only your account can reach; it closes with the window, or when you turn the plugin off.
It is off by default because, once on, any program running as you can reach the window, as it can your files. What an agent may do through it is limited (What an agent may do); a script you run yourself acts as you.
Every action is a command
Under its windows and menus, Skwir is a list of named commands. A key, a line of the palette, a menu item and a button each run one: a name plus arguments, as JSON. Scripts and agents run the same ones. Plugins register their own, and turning a plugin off withdraws them.
Every command carries a title, a description and the JSON Schema of its arguments, made from the code that handles it, so they can’t drift apart. Arguments are checked before anything runs, whoever sent them. A command answers with done, a value, or a plan.
The commands command lists them all: about 150, plus the window’s own ui.* commands. One entry, abridged:
Some of them, by family:
| Family | Commands |
|---|---|
| Places, selection and file operations | navigate, back, forward, up, select, entries, sort, filter, rename, copy, move, trash, mkdir, new_file, undo, redo, plan_diff, apply_plan, commands |
| Search | query.search, query.refine, query.save, query.describe, query.similar |
| Tags and notes | tags.edit, tags.set, tags.rename, tags.list, notes.rate, notes.comment |
| Places on the map | geo.search_area, geo.geotag |
| Batch changes | rename_bulk, rename_bulk.preview, edit_as_buffer, shelf.add |
| Tabs and more | tab.new, timeline.select, jump.to, open_with, shell.run, settings.apply, plugins.list, plugins.set_enabled |
| The window itself | ui.view, ui.peek, ui.split, ui.toggle_preview, ui.sort, ui.zoom, ui.palette, ui.settings |
A command acts on the files it names, by absolute path, in its entries; with none named, it acts on the active pane’s selection. A relative path is refused: the window’s working folder is not yours.
Scripts: skwir cmd
skwir cmd runs one command in the open window and prints its answer as JSON:
A command that changes files prints its plan. With --apply, the plan is applied as your own action, as a key would apply it, and you can undo it in the window.
With no window open, the command runs in a Skwir of its own, without a window: a reading answers, a plan is printed, or applied with --apply. The window’s own commands (ui.*) need the window.
Agents: skwir mcp
An agent’s MCP client starts skwir mcp, which carries the agent’s requests to the open window. For Claude Code:
Other clients take it as a local server that runs skwir with the argument mcp, often in a configuration like this one:
Every command is a tool, with its description and argument schema. Its dots are written __: tags.edit is the tool tags__edit. If no window is listening, skwir mcp says to open Skwir and turn on Agents and scripts, and starts nothing.
What an agent may do
Every command says what it does when it is registered, and an agent’s call is held to it:
| The command… | When an agent calls it |
|---|---|
| reads, or changes only what the window shows | runs |
| changes files | its plan waits in the window for your review; the tool answers once you have applied it (with what applied) or dismissed it |
| does anything else: a setting, a plugin turned on or off, a program started, an undo | waits for you to Allow it in the window (“An agent asks to run …”) |
| is yours alone: applying a plan, pressing a button, answering the window | is refused, and not offered to the agent at all |
So an agent can never apply its own plan, answer a question it caused, or press a button for you. The tool list marks which tools ask first, and which change nothing.
A script you run with skwir cmd is held to none of this: it is you.
Plans and review
A bulk rename, a folder edited as text, geotagging photos from a track, and anything an agent proposes all come as a plan: an ordered list of operations on files identified by who they are, not just by name.
Before it runs, Skwir checks each step against the disk and marks it pending, already done or in conflict, with the reason, and shows you the list. Nothing happens until you apply it. Every step it applies is recorded with its inverse (renames, moves, copies, the Trash, tags and photo locations), so you can undo it for as long as Skwir is open; a step that can't be undone is marked not undoable. Applying the same plan twice changes nothing the second time.
Private folders
Folders you mark private in Settings › Privacy never leave this machine. From the start, they are where keys and credentials live (~/.ssh, ~/.gnupg, ~/.password-store, the keyrings, ~/.pki, ~/.aws, ~/.kube, ~/.docker, ~/.netrc), and any folder holding a file named .private, each with everything under it.
- An agent never names one: a call that does is refused.
- An agent never sees one: its answers leave out every private file, and every text that names one. What it counts leaves them out too, so the numbers are those of a window without them.
- A plugin from outside Skwir is never handed one.
What Skwir gathers about a whole folder in the background, like the sizes in the Usage view or the timeline over a folder and everything below it, includes private folders: an agent can learn how much they hold and when it changed, never their names nor what they hold.
Searching and settings from a command
query.search takes the same text as the address bar; Searching lists what it understands. A few examples:
expr |
Finds |
|---|---|
*.pdf modified<7d |
PDFs changed in the last week |
kind:image,video size>10M |
Photos and videos over 10 MB |
content:"invoice" -ext:eml |
Files that mention “invoice”, except emails |
tag:coast rating>=4 |
Your best-rated coast pictures |
near:48.38,-4.49~5km |
Anything taken within 5 km of a place |
query.describe breaks a search into its parts (where it looks, words, kind, date, size, tag, content), each with the command its choices run.
Settings are one file, checked against its schema and changed through settings.apply, so what you can set by hand, a script can set too, and an agent can ask to. Your keyboard shortcuts are records of the keyboard.bindings setting, each naming a command:
| Keys | Command | Arguments | When |
|---|---|---|---|
ctrl-alt-g |
ui.view |
{"id": "grid"} |
pane |
ctrl-alt-r |
rename_bulk |
{"template": "{date:%Y-%m-%d} {name}.{ext}"} |
pane selection |
g d |
navigate |
{"location": "~/Downloads"} |
pane |
What comes next
- Plugins from outside Skwir are here, and experimental: a folder with a
plugin.tomland a WebAssembly module, or a program that speaks JSON lines. Each stays off until you turn it on, and gets nothing it did not declare. The protocol may change before it is declared stable; Writing a plugin explains it. - Skwir’s own assistant is planned. You will ask in plain words (“organise this folder”, “what is safe to delete?”); its tools will be these commands, its answer a plan, and it will never run anything itself. Local models first; remote ones only if you opt in, with a visible sign while anything leaves your computer.
This page is also served as Markdown at /agents.md, for agents that would rather not read HTML.