Small, focused TypeScript packages for building CLI tools.
Structured terminal logging with semantic methods and themed output. Phase markers, status messages, color themes, and unicode symbols.
bun add @ulnd/log
Generic pairwise ranking engine with pluggable storage. Elo scoring, interactive sessions, and agent voting with separate human/AI tracks.
bun add @ulnd/ranking
Lazy, Zod-validated environment variable access with fallbacks. Define your schema once, get typed, cached values everywhere.
bun add @ulnd/env
Toolkit for working with code repositories. GitHub API with flexible auth, PR management, Dependabot automation, and git remote parsing.
bun add @ulnd/repo
1Password CLI wrapper and env sync. CRUD operations on vault items, .env parsing, and bidirectional environment variable synchronization.
bun add @ulnd/op
Generate structured plaintext documentation from TypeScript packages. Collects metadata, README, and source into LLM-readable formats.
bun add @ulnd/llms
Replace console.log with purpose-driven output. Every method adds appropriate indentation, icons, and color so your CLI looks polished without effort.
import { log, sym, theme } from "@ulnd/log";
log.phase("Building project");
log.step("Compiling TypeScript");
log.success("Build complete");
log.fail("Lint errors found");
log.warn("Deprecated API usage");
log.raw(`PR ${theme.pr(42)} on ${theme.branch("main")}`);
log.divider("Summary");
Bring your own storage backend. Implement the RankingStore interface, then use interactive sessions or programmatic voting to rank anything.
import { applyRankVote, runInteractiveRank } from "@ulnd/ranking";
import type { RankingStore } from "@ulnd/ranking";
// Implement for your database
const store: RankingStore = {
loadElo(storage, id) { /* ... */ },
updateElo(storage, ...) { /* ... */ },
recordVote(storage, ..) { /* ... */ },
resetElo(storage, ...) { /* ... */ },
};
// Interactive TUI session
await runInteractiveRank(store, storage, items);
// Or apply a single vote
applyRankVote(store, storage, items, {
option1Id: 1, option2Id: 2,
choice: "1", agent: false,
});
Define your environment schema with Zod. Access is lazy — validation happens on first read and the result is cached. Invalid values fall back silently.
import { z } from "zod";
import { createEnv } from "@ulnd/env";
const env = createEnv({
PORT: { type: z.coerce.number().int().positive(), fallback: 3000 },
DEBUG: { type: z.coerce.boolean(), fallback: false },
API_KEY: { type: z.string().min(1), fallback: "" },
});
env.PORT; // number — parsed from process.env or 3000
env.DEBUG; // boolean — lazy, cached after first access
env.API_KEY; // string — fully typed from your schema
All GitHub operations take an explicit Octokit instance. Auth is flexible — use gh auth token, a direct token, or a custom command.
import {
getOctokit, getPullRequestDetail,
searchPRsForReview, getRepoInfo,
} from "@ulnd/repo";
const ok = getOctokit(); // defaults to `gh auth token`
const pr = await getPullRequestDetail(ok, "ulnd", "lib", 42);
// { title, files, additions, deletions, statusChecks }
const prs = await searchPRsForReview(ok, { org: "ulnd" });
const { owner, repo } = getRepoInfo("/path/to/repo");
Programmatic 1Password access and bidirectional .env synchronization. Store environment variables as Secure Notes with automatic merging and conflict resolution.
import {
getItem, setField,
getEnvEntries, saveEnvEntries,
parseDotenv, diffEnvEntries,
} from "@ulnd/op";
// Read from 1Password
const item = getItem("my-app:env:prod", "Development");
// Sync env vars (merges global + project-specific)
const entries = getEnvEntries("myapp", "prod", {
vault: "Development", tag: "env",
});
// Diff local .env against 1Password
const local = parseDotenv(localFile);
const diff = diffEnvEntries(local, entries);
Collect package metadata, README, and source files into structured plaintext. Use it to generate llms.txt files for your packages.
import {
collectPackage, formatPackageFull, formatIndex,
} from "@ulnd/llms";
// Collect a single package
const pkg = collectPackage("/path/to/my-package");
// { info, readme, sources: [{ relativePath, content }] }
// Generate full plaintext with all source code
const txt = formatPackageFull(pkg);
// Generate an index linking to per-package docs
const packages = dirs.map(d => collectPackage(d));
const index = formatIndex(packages, {
title: "My Org",
description: "Our packages.",
baseUrl: "https://example.com",
});