# Automation

## Overview

The `/automation` route is the hub for managing AI agent capabilities: skills, tools, tasks, and processes. Each capability is a markdown file (with YAML frontmatter) stored on the filesystem. There is no relational database for automation data.

The page uses a resizable `WorkspaceView` layout with three panels: a sidebar to pick categories, a right panel for browsing/viewing, and an optional bottom-right chat panel for AI-assisted editing.

```
+------------------+------------------------------------------+
|  Sidebar (20%)   |  Right Panel (80%)                       |
|                  |  - CapabilityList       (browse)         |
|  8 categories    |  - CapabilityDetailView (selected item)  |
|                  |  - New* form            (creating)       |
|                  +------------------------------------------+
|                  |  AutomationEditChat (when editing, 50%)  |
+------------------+------------------------------------------+
```

## Route & Entry Point

Defined in `App.tsx`:
```tsx
<Route path="/automation" element={<Dashboard.Automation />} />
<Route path="/new-automation" element={<Dashboard.NewAutomation />} />  // stub, not implemented
```

Entry component: `index.tsx` (`Automation`). Manages the `WorkspaceView` layout and dynamically adds/removes the chat panel based on `selection.editing`.

## Capability Categories

| Category | Kind | Backend Route | Status |
|----------|------|---------------|--------|
| Skills | `Skill` | `GET/POST/DELETE /api/skills` | Working |
| Tools | `Tool` | `GET/POST/DELETE /api/tools` | Working |
| Tasks | `Task` | `GET/POST/DELETE /api/tasks` | Working |
| Processes | `Process` | `GET/POST/DELETE /api/processes` | Working |
| Pipelines | `Pipeline` | None | Placeholder (404) |
| Workflows | `Workflow` | None | Placeholder (404) |
| Crons | `Cron` | None | Placeholder (404) |
| Services | `Service` | None | Placeholder (404) |

Defined as `capabilityItems` in `AutomationSidebar.tsx`.

## State Management

All cross-panel communication uses a single shared channel:

```ts
usePanelChannel<AutomationSelection>('automation:selected-capability', null)
```

`AutomationSelection` (defined in `AutomationRightPanel.tsx`):
```ts
type AutomationSelection = {
  kind: string;        // 'Skill' | 'Task' | 'Tool' | 'Process' | etc.
  endpoint: string;    // '/skills' | '/tasks' | etc.
  queryKey: string;    // React Query cache key
  dirName: string;     // '' = list view, non-empty = detail view
  isNew?: boolean;     // just created, AI chat opens in creation mode
  editing?: boolean;   // chat panel is open
  creating?: boolean;  // New* form is shown
  description?: string; // passed to AI as initial context
} | null;
```

Layout state persisted via `useDashboardState('screens/automation')`.

## Components

### `AutomationSidebar.tsx`
Left nav with the 8 category buttons. Clicking sets selection to `{ kind, endpoint, queryKey, dirName: '' }` (list view).

### `AutomationRightPanel.tsx`
Routes to one of three views based on selection state:
- `creating === true` -> `New*` form (mapped via `newComponentMap[kind]`)
- `dirName === ''` -> `CapabilityList` (browse items)
- `dirName !== ''` -> `CapabilityDetailView` (detail for specific item)

### `CapabilityList.tsx`
Fetches items from `GET {endpoint}`, renders filterable list. Has `+` button to set `creating: true`.

### `CapabilityDetailView.tsx`
Shows a selected capability's detail (frontmatter + markdown body). Header actions:
- **Back arrow** - returns to list view
- **Run** (tasks only) - parses `inputs:` from frontmatter YAML, opens `TaskRunnerModal`
- **Edit** - toggles `selection.editing` to open/close chat panel
- **Delete** - confirmation dialog, then `DELETE {endpoint}/{dirName}`

Task input parsing (`parseInputs`) supports: `string`, `number`, `boolean`, `select` types.

### `AutomationEditChat.tsx`
Bottom chat panel for AI-assisted editing. Uses `CapabilityChat` from `CapabilityPage.tsx`. Features:
- Delete chat history button (`DELETE {endpoint}/{dirName}/chat`)
- Close button (sets `editing: false`)
- On AI response end, invalidates both individual and list query caches

### `New*.tsx` (8 components)
`NewTask`, `NewSkill`, `NewTool`, `NewProcess`, `NewPipeline`, `NewCron`, `NewService`, `NewWorkflow`. All structurally identical:
1. Name + description form
2. `POST {endpoint}` with `{ name }`
3. On success: transitions to detail view with `isNew: true, editing: true` (opens AI chat in creation mode)

## Shared Components (`CapabilityPage.tsx`)

Located at `../CapabilityPage.tsx`. Exports used by automation:

- **`CapabilityChat`** - AI chat wired to Pi agent via `usePiChat()` + `EmbeddableChat`. Injects `promptFrontmatter` prefix before every message. For `isNew === true`, uses rich creation guides (`buildTaskCreationPrefix`, `buildSkillCreationPrefix`, `buildToolCreationPrefix`).
- **`FrontmatterBlock`** - Collapsible YAML frontmatter display.
- **`CapabilityDetail`** type - `{ dirName, name, description, scope, body, rawFrontmatter, filePath, chatSessionId }`.
- **`CapabilityPage`** - Legacy standalone two-panel page (used by `/skills`, `/tasks`, `/processes` routes, not by `/automation`).

## Backend

All four working routers (`skills`, `tools`, `tasks`, `processes`) follow the exact same pattern. Registered on the protected router in `hono.ts`.

### API Endpoints (per capability type)

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/{type}` | List all items (native + global + user merged) |
| `GET` | `/{type}/:name` | Get detail: frontmatter, body, rawYaml, filePath, chatSessionId |
| `POST` | `/{type}` | Create new item (dir + stub `.md`) |
| `DELETE` | `/{type}/:name` | Delete item directory |
| `GET` | `/{type}/:name/chat` | Get saved chat messages |
| `PUT` | `/{type}/:name/chat` | Save chat messages + session meta |
| `DELETE` | `/{type}/:name/chat` | Delete chat directory |

Route files:
- `src/servers/api/skills/skills.ts`
- `src/servers/api/tools/tools.ts`
- `src/servers/api/tasks/tasks.ts` (also parses `trigger:` block from frontmatter)
- `src/servers/api/processes/processes.ts`

### Task Logs

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/task-logs` | List all log metadata (newest first) |
| `GET` | `/task-logs/:filename` | Get full log with messages |

Route file: `src/servers/api/task-logs/task-logs.ts`

## Storage Model

Everything is stored on the filesystem. No database tables.

```
DATA_PATH/
  skills/                         <- global scope
    {skill-name}/
      SKILL.md                    <- frontmatter + markdown body
      chat/
        meta.json                 <- { id: sessionId }
        messages.json             <- ChatMessage[]
  tasks/                          <- global scope
    {task-name}/TASK.md
  tools/                          <- global scope
    {tool-name}/TOOL.md
  processes/                      <- global scope
    {process-name}/PROCESS.md
  {user-email}/                   <- user scope
    skills/
    tasks/
    tools/
    processes/
    logs/tasks/                   <- task execution logs
      {timestamp}-{dirName}.json

SEED_PATH/                        <- native scope (read-only, shipped with app)
  skills/   (sharp, whisper-cpp, google-mail-api, ffmpeg, mutagen, fizzy-cli, mlxaudio)
  tasks/    (convert-to-mp3, sync-gmail-inbox, tiktok-trends, transcribe-audio-file, ...)
  tools/    (apify, browser, email-db, ffmpeg, gmail, ocr, web-fetch, web-search, ...)
```

Path helpers: `src/servers/data-path.ts` (`getNativeSkillsDir`, `getGlobalSkillsDir`, `getUserSkillsDir`, etc.)

### Scope & Permissions

Three tiers with override resolution: **user > global > native**.

- `native`: seed directory, read-only, shipped with the app
- `global`: shared data directory, writable by Super Admin
- `user`: per-user directory, writable by the owning user

Regular users (`Member`) can only create/delete `user`-scoped items. `Super Admin` can also create/delete `global` items.

## Markdown File Formats

### TASK.md
```yaml
---
name: Task Name
description: What the task does.
version: 1
author: pastilhas
tags: [tag1, tag2]
skills: [skill-name]       # optional, skills the agent can consult
tools: [tool_name]         # optional, tools the agent can call
trigger:                   # optional, when omitted: only runnable from Automation page
  - type: file
    extensions: [mp3, flac]
  - type: directory
inputs:                    # optional, parameters the user fills in before running
  - name: param_name
    type: string           # string | number | boolean | select
    required: true
    default: some value
---

(Agent instructions in markdown)
```

### SKILL.md
```yaml
---
name: skill-name
description: When to use this skill.
---

(Knowledge base / reference documentation in markdown)
```

### TOOL.md
```yaml
---
name: tool_name
label: Tool Display Name
description: What the tool does.
language: typescript        # typescript | bash | python
inputs:
  param_name:
    type: string            # string | number | boolean | enum | object
    description: What this parameter is for.
    optional: true
    sensitive: true
---

(Usage notes, output format, examples in markdown)
```

### PROCESS.md
Same structure as SKILL.md (name + description frontmatter, markdown body).

## Task Execution Flow

1. User clicks "Run" on a task in `CapabilityDetailView`
2. `parseInputs()` extracts `inputs:` from YAML frontmatter
3. If inputs exist: shows input form dialog; if none: runs immediately
4. Opens `TaskRunnerModal` with prompt: `"Read the task instructions at {filePath} and execute them"` (+ input values if any)
5. Modal connects to Pi agent WebSocket via `usePiChat()`
6. Agent reads the `TASK.md`, resolves skill/tool references, executes in the user's sandboxed container
7. `task-logger.ts` (`createTaskLog` -> `appendToLog` -> `finalizeLog`) writes execution log to `DATA_PATH/{email}/logs/tasks/`

## Task Trigger Integration

Tasks with `trigger:` in their frontmatter appear in the file browser context menu:
- `type: file` + `extensions: [mp3]` -> right-click on `.mp3` files shows this task
- `type: directory` -> right-click on directories shows this task

Implemented via `useTasks` hook in the `officerdev` workspace, which provides `getMatchingTasks(fileName, entryType)`.

## Seed Sync (Startup)

On server startup (`bootstrap.ts`):
- `syncSeedSkills()` and `syncSeedTools()` copy/update native seeds to global based on `version:` in frontmatter
- `syncSeedTasks()` exists in code but is **not called** in bootstrap

## Legacy Routes

Standalone pages still exist using the older `CapabilityPage` component:
- `/skills` -> `CapabilityPage` (two-panel, list + detail/chat)
- `/tasks` -> `CapabilityPage`
- `/processes` -> `CapabilityPage`

These share the same API endpoints but use the single-component layout instead of the workspace view.
