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
/api/workspace/context Return the current workspace context including launch directory, platform info, and runtime state. Cached for 60 seconds.
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| cwd | string | * | Current working directory at launch |
| platform | string | * | Operating system platform |
| arch | string | * | CPU architecture |
| nodeVersion | string | * | Node.js version |
| homedir | string | * | 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
/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
| Field | Type | Req | Description |
|---|---|---|---|
| parentDir | string | * | Parent directory to scan (supports ~ expansion) |
| maxDepth | number | — | Maximum directory depth to search |
| markers | string[] | — | Marker filenames to detect projects (e.g., package.json, .git) |
Response Body
| Field | Type | Req | Description |
|---|---|---|---|
| projects | object[] | * | Discovered project directories |
| projects[].path | string | * | Absolute path to the project |
| projects[].name | string | * | Directory name |
| projects[].markers | string[] | * | Which marker files were found |
| projects[].alreadyImported | boolean | * | 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)
/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)
| Field | Type | Req | Description |
|---|---|---|---|
| projects | object[] | * | Array of projects to import |
| projects[].path | string | * | Absolute path to the project directory |
| projects[].name | string | * | Project name |
SSE Event Types:
| Event type | Fields | Description |
|---|---|---|
progress | index, name, status: "creating" | Import started for a project |
progress | index, name, status: "done", projectId, artifactCount | Project created and scanned |
progress | index, name, status: "failed", error | Import failed for a project |
complete | created, failed | All 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}