npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jund/core

v0.1.0

Published

Platform-agnostic headless coding agent.

Readme

jund

Platform-agnostic coding agent primitive inspired by opencode and pi.

jund accepts a FileSystem interface and an exec function instead of touching the host directly, so the same agent session works against a real disk, an in-memory FS, or a virtual sandbox like just-bash. The only runtime dependency is zod, so it runs anywhere (Node, Bun, Deno, edge workers, the browser).

Status: Experimental - the public API is not stable and may change without notice.

Install

npm install @jund/core

Provider adapters are optional:

npm install ai              # for @jund/core/ai-sdk
npm install @openrouter/sdk  # for @jund/core/openrouter-sdk
npm install @tanstack/ai     # for @jund/core/tanstack-ai

Quick start

import Database from "better-sqlite3";
import { Bash, ReadWriteFs } from "just-bash";
import { anthropic } from "@ai-sdk/anthropic";
import { createAISDKProvider } from "@jund/core/ai-sdk";
import { createSession, getAssistantText, BetterSqlite3Storage } from "@jund/core";

const fs = new ReadWriteFs({ root: "./my-project" });
const shell = new Bash({ fs, cwd: "/" });

const storage = new BetterSqlite3Storage(new Database("sessions.db"));

const llm = createAISDKProvider({
  model: anthropic("claude-opus-4-6"),
  id: "claude-opus-4-6",
  contextLimit: 200_000,
  outputLimit: 64_000,
  capabilities: { reasoning: true, toolCalls: true, images: true },
});

const session = await createSession({
  llm,
  workdir: "/",
  env: { fs, shell },
  storage,
  onEvent: (event) => console.log(event.type),
});

const reply = await session.prompt("Add a health-check endpoint to the server.");
console.log(getAssistantText(reply));

Entry points

| Import | Description | | --------------------------- | ---------------------------------------------------- | | @jund/core | Core session, tools, types, events, context pipeline | | @jund/core/ai-sdk | Adapter for the Vercel AI SDK (ai) | | @jund/core/openrouter-sdk | Adapter for @openrouter/sdk | | @jund/core/tanstack-ai | Adapter for @tanstack/ai |

Core API

createSession(options): Promise<Session>

Creates a stateful agent session. Key options:

| Option | Type | Description | | --------------- | ------------------------------- | -------------------------------------------------- | | llm | LLMProviderWithModel | LLM backend + model metadata (use an adapter) | | workdir | string | Root working directory exposed to tools | | env | Environment | { fs: FileSystem; shell: ShellOps } | | tools | ToolDef[] | Additional tools beyond the built-ins | | systemPrompt | string | Prepended to the default system prompt | | toolExecution | "parallel" \| "sequential" | How tool calls in a single step run | | onEvent | EventHandler | Stream of AgentEvents (deltas, tool calls, etc.) | | compaction | false \| CompactionOptions | Context-window compaction strategy | | storage | SessionStorageDriver | Persist turns and messages | | retry | { maxAttempts?, maxDelayMs? } | LLM retry policy |

Lifecycle hooks: beforeInput, beforeToolCall, afterToolCall, beforeLLMCall, beforePrompt, beforeCompaction, beforeBranch, transformContext.

Session

interface Session {
  readonly id: string;
  readonly parentId: string | undefined;
  readonly branchedFromMessageId: string | undefined;
  readonly model: ModelInfo;
  readonly workdir: string;
  readonly isStreaming: boolean;
  prompt(input: string | UserPart[]): Promise<AssistantMessage>;
  cancel(): void;
  messages(): Message[];
  branchFrom(messageId: string, options?: BranchOptions): Promise<Session>;
  addTool(tool: ToolDef): void;
  removeTool(id: string): void;
  setTools(tools: ToolDef[]): void;
  compact(): Promise<boolean>;
  setLLM(llm: LLMProviderWithModel): Promise<void>;
  // + setters for every lifecycle hook
}

resumeSession(sessionId, options): Promise<Session>

Resumes an existing persisted session. Throws if the session does not exist. Runtime dependencies (llm, env, storage) are required; workdir and defaultAgent default from the stored session state.

const session = await resumeSession("session-123", {
  llm,
  env,
  storage,
});

Session discovery

Query stored sessions without creating a live Session:

const all = await listSessions(storage);
const branches = await listBranches(storage, parentSessionId);
const info = await getSessionInfo(storage, sessionId);

Each returns SessionInfo objects with id, workdir, parentSessionId, branchedFromMessageId, model, agent, and timestamps.

Built-in tools

| Tool | Description | | ----------- | -------------------------------------- | | readTool | Read file contents | | writeTool | Write / create files | | editTool | Apply targeted edits to existing files | | bashTool | Execute shell commands | | taskTool | Spawn sub-agent sessions |

All tools are registered by default; override with tools or setTools.

LLM provider adapters

Each adapter exports a create*Provider(options): LLMProviderWithModel factory plus lower-level conversion helpers.

Each adapter factory accepts model metadata alongside the provider-specific model and returns a LLMProviderWithModel — pass it straight to createSession:

// Vercel AI SDK
import { createAISDKProvider } from "@jund/core/ai-sdk";
const llm = createAISDKProvider({
  model: anthropic("claude-opus-4-6"),
  id: "claude-opus-4-6",
  contextLimit: 200_000,
  outputLimit: 64_000,
  capabilities: { reasoning: true, toolCalls: true, images: true },
});

// OpenRouter
import { createOpenRouterProvider } from "@jund/core/openrouter-sdk";
const llm = createOpenRouterProvider({
  client: openRouterClient,
  model: "anthropic/claude-opus-4-6",
  id: "claude-opus-4-6",
  contextLimit: 200_000,
  outputLimit: 64_000,
  capabilities: { reasoning: true, toolCalls: true, images: true },
});

// TanStack AI
import { createTanStackTextProvider } from "@jund/core/tanstack-ai";
const llm = createTanStackTextProvider({
  adapter: myAdapter,
  id: "claude-opus-4-6",
  contextLimit: 128_000,
  outputLimit: 32_000,
  capabilities: { reasoning: false, toolCalls: true, images: false },
});

Environment

Tools interact with the host through the Environment interface:

interface Environment {
  fs: FileSystem; // readFile, writeFile, mkdir, exists, stat, readdir
  shell: ShellOps; // exec(command, options?) → { stdout, stderr, exitCode }
}

Events

Subscribe via onEvent to receive a stream of typed AgentEvents:

turn.start, turn.end, message.created, text.delta, reasoning.delta, tool.start, tool.update, tool.complete, tool.error, step.finish, compaction, retry, tools.changed, error, done.

Context compaction

When conversation history approaches the model's context limit, jund automatically summarises older messages. Configure the threshold, strategy, or disable entirely:

createSession({
  // ...
  compaction: { threshold: 0.8, strategy: myStrategy },
  // or: compaction: false,
});

You can also trigger compaction manually at any time (e.g. from a UI button):

const didCompact = await session.compact();

Returns true if history was compacted, false if compaction is disabled or was cancelled by the beforeCompaction hook.

Persistence

Pass any SessionStorageDriver as storage - createSession handles the rest internally (see the quick start above). Five drivers ship out of the box:

| Driver | Backend | | ---------------------------- | --------------------------------- | | MemoryStorage | In-memory (testing / ephemeral) | | BetterSqlite3Storage | better-sqlite3 (Node) | | BunSqliteStorage | bun:sqlite (Bun) | | DurableObjectSqliteStorage | Cloudflare Durable Objects SQLite | | PgStorage | PostgreSQL via pg |

Implement SessionStorageDriver to bring your own backend.

Resume and branching

Resuming a session

Use resumeSession to pick up where a previous session left off. The session must already exist in storage:

import { resumeSession } from "@jund/core";

const session = await resumeSession("session-123", {
  llm,
  env: { fs, shell },
  storage,
  // workdir and defaultAgent default from stored session
});

// Conversation history is restored automatically
const reply = await session.prompt("Continue where we left off.");

createSession with an explicit sessionId will throw if a session with that ID already exists - use resumeSession when you intend to continue an existing conversation.

Branching

Create an alternate conversation timeline from any message:

const branch = await session.branchFrom(messageId);
// branch.parentId === session.id
// branch.messages() contains history up to and including messageId

const reply = await branch.prompt("Try a different approach.");

Branches are independent sessions - prompting the parent or child does not affect the other. Branch metadata is accessible via session.parentId and session.branchedFromMessageId.

Discovering sessions and branches

import { listSessions, listBranches, getSessionInfo } from "@jund/core";

const all = await listSessions(storage);
const branches = await listBranches(storage, session.id);
const info = await getSessionInfo(storage, "session-123");

Lower-level primitives

For custom orchestration loops without the full Session:

  • callLLM(options) - stream a single LLM call with retry.
  • processTurn(options) - one model step with tool execution.
  • buildSystemPrompt(options) - assemble the system prompt.
  • ToolRegistry - manage tool definitions.
  • estimateTokens / shouldCompact / runContextPipeline - context utilities.

License

MIT