Skip to content
Perstack

Base Skill

Every Expert automatically has access to @perstack/base — a built-in skill that provides file operations, runtime control, and other essential tools.

No configuration needed. It’s always available.

Base Skill is the only skill tightly coupled with @perstack/runtime. The name “base” reflects this fundamental role — Perstack cannot operate without it, and the runtime assumes Base Skill is always present.

This coupling enables MCP-native runtime control. Rather than implementing special control mechanisms outside MCP, runtime operations (task completion, todos) are exposed as standard MCP tools.

For binary files (images, PDFs), Base Skill returns only path and mimeType. The runtime then:

  1. Reads the file from the returned path
  2. Base64 encodes the content
  3. Passes the encoded data to the LLM

This separation keeps Base Skill simple while letting the runtime handle LLM-specific formatting.

Base Skill never accesses external networks. This is intentional:

  • Keeps the attack surface minimal
  • Forces explicit network dependencies via separate skills
  • Makes network access auditable

If your Expert needs network access, define a dedicated skill for it.

All file operations are restricted to the workspace directory (where perstack run was executed).

  • Experts cannot read, write, or access files outside the workspace
  • Path traversal attempts (e.g., ../) are blocked
  • The .perstack directory is hidden from directory listings

ToolCategoryDescription
attemptCompletionRuntimeSignal task completion
todoRuntimeManage task list
clearTodoRuntimeClear task list
healthCheckSystemCheck MCP server health
execSystemExecute system commands
readTextFileFileRead text files
readImageFileFileRead image files (PNG, JPEG, GIF, WebP)
readPdfFileFileRead PDF files
writeTextFileFileCreate or overwrite text files
appendTextFileFileAppend to text files
editTextFileFileSearch and replace in text files
moveFileFileMove or rename files
deleteFileFileDelete files
getFileInfoFileGet file/directory metadata
listDirectoryDirectoryList directory contents
createDirectoryDirectoryCreate directories
deleteDirectoryDirectoryDelete directories

Returns Perstack runtime health status and diagnostics.

Parameters: None

Returns:

{
"status": "ok",
"workspace": "/path/to/workspace",
"uptime": "42s",
"memory": { "heapUsed": "25MB", "heapTotal": "50MB" },
"pid": 12345
}

Use cases:

  • Verify Perstack runtime is running and responsive
  • Check workspace configuration
  • Monitor runtime uptime and memory usage
  • Debug connection issues

Executes system commands within the workspace.

Parameters:

NameTypeRequiredDescription
commandstringYesCommand to execute (e.g., ls, python)
argsstring[]YesArguments to pass to the command
envRecord<string, string>YesEnvironment variables
cwdstringYesWorking directory (must be within workspace)
stdoutbooleanYesWhether to capture stdout
stderrbooleanYesWhether to capture stderr
timeoutnumberNoTimeout in milliseconds

Returns:

{ "output": "command output here" }

Behavior:

  • Executes command using Node.js execFile
  • Validates cwd is within the workspace
  • Merges provided env with process.env
  • Returns “Command executed successfully, but produced no output.” if no output captured
  • Returns timeout error if command exceeds timeout

Constraints:

  • Working directory must be within workspace
  • Do not execute long-running foreground commands (e.g., tail -f)
  • Be cautious with resource-intensive commands

Security Note: While cwd is validated, the executed command itself can still access files outside the workspace (e.g., cat /etc/passwd). For production deployments, use infrastructure-level isolation (containers, sandboxes) to enforce strict boundaries.


Signals task completion with automatic todo validation.

Parameters: None

Returns (remaining todos):

{
"remainingTodos": [
{ "id": 0, "title": "Incomplete task 1", "completed": false },
{ "id": 2, "title": "Incomplete task 2", "completed": false }
]
}

Returns (all todos complete or no todos):

{}

Behavior:

  • Checks the current todo list for incomplete items
  • If incomplete todos exist: returns them in remainingTodos and continues the agent loop
  • If no incomplete todos (or no todos at all): triggers run result generation and ends the agent loop
  • The Expert should complete remaining todos before calling again

Run result:

When todo validation passes, the LLM generates a run result — a summary of the work done. This ensures every completed run has a clear outcome, regardless of whether the Expert was delegated or run directly.

For delegated Experts, the run result is returned to the delegating Expert as the tool call result, maintaining context isolation.

Best Practice:

  • Mark all todos as complete before calling attemptCompletion
  • Use clearTodo if you want to reset and start fresh
  • The tool prevents premature completion by surfacing forgotten tasks

Task list manager for tracking work items.

Parameters:

NameTypeRequiredDescription
newTodosstring[]NoTask descriptions to add
completedTodosnumber[]NoTodo IDs to mark as completed

Returns:

{
"todos": [
{ "id": 0, "title": "Task 1", "completed": false },
{ "id": 1, "title": "Task 2", "completed": true }
]
}

Behavior:

  • Each todo gets a unique incremental ID when created
  • Returns the full todo list after every operation
  • State persists across calls within the session

Observability: The runtime records all tool calls (including todo) into checkpoints, making task progress visible in execution history.


Resets the todo list to empty state.

Parameters: None

Returns:

{ "todos": [] }

Reads text files with optional line range support.

Parameters:

NameTypeRequiredDescription
pathstringYesFile path
fromnumberNoStart line (0-indexed)
tonumberNoEnd line (exclusive)

Returns:

{ "path": "file.txt", "content": "file content", "from": 0, "to": 10 }

Behavior:

  • Reads as UTF-8 encoded text
  • Supports partial reading via line range
  • Defaults to reading entire file if range not specified

Constraints:

  • File must exist
  • Binary files will cause errors or corrupted output

Validates and returns image file metadata for runtime processing.

Parameters:

NameTypeRequiredDescription
pathstringYesImage file path

Returns:

{ "path": "/workspace/image.png", "mimeType": "image/png", "size": 12345 }

Runtime Integration: The runtime reads the file at path, base64 encodes it, and passes it to the LLM as an inline image.

Supported formats: PNG, JPEG, GIF, WebP

Constraints:

  • Maximum file size: 15MB
  • File must exist and be a supported image format

Validates and returns PDF file metadata for runtime processing.

Parameters:

NameTypeRequiredDescription
pathstringYesPDF file path

Returns:

{ "path": "/workspace/doc.pdf", "mimeType": "application/pdf", "size": 54321 }

Runtime Integration: The runtime reads the file at path, base64 encodes it, and passes it to the LLM as an inline file.

Constraints:

  • Maximum file size: 30MB
  • File must exist and be a valid PDF

Creates or overwrites text files.

Parameters:

NameTypeRequiredDescription
pathstringYesTarget file path
textstringYesContent to write (max 10,000 characters)

Returns:

{ "path": "/workspace/file.txt", "text": "written content" }

Behavior:

  • Creates parent directories automatically
  • Overwrites existing files
  • Writes as UTF-8 encoded text
  • Pass empty string to clear file contents

Constraints:

  • Maximum 10,000 characters per call
  • Use appendTextFile for larger files

Appends content to existing files.

Parameters:

NameTypeRequiredDescription
pathstringYesTarget file path
textstringYesContent to append (max 2,000 characters)

Returns:

{ "path": "/workspace/file.txt", "text": "appended content" }

Behavior:

  • Appends to end of file without modifying existing content
  • Does not add newline automatically

Constraints:

  • File must exist
  • Maximum 2,000 characters per call
  • Call multiple times for larger content

Performs search-and-replace in text files.

Parameters:

NameTypeRequiredDescription
pathstringYesTarget file path
oldTextstringYesText to find (max 2,000 characters)
newTextstringYesReplacement text (max 2,000 characters)

Returns:

{ "path": "/workspace/file.txt", "oldText": "old", "newText": "new" }

Behavior:

  • Performs exact string replacement (first occurrence only)
  • Normalizes line endings (CRLF → LF) before matching
  • File must contain exact match of oldText

Constraints:

  • File must exist
  • oldText must exist in file
  • Maximum 2,000 characters for both oldText and newText

Moves or renames files.

Parameters:

NameTypeRequiredDescription
sourcestringYesCurrent file path
destinationstringYesTarget file path

Returns:

{ "source": "/workspace/old.txt", "destination": "/workspace/new.txt" }

Behavior:

  • Creates destination directory if needed
  • Performs atomic move operation

Constraints:

  • Source must exist
  • Destination must not exist
  • Source must be writable

Removes files from the workspace.

Parameters:

NameTypeRequiredDescription
pathstringYesFile path to delete

Returns:

{ "path": "/workspace/deleted.txt" }

Constraints:

  • File must exist
  • File must be writable
  • Cannot delete directories (use directory-specific tools)

Retrieves detailed file or directory metadata.

Parameters:

NameTypeRequiredDescription
pathstringYesFile or directory path

Returns:

{
"exists": true,
"path": "file.txt",
"absolutePath": "/workspace/file.txt",
"name": "file.txt",
"directory": "/workspace",
"extension": ".txt",
"type": "file",
"mimeType": "text/plain",
"size": 1234,
"sizeFormatted": "1.21 KB",
"created": "2024-01-01T00:00:00.000Z",
"modified": "2024-01-02T00:00:00.000Z",
"accessed": "2024-01-03T00:00:00.000Z",
"permissions": {
"readable": true,
"writable": true,
"executable": false
}
}

Behavior:

  • Works for both files and directories
  • Returns null for extension and mimeType on directories
  • Formats size in human-readable format (B, KB, MB, GB, TB)

Lists directory contents with metadata.

Parameters:

NameTypeRequiredDescription
pathstringYesDirectory path

Returns:

{
"path": "/workspace/src",
"items": [
{ "name": "index.ts", "path": "index.ts", "type": "file", "size": 256, "modified": "2024-01-01T00:00:00.000Z" },
{ "name": "lib", "path": "lib", "type": "directory", "size": 4096, "modified": "2024-01-01T00:00:00.000Z" }
]
}

Behavior:

  • Lists only immediate children (non-recursive)
  • Sorts entries alphabetically
  • Excludes .perstack directory from results

Constraints:

  • Path must exist
  • Path must be a directory

Creates directories with recursive parent creation.

Parameters:

NameTypeRequiredDescription
pathstringYesDirectory path to create

Returns:

{ "path": "/workspace/new/nested/dir" }

Behavior:

  • Creates all parent directories as needed
  • Uses mkdir with recursive: true

Constraints:

  • Directory must not already exist
  • Parent directory must be writable

Removes directories from the workspace.

Parameters:

NameTypeRequiredDescription
pathstringYesDirectory path to delete
recursivebooleanNoWhether to delete contents recursively

Returns:

{ "path": "/workspace/old-dir" }

Behavior:

  • Validates directory existence and permissions
  • Removes directory (and contents if recursive: true)

Constraints:

  • Directory must exist
  • Directory must be writable
  • Non-empty directories require recursive: true
  • Cannot delete files (use deleteFile instead)

Here’s how an Expert uses Base Skill tools in a typical agent loop:

┌─────────────────────────────────────────────────────────────────┐
│ User: "Organize files in this directory by type" │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 1. LIST DIRECTORY │
│ ───────────────────────────────────────────────────────────── │
│ tool: listDirectory │
│ input: { path: "." } │
│ output: { items: [ │
│ { name: "photo.jpg", type: "file" }, │
│ { name: "report.pdf", type: "file" }, │
│ { name: "notes.txt", type: "file" } │
│ ]} │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 2. TODO │
│ ───────────────────────────────────────────────────────────── │
│ tool: todo │
│ input: { newTodos: [ │
│ "Create images/ directory", │
│ "Create documents/ directory", │
│ "Move files to appropriate directories" │
│ ]} │
│ output: { todos: [{ id: 0, title: "...", completed: false }] } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3. CREATE DIRECTORIES │
│ ───────────────────────────────────────────────────────────── │
│ tool: createDirectory │
│ input: { path: "images" } │
│ output: { path: "/workspace/images" } │
│ │
│ tool: createDirectory │
│ input: { path: "documents" } │
│ output: { path: "/workspace/documents" } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 4. MOVE FILES │
│ ───────────────────────────────────────────────────────────── │
│ tool: moveFile │
│ input: { source: "photo.jpg", destination: "images/photo.jpg" }│
│ output: { source: "...", destination: "..." } │
│ │
│ tool: moveFile │
│ input: { source: "report.pdf", destination: "documents/..." } │
│ ... │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 5. MARK TODOS COMPLETE │
│ ───────────────────────────────────────────────────────────── │
│ tool: todo │
│ input: { completedTodos: [0, 1, 2] } │
│ output: { todos: [{ id: 0, completed: true }, ...] } │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 6. COMPLETE │
│ ───────────────────────────────────────────────────────────── │
│ tool: attemptCompletion │
│ input: {} │
│ output: {} (no remaining todos) │
│ → Agent loop ends │
└─────────────────────────────────────────────────────────────────┘

The Expert definition for this workflow:

[experts."file-organizer"]
description = "Organizes files in the workspace by type"
instruction = """
You organize files in the current directory.
1. List all files using listDirectory
2. Create subdirectories by file type (images/, documents/, etc.)
3. Move files to appropriate directories using moveFile
"""

All tools come from Base Skill — no skill definitions needed.