# Tela Skills Documentation

Complete reference for the `@meistrari/tela-skills` CLI, Skills, and Agents.

---

## Table of Contents

- [Overview](#overview)
- [CLI](#cli)
- [Skills](#skills)
  - [Canvas Management](#canvas-management)
  - [Project Management](#project-management)
  - [Prompt Listing](#prompt-listing)
  - [Test Cases](#test-cases)
  - [Task Management](#task-management)
  - [Vault (File Management)](#vault-file-management)
  - [Common Utilities](#common-utilities)
- [Agents](#agents)
  - [test-case-handler](#test-case-handler)
  - [file-explorer](#file-explorer)
- [Architecture](#architecture)
- [Key Patterns](#key-patterns)

---

## Overview

`@meistrari/tela-skills` is a Claude Code skill package that connects Claude to the [Tela](https://app.tela.com) platform. It enables AI-assisted prompt engineering, test case management, task validation workflows, and file operations through Tela's API.

**Install:**

```bash
bunx @meistrari/tela-skills
```

---

## CLI

The CLI (`cli.ts`) is an interactive installer that sets up Tela Skills for Claude Code.

### What it does

1. **Environment selection** — Choose between Production (`api.tela.com`), Staging (`api.telastaging.com`), or Localhost (Docker-based local development).
2. **Skill installation** — Copies the main Tela API skill to `~/.claude/skills/tela-studio/` and installs standalone skills under their own `~/.claude/skills/<skill-name>/` directories.
3. **Agent deployment** — Copies agent definitions from `agents/` to `~/.claude/agents/` (if available).
4. **Dependency installation** — Installs `@meistrari/vault-sdk` into the skills directory via Bun.
5. **Authentication** — Production/Staging use the auth-api device flow: the terminal shows a code, you approve it in the browser and pick a workspace; Docker discovery + JWT generation handles Localhost.
6. **Session persistence** — Saves credentials to `~/.tela/session.production`, `~/.tela/session.staging`, or `~/.tela/session.local` and environment config to `~/.tela/environment` and `~/.tela/.env`.

### Usage

```bash
# Install and configure (interactive)
bunx @meistrari/tela-skills

# Non-interactive localhost setup
bunx @meistrari/tela-skills --env localhost --repos-path /path/to/tela --workspace <slug-or-id> --yes

# Test locally during development
bun run cli.ts

# Verify skill loads correctly
bun --preload skill/preload.ts -e "console.log(Object.keys(tela))"

# Run commands against localhost
bun localhost.ts "console.log(await tela.listProjects())"

# List available localhost workspaces
bun list-workspaces.ts
```

### File layout after installation

```
~/.claude/
  skills/tela-studio/                  # Main Tela API skill source files + dependencies
  skills/tela/                         # Pre-rename install: SKILL.md removed, preload.ts and localhost-utils.ts forward to tela-studio
  skills/tela-open-issue/              # Open issue skill
  skills/tela-localhost/               # Localhost setup skill
  skills/workflow-code-builder/        # Workflow Code source implementation skill
  skills/workflow-code-builder-framework/ # Workflow Code proposal/framework skill
  skills/workflow-code-operations/     # Workflow Code CLI operations skill
  skills/update-repo-changelog/        # Repository changelog Page skill
  agents/                           # Agent definitions (test-case-handler, file-explorer)

~/.tela/
  session.production  # Device-flow access + refresh tokens, JSON (production)
  session.staging     # Device-flow access + refresh tokens, JSON (staging)
  session.local       # JWT token (localhost, 30-day expiry)
  environment         # "production", "staging", or "localhost"
  .env                # TELA_API_URL, TELA_APP_URL
  tela-repo-path      # Path to tela monorepo (localhost only, for key discovery)
```

### Localhost environment

For internal development against locally running Tela services:

1. Start Docker services in the tela monorepo (`make dev`)
2. Run `bunx @meistrari/tela-skills` and select "Localhost"
3. The CLI discovers Docker ports, reads `AUTH_API_SECRET` from `packages/api/.env`, loads auth settings from `.repositories/auth-api/.env`, fetches workspaces, and generates a JWT through the local auth-api JWKS signing flow

The localhost JWT has a 30-day expiry and is auto-regenerated when expired. Workspace selection is required during setup — use `bun list-workspaces.ts` to see available workspaces.

---

## Skills

All skill functions are executed via `bun --preload` and accessed through the global `tela` object. The preload script (`preload.ts`) loads environment variables from `~/.tela/.env` and exposes all exports as `globalThis.tela`.

**Runtime pattern:**

```bash
bun --preload ~/.claude/skills/tela-studio/preload.ts -e "
  const result = await tela.someFunction(args);
  console.log(JSON.stringify(result, null, 2));
"
```

---

### Canvas Management

**File:** `skill/canvas.ts`

Canvas is Tela's core concept — a versioned AI prompt with a message, variables, configuration, and optional structured output.

#### Types

| Type | Description |
|------|-------------|
| `Canvas` | Top-level prompt object with metadata |
| `CanvasVersion` | Versioned snapshot: message, variables, config |
| `Variable` | Input/output definition (text, file, or mixed) |
| `StructuredOutput` | JSON schema for enforcing response format |
| `CanvasMessage` | Chat message (system, user, assistant, function) |
| `CanvasVersionConfiguration` | Model settings (model, temperature, type, functions) |

#### Functions

**`createCanvas(payload)`** — Create a new canvas with an optional initial version. Omit `projectId` to create a personal draft; include it to create the canvas in a project. `workspaceId` is inferred from supported CLI/data-token sessions when omitted and can be passed explicitly as an override or as a fallback for legacy or manually managed credentials without workspace claims.

```ts
tela.createCanvas({
  title: "My Prompt",
  version: {
    title: "v1",
    message: tela.createMessage("system", "You are a helpful assistant.", 0),
    variables: [tela.createVariable("input", "text")],
    configuration: { model: "gpt-5", type: "chat", temperature: 0 }
  }
})
```

**`promoteCanvas(canvasId, projectId)`** — Move a personal draft into a project. The only way to do it: `updateCanvas({ projectId })` discards the field and `PATCH /prompt/:id` returns `400`. A canvas can be promoted once; afterwards it stays in that project.

**`getCanvas(canvasId)`** — Fetch a canvas and its latest version.

**`updateCanvas(canvasId, payload, options?)`** — Creates a new version. Pass `{ merge: true }` to preserve existing fields like `structuredOutput`.

**`createCanvasVersion(canvasId, payload)`** — Explicitly create a new version for an existing canvas.

**`updateCanvasVersion(versionId, payload)`** — Modify an existing version by ID.

**`getCanvasVersion(versionId)`** — Fetch a specific version by ID.

#### Helpers

| Function | Purpose |
|----------|---------|
| `createMessage(role, content, index?)` | Build a message with auto-generated HTML from markdown |
| `createVariable(name, type, options?)` | Define an input/output variable |
| `createStructuredOutput(properties)` | Build a JSON schema for validated responses |
| `markdownToHtml(markdown)` | Convert markdown to HTML, replacing `{var}` with `<variable>` tags |

#### Defaults

- **Model:** `gpt-5`
- **Temperature:** `0`
- **Structured output:** Use `structuredOutput` config — never embed schema in prompt text

---

### Project Management

**File:** `skill/projects.ts`

| Function | Description |
|----------|-------------|
| `listProjects(options?)` | List projects (default limit: 10) |
| `createProject(payload)` | Create a new project with a title |

```ts
tela.listProjects({ limit: 20 })
tela.createProject({ title: "New Project" })
```

---

### Prompt Listing

**File:** `skill/prompts.ts`

List and retrieve canvas metadata without loading full version content.

| Function | Description |
|----------|-------------|
| `listPrompts(options?)` | List prompts with optional filtering |
| `getPrompt(promptId)` | Get a single prompt |
| `getPromotedVersion(promptId)` | Get the promoted (production) version |

---

### Test Cases

**File:** `skill/test-cases.ts`

Create, run, and manage evaluation datasets for canvas/prompt testing.

#### Statuses

`initializing` → `ready` → `starting` → `running` → `finalizing` → `completed` | `failed` | `aborted` | `timeout` | `pending_review`

#### Types

| Type | Description |
|------|-------------|
| `TestCase` | Test definition: variables, files, messages, expected output |
| `Generation` | Execution result: content, status, feedback, validations |

#### Functions

| Function | Description |
|----------|-------------|
| `listTestCases(promptId, options)` | List with pagination and filtering |
| `getTestCase(testCaseId)` | Get a single test case with its generations |
| `createTestCase(payload)` | Create one test case |
| `createTestCases(payloads)` | Batch create multiple test cases |
| `updateTestCase(testCaseId, payload)` | Update an existing test case |
| `deleteTestCase(testCaseId)` | Soft delete |
| `runTestCase(testCaseId, versionId)` | Execute against a specific canvas version |
| `runTestCases(testCaseIds, versionId)` | Batch execution |
| `waitForTestCase(testCaseId, options)` | Poll until completion (max 5 min default) |
| `abortTestCase(testCaseId)` | Stop a running execution |

#### Helper

**`createTestCasePayload(promptId, variables, options)`** — Builds a properly formatted payload:
- Separates text variables from `vault://` file references automatically
- Generates HTML for `variablesRichContent`
- Detects MIME types for file entries
- Constructs the `files[]` array

```ts
tela.createTestCasePayload("prompt-uuid", {
  document: "vault://file-uuid",
  question: "What is the main topic?"
}, {
  title: "Topic extraction test",
  expectedOutput: "Machine learning"
})
```

---

### Task Management

**File:** `skill/tasks.ts`

Manage human-in-the-loop validation tasks generated from prompt executions.

#### Task Statuses

`created` → `running` → `validating` → `completed` | `failed` | `cancelled`

#### Functions

| Function | Description |
|----------|-------------|
| `listTasks(options)` | List with filtering by status, tags, date, creator, etc. |
| `getTask(taskId, options?)` | Get task details, optionally with analytics |
| `updateTask(taskId, payload)` | Update status, name, tags, or output content |
| `approveTask(taskId)` | Shorthand: set status to `completed` |
| `deleteTask(taskId)` | Soft delete one task |
| `deleteTasks(taskIds)` | Bulk soft delete |
| `undoApprovalTasks(taskIds)` | Revert completed tasks back to `validating` |
| `getTaskVersions(taskId)` | Get version/edit history |
| `getTaskInsightsByVersion(versionId)` | Approved/corrected/pending counts |
| `getTaskMetrics()` | Workspace-level metrics |

---

### Vault (File Management)

**File:** `skill/vault.ts`

Upload, download, and manage files using immutable `vault://` references.

| Function | Description |
|----------|-------------|
| `uploadFile(filePath, opts?)` | Upload a local file, returns vault reference |
| `uploadContent(name, content, opts?)` | Upload string/Blob content |
| `downloadFile(vaultRef)` | Get file as Blob |
| `downloadFileUrl(vaultRef)` | Get pre-signed download URL |
| `deleteFile(vaultRef)` | Delete from vault |
| `getFileMetadata(vaultRef)` | File metadata |
| `bulkUploadFiles(files)` | Batch upload, returns array of references |
| `uploadFileForVariable(filePath)` | Upload specifically for test case variables |
| `isVaultReference(value)` | Check if a string is a `vault://` reference |
| `createPermalink(vaultRef, opts?)` | Create a public shareable link |
| `listPermalinks(vaultRef)` | List all permalinks for a file |
| `deletePermalink(vaultRef, id)` | Delete a specific permalink |

---

### Common Utilities

**File:** `skill/common.ts`

| Function | Description |
|----------|-------------|
| `loadApiKey()` | Uses the hosted runtime's `DATA_TOKEN`, falling back to `~/.tela/session.production`, `~/.tela/session.staging`, or `~/.tela/session.local` |
| `loadFreshApiKey()` | Like `loadApiKey()`, but first renews a device-flow session whose access token is about to expire; use it before any raw `fetch` |
| `authHeaders(token)` | Returns the auth header for a credential: `Authorization: Bearer` for device-flow tokens, `x-data-token` otherwise |
| `getAuthenticatedWorkspaceId()` | Gets the workspace ID from a supported CLI/data-token session; throws with an explicit `workspaceId` fallback when claims are unavailable |
| `getApiBaseUrl()` | Returns API URL from globals or defaults |
| `getAppBaseUrl()` | Returns app URL for constructing viewer links |
| `apiRequest<T>(method, path, body?)` | Generic HTTP client with Bearer auth |
| `parsePayload(payload)` | Transforms `vault://` references into `{file_url}` format |
| `getProjectUrl(projectId)` | Constructs viewer URL for a project |
| `getCanvasUrl(canvasId)` | Constructs viewer URL for a canvas |

---

## Agents

Agents are specialized sub-processes that Claude Code dispatches for complex, multi-step tasks. They run autonomously with their own context and tool access.

### test-case-handler

**Model:** Opus (high capability)

**Trigger:** Use when the user wants to create, run, list, update, or delete test cases, or build evaluation datasets.

**Capabilities:**
- List test cases with pagination and filtering
- Batch create test cases from structured data
- Run tests against specific canvas versions and wait for results
- Handle file uploads to Vault before test case creation
- Automatically separate text vs. file variables
- Batch file operations for large datasets

**Example prompts that activate this agent:**
- "Create test cases for this canvas"
- "Run all test cases"
- "Build an evaluation dataset from these documents"

### file-explorer

**Model:** Sonnet (balanced speed/capability)

**Trigger:** Use when files need to be analyzed before uploading to test cases or workstation tasks.

**Capabilities:**
- Find files by glob patterns
- Search file content with grep
- Read and categorize documents by topic or type
- Extract metadata and summaries
- Filter files by criteria (size, type, content)
- Prepare file lists for batch test case creation

**Example prompts that activate this agent:**
- "Find all markdown files about authentication"
- "Categorize these documents by topic"
- "Which PDFs in this folder are invoices?"

---

## Architecture

```
Installation:
  bunx @meistrari/tela-skills
       │
       ▼
    cli.ts (interactive setup)
       │
       ├─→ ~/.claude/skills/tela-studio/ (skill source + deps)
       ├─→ ~/.claude/agents/        (agent definitions)
       └─→ ~/.tela/                 (session, env config)

Runtime:
  bun --preload preload.ts -e "tela.someFunction()"
       │
       ▼
    preload.ts  →  loads ~/.tela/.env  →  sets global `tela`
       │
       ▼
    common.ts   →  auth (loadApiKey)  →  HTTP (apiRequest)
       │
       ├─→ canvas.ts      (create/update/get prompts + versions)
       ├─→ projects.ts    (workspace/project CRUD)
       ├─→ prompts.ts     (list/get prompt metadata)
       ├─→ test-cases.ts  (create/run/manage evaluations)
       ├─→ tasks.ts       (human-in-the-loop validation)
       ├─→ vault.ts       (file upload/download/manage)
       └─→ localhost-utils.ts (Docker discovery, JWT signing, workspace fetching)
```

---

## Key Patterns

### Dual content format

Every message and variable has both **markdown** and **HTML** representations. The markdown version uses `{variableName}` syntax; the HTML version uses `<variable>` tags.

### Vault references

All files are referenced by immutable `vault://UUID` identifiers — never local paths. Upload files to Vault first, then use the returned reference in a canvas message, test cases, or task inputs.

### Smart merging

`updateCanvas()` creates a new version rather than modifying existing ones. Pass `{ merge: true }` to preserve nested config (like `structuredOutput`) while updating other fields.

### Variable syntax

Reference variables as `{variableName}` (single braces) in markdown content. The `markdownToHtml` helper converts these to proper `<variable>` tags automatically.

### Structured output

Define output schemas via the `structuredOutput` config on canvas versions. Never embed JSON schema instructions in the prompt text itself — use `createStructuredOutput()` instead.

### Polling for completion

Long-running operations like test case execution use `waitForTestCase()` which polls at configurable intervals (default: 2s) up to a max timeout (default: 5 min).
