Workspace API

The Workspace API provides discovery of project directories on the filesystem, bulk import into Stagent with automatic environment scanning, and runtime context about the current workspace. The import endpoint uses SSE to stream progress as each project is created and scanned.

Quick Start

Discover projects in a parent directory, then import the new ones with streamed progress:

// 1. Discover project directories under ~/Developer
interface DiscoveredProject {
path: string;
name: string;
markers: string[];
alreadyImported: boolean;
}

const { projects }: { projects: DiscoveredProject[] } = await fetch('/api/workspace/discover', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  parentDir: '~/Developer',
  maxDepth: 2,
  markers: ['package.json', 'Cargo.toml', 'go.mod'],
}),
}).then((r: Response) => r.json());

// 2. Filter to projects not yet imported
const newProjects: DiscoveredProject[] = projects.filter((p) => !p.alreadyImported);
console.log(`Found ${projects.length} projects, ${newProjects.length} new`);

// 3. Import new projects with streamed progress (SSE)
const res: Response = await fetch('/api/workspace/import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  projects: newProjects.map((p) => ({ path: p.path, name: p.name })),
}),
});

const reader: ReadableStreamDefaultReader<Uint8Array> = res.body!.getReader();
const decoder: TextDecoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

const text: string = decoder.decode(value);
for (const line of text.split('\n')) {
  if (line.startsWith('data: ')) {
    const event: { type: string; name?: string; status?: string; created?: number; failed?: number } =
      JSON.parse(line.slice(6));
    if (event.type === 'progress') {
      console.log(`${event.name}: ${event.status}`);
    } else if (event.type === 'complete') {
      console.log(`Done! ${event.created} imported, ${event.failed} failed`);
    }
  }
}
}

Base URL

/api/workspace

Endpoints

Get Workspace Context

GET /api/workspace/context

Return the current workspace context including launch directory, platform info, and runtime state. Cached for 60 seconds.

Response Body

FieldTypeReqDescription
cwdstring*Current working directory at launch
platformstring*Operating system platform
archstring*CPU architecture
nodeVersionstring*Node.js version
homedirstring*User home directory

Get runtime context — useful for displaying workspace info in the UI or setting default paths:

// Get workspace context for the UI
interface WorkspaceContext {
cwd: string;
platform: string;
arch: string;
nodeVersion: string;
homedir: string;
}

const context: WorkspaceContext = await fetch('/api/workspace/context')
.then((r: Response) => r.json());

console.log(`CWD: ${context.cwd}`);
console.log(`Platform: ${context.platform} (${context.arch})`);
console.log(`Node: ${context.nodeVersion}`);
console.log(`Home: ${context.homedir}`);

Example response:

{
  "cwd": "/Users/team/Developer",
  "platform": "darwin",
  "arch": "arm64",
  "nodeVersion": "v22.4.0",
  "homedir": "/Users/team"
}

Discover Projects

POST /api/workspace/discover

Scan a parent directory for project directories using configurable marker files (package.json, Cargo.toml, etc.). Flags projects that are already imported into Stagent.

Request Body

FieldTypeReqDescription
parentDirstring*Parent directory to scan (supports ~ expansion)
maxDepthnumberMaximum directory depth to search
markersstring[]Marker filenames to detect projects (e.g., package.json, .git)

Response Body

FieldTypeReqDescription
projectsobject[]*Discovered project directories
projects[].pathstring*Absolute path to the project
projects[].namestring*Directory name
projects[].markersstring[]*Which marker files were found
projects[].alreadyImportedboolean*Whether this project already exists in Stagent

Errors: 400 — Validation failure or directory not found

Scan a developer directory to find all project directories — already-imported projects are flagged so you can skip them:

// Discover project directories
interface DiscoveredProject {
path: string;
name: string;
markers: string[];
alreadyImported: boolean;
}

const { projects }: { projects: DiscoveredProject[] } = await fetch('/api/workspace/discover', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  parentDir: '~/Developer',
  maxDepth: 2,
}),
}).then((r: Response) => r.json());

// Show discovered projects, highlighting new ones
projects.forEach((p: DiscoveredProject) => {
const status: string = p.alreadyImported ? 'imported' : 'NEW';
console.log(`[${status}] ${p.name} — ${p.path} (${p.markers.join(', ')})`);
});

const newProjects: DiscoveredProject[] = projects.filter((p) => !p.alreadyImported);
console.log(`${newProjects.length} new projects ready to import`);

Example response:

{
  "projects": [
    {
      "path": "/Users/team/Developer/my-app",
      "name": "my-app",
      "markers": ["package.json", ".git"],
      "alreadyImported": false
    },
    {
      "path": "/Users/team/Developer/api-server",
      "name": "api-server",
      "markers": ["package.json", ".git"],
      "alreadyImported": true
    },
    {
      "path": "/Users/team/Developer/rust-cli",
      "name": "rust-cli",
      "markers": ["Cargo.toml", ".git"],
      "alreadyImported": false
    }
  ]
}

Import Projects (SSE)

SSE /api/workspace/import

Bulk-import discovered projects into Stagent. Each project is created with an automatic environment scan. Progress is streamed via Server-Sent Events so the UI can update in real time.

Request Body (POST)

FieldTypeReqDescription
projectsobject[]*Array of projects to import
projects[].pathstring*Absolute path to the project directory
projects[].namestring*Project name

SSE Event Types:

Event typeFieldsDescription
progressindex, name, status: "creating"Import started for a project
progressindex, name, status: "done", projectId, artifactCountProject created and scanned
progressindex, name, status: "failed", errorImport failed for a project
completecreated, failedAll imports finished

Errors: 400 — Validation failure

Import multiple projects at once and stream progress to the UI — each project gets an environment scan automatically:

// Import projects with streamed progress
const res: Response = await fetch('/api/workspace/import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  projects: [
    { path: '/Users/team/Developer/my-app', name: 'My App' },
    { path: '/Users/team/Developer/rust-cli', name: 'Rust CLI' },
  ],
}),
});

// Read the SSE stream
const reader: ReadableStreamDefaultReader<Uint8Array> = res.body!.getReader();
const decoder: TextDecoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

const text: string = decoder.decode(value);
for (const line of text.split('\n')) {
  if (line.startsWith('data: ')) {
    const event: Record<string, unknown> = JSON.parse(line.slice(6));

    if (event.type === 'progress') {
      if (event.status === 'creating') {
        console.log(`Importing ${event.name}...`);
      } else if (event.status === 'done') {
        console.log(`  Done: ${event.projectId} (${event.artifactCount} artifacts)`);
      } else if (event.status === 'failed') {
        console.error(`  Failed: ${event.error}`);
      }
    } else if (event.type === 'complete') {
      console.log(`Import complete: ${event.created} created, ${event.failed} failed`);
    }
  }
}
}

Example stream events:

data: {"type":"progress","index":0,"name":"My App","status":"creating"}

data: {"type":"progress","index":0,"name":"My App","status":"done","projectId":"proj-1a2b-3c4d","artifactCount":24}

data: {"type":"progress","index":1,"name":"Rust CLI","status":"creating"}

data: {"type":"progress","index":1,"name":"Rust CLI","status":"done","projectId":"proj-5e6f-7g8h","artifactCount":12}

data: {"type":"complete","created":2,"failed":0}