Skip to content

ctx.process stable

The "running things" domain. Two halves: persistent process / PTY sessions that survive app restarts (the core primitive under the terminal and future task runners / REPLs), and one-shot exec for extensions that wrap a CLI (git, formatters, linters) rather than drive an interactive shell.

ts
ctx.process: ProcessService

Example

tsx
// spawn a shell session in the workspace folder
const session = await ctx.process.spawn({ cwd, cols: 120, rows: 40 });

session.onData((data) => term.write(data)); // stream output to your UI
session.write("ls -la\n"); // send input

// later, after an app restart, reconnect by id:
const again = await ctx.process.attach(session.id);

One-shot exec

For fire-and-forget commands, exec runs a subprocess off the UI thread and resolves with its captured output. Arguments are passed verbatim (not shell-interpreted), so there's no quoting or shell-injection concern.

ts
const { stdout, stderr, code } = await ctx.process.exec(
  "git",
  ["status", "--porcelain=v2", "--untracked-files=all"],
  { cwd: workspaceFolder },
);
if (code === 0) parseStatus(stdout);
else console.error(stderr);

A command that runs but exits non-zero still resolves — inspect code / stderr to decide. The promise rejects only when the process can't be spawned at all (e.g. the executable isn't on PATH). This is the primitive the git extension builds on, replacing bespoke per-tool host commands.

Methods

ProcessService (ctx.process):

MethodWhat it does
spawn(opts)Start a new session in opts.cwd; resolves to a ProcessSession.
attach(id, opts?)Re-attach to an existing session by id; rejects if it's gone.
exec(command, args, options?)Run a one-shot command off the UI thread; resolves to a ProcessExecResult.

ProcessSession (the handle):

MethodWhat it does
write(data)Send input to the session.
resize(cols, rows)Notify the session of a viewport size change.
kill()Terminate and release the session.
getBuffer() / saveBuffer(data)Read / persist the output buffer (restore a view on re-attach).
onData(listener) / onExit(listener)Subscribe to output / exit; returns a Disposable.

Types

ProcessService · ProcessSession · ProcessSpawnOptions · ProcessExecOptions · ProcessExecResult.

Workspace scoping

A third-party extension's processes are scoped to the open workspace: a session or exec defaults its working directory to the workspace folder, and a cwd outside it throws PathDeniedError unless the extension declared the processPermission (consented to at install). The command and its arguments aren't constrained — cwd is the enforceable knob in-process. See the Permissions & access guide. (First-party bundled extensions are unscoped.)

Notes

Today sessions are shell PTYs, backed by Silo's self-owned PTY session host — a daemon the app owns, with no external dependency. The persistence lives in core, so a session outlives the extension that opened it and an app restart — which is why the built-in terminal can be a removable/replaceable extension on top of this primitive.