Skip to content
nikcli
Docs
Documentation

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.

Prefer a walkthrough?

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.

None of these need a fork

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.

my-plugin.ts
ts
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.

A shell hook cannot smuggle past approval

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.

Terminal
bash
npm install @nikcli-ai/sdk
ts
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.

ts
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…PathPackage
Give the agent a new toolPlugin@nikcli-ai/plugin
Change how the model is calledPlugin@nikcli-ai/plugin
Add a panel or route to the TUITUI plugin@nikcli-ai/plugin/tui
Script nikcli from CI or a botClient@nikcli-ai/sdk
Build a different interface on topClient@nikcli-ai/sdk
Ship the agent inside your own appEmbedded@nikcli-ai/sdk-next
Generate a client for another languageOpenAPIthe document
Plugins and the client are not exclusive

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

Plugin

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.

Plugin

Policy

Auto-deny a permission class, force a timeout, inject credentials into the shell environment, or strip secrets before a request leaves the process.

Plugin

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.

Client

Automation

Open a session from a webhook, prompt it, wait on the event feed, and post the diff back to a pull request.

Client

Alternative interfaces

The web app, the desktop app and the mobile app are all just clients of these endpoints. Yours can be too.

Embedded

Embedded agents

A product that runs sessions on its own, with your tools registered and no server for a user to start.

Source anchors

NeedWhere
Plugin types, hooks, tool()packages/plugin/src/index.ts, src/tool.ts
TUI plugin contractpackages/plugin/src/tui.ts, src/v2/
Plugin loading and hook dispatchpackages/nikcli/src/plugin/index.ts
Generated HTTP clientpackages/sdk/js/src/httpapi/
Client generationpackages/nikcli/script/generate-httpapi-clients.ts
Embedded hostpackages/sdk-next/src/nikcli.ts
Tool registrypackages/nikcli/src/tool/registry.ts