Skip to main content
Smithers exports a small VCS helper surface for applications that inspect or manage Jujutsu state directly. Lightweight by design:
  • every helper accepts an optional cwd to target a specific repository
  • spawn failures are normalized instead of throwing, so they are safe to call even when jj is not installed
  • workspace helpers try a few command shapes to tolerate JJ version drift
  • JJ command helpers return Effect values, so direct callers must provide an Effect CommandExecutor layer

Import

The root smithers-orchestrator facade exports the main JJ helpers:
import {
  runJj,
  getJjPointer,
  revertToJjPointer,
  isJjRepo,
  workspaceAdd,
  workspaceList,
  workspaceClose,
} from "smithers-orchestrator";
The lower-level VCS package also exports repository discovery, binary resolution, tooling preflight, and snapshot capture helpers:
import {
  captureWorkspaceSnapshot,
  findVcsRoot,
  resolveGitBinary,
  resolveJjBinary,
  vcsToolingStatus,
} from "@smithers-orchestrator/vcs";
Direct JJ helper calls need a platform layer:
import type * as CommandExecutor from "@effect/platform/CommandExecutor";
import * as BunContext from "@effect/platform-bun/BunContext";
import { Effect } from "effect";

type VcsEffect<A> = Effect.Effect<A, never, CommandExecutor.CommandExecutor>;

const runVcs = <A,>(effect: VcsEffect<A>) =>
  Effect.runPromise(effect.pipe(Effect.provide(BunContext.layer)));
Install @effect/platform-bun when using the Bun snippet, or use the equivalent CommandExecutor layer for another runtime.

runJj(args, opts?)

Run an arbitrary jj command and capture its output.
const result = await runVcs(runJj(["status"], { cwd: "/path/to/repo" }));
type RunJjOptions = {
  cwd?: string;
};

type RunJjResult = {
  code: number;
  stdout: string;
  stderr: string;
};

function runJj(args: string[], opts?: RunJjOptions): VcsEffect<RunJjResult>;
Notes:
  • returns { code: 127, stdout: "", stderr: "..." } when jj cannot be started
  • does not throw for ordinary process failures
  • a raw escape hatch beyond the higher-level helpers below

getJjPointer(cwd?)

Return the current workspace change_id for @, or null when JJ is unavailable or the current directory is not a JJ repo.
const pointer = await runVcs(getJjPointer("/path/to/repo"));
function getJjPointer(cwd?: string): VcsEffect<string | null>;
Smithers uses the same pointer model internally for revert support and cache invalidation.

revertToJjPointer(pointer, cwd?)

Restore the working copy from a previously recorded JJ pointer. A pointer is a JJ change_id string, as returned by getJjPointer.
const result = await runVcs(revertToJjPointer("zqkopwvn", "/path/to/repo"));
type JjRevertResult = {
  success: boolean;
  error?: string;
};

function revertToJjPointer(pointer: string, cwd?: string): VcsEffect<JjRevertResult>;
This helper wraps jj restore --from <pointer>.

isJjRepo(cwd?)

Detect whether a directory is a readable JJ repository.
const enabled = await runVcs(isJjRepo("/path/to/repo"));
function isJjRepo(cwd?: string): VcsEffect<boolean>;
Use this before showing JJ-specific UI or attempting a revert flow.

workspaceAdd(name, path, opts?)

Create a JJ workspace with a friendly name at a target filesystem path.
const result = await runVcs(
  workspaceAdd("feature-auth", "/tmp/wt-feature-auth", {
    cwd: "/path/to/repo",
    atRev: "@",
  }),
);
type WorkspaceAddOptions = {
  cwd?: string;
  atRev?: string;
};

type WorkspaceResult = {
  success: boolean;
  error?: string;
};

function workspaceAdd(name: string, path: string, opts?: WorkspaceAddOptions): VcsEffect<WorkspaceResult>;
Behavior notes:
  • removes an existing workspace with the same name before retrying
  • removes the target directory if it exists and creates its parent directory if needed
  • tries multiple jj workspace add syntaxes to work across JJ versions

workspaceList(cwd?)

List known workspaces for the current JJ repo.
const workspaces = await runVcs(workspaceList("/path/to/repo"));
type WorkspaceInfo = {
  name: string;
  path: string | null;
  selected: boolean;
};

function workspaceList(cwd?: string): VcsEffect<WorkspaceInfo[]>;
Prefers template output when supported, falls back to parsing the human-readable jj workspace list output.

workspaceClose(name, opts?)

Forget a JJ workspace by name.
const result = await runVcs(
  workspaceClose("feature-auth", {
    cwd: "/path/to/repo",
  }),
);
function workspaceClose(
  name: string,
  opts?: { cwd?: string },
): VcsEffect<WorkspaceResult>;
This wraps jj workspace forget <name>.

captureWorkspaceSnapshot(cwd?)

Capture the current JJ working-copy state as a restorable handle. This helper is exported by @smithers-orchestrator/vcs, not by the root facade.
const snapshot = await runVcs(captureWorkspaceSnapshot("/path/to/repo"));
type WorkspaceSnapshot = {
  commitId: string;
  changeId: string;
  operationId: string;
};

function captureWorkspaceSnapshot(cwd?: string): VcsEffect<WorkspaceSnapshot | null>;
It returns null on failures and timeouts, including non-JJ directories. Smithers uses the commitId and operationId values for workspace durability checkpoints.

findVcsRoot(startDir)

Walk upward from a directory and return the nearest .jj or .git root. JJ wins when both markers exist in the same directory.
const root = await Effect.runPromise(findVcsRoot(process.cwd()));
type VcsRoot =
  | { type: "jj"; root: string }
  | { type: "git"; root: string };

function findVcsRoot(startDir: string): Effect.Effect<VcsRoot | null, never, never>;

resolveGitBinary() and resolveJjBinary()

Resolve the executable Smithers will spawn for VCS commands.
const git = resolveGitBinary();
const jj = resolveJjBinary();
type ResolvedBinary = {
  path: string;
  source: "env" | "bundled" | "path";
};

function resolveGitBinary(): ResolvedBinary;
function resolveJjBinary(): ResolvedBinary;
resolveGitBinary() checks SMITHERS_GIT_PATH, then falls back to git on PATH. resolveJjBinary() checks SMITHERS_JJ_PATH, then a bundled @smithers-orchestrator/jj-<platform> package, then jj on PATH.

vcsToolingStatus()

Probe whether the resolved VCS binaries are usable on the current host.
const status = vcsToolingStatus();
type VcsToolingStatus = {
  jj: ResolvedBinary | null;
  git: ResolvedBinary | null;
  ok: boolean;
};

function vcsToolingStatus(): VcsToolingStatus;
This is synchronous and best-effort. It runs version probes with a short timeout and powers the CLI’s VCS preflight checks.

When To Use These Helpers

Use these helpers when your application needs to:
  • show whether JJ-backed revert is available
  • record or inspect a pointer outside the Smithers engine
  • manage JJ workspaces directly from an app or integration layer
  • check whether JJ or Git tooling is available before starting worktree logic
For workflow-level revert behavior, prefer the runtime and CLI docs: