ulnd

Small, focused TypeScript packages for building CLI tools.

@uln/assistant

Run AI coding assistants (Claude Code, Codex) from your own CLI with automatic fallback. When one assistant hits a quota limit or service error, the next one picks up seamlessly.

bun add @uln/assistant

@uln/cmd

Commander.js wrapper with styled help theming, plus robust command execution with timeout, streaming, and signal support.

bun add @uln/cmd

@uln/lint

Structural linter for TypeScript projects. Checks project-level conventions (package manager, monorepo layout) rather than code syntax.

bun add @uln/lint

@uln/ranking

Generic pairwise ranking engine with pluggable storage. Currently implements Elo; designed to support alternative scoring algorithms in the future.

bun add @uln/ranking

@uln/llm-prompts

LLM prompt templates, Zod output schemas, and response parsing utilities. Everything needed to ask an LLM a structured question and validate the response.

bun add @uln/llm-prompts

@uln/op

1Password CLI wrapper and environment variable sync utilities. Programmatic access to `op` CLI operations and bidirectional .env ↔ 1Password synchronization.

bun add @uln/op

@uln/editor-stats

Extract AI usage statistics, token consumption, and MCP server configuration from VS Code, Cursor, and Claude Code.

bun add @uln/editor-stats

@uln/env

Lazy, Zod-validated environment variable access with fallbacks.

bun add @uln/env

@uln/prompts

Interactive CLI prompts: readline wrappers, fuzzy matching, and fzf-powered selection.

bun add @uln/prompts

@uln/log

Structured terminal logging with semantic methods and themed output. Replaces `console.log` with purpose-driven helpers for CLI tools.

bun add @uln/log

@uln/llms

Generate structured plaintext documentation from TypeScript packages. Collects package metadata, README, and source files into LLM-readable formats.

bun add @uln/llms

@uln/essentials

Bundle of essential `@uln` packages for every CLI project. Install one package instead of four.

bun add @uln/essentials

@uln/repo

Toolkit for working with code repositories. GitHub API utilities with flexible Octokit authentication, git remote parsing, and PR management.

bun add @uln/repo

@uln/assistant

Run AI coding assistants (Claude Code, Codex) from your own CLI with automatic fallback. When one assistant hits a quota limit or service error, the next one picks up seamlessly.

import { runAssistant } from "@uln/assistant";

const result = await runAssistant({
  assistants: [
    { name: "claude", command: "claude" },
    { name: "codex", command: "codex" },
  ],
  args: ["--print", "Explain this repo"],
  claudeMd: "# Instructions\nUse Bun, not Node.",
});

if (result.ok) {
  console.log(`Ran with ${result.assistant}`);
} else {
  console.error(result.error);
}

@uln/cmd

Commander.js wrapper with styled help theming, plus robust command execution with timeout, streaming, and signal support.

import { exec, execOrThrow, execStdout, execInherit } from "@uln/cmd";

// Capture output
const result = exec(["git", "status"]);
if (result.ok) console.log(result.stdout);

// Throw on failure
const { stdout } = execOrThrow(["git", "rev-parse", "HEAD"]);

// Just the stdout string
const branch = execStdout(["git", "branch", "--show-current"]);

// Passthrough to terminal (interactive)
execInherit(["bun", "test"]);

@uln/lint

Structural linter for TypeScript projects. Checks project-level conventions (package manager, monorepo layout) rather than code syntax.

import { createLinter } from "@uln/lint";

const linter = createLinter();
const result = linter.run(".");

for (const diagnostic of result.diagnostics) {
  console.log(`${diagnostic.severity}: ${diagnostic.message}`);
}

@uln/ranking

Generic pairwise ranking engine with pluggable storage. Currently implements Elo; designed to support alternative scoring algorithms in the future.

import type { RankingStore } from "@uln/ranking";

const store: RankingStore = {
  loadElo(storage, entityId) { /* load from your DB */ },
  updateElo(storage, entityId, rating, matches, agent) { /* persist */ },
  recordVote(storage, id1, id2, selectedId, opts) { /* save vote */ },
  resetElo(storage, entityId, agent) { /* reset to defaults */ },
};

@uln/llm-prompts

LLM prompt templates, Zod output schemas, and response parsing utilities. Everything needed to ask an LLM a structured question and validate the response.

import { composePrompt, parseLlmJsonArray, PREFERENCE_EXTRACTION, LlmPreferenceSchema } from "@uln/llm-prompts";

// Build the prompt
const prompt = composePrompt(PREFERENCE_EXTRACTION, transcriptText);

// Send to your LLM of choice, then parse
const preferences = parseLlmJsonArray(llmResponse, LlmPreferenceSchema);
// Typed array — invalid elements silently dropped

@uln/op

1Password CLI wrapper and environment variable sync utilities. Programmatic access to `op` CLI operations and bidirectional .env ↔ 1Password synchronization.

import { getItem, createSecureNote, setField, deleteField } from "@uln/op";

const item = getItem("my-app-config", "Development");
// { id, title, fields: [{ label, value, type, section }] }

const note = createSecureNote({
  title: "my-app:env:production",
  vault: "Development",
  tags: ["env"],
});

setField("my-app:env:production", "Development", "env", "API_KEY", "sk-...");
deleteField("my-app:env:production", "Development", "env", "OLD_VAR");

@uln/editor-stats

Extract AI usage statistics, token consumption, and MCP server configuration from VS Code, Cursor, and Claude Code.

import { fetchEditorStats } from "@uln/editor-stats";

const stats = fetchEditorStats();

// AI usage from VS Code / Cursor
for (const day of stats.aiStats) {
  console.log(`${day.day}: ${day.totalAccepted} accepted, ${day.totalRejected} rejected`);
}

// Claude Code daily stats
for (const day of stats.claudeCodeStats) {
  console.log(`${day.day}: ${day.totalCost}`);
}

// Claude Code token usage by model
for (const usage of stats.claudeCodeTokens) {
  console.log(`${usage.model}: ${usage.totalTokens} tokens, $${usage.totalCostUsd}`);
}

// Discovered MCP servers
for (const server of stats.mcpServers) {
  console.log(`${server.name}: ${server.command} (${server.source})`);
}

@uln/env

Lazy, Zod-validated environment variable access with fallbacks.

import { z } from "zod";
import { createEnv, v } from "@uln/env";

const env = createEnv({
  // Required — throws on startup if missing or invalid
  DATABASE_URL: { schema: z.string().url() },
  API_KEY: { schema: v.nonEmpty() },

  // Optional — uses fallback when missing, warns on invalid
  PORT: { schema: v.port(), fallback: 3000 },
  DEBUG: { schema: v.boolean(), fallback: false },
  NODE_ENV: { schema: v.oneOf(["development", "production", "test"]), fallback: "development" },
  ALLOWED_ORIGINS: { schema: v.csv(), fallback: ["localhost"] },
  REQUEST_TIMEOUT: { schema: v.duration(), fallback: 30_000 },
});

env.PORT;         // number — 3000 or parsed from process.env.PORT
env.DATABASE_URL; // string — validated URL, throws if not set
env.DEBUG;        // boolean — parsed from "true"/"false"/"1"/"0"/"yes"/"no"

@uln/prompts

Interactive CLI prompts: readline wrappers, fuzzy matching, and fzf-powered selection.

import { askLine } from "@uln/prompts";

const name = await askLine("Project name: ");

@uln/log

Structured terminal logging with semantic methods and themed output. Replaces `console.log` with purpose-driven helpers for CLI tools.

import { log, sym, theme } from "@uln/log";

log.phase("Building project");
log.step("Compiling TypeScript");
log.success("Build complete");
log.fail("Lint errors found");
log.warn("Deprecated API usage");
log.info("Using config from ~/.config/app");
log.dim("skipped (already up to date)");
log.item("src/index.ts");
log.detail("14 files changed");
log.divider("Summary");
log.blank();

@uln/llms

Generate structured plaintext documentation from TypeScript packages. Collects package metadata, README, and source files into LLM-readable formats.

import { collectPackage } from "@uln/llms";

const pkg = collectPackage("/path/to/my-package");
// { info: { name, version, dependencies }, readme, sources: [{ relativePath, content }] }

@uln/essentials

Bundle of essential `@uln` packages for every CLI project. Install one package instead of four.

import { log, createEnv, exec, askLine, fzfSelect, Command } from "@uln/essentials";

@uln/repo

Toolkit for working with code repositories. GitHub API utilities with flexible Octokit authentication, git remote parsing, and PR management.

import { getOctokit } from "@uln/repo";

// Default: runs `gh auth token` to get a token
const octokit = getOctokit();

// Or provide a token directly
const octokit = getOctokit({ token: "ghp_..." });

// Or use a custom command
const octokit = getOctokit({ tokenCommand: ["op", "read", "op://vault/github/token"] });