SDK
@putnami/sdk is the core library that powers the Putnami CLI. It provides the command system, job execution runner, workspace/project discovery, caching, and utilities that extension authors use to extend Putnami.
What it is used for
Use the SDK when you need to:
- Build custom Putnami extensions (commands + jobs)
- Automate workspace operations in code
- Reuse job orchestration, dependency resolution, and caching
Most users interact with the SDK indirectly through the CLI.
Install
Install at the workspace root:
bun add @putnami/sdkCore concepts
Workspace
The workspace represents the root orchestration unit and is accessed via WorkspaceService:
import { ProjectsService, WorkspaceService } from '@putnami/sdk';
const workspace = WorkspaceService.getInstance();
const description = workspace.describe();
const projectsService = ProjectsService.getInstance();
const projects = projectsService.listProjects();Projects
Projects are individual apps/libraries in the workspace:
const project = projectsService.getProject('my-app');
console.log(project?.name);
console.log(project?.path);Commands
Commands define the CLI surface. Use the fluent model API to define arguments and flags, then bind them to a CommandLine.
import { CommandLine, projectJobModel, Flag, type InferCommandModel } from '@putnami/sdk';
const buildModel = projectJobModel.extend({
description: 'Build project',
flags: {
target: Flag.string({ flag: '--target', description: 'Build target', default: 'bun' }),
},
});
export type BuildOptions = InferCommandModel<typeof buildModel>;
export default new CommandLine('build')
.description('Build project')
.category('build')
.relatedCommands('test', 'lint')
.model(buildModel)
.jobRunner();Commands support metadata for enhanced help output:
.category(cat)— groups the command in help output (build, test, deploy, workspace, project, extension, utility).relatedCommands(...cmds)— shows related commands in help.seeAlso(...refs)— shows documentation links in help
Help generation
The HelpGeneratorService generates structured help data for commands:
import { HelpGeneratorService } from '@putnami/sdk';
const helpGenerator = HelpGeneratorService.getInstance();
const helpData = await helpGenerator.generateHelp(command, 'putnami build', {
format: 'text',
includeExamples: true,
includeRelated: true,
groupByCategory: true,
});The service supports:
- Category grouping — subcommands organized by
CommandCategory - Dynamic examples — command-defined examples with fallback generation
- Related commands — cross-references between commands
- Dual output — structured data for both text and JSON rendering
Jobs
Jobs are executed as subprocesses using the JSONL event protocol. The SDK provides runJobCommandCli for building job CLI scripts:
#!/usr/bin/env bun
import {
runJobCommandCli,
standardJobModel,
Flag,
emitLog,
type InferCommandModel,
type JobCommandContext,
type JobResult,
} from '@putnami/sdk';
const buildCliModel = standardJobModel.extend({
name: 'build',
description: 'Build project',
flags: {
minify: Flag.boolean({ default: true }),
},
});
async function runBuild(
options: InferCommandModel<typeof buildCliModel>,
context: JobCommandContext
): Promise<JobResult> {
if (!context.project) return { status: 'SKIP' };
emitLog('info', `Building ${context.project.name}`);
// do work here
return { status: 'OK' };
}
if (import.meta.main) {
runJobCommandCli({
model: buildCliModel,
extension: '@my/extension',
job: 'build',
run: runBuild,
});
}Job results return a status: OK, FAILED, or SKIP.
Creating commands and jobs
1) Define options
// src/build/options.ts
import { projectJobModel, Flag, type InferCommandModel } from '@putnami/sdk';
export const buildModel = projectJobModel.extend({
description: 'Build options',
flags: {
minify: Flag.boolean({ default: true, description: 'Minify output' }),
},
});
export type BuildOptions = InferCommandModel<typeof buildModel>;2) Wire command to the job execution runner
// src/build/command.ts
import { CommandLine } from '@putnami/sdk';
import { buildModel } from './options';
export default new CommandLine('build').model(buildModel).jobRunner();3) Implement the job CLI script
// bin/build.ts
#!/usr/bin/env bun
import {
runJobCommandCli,
standardJobModel,
Flag,
emitLog,
type InferCommandModel,
type JobCommandContext,
type JobResult,
} from '@putnami/sdk';
const buildCliModel = standardJobModel.extend({
name: 'build',
description: 'Build project',
flags: {
minify: Flag.boolean({ default: true }),
},
});
async function runBuild(
options: InferCommandModel<typeof buildCliModel>,
context: JobCommandContext
): Promise<JobResult> {
if (!context.project) return { status: 'SKIP' };
emitLog('info', `Building ${context.project.name}`);
// Build logic here
return { status: 'OK' };
}
if (import.meta.main) {
runJobCommandCli({
model: buildCliModel,
extension: '@my/extension',
job: 'build',
run: runBuild,
});
}Extension wiring
putnami.extension.json
Declare commands and jobs in putnami.extension.json:
{
"commands": {
"build": "./build"
},
"jobs": {
"build": {
"kind": "command",
"command": "bun",
"args": ["@my/extension:build"],
"cwd": "{projectRoot}",
"filePatterns": ["src/**/*", "package.json"]
}
}
}package.json exports
Export commands and job CLI scripts:
{
"bin": {
"my-extension-build": "./bin/build"
},
"exports": {
"./build": "./src/build/command.ts"
}
}Job dependencies
Use dependsOn on pipeline steps in putnami.extension.json:
"stepId"depends on an intra-pipeline step"^job"runs the job on upstream dependencies"*job"runs it on downstream dependents"/job"runs a workspace-level job
Job orchestration, execution trees, caching, and scope resolution are documented in 03-jobs-&-commands.md.
Caching
Caching behavior is documented in 03-jobs-&-commands.md.
Service discovery and watch
The SDK provides service discovery and multi-service watch capabilities for putnami serve:
Project runsWith config
Declare runtime service dependencies in a project's package.json:
{
"putnami": {
"runsWith": ["catalogue-api", "profile-api"]
}
}When putnami serve web-app is run, the system discovers all declared services, allocates ports, and starts them alongside the primary project.
Service discovery API
import { discoverServices, PortAllocator, type DiscoveredService } from '@putnami/sdk';
// Discover all services for a project
const services: DiscoveredService[] = discoverServices('web-app');
// Returns: [{ name: 'web-app', path: '...', primary: true }, { name: 'catalogue-api', ... }, ...]
// Allocate ports: per-project config → auto-assign from base
const allocator = new PortAllocator(3000);
const assignments = allocator.allocate(services);See core/sdk/doc/SERVICE-DISCOVERY.md for full API reference.
See also
core/sdk/doc/COMMAND-SYSTEM.mdcore/sdk/doc/JOB-SYSTEM.mdcore/sdk/doc/HOOK-COMMANDS.md- JSONL protocol for subprocess executioncore/sdk/doc/CACHING.mdcore/sdk/doc/SERVICE-DISCOVERY.md- Multi-service discovery and port allocationcore/sdk/doc/WATCH-MODE.md- Watch mode architecture
Next steps
- Previous: Jobs & commands
- Next: Extensions