Files
platform/src/apps/officer-web/Screens/Dashboard/Automation/CLAUDE.md
T
pastilhas ef13f96d36 fix(pi): add snap node compatibility diagnostics and documentation
- Added detailed error logging to detect snap node compatibility issues
- When Pi process exits with code 1, log helpful diagnostic info including node path
- Add hint to check for snap node and reinstall via apt/nvm
- Create SNAP_NODE_COMPATIBILITY.md with full troubleshooting guide
- Document root cause: snap node has file descriptor incompatibility with Bun.spawn stdin pipes
- Provide clear installation instructions for NodeSource and nvm alternatives
2026-03-04 01:45:36 +00:00

11 KiB

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:

<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:

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

AutomationSelection (defined in AutomationRightPanel.tsx):

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

---
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

---
name: skill-name
description: When to use this skill.
---

(Knowledge base / reference documentation in markdown)

TOOL.md

---
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.