← For developersExtend it · Plugins

Ship an automation,
not an app.

A plugin adds your own commands to the ilml CLI. Publish one package and it runs in someone else’s terminal, under their own account, writing into their graph — so the login screen, the settings UI and the database are all things you never build.

The whole thing

A manifest and a script.

A plugin is an npm-style package: a manifest that names your commands and the settings they need, plus the code that runs them. Install it once and it’s available in every login scope on that machine.

ilml-plugin.json — the manifest
{
  "name": "acme",
  "version": "1.0.0",
  "commands": {
    "sync":   { "run": "node dist/sync.mjs" },
    "report": { "run": "node dist/report.mjs" }
  },
  "config": { "sections": [
    { "title": "Acme account", "fields": [
      { "key": "ACME_TOKEN", "label": "API token",
        "type": "secret", "required": true } ] },
    { "title": "iLiveMyLife", "fields": [
      { "key": "REPORT_NODE", "label": "Where to post",
        "type": "nodeId", "required": true,
        "hint": "Reports land in this node's chat" } ] },
    { "title": "Limits", "fields": [
      { "key": "PER_RUN", "label": "Items per run",
        "type": "number", "default": 50 } ] }
  ] }
}
dist/sync.mjs — the command
import { getPluginConfig, getCachedUser,
    createGraphClient } from '@ilivemylife/graph-sdk'

// No arguments: the SDK walks up to your manifest,
// and THROWS if a required field is still blank —
// naming the keys and the command that sets them.
const cfg = await getPluginConfig()

// who is running it — no network call
const user = getCachedUser()

// the SDK, as that user
const graph = createGraphClient({
    token: process.env.ILML_TOKEN })

await graph.addItem(cfg.REPORT_NODE, {
    title: `Acme sync — ${user.displayName}` })

graph.destroy()

That’s the contract in full: the CLI finds your manifest, collects what’s missing, injects the token, and runs the command the user typed.

What you don’t build

Three quarters of the product, already there.

The reason a plugin is a weekend and an app is a quarter: sign-in, settings and storage arrive with the runtime.

The user is already signed in

The CLI hands your process ILML_TOKEN, so the whole SDK works as that person — their nodes, their permissions, nothing of yours to store.getCachedUser() tells you who they are without a network call.

Settings you didn’t have to design

Declare them in your manifest, grouped into sections, each field typed —secret, number, url, path, evennodeId for “pick a node in your graph”. The CLI collects what’s missing, stores it and masks the secrets; your command refuses to start until it’s complete.

A database with an AI in it

Write results straight into the user’s graph — and ask askLifebot about their own data mid-run, with a node as the context. No schema to design, no prompt to engineer, no storage to host.

stuck on a field? ask the user’s own AI
import { LifebotTimeoutError } from '@ilivemylife/graph-sdk'

// A form wants something you don't know. Lifebot does —
// it reads the node the user pointed you at, so the
// answer comes from THEIR data, on THEIR token.
try {
    const reply = await graph.askLifebot(
        cfg.REPORT_NODE,
        `Form field "${label}" — reply with ONLY the value.`,
        { timeout: 120000 })            // max 300000

    await form.fill(reply.content)      // and carry on
} catch (e) {
    if (e instanceof LifebotTimeoutError) retryLater()
}

// askLifebot forces a reply and waits for it — no
// "@Lifebot" prefix, no polling, no prompt plumbing.
// This is how the LinkedIn plugin answers job forms
// in the user's own voice.
Through MCP

Their assistant finds your plugin.

The MCP server reports installed plugins to whatever AI the user talks to — name, version, commands, and which settings are still blank (secrets masked). So the assistant can suggest your command at the right moment, and run it in the terminal.

what the assistant sees, and then runs
graph_plugins        → acme 1.0.0 · sync, report
graph_plugin_config  → ACME_TOKEN ✓ · REPORT_NODE (unset)

$ ilml plugin config acme set REPORT_NODE <node>
$ ilml acme report
Distribution

Publish it your way.

A plugin is a tarball. Put it on npm and everyone types a short name — or host it yourself and hand the URL only to the people you choose.

1

On npm — short name, real updates

Publish as ilml-plugin-<name> and the user just types ilml plugin install <name>. npm sources are version-aware: ilml plugin update compares the installed version against the latest and downloads only when there’s something new.

2

Or from your own URL — private, or paid

ilml plugin install acme https://you.example/acme-1.0.tgz installs from anywhere you can host a file. Nothing goes through us: who gets the link, and on what terms, is between you and them.

3

While you build it — straight off disk

Point the installer at a local tarball with file:///…, or run your script directly and let the SDK read a .env instead of the stored settings. Same code path, no publishing round-trip.

the whole lifecycle, from the user’s side
ilml plugin install acme                 # npm:ilml-plugin-acme
ilml plugin install acme npm:ilml-plugin-acme@1.2.0
ilml plugin install acme https://you.example/acme.tgz
ilml plugin list                         # what's installed
ilml plugin config acme show             # secrets masked
ilml plugin update acme                  # newer? then download
ilml plugin remove acme
What to build

Anything the terminal can reach.

Your plugin is ordinary Node running on the user’s machine — with their graph, their AI and their permissions already wired in.

Mirror

Bring a closed service home

Pull a service the user is locked out of programmatically — their orders, their bookings, their inbox — into nodes they own, and keep it in sync. That’s exactly what the LinkedIn plugin does.

Report

Turn a night’s work into one node

Crunch whatever you’re good at — logs, prices, a codebase — and post the result as a node with its own chat, where the team can argue with it and Lifebot can answer follow-ups.

Agent

A domain expert that runs on a schedule

Your command wakes up, reads the graph for context, asks Lifebot to judge what changed, and writes back a decision — under the user's account, so the history shows who did what.

Bridge

Push the graph where the work happens

Read a branch and drive something outside — a printer, a stock list, a deployment. Nodes in, real-world action out.

Note

A plugin runs where the user runs it. For automation that fires on its own, inside the graph, a node can hold code and run it whenever anything in its branch changes — that’s contracts, and the two work together: a contract reacts, your plugin does the heavy lifting.

The reference plugin

Read one that’s real.

ilml-plugin-linkedin is the worked example: it keeps a local mirror of someone’s LinkedIn, drafts replies they approve before sending, and reports into their graph — all through the same manifest, config schema and token you’d use.

One package. Everyone’s graph.

The authoring example ships inside the SDK — examples/plugin-author.mjs, right there after you install it.

Install the SDK & CLI →