---
title: VCS Helper Reference
description: Public JJ and VCS helper APIs for repo detection, snapshot inspection, binary resolution, and workspace management.
---

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:

```ts
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:

```ts
import {
  captureWorkspaceSnapshot,
  findVcsRoot,
  resolveGitBinary,
  resolveJjBinary,
  vcsToolingStatus,
} from "@smithers-orchestrator/vcs";
```

Direct JJ helper calls need a platform layer:

```ts
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.

```ts
const result = await runVcs(runJj(["status"], { cwd: "/path/to/repo" }));
```

```ts
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.

```ts
const pointer = await runVcs(getJjPointer("/path/to/repo"));
```

```ts
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`.

```ts
const result = await runVcs(revertToJjPointer("zqkopwvn", "/path/to/repo"));
```

```ts
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.

```ts
const enabled = await runVcs(isJjRepo("/path/to/repo"));
```

```ts
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.

```ts
const result = await runVcs(
  workspaceAdd("feature-auth", "/tmp/wt-feature-auth", {
    cwd: "/path/to/repo",
    atRev: "@",
  }),
);
```

```ts
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.

```ts
const workspaces = await runVcs(workspaceList("/path/to/repo"));
```

```ts
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.

```ts
const result = await runVcs(
  workspaceClose("feature-auth", {
    cwd: "/path/to/repo",
  }),
);
```

```ts
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.

```ts
const snapshot = await runVcs(captureWorkspaceSnapshot("/path/to/repo"));
```

```ts
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.

```ts
const root = findVcsRoot(process.cwd());
```

```ts
type VcsRoot =
  | { type: "jj"; root: string }
  | { type: "git"; root: string };

function findVcsRoot(startDir: string): VcsRoot | null;
```

## `resolveGitBinary()` and `resolveJjBinary()`

Resolve the executable Smithers will spawn for VCS commands.

```ts
const git = resolveGitBinary();
const jj = resolveJjBinary();
```

```ts
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.

```ts
const status = vcsToolingStatus();
```

```ts
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:

- [VCS Integration](/guides/vcs)
- [CLI Reference](/cli/overview)
- [Revert](/runtime/revert)
