Build
Extend nikcli with plugins, drive it from your own program over HTTP, or embed the whole agent in-process — without forking it.
Three ways in
nikcli is a CLI, a server and a terminal UI that all sit on the same typed HTTP API. That means there is no privileged inside: the TUI reaches the session engine through the same endpoints your code can. Which path you take depends on where your code should run.
Extend
Your code runs inside nikcli. Add tools, intercept the model call, register a provider, gate permissions, extend the TUI.
Connect
Your program runs outside and drives a nikcli server over HTTP. Scripts, bots, CI jobs, alternative interfaces.
Embed
nikcli runs inside your process, with no listener and no socket. For products that ship the agent rather than talk to one.
The guides take each of these paths end to end with working code — Writing a plugin, Plugin recipes, Using the API and Streaming a response.
Every extension point below is a published contract. If you find yourself patching
packages/nikcli to do something on this page, that is a gap worth reporting
rather than a fork worth maintaining.
Extend — plugins
A plugin is a module nikcli loads at startup. It receives a client bound to the running
instance and returns a set of hooks. @nikcli-ai/plugin is the contract package;
it carries only types and the tool() helper, so it adds nothing to your runtime.
import type { Plugin } from "@nikcli-ai/plugin"
import { tool } from "@nikcli-ai/plugin/tool"
export const MyPlugin: Plugin = async ({ client, directory, $ }) => ({
tool: {
deploy: tool({
description: "Deploy the current branch to staging",
args: { branch: tool.schema.string().describe("branch to deploy") },
async execute(args, ctx) {
ctx.metadata({ title: `Deploying ${args.branch}` })
const out = await $`./script/deploy.sh ${args.branch}`.text()
return { title: "Deployed", output: out }
},
}),
},
async "tool.execute.before"(input, output) {
if (input.tool === "bash") output.args.timeout = 120_000
},
})
The hook surface is the interesting part. Beyond tool, a plugin can observe every
server event, rewrite the parameters and headers of the model call
(chat.params, chat.headers, chat.request), decide
permissions (permission.ask), shape the shell environment
(shell.env, shell.create.before), register an
auth method or a whole provider, and transform the messages and
system prompt before they reach the model.
shell.create.before runs before the permission check, and the check is
performed against the final values the hook wrote. Rewriting a command does not
inherit the approval granted for the original one.
TUI feature plugins are a separate runtime with its own contract
(@nikcli-ai/plugin/tui): they add routes, sidebar panels, dialogs and keybinds to
the terminal UI, and local ones hot-reload in place while it runs. See
TUI Feature Plugins.
Connect — the client
@nikcli-ai/sdk is a typed client generated from the same OpenAPI document as the
API reference, so every endpoint is a method and contract drift fails
generation rather than surfacing as a runtime 404.
npm install @nikcli-ai/sdk import { createNikcliClient } from "@nikcli-ai/sdk/httpapi"
const client = createNikcliClient({
baseUrl: "http://127.0.0.1:4096",
directory: "/Users/me/project",
})
const { data: session } = await client.session.create({ title: "release notes" })
await client.session.prompt({
sessionID: session!.id,
parts: [{ type: "text", text: "Summarise what changed since v1.2.0" }],
}) Full walkthrough — auth, the result envelope, instance selection, streaming and errors — in Client.
Embed — in-process
The embedded host runs the server's request handler in memory. It opens no listener and does no network I/O, while keeping the same routing, middleware, handlers, codecs and errors as the network client — so code written against the client works unchanged against the embedded host.
import { NikCli } from "@nikcli-ai/sdk-next"
import { Effect } from "effect"
const program = Effect.gen(function* () {
const nikcli = yield* NikCli.create({ directory: process.cwd() })
return yield* Effect.promise(() => nikcli.session.list())
}) It also lets you register tools directly, which the HTTP path cannot do — a tool is executable code and does not travel over a wire. See Embedded SDK.
Which one do I want?
| If you want to… | Path | Package |
|---|---|---|
| Give the agent a new tool | Plugin | @nikcli-ai/plugin |
| Change how the model is called | Plugin | @nikcli-ai/plugin |
| Add a panel or route to the TUI | TUI plugin | @nikcli-ai/plugin/tui |
| Script nikcli from CI or a bot | Client | @nikcli-ai/sdk |
| Build a different interface on top | Client | @nikcli-ai/sdk |
| Ship the agent inside your own app | Embedded | @nikcli-ai/sdk-next |
| Generate a client for another language | OpenAPI | the document |
A plugin is handed a client bound to the instance it is running in — the same
NikcliClient the Connect path uses. Reaching for the API from inside a plugin is
the normal thing to do, not a workaround.
What people build
Domain tools
Wrap a deploy script, a migration runner or an internal API so the agent can call it with arguments it cannot get wrong.
Policy
Auto-deny a permission class, force a timeout, inject credentials into the shell environment, or strip secrets before a request leaves the process.
Custom providers
Register a model provider with its own auth flow and model list — including an internal gateway that is not in the public catalogue.
Automation
Open a session from a webhook, prompt it, wait on the event feed, and post the diff back to a pull request.
Alternative interfaces
The web app, the desktop app and the mobile app are all just clients of these endpoints. Yours can be too.
Embedded agents
A product that runs sessions on its own, with your tools registered and no server for a user to start.
Source anchors
| Need | Where |
|---|---|
Plugin types, hooks, tool() | packages/plugin/src/index.ts, src/tool.ts |
| TUI plugin contract | packages/plugin/src/tui.ts, src/v2/ |
| Plugin loading and hook dispatch | packages/nikcli/src/plugin/index.ts |
| Generated HTTP client | packages/sdk/js/src/httpapi/ |
| Client generation | packages/nikcli/script/generate-httpapi-clients.ts |
| Embedded host | packages/sdk-next/src/nikcli.ts |
| Tool registry | packages/nikcli/src/tool/registry.ts |