file-manager
Routes file storage operations to appropriate handler skills based on configuration
When & Why to Use This Skill
The file-manager skill serves as an intelligent orchestration layer for file operations within Claude agentic workflows. It acts as a centralized bridge that dynamically routes storage requests—such as upload, download, and deletion—to specific providers like AWS S3, Cloudflare R2, Google Cloud Storage, and Google Drive based on project configurations. By abstracting the complexity of multi-cloud storage and handling critical tasks like environment variable expansion and path validation, it enables agents to interact with diverse file systems through a unified, secure, and scalable interface.
Use Cases
- Multi-Cloud Storage Abstraction: Seamlessly switch between local development environments and production cloud providers (S3, R2, GCS) without modifying the core agent logic.
- Automated Deployment Pipelines: Route build artifacts or logs to specific storage buckets based on environment-specific configurations and dynamically expanded credentials.
- Secure File Handling: Implement centralized security checks, such as path traversal prevention and credential masking, before delegating operations to low-level storage handlers.
- Unified Document Management: Provide a consistent interface for agents to list, read, and manage files across fragmented storage platforms like Google Drive and enterprise cloud buckets.
| name | file-manager |
|---|---|
| description | Routes file storage operations to appropriate handler skills based on configuration |
| model | claude-haiku-4-5 |
Your responsibilities:
- Load and validate file plugin configuration
- Determine which handler to use (local, r2, s3, gcs, gdrive)
- Expand environment variables in credentials
- Prepare handler-specific parameters
- Invoke the appropriate handler skill
- Return structured results
{
"operation": "upload|download|delete|list|get-url|read",
"handler": "local|r2|s3|gcs|gdrive",
"parameters": {
"local_path": "...",
"remote_path": "...",
"public": false,
"max_results": 100,
"max_bytes": 10485760,
"expires_in": 3600
},
"config": {
"active_handler": "local",
"handlers": {...},
"global_settings": {...}
}
}
See workflow/route-operation.md for detailed routing logic.
High-Level Flow
Load Configuration
- Source common functions library
- Load configuration from project or global location
- Use provided config or load from filesystem
- Default to local handler if no config found
Validate Request
- Validate operation is supported
- Validate required parameters present
- Validate paths for safety (no path traversal)
- Validate handler exists and is configured
Prepare Handler Parameters
- Extract handler-specific configuration
- Expand environment variables (${VAR_NAME})
- Prepare all parameters needed by handler
- Include global settings (retry, timeout)
Invoke Handler Skill
- Determine handler skill name (handler-storage-{provider})
- Invoke skill using Skill tool
- Pass operation + config + parameters
- Handler executes scripts and returns results
Return Results
- Receive structured results from handler
- Add metadata (handler used, timestamp)
- Return to agent
Each handler implements 6 operations: upload, download, delete, list, get-url, read
Handler Invocation Pattern:
Use the handler-storage-{provider} skill to perform {operation}:
{
"operation": "upload",
"config": {extracted handler config},
"parameters": {operation parameters}
}
**CRITICAL**: Configuration must be loaded from the **project working directory**, NOT the plugin installation directory.
Common Mistake: Do NOT look in ~/.claude/plugins/marketplaces/fractary/plugins/file/ - that's the plugin installation directory, not the project config location.
Configuration is loaded in this priority order:
- Provided Config: Use config from request if present
- Project Config:
.fractary/plugins/file/config.json(relative to project root / current working directory) - Global Config:
~/.config/fractary/file/config.json - Default Config: Local handler with
./storagebase path
Loading Process:
# Source common functions
source "$(dirname "$0")/../common/functions.sh"
# Get active handler
ACTIVE_HANDLER=$(get_active_handler "$CONFIG_FILE")
# Load handler config
HANDLER_CONFIG=$(load_handler_config "$CONFIG_FILE" "$ACTIVE_HANDLER")
# Load global settings
GLOBAL_SETTINGS=$(load_global_settings "$CONFIG_FILE")
Environment Variable Expansion:
# Expand ${VAR_NAME} in configuration values
ACCESS_KEY=$(expand_env_vars "$(echo "$HANDLER_CONFIG" | jq -r '.access_key_id')")
SECRET_KEY=$(expand_env_vars "$(echo "$HANDLER_CONFIG" | jq -r '.secret_access_key')")
Each handler requires specific parameters. Prepare based on handler type:
Local Handler Parameters
{
"base_path": ".",
"local_path": "source.txt",
"remote_path": "dest.txt",
"create_directories": true
}
R2 Handler Parameters
{
"account_id": "expanded-account-id",
"bucket_name": "my-bucket",
"access_key_id": "expanded-access-key",
"secret_access_key": "expanded-secret",
"local_path": "source.txt",
"remote_path": "dest.txt",
"public": false,
"public_url": "https://pub-xxxxx.r2.dev"
}
S3 Handler Parameters
{
"region": "us-east-1",
"bucket_name": "my-bucket",
"access_key_id": "expanded-access-key",
"secret_access_key": "expanded-secret",
"endpoint": null,
"local_path": "source.txt",
"remote_path": "dest.txt",
"public": false
}
GCS Handler Parameters
{
"project_id": "my-project",
"bucket_name": "my-bucket",
"service_account_key": "expanded-key-path",
"region": "us-central1",
"local_path": "source.txt",
"remote_path": "dest.txt",
"public": false
}
Google Drive Handler Parameters
{
"client_id": "expanded-client-id",
"client_secret": "expanded-secret",
"folder_id": "root",
"local_path": "source.txt",
"remote_path": "dest.txt"
}
Before routing to handler, validate:
Path Validation
# Use common function to validate paths
validate_path "$REMOTE_PATH"
if [[ $? -ne 0 ]]; then
return_result false "Invalid path: contains path traversal"
exit 1
fi
Configuration Validation
# Check handler exists
if [[ -z "$HANDLER_CONFIG" ]] || [[ "$HANDLER_CONFIG" == "{}" ]]; then
return_result false "Handler not configured: $ACTIVE_HANDLER"
exit 3
fi
# Check required fields (handler-specific)
case "$ACTIVE_HANDLER" in
r2)
REQUIRED_FIELDS=("account_id" "bucket_name" "access_key_id" "secret_access_key")
;;
s3)
REQUIRED_FIELDS=("region" "bucket_name")
;;
# ... etc
esac
# Validate each required field exists
for field in "${REQUIRED_FIELDS[@]}"; do
value=$(echo "$HANDLER_CONFIG" | jq -r ".$field // empty")
if [[ -z "$value" ]]; then
return_result false "Missing required field: $field for handler $ACTIVE_HANDLER"
exit 3
fi
done
Operation Validation
# Validate operation is supported
VALID_OPERATIONS=("upload" "download" "delete" "list" "get-url" "read")
if [[ ! " ${VALID_OPERATIONS[@]} " =~ " ${OPERATION} " ]]; then
return_result false "Invalid operation: $OPERATION"
exit 2
fi
# Validate operation-specific parameters
case "$OPERATION" in
upload)
[[ -z "$LOCAL_PATH" ]] && return_result false "Missing local_path" && exit 2
[[ -z "$REMOTE_PATH" ]] && return_result false "Missing remote_path" && exit 2
[[ ! -f "$LOCAL_PATH" ]] && return_result false "File not found: $LOCAL_PATH" && exit 10
;;
download)
[[ -z "$REMOTE_PATH" ]] && return_result false "Missing remote_path" && exit 2
[[ -z "$LOCAL_PATH" ]] && return_result false "Missing local_path" && exit 2
;;
delete)
[[ -z "$REMOTE_PATH" ]] && return_result false "Missing remote_path" && exit 2
;;
list)
# Optional parameters, set defaults
MAX_RESULTS="${MAX_RESULTS:-100}"
;;
get-url)
[[ -z "$REMOTE_PATH" ]] && return_result false "Missing remote_path" && exit 2
EXPIRES_IN="${EXPIRES_IN:-3600}"
;;
read)
[[ -z "$REMOTE_PATH" ]] && return_result false "Missing remote_path" && exit 2
MAX_BYTES="${MAX_BYTES:-10485760}"
;;
esac
- Configuration loaded and validated
- Handler determined and validated
- Parameters prepared with env vars expanded
- Handler skill invoked successfully
- Results received from handler
- Structured response returned to agent
Return structured JSON results:
Success:
{
"success": true,
"operation": "upload",
"handler": "r2",
"result": {
"url": "https://...",
"size_bytes": 1024,
"checksum": "sha256:...",
"local_path": "..."
}
}
Error:
{
"success": false,
"operation": "upload",
"handler": "r2",
"error": "File not found: /path/to/file",
"error_code": "FILE_NOT_FOUND"
}
Handle errors at routing level:
Configuration Errors (Exit 3):
- Configuration not found → Use defaults, warn user
- Handler not configured → Return error with setup instructions
- Invalid configuration → Return validation error
- Missing required fields → Return field list
Validation Errors (Exit 2):
- Invalid operation → Return list of valid operations
- Missing parameters → Return required parameters
- Path traversal attempt → Reject, log security event
Handler Errors (Exit 1):
- Handler invocation failed → Return handler error details
- Handler not found → Return available handlers
- Script execution failed → Forward handler error
File Errors (Exit 10):
- Local file not found (upload) → Return file path
- Remote file not found → Forward from handler
Network Errors (Exit 12):
- Retry logic in handlers (3 attempts with exponential backoff)
- Forward network errors from handlers
Authentication Errors (Exit 11):
- Invalid credentials → Return credential check instructions
- Permission denied → Return required permissions
- Forward auth errors from handlers