PluginsEarly
Methods
The calls on $, the object a plugin's default export is given, and the capability each one needs.
From the plugin interface of gr 0.2.16 (API 0.1, 0.2, 0.3). gr plugin api methods prints it on your machine.
Every method is on $. One that needs a capability works only when the plugin asks for it in plugin.toml and the person grants it; otherwise the call fails with capability.undeclared or capability.denied. Methods marked async return a promise.
$.editor
$.editor.context(): Context
This event's context, or the last the editor sent for this root; text only with fs:read:workspace.
Sync
$.editor.run(...cmds: Cmd[]): void
Commands, in order, applied by the editor at its next frame.
Sync
$.views
$.views.refresh(view: string): void
Render a view again.
Sync
$.status
$.status.set(segment: string, value: Segment | null): void
Set or clear one of the plugin's status segments.
Sync
$.badges
$.badges.set(set: string, values: Record<string, Badge | null>): void
Put or remove badges on the map's cards, by node id, in one of the plugin's sets.
Sync
$.badges.clear(set: string): void
Remove every badge of a set.
Sync
$.ui
$.ui.select<T extends SelectItem>(items: T[], opts?: { title?: string; placeholder?: string }): Promise<T | undefined>Ask the person to choose one; undefined when dismissed.
Async
$.ui.input(opts: { title: string; value?: string; placeholder?: string }): Promise<string | undefined>Ask the person for a line; undefined when dismissed.
Async
$.state
$.state.get<T extends Json>(key: string): T | undefined
A value of this instance's state (one per plugin and root, held by the daemon for the session); a view that reads it re-renders when it is set.
Sync
$.state.set(key: string, value: Json): void
Set a value of this instance's state; at most 4 MiB an instance.
Sync
$.state.delete(key: string): void
Remove a value of this instance's state.
Sync
$.state.keys(): string[]
The keys of this instance's state.
Sync
$.store
$.store.get<T extends Json>(key: string): T | undefined
A value of the plugin's persisted store.
Sync
$.store.set(key: string, value: Json): void
Set a value of the persisted store; at most 1 MiB a plugin.
Sync
$.store.delete(key: string): void
Remove a value of the persisted store.
Sync
$.store.keys(): string[]
The keys of the persisted store.
Sync
$.settings
$.settings.get<T extends Json>(key: string): T
One of the plugin's settings, or its default.
Sync
$.settings.all(): Record<string, Json>
Every setting of the plugin.
Sync
$.graph
$.graph.node(id: string): Promise<GraphNode | undefined>
A node of the root's graph.
Async · needs graph:read
$.graph.children(id: string, opts?: { limit?: number }): Promise<GraphNode[]>A node's children; at most 500.
Async · needs graph:read
$.graph.neighbors(id: string, opts?: { direction?: "in" | "out" | "both"; kinds?: string[]; limit?: number }): Promise<Neighbor[]>A node's neighbours by edge; at most 200.
Async · needs graph:read
$.graph.search(query: string, opts?: { kind?: string; limit?: number }): Promise<GraphNode[]>Nodes by words; at most 50.
Async · needs graph:read
$.graph.create(node: GraphCreate): Promise<GraphNode>
Create a node through the MCP authored writer, attributed as plugin:<name>. Writes are not undoable, like every graph writer since git-tied graphs. At most 10 write calls a second per plugin across its instances and roots (graph.rate).
Async · needs graph:write · since API 0.2
$.graph.patch(id: string, patch: GraphPatch): Promise<void>
Rename, describe or tag a node in this root, attributed as plugin:<name>, not undoable. Extracted identity is refused (graph.derived); descriptions and tags may annotate extracted nodes. An authored tag goes to the mirror, any other to derived storage, as MCP TagNode routes it. graph.changed reaches the writer too.
Async · needs graph:write · since API 0.2
$.graph.move(ids: string[], parent: string): Promise<void>
Move at most 1,000 selected nodes under a node in this root, through the MCP writer with one call's group, attributed as plugin:<name>, not undoable. Extracted nodes are refused as graph.derived. parent must be a nonempty node id.
Async · needs graph:write · since API 0.2
$.graph.remove(ids: string[]): Promise<void>
Remove at most 1,000 selected nodes in this root through the MCP writer with one call's group, attributed as plugin:<name>, not undoable. Children must be selected too; selected children are removed first. Extracted nodes are refused as graph.derived.
Async · needs graph:write · since API 0.2
$.agents
$.agents.list(opts?: { scope?: "workspace" | "repository" }): Promise<Agent[]>The agents' roster, without transcripts: the root's (scope workspace, the default), or every worktree's of its repository (scope repository, since 0.2)
Async · needs agents:read
$.agents.get(id: string): Promise<Agent | undefined>
One agent of the roster.
Async · needs agents:read
$.agents.transcript(id: string, opts?: { since?: number; limit?: number }): Promise<TranscriptBlock[]>What an agent of the roster (agents.list, either scope) and its person have said, oldest first: since (ms since 1970) keeps the blocks of turns that began then or later; limit keeps the newest that many (100 when not said, at most 1,000). Thinking, tool results and command output are never in it. Each text at most 4 KiB, the answer at most 256 KiB, the oldest left out first; [] for an agent whose conversation the daemon cannot read (a terminal, a Codex agent not running). Example: const last = (await $.agents.transcript(id, { limit: 1 }))[0].
Async · needs agents:read · since API 0.2
$.agents.send(id: string, text: string): Promise<void>
Write a message to an agent of the roster and send it, as the person would from its composer: a Claude Code agent's terminal takes it, queued if it is busy; a Codex agent takes it as its next prompt. Refused (agents.unreachable) for an agent not running, a terminal agent, or one showing a dialog, where it would answer the dialog. At most 20 a minute a plugin (agents.rate) and 64 KiB of text (agents.bound), with no control character but newlines and tabs (result.shape: in a terminal an escape sequence is a key); the person's message line says which plugin wrote to which agent, once a minute at most. Example: await $.agents.send(id, "Run the tests again and say what failed.")
Async · needs agents:write · since API 0.2
$.agents.start(opts?: { prompt?: string; harness?: "claude-code" | "codex"; worktree?: string; name?: string }): Promise<{ id: string }>Start a new agent in the root, or in worktree (a worktree worktrees.list or the roster knows), with the harness the person starts new agents with unless harness says, called name (at most 80 characters), its first message prompt (which needs agents:write as well). Starting an agent costs money: at most 3 every 10 minutes a plugin unless the person raises [plugins.<name>.limits] agent_starts (at most 30), refused past it (agents.rate). Each start is said on the person's message line, and the agent's row says which plugin started it (Agent.startedBy). Example: const { id } = await $.agents.start({ prompt: "Find out what the build error means", name: "research" })
Async · needs agents:start · since API 0.2
$.fs
$.fs.read(path: string, opts?: { maxBytes?: number }): Promise<string>A file inside the root (or the plugin's own folder, which needs nothing); 1 MiB unless maxBytes says more, at most 4 MiB.
Async · needs fs:read:workspace
$.fs.stat(path: string): Promise<Stat | undefined>
A path inside the root.
Async · needs fs:read:workspace
$.fs.files(glob: string, opts?: { limit?: number; ignoreCase?: boolean }): Promise<string[]>Files inside the root matching a glob, minding .gitignore, letters matching in either case with ignoreCase (since 0.2); at most 10,000.
Async · needs fs:read:workspace
$.fs.readData(name: string): Promise<string | undefined>
A file of the plugin's own data folder; a name, no separators.
Async
$.fs.writeData(name: string, text: string): Promise<void>
Write a file of the plugin's own data folder.
Async · needs fs:write:data
$.exec
$.exec.run(program: string, args: string[], opts?: { cwd?: string; input?: string; timeoutMs?: number }): Promise<ExecResult>Run an approved program in the sandbox: the root read-only, no network, five environment names; arguments are data, never shell; 30 s and 1 MiB each way.
Async · needs exec:<program>
$.net
$.net.fetch(url: string, opts?: { method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; headers?: Record<string, string>; body?: string; timeoutMs?: number }): Promise<FetchResult>HTTPS to a granted host; never Graphide's own hosts, private or metadata addresses; 30 s, 5 MiB, 5 redirects, no cookies.
Async · needs net:<host>
$.log
$.log.info(...a: unknown[]): void
A line in the plugin's log.
Sync
$.log.warn(...a: unknown[]): void
A warning in the plugin's log.
Sync
$.log.error(...a: unknown[]): void
An error in the plugin's log.
Sync
$.clock
$.clock.now(): number
Milliseconds since the epoch; fake time in tests.
Sync
$.worktrees
$.worktrees.list(): Promise<Worktree[]>
The worktrees of the root's repository, the root's own marked current; none outside a repository.
Async · needs agents:read · since API 0.2
$.git
$.git.changes(): Promise<GitChange[]>
The paths of the root's working tree that differ from HEAD, untracked ones too; at most 10,000; none outside a repository.
Async · needs fs:read:workspace · since API 0.2
$.clipboard
$.clipboard.write(text: string): void
Put text on the person's clipboard, at most 1 MiB: a clipboard.write command, carried as any write is.
Sync · needs clipboard:write · since API 0.2
$.terminal
$.terminal.open(opts?: { command?: string; cwd?: string; title?: string }): Promise<{ id: string }>Open a terminal in the root (or cwd, a folder inside it) running the person's shell, as they would, and type command into it; the editor shows it at once; at most 4 open a plugin.
Async · needs terminal · since API 0.2
$.terminal.close(id: string): Promise<void>
Close one of the plugin's own terminals.
Async · needs terminal · since API 0.2
$.docs
$.docs.list(): Promise<DocInfo[]>
The documents open in an editor of this root, of the languages [documents] names, or all of them when it names none.
Async · needs fs:read:workspace · since API 0.2
$.docs.read(path: string): Promise<Doc | undefined>
A document's text as the editor has it, saved or not; undefined when it is not open in an editor of this root, or is too large (use fs.read)
Async · needs fs:read:workspace · since API 0.2
$.docs.lines(path: string, from: number, to: number): Promise<{ version: number; lines: string[] } | undefined>Lines from to to (exclusive, at most 5,000) of a document and the version they are of; undefined as docs.read.
Async · needs fs:read:workspace · since API 0.2
$.decorations
$.decorations.set(set: string, path: string, version: number, marks: Decoration[]): void
Put marks in a document, in place of what the set held there: set is one of the plugin's own [[decorations]] ids, path is within the root, version is the document's version the marks were computed against (from a document event, $.docs or the context's file.version), or 0 for the file as saved. Sent, not awaited; the editor carries the marks through the changes made since version and drops a mark whose text a change touched. Over a bound (10,000 marks, 256 KiB of text a set a document) it throws decorations.bound and the set held before stays. Example: $.decorations.set("todos.marks", e.path, e.version, [{ kind: "underline", from: { line: 3, column: 7 }, to: { line: 3, column: 11 }, style: "wavy", tone: "warning" }])
Sync · since API 0.2
$.decorations.clear(set: string, path?: string): void
Take a set's marks away: from one document, or with no path from every document.
Sync · since API 0.2
$.context
$.context.set(key: string, value: boolean | number | string | null): void
Set one of the plugin's own context keys (<plugin>.<name>, - written _), which its when clauses and the person's test; null takes it away: a context.set command, carried as any write is.
Sync · since API 0.2
$.lsp
$.lsp.diagnostics(opts?: { path?: string }): Promise<Diagnostic[]>What the language servers say of the root's files now, or of the one file path names: errors first, then warnings, then the rest, each by path and place; at most 1,000, so an answer of 1,000 may have been cut. None is an answer (a server with nothing to say, or one still starting); a root that can have no server (not trusted, or [lsp] switched off) fails lsp.no_server.
Async · needs fs:read:workspace · since API 0.2
$.lsp.symbols(path: string): Promise<DocSymbol[]>
The symbols of a file of the root, as its language server lists them, a tree where the server gives one; at most 5,000 with their children counted and 1,000 at the top, the rest left out. Starts the file's server when none runs. Fails lsp.no_server where no server serves the file, lsp.unsupported where its server lists no symbols.
Async · needs fs:read:workspace · since API 0.2
$.setup
$.setup.read(opts?: { harness?: "claude-code" | "codex"; launch?: "gr" | "plain"; budget?: number }): Promise<HarnessSetup>What a coding agent loads when it starts in the root, and from where, with every check run: harness claude-code (the default) or codex, launch gr (the default) or plain, budget the size check's budget in estimated tokens (20,000 when not said). Read from this machine's files each call. Example: const s = await $.setup.read({ harness: "codex" })
Async · needs setup:read · since API 0.2
$.setup.layer(on: boolean): Promise<SetupLayerState>
Keep the Agent setup layer in the root's graph (on), rebuilt from both harnesses' gr setups, or let it go: one Agent setup container (kind module, tag layer:setup) of nodes of kind setup, local to this machine, never in the repository's mirror or the cloud. The layer stays while any instance holds it on, and goes when the last lets go; setup.unavailable for a root whose graph is not open. Example: await $.setup.layer(true)
Async · needs setup:read · since API 0.2
Removed
These were in earlier versions of the interface. Use the replacement instead.
| Was | Use instead |
|---|---|
editor.open (method) | a Cmd: { cmd: "open.code", path, line } returned from the handler, or $.editor.run(...) |
editor.message (method) | a Cmd: { cmd: "message", text, level } |
views.show (method) | a Cmd: { cmd: "panel.show", panel } |
views.hide (method) | a Cmd: { cmd: "panel.hide", panel } |
views.toggle (method) | a Cmd: { cmd: "panel.toggle", panel } |
views.float (method) | a Cmd: { cmd: "float.show", view, anchor } |
pickers.open (method) | a Cmd: { cmd: "picker.open", picker, query } |
badge (element) | the element tag, the same props |
agent.finished (event) | the event agent.stopped, the same payload |
These docs describe Graphide 0.2.16, the latest release. Something wrong or missing? Tell us onDiscord or at hello@graphide.com.