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.
Design Philosophy
Runtime-Coupled Skill
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, thinking, todos) are exposed as standard MCP tools.
Thinking Tools and Observability
The think and todo tools serve two purposes:
-
Test-time scaling for any model β These tools enable extended reasoning for models that donβt natively support it, allowing step-by-step problem solving regardless of the underlying model.
-
Observable reasoning β For reasoning models that already have test-time scaling, these tools donβt interfere with native capabilities. Instead, they ensure thought processes are recorded in checkpoints rather than hidden internally.
This design guarantees:
- All reasoning steps are visible in execution history
- Debugging and auditing are always possible
- No hidden Chain-of-Thought that cannot be inspected
Binary Data Handling
For binary files (images, PDFs), Base Skill returns only path and mimeType. The runtime then:
- Reads the file from the returned path
- Base64 encodes the content
- Passes the encoded data to the LLM
This separation keeps Base Skill simple while letting the runtime handle LLM-specific formatting.
No External Network Access
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.
Workspace Isolation
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
.perstackdirectory is hidden from directory listings
Tool Reference
| Tool | Category | Description |
|---|---|---|
attemptCompletion | Runtime | Signal task completion |
think | Runtime | Record step-by-step reasoning |
todo | Runtime | Manage task list |
clearTodo | Runtime | Clear task list |
healthCheck | System | Check MCP server health |
exec | System | Execute system commands |
readTextFile | File | Read text files |
readImageFile | File | Read image files (PNG, JPEG, GIF, WebP) |
readPdfFile | File | Read PDF files |
writeTextFile | File | Create or overwrite text files |
appendTextFile | File | Append to text files |
editTextFile | File | Search and replace in text files |
moveFile | File | Move or rename files |
deleteFile | File | Delete files |
getFileInfo | File | Get file/directory metadata |
listDirectory | Directory | List directory contents |
createDirectory | Directory | Create directories |
deleteDirectory | Directory | Delete directories |
System Execution
healthCheck
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
exec
Executes system commands within the workspace.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
command | string | Yes | Command to execute (e.g., ls, python) |
args | string[] | Yes | Arguments to pass to the command |
env | Record<string, string> | Yes | Environment variables |
cwd | string | Yes | Working directory (must be within workspace) |
stdout | boolean | Yes | Whether to capture stdout |
stderr | boolean | Yes | Whether to capture stderr |
timeout | number | No | Timeout in milliseconds |
Returns:
{ "output": "command output here" }Behavior:
- Executes command using Node.js
execFile - Validates
cwdis 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.
Runtime Control
attemptCompletion
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
remainingTodosand 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
clearTodoif you want to reset and start fresh - The tool prevents premature completion by surfacing forgotten tasks
think
Sequential thinking tool for step-by-step problem analysis.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
thought | string | Yes | Current reasoning step |
nextThoughtNeeded | boolean | No | Whether additional thinking is required |
Returns:
{ "nextThoughtNeeded": true, "thoughtHistoryLength": 3 }Behavior:
- Records each thinking step sequentially
- Maintains thought history across calls within the session
- Returns current thought count and continuation status
Observability: The runtime records all tool calls (including think) into checkpoints, making thought processes visible in execution history.
todo
Task list manager for tracking work items.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
newTodos | string[] | No | Task descriptions to add |
completedTodos | number[] | No | Todo 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.
clearTodo
Resets the todo list to empty state.
Parameters: None
Returns:
{ "todos": [] }File Operations
readTextFile
Reads text files with optional line range support.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path |
from | number | No | Start line (0-indexed) |
to | number | No | End 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
readImageFile
Validates and returns image file metadata for runtime processing.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Image 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
readPdfFile
Validates and returns PDF file metadata for runtime processing.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | PDF 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
writeTextFile
Creates or overwrites text files.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Target file path |
text | string | Yes | Content 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
appendTextFilefor larger files
appendTextFile
Appends content to existing files.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Target file path |
text | string | Yes | Content 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
editTextFile
Performs search-and-replace in text files.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Target file path |
oldText | string | Yes | Text to find (max 2,000 characters) |
newText | string | Yes | Replacement 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
oldTextmust exist in file- Maximum 2,000 characters for both
oldTextandnewText
moveFile
Moves or renames files.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Current file path |
destination | string | Yes | Target 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
deleteFile
Removes files from the workspace.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path to delete |
Returns:
{ "path": "/workspace/deleted.txt" }Constraints:
- File must exist
- File must be writable
- Cannot delete directories (use directory-specific tools)
getFileInfo
Retrieves detailed file or directory metadata.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File 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
nullfor extension and mimeType on directories - Formats size in human-readable format (B, KB, MB, GB, TB)
Directory Operations
listDirectory
Lists directory contents with metadata.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Directory 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
.perstackdirectory from results
Constraints:
- Path must exist
- Path must be a directory
createDirectory
Creates directories with recursive parent creation.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Directory path to create |
Returns:
{ "path": "/workspace/new/nested/dir" }Behavior:
- Creates all parent directories as needed
- Uses
mkdirwithrecursive: true
Constraints:
- Directory must not already exist
- Parent directory must be writable
deleteDirectory
Removes directories from the workspace.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Directory path to delete |
recursive | boolean | No | Whether 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
deleteFileinstead)
Example: Agent Loop Flow
Hereβs how an Expert uses Base Skill tools in a typical agent loop:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User: "Organize files in this directory by type" β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. THINK β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: think β
β input: { thought: "I need to scan the directory first..." } β
β output: { nextThoughtNeeded: false, thoughtHistoryLength: 1 } β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. LIST DIRECTORY β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: listDirectory β
β input: { path: "." } β
β output: { items: [ β
β { name: "photo.jpg", type: "file" }, β
β { name: "report.pdf", type: "file" }, β
β { name: "notes.txt", type: "file" } β
β ]} β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. TODO β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: todo β
β input: { newTodos: [ β
β "Create images/ directory", β
β "Create documents/ directory", β
β "Move files to appropriate directories" β
β ]} β
β output: { todos: [{ id: 0, title: "...", completed: false }] } β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. CREATE DIRECTORIES β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: createDirectory β
β input: { path: "images" } β
β output: { path: "/workspace/images" } β
β β
β tool: createDirectory β
β input: { path: "documents" } β
β output: { path: "/workspace/documents" } β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 5. MOVE FILES β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: moveFile β
β input: { source: "photo.jpg", destination: "images/photo.jpg" }β
β output: { source: "...", destination: "..." } β
β β
β tool: moveFile β
β input: { source: "report.pdf", destination: "documents/..." } β
β ... β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 6. MARK TODOS COMPLETE β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β tool: todo β
β input: { completedTodos: [0, 1, 2] } β
β output: { todos: [{ id: 0, completed: true }, ...] } β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 7. 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.