Quickstart
Last updated April 7, 2026
A minimal KB Labs plugin built with the SDK, in under five minutes.
The goal of this page is to get a plugin running end-to-end. Nothing fancy — one CLI command that greets the user and optionally uses the LLM if available. You'll learn the package layout, the manifest shape, the handler signature, and how to link and run.
If you want to go deeper after this, read Plugins → Overview and SDK → Commands.
Prerequisites
- A working KB Labs install (workspace with
pnpm kb --helpfunctional). - Node 20+.
pnpm.
Step 1 — Create the package
mkdir -p packages/hello-plugin/src/cli/commands
cd packages/hello-plugin
pnpm initEdit package.json:
{
"name": "@example/hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@kb-labs/sdk": "^1.5.0"
},
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.5.0"
}
}The only runtime dependency is @kb-labs/sdk. That's the whole rule — plugins see the SDK and nothing else.
Step 2 — Write the handler
src/cli/commands/hello.ts:
import { defineCommand, useLLM, useLogger, type CLIInput } from '@kb-labs/sdk';
interface HelloFlags {
name?: string;
ai?: boolean;
}
interface HelloResult {
greeting: string;
source: 'deterministic' | 'llm';
}
export default defineCommand<unknown, CLIInput<HelloFlags>, HelloResult>({
id: 'hello:greet',
description: 'Say hi to someone',
handler: {
async execute(ctx, input) {
const logger = useLogger();
const name = input.flags.name ?? 'world';
logger.info('hello:greet invoked', { name, ai: input.flags.ai });
if (input.flags.ai) {
const llm = useLLM();
if (llm) {
const response = await llm.complete(
`Write a one-sentence friendly greeting for ${name}.`,
);
return {
exitCode: 0,
result: { greeting: response.content.trim(), source: 'llm' },
};
}
}
return {
exitCode: 0,
result: { greeting: `Hello, ${name}!`, source: 'deterministic' },
};
},
},
});A few things to notice:
- Every import comes from
@kb-labs/sdk.defineCommand,useLLM,useLogger,CLIInput— all one import. useLLM()can returnundefined. The handler degrades gracefully: if there's no LLM, it uses a canned greeting.useLogger()always returns a logger. No null check needed.- The return value is a
CommandResult.exitCode: 0means success;resultis the structured output the CLI will render.
Step 3 — Write the manifest
src/manifest.ts:
import {
defineFlags,
combinePermissions,
minimalPreset,
} from '@kb-labs/sdk';
const helloFlags = defineFlags({
name: {
type: 'string',
description: 'Name to greet',
},
ai: {
type: 'boolean',
description: 'Use LLM to generate the greeting',
default: false,
},
});
export const manifest = {
schema: 'kb.plugin/3',
id: '@example/hello-plugin',
version: '0.1.0',
display: {
name: 'Hello Plugin',
description: 'A minimal example plugin',
tags: ['example', 'tutorial'],
},
permissions: combinePermissions()
.with(minimalPreset)
.withPlatform({ llm: true })
.build(),
cli: {
commands: [
{
id: 'hello:greet',
group: 'hello',
describe: 'Say hi to someone',
handler: './cli/commands/hello.js#default',
flags: helloFlags,
examples: [
'pnpm kb hello:greet',
'pnpm kb hello:greet --name=Alice',
'pnpm kb hello:greet --name=Alice --ai',
],
},
],
},
} as const;
export default manifest;Key points:
schema: 'kb.plugin/3'is mandatory and a literal string.idmust be in@scope/nameformat.handlerpath is relative to the package root and points at.js(the built output).permissionscombinesminimalPreset(baseline Node env) with.withPlatform({ llm: true })to grant LLM access.
Step 4 — Export everything
src/index.ts:
export { default as manifest } from './manifest.js';The plugin's runtime entry point just re-exports the manifest. The handler files are loaded separately by the runtime via the paths in the manifest.
Step 5 — Set up tsconfig and build config
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}tsup.config.ts:
import { defineConfig } from 'tsup';
export default defineConfig({
entry: {
index: 'src/index.ts',
'cli/commands/hello': 'src/cli/commands/hello.ts',
},
format: ['esm'],
dts: true,
clean: true,
});The entry map builds each handler as a separate file, matching the path the manifest points at. Without this, tsup bundles everything into dist/index.js and the manifest's ./cli/commands/hello.js path wouldn't resolve.
Step 6 — Build and link
pnpm install
pnpm build
# → dist/index.js, dist/cli/commands/hello.js, dist/manifest.js, plus .d.ts files
# Link into your KB Labs workspace:
pnpm kb marketplace link /absolute/path/to/packages/hello-plugin
pnpm kb marketplace clear-cacheThe clear-cache step is mandatory — without it, the CLI uses a cached registry and won't see your new plugin.
Step 7 — Run it
pnpm kb hello:greet
# → { greeting: 'Hello, world!', source: 'deterministic' }
pnpm kb hello:greet --name=Alice
# → { greeting: 'Hello, Alice!', source: 'deterministic' }
pnpm kb hello:greet --name=Alice --ai
# → { greeting: 'Hi Alice, hope you're having a fantastic day!', source: 'llm' }
# (actual text depends on the configured LLM)The --help output is built from the manifest:
pnpm kb hello:greet --helpWhat just happened
Five files:
package.json— declares the package and its SDK dependency.tsconfig.json— standard TypeScript config with ESM and strict mode.tsup.config.ts— build config that produces one file per handler.src/manifest.ts— describes the plugin to the platform.src/cli/commands/hello.ts— the handler itself.
The runtime loaded the manifest, saw the hello:greet command, resolved the handler path, imported it, and called handler.execute(ctx, { flags: { name, ai } }) when you typed pnpm kb hello:greet. The result was rendered by the CLI's presenter layer.
Swap useLLM() for useCache(), useStorage(), or any other hook and you have a different plugin with the same shape. The SDK is intentionally small.
What to read next
- Plugins → Overview — the full plugin lifecycle.
- SDK → Commands —
defineCommandanddefineFlagsin full detail. - SDK → Hooks — the complete hook catalog.
- Plugins → Manifest Reference — every field in the manifest schema.
- Plugins → Permissions — the permission model and presets.
- Guides → Your First Plugin — a deeper version of this tutorial.