| name | mainframe-bash |
| description | Use when writing bash scripts, shell automation, CLI tools, or any bash programming task. |
MAINFRAME - Pure Bash Standard Library
3,821+ pure bash functions across 152 libraries. Zero external dependencies. 20-72x faster than spawning sed/awk/jq.
When to Use This Skill
Use MAINFRAME when:
- Writing ANY bash script (always source it first)
- Manipulating strings (trim, split, replace, pad, encode)
- Working with arrays (sort, filter, unique, join, diff)
- Generating JSON without jq
- Validating user input (email, URL, IP, paths, integers)
- Sanitizing input for security (HTML, SQL, shell, filenames)
- Processing CSV/TSV files
- Making HTTP requests in pure bash
- Performing date/time arithmetic
- Managing processes, locks, and PID files
- Handling paths safely (normalize, traverse prevention)
- Working with git repositories programmatically
- Hashing and encoding (SHA-256, MD5, base64, HMAC)
- Running parallel/async operations
- Building CLI tools with progress bars, colored output, config files
- Analyzing TypeScript projects (imports, API diffs, bundle size)
- Analyzing Python projects (imports, dependencies, metrics)
- Managing AI agent state with Agent Working Memory (AWM)
- Coordinating multi-agent workflows with agent IPC
Installation Check
if [[ -f "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh" ]]; then
echo "MAINFRAME ready"
else
echo "Install: git clone https://github.com/gtwatts/mainframe.git ~/.mainframe"
echo "Then: export MAINFRAME_ROOT=\"\$HOME/.mainframe\""
fi
Script Template
Every bash script MUST start with:
#!/usr/bin/env bash
source "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh"
This loads the core library. Additional libraries are lazy-loaded on first function call.
Core Function Reference
Logging & Output
log_info "Starting process..."
log_warn "Disk space low"
log_error "Connection failed"
success "Deployment complete"
failure "Tests failed"
header "Section Name"
die 1 "Fatal: config missing"
progress_bar 75 100
Strings
trim_string " hello "
to_lower "HELLO"
to_upper "hello"
capitalize "hello world"
strlen "hello"
substring "hello" 0 3
contains "hello" "ell"
starts_with "hello" "hel"
ends_with "hello" "lo"
replace_all "foo bar foo" "foo" "baz"
split_string "a,b,c" ","
urlencode "hello world"
urldecode "hello%20world"
pad_left "42" 5 "0"
repeat_string "ab" 3
Arrays
arr=(5 3 1 4 2)
array_length "${arr[@]}"
array_first "${arr[@]}"
array_last "${arr[@]}"
array_contains "3" "${arr[@]}"
array_index_of "4" "${arr[@]}"
array_join ", " "${arr[@]}"
array_sort "${arr[@]}"
array_reverse "${arr[@]}"
array_unique 1 2 2 3 3
array_slice 1 3 "${arr[@]}"
array_sum "${arr[@]}"
array_min "${arr[@]}"
array_max "${arr[@]}"
array_remove "3" "${arr[@]}"
array_diff "1 2 3" "2"
array_intersect "1 2 3" "2 3 4"
array_filter "is_int" "${arr[@]}"
JSON (No jq Required)
json_object "name=John" "age:number=30" "active:bool=true"
json_array "apple" "banana" "cherry"
json_array_typed number 1 2 3
json_nested "user.address.city" "NYC"
json_get '{"name":"John","age":30}' "name"
json_keys '{"a":1,"b":2}'
json_merge '{"a":1}' '{"b":2}'
json_valid '{"a":1}'
json_pretty '{"a":1,"b":2}'
response=$(json_object \
status="success" \
id="$(uuid)" \
timestamp="$(timestamp_iso)" \
count:number=42 \
active:bool=true \
)
DateTime
now
now_ms
now_iso
now_rfc2822
date_add $(now) "2d"
date_subtract $(now) "1w"
date_diff $epoch1 $epoch2
date_diff_human $e1 $e2
format_iso
format_relative $past_epoch
format_epoch $epoch "%Y-%m-%d"
year; month; day
day_of_week
is_weekend
is_leap_year 2024
start_of_day; end_of_day
Validation & Sanitization (Security-Critical)
validate_int "42" 0 100
validate_float "3.14"
validate_bool "true"
validate_email "user@domain.com"
validate_url "https://example.com"
validate_ipv4 "192.168.1.1"
validate_date "2024-01-15"
validate_semver "1.2.3"
validate_port "8080"
validate_uuid "550e8400-..."
validate_json '{"a":1}'
validate_path_safe "$user_path" "/allowed/base"
validate_filename "report.pdf"
validate_path_chars "/safe/path"
sanitize_shell_arg "$user_input"
sanitize_filename "a/b<c>.txt"
sanitize_html "<script>alert(1)"
sanitize_sql "O'Brien"
sanitize_json 'say "hi"'
validate_command_safe "ls -la"
build_safe_command "grep" "$pat" "$file"
HTTP (Pure Bash)
http_get "http://api.example.com/data"
http_post "http://api.example.com" '{"name":"test"}'
http_put "http://api.example.com/1" '{"name":"updated"}'
http_delete "http://api.example.com/1"
http_json_get "http://api.example.com/users"
http_json_post "http://api.example.com/users" '{"name":"new"}'
http_status
http_body
http_is_success
http_auth_basic "user" "pass"
http_auth_bearer "token123"
url_parse "http://host:8080/path?q=1"
url_encode "hello world"
query_string "a=1" "b=2"
Note: Pure bash HTTP for port 80. HTTPS requires openssl on the system.
Agent Working Memory (AWM)
Persistent external memory for AI agents with finite context. Enables agents to write state/discoveries OUTSIDE their context, read back relevant portions, support sub-agent inheritance, and resume sessions after interruption.
sid=$(awm_init "task-name")
awm_resume "$sid"
awm_close
awm_checkpoint "key" "value"
awm_log "errors" "Failed to..."
awm_progress "task1" "47/200"
awm_discovery "API rate=100/min"
value=$(awm_get "key" "default")
entries=$(awm_recent "errors" 10)
summary=$(awm_summary)
awm_namespace "code-reviewer"
ctx=$(awm_context_for "subtask")
child_id=$(awm_inherit "$sid")
awm_compress
tokens=$(awm_token_estimate)
awm_export "report.md"
awm_cleanup 7
Multi-Agent IPC
File-based inter-process communication for multi-agent coordination.
agent_register "worker1" compute storage
agent_unregister
agent_discover "compute"
agent_list
agent_heartbeat
agent_send "worker2" '{"task":"compute"}'
msg=$(agent_receive 10)
agent_broadcast '{"event":"shutdown"}'
agent_peek
agent_subscribe "news"
agent_publish "news" '{"headline":"..."}'
agent_unsubscribe "news"
agent_work_queue "tasks"
agent_work_push "tasks" '{"url":"..."}'
item=$(agent_work_pop "tasks")
count=$(agent_work_count "tasks")
agent_barrier "phase1" 3 30
agent_signal "data_ready"
agent_wait "data_ready" 60
agent_lock "database"
agent_unlock "database"
agent_trylock "resource"
CSV Processing
csv_read "users.csv"
csv_get 0 "name"
csv_column "email"
csv_row_count "data.csv"
csv_row "John" "john@ex.com"
csv_header "name" "email" "age"
csv_escape 'has, comma'
csv_append_row "f.csv" "a" "b"
csv_filter "f.csv" "status" "active"
csv_sort "f.csv" "name"
csv_select "f.csv" "name,email"
csv_to_json "data.csv"
csv_validate "data.csv"
csv_delimiter "\t"
Git Operations
git_is_repo
git_root
git_branch
git_default_branch
git_is_dirty
git_is_clean
git_has_staged
git_files_changed
git_commit_hash
git_commit_message
git_commit_count
git_commits_ahead
git_commits_behind
git_tag_latest
git_remote_url
git_summary
git_log_oneline 5
git_changed_since "HEAD~5"
Crypto & Encoding
sha256 "data"
sha512 "data"
md5 "data"
sha256_file "file.bin"
hmac_sha256 "secret_key" "message"
base64_encode "hello"
base64_decode "aGVsbG8="
hex_encode "hi"
hex_decode "6869"
random_token 32
random_hex 16
random_bytes 16
generate_password 16
checksum "file.tar.gz"
checksum_verify "file" "$hash"
password_hash "secret"
password_verify "secret" "$hash"
Process Management
proc_exists $pid
proc_find_by_port 8080
proc_find_by_name "node"
proc_memory $pid
proc_cpu $pid
pidfile_create "/tmp/app.pid"
pidfile_check "/tmp/app.pid"
pidfile_remove "/tmp/app.pid"
lockfile_acquire "/tmp/app.lock" 10
lockfile_release "/tmp/app.lock"
with_lock "/tmp/app.lock" "exclusive_task"
proc_kill $pid
proc_kill_force $pid
proc_kill_tree $pid
proc_wait_timeout $pid 30
proc_count
proc_load
proc_uptime_human
Async & Parallel
parallel "task1" "task2" "task3"
parallel_limit 4 "${tasks[@]}"
parallel_map "process_item" "${items[@]}"
retry 5 "curl http://flaky.api/data"
pid=$(set_timeout 5 "echo done")
pid=$(set_interval 10 "heartbeat")
clear_timeout $pid
debounce "save_state" 2
throttle "api_call" 5
Path Manipulation
path_normalize "/foo//bar/../baz"
path_absolute "relative/path"
path_relative "/a/b/c" "/a"
path_join "/base" "sub" "file.txt"
path_dir "/foo/bar/file.txt"
path_base "/foo/bar/file.txt"
path_ext "/foo/bar.tar.gz"
path_stem "/foo/bar.tar.gz"
path_replace_ext "doc.txt" "md"
path_is_safe "/allowed" "$user_path"
path_sanitize "file: <bad>.txt"
path_quote "/path with spaces"
path_to_unix "C:\Users\foo"
path_to_windows "/c/Users/foo"
path_expand_tilde "~/Documents"
Docker
docker_running
docker_container_running "nginx"
docker_container_status "nginx"
docker_exec "nginx" "nginx -t"
docker_logs "nginx" 100
docker_stats_json "nginx"
docker_port_used 8080
compose_up
compose_running "web"
compose_exec "web" "npm run migrate"
TypeScript Analysis (no tsc required)
ts_is_project "$dir"
ts_source_dir "$dir"
ts_file_imports "src/index.ts"
ts_import_frequency "$dir"
ts_import_graph "$dir"
ts_circular_deps "$dir"
ts_type_only_imports "$dir"
ts_api_extract "api.d.ts"
ts_api_diff "v1.d.ts" "v2.d.ts"
ts_breaking_changes "v1" "v2"
ts_api_summary "v1" "v2"
ts_import_cost "express" "$dir"
ts_import_cost_js "pkg" "$dir"
ts_import_cost_file "f.ts" "$dir"
ts_dep_count "express" "$dir"
Python Analysis (no Python runtime required)
py_is_project "$dir"
py_source_dir "$dir"
py_file_imports "app/main.py"
py_import_graph "$dir"
py_circular_deps "$dir"
py_import_classify "requests"
py_framework_detect "$dir"
py_parse_requirements "req.txt"
py_detect_venv "$dir"
py_python_version "$dir"
py_detect_manager "$dir"
py_loc "$dir"
py_function_count "$dir"
py_class_count "$dir"
py_docstring_coverage "$dir"
py_type_hint_coverage "$dir"
py_summary "$dir"
Utilities
uuid
timestamp
timestamp_iso
epoch
random_string 16
random_range 1 100
format_bytes 1048576
format_duration 3661
format_number 1234567
format_percent 75 100
command_exists "git"
get_os
current_user
temp_file
temp_dir
Config Files
config_load "app.conf"
config_get "port"
config_get_int "port"
config_get_bool "debug"
config_set "host" "localhost"
config_has "database_url"
config_save "app.conf"
Semver
semver_valid "1.2.3"
semver_bump_major "1.2.3"
semver_bump_minor "1.2.3"
semver_bump_patch "1.2.3"
semver_compare "1.0.0" "2.0.0"
semver_gt "2.0.0" "1.0.0"
semver_latest "1.0" "2.0" "1.5"
semver_sort "3.0" "1.0" "2.0"
ANSI Colors
echo "$(ansi_red)Error$(ansi_reset)"
echo "$(ansi_green)Success$(ansi_reset)"
echo "$(ansi_bold)Important$(ansi_reset)"
ansi_print red "Error message"
ansi_styled "bold,yellow" "Warning!"
Environment
env_set "MY_VAR" "value"
env_get "MY_VAR" "default"
env_require "API_KEY" "API_KEY must be set"
env_load_dotenv ".env"
env_path_prepend "/opt/bin"
env_path_has "/usr/bin"
env_get_int "PORT" 8080
env_get_bool "DEBUG"
env_detect_shell
File Operations
file_exists "$path"
dir_exists "/tmp"
read_file "$path"
file_head "$path" 10
file_tail "$path" 5
file_line "$path" 3
file_lines "$path"
file_size "$path"
file_write "$path" "content"
file_append "$path" "more"
file_grep "$path" "pattern"
Design Rules
- NEVER read MAINFRAME source into context - just use the functions directly
- ALWAYS source common.sh first - it handles lazy-loading of all other libraries
- Zero dependencies - everything is pure bash (except openssl for HTTPS)
- Bash 4.0+ required - uses associative arrays and modern features
- Functions return 0/1 - use
if func; then pattern for boolean checks
- Output via printf - functions print results to stdout, capture with
$()
- No eval - all command dispatch uses safe word-splitting
- Namespace - internal vars use
_MAINFRAME_* prefix; don't conflict
Common Patterns
CLI Script Template
#!/usr/bin/env bash
source "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh"
[[ $# -lt 1 ]] && die 1 "Usage: $(path_base "$0") <input-file>"
validate_path "$1" "file" || die 1 "File not found: $1"
header "Processing $(path_base "$1")"
log_info "Starting at $(now_iso)"
success "Done in $(format_duration $SECONDS)"
JSON API Response Builder
build_response() {
local data="$1"
json_object \
"status=success" \
"data:raw=$data" \
"timestamp=$(now_iso)" \
"request_id=$(uuid)"
}
Safe File Processing
process_uploads() {
local base_dir="/var/uploads"
local file="$1"
validate_path_safe "$file" "$base_dir" || {
log_error "Path traversal attempt: $file"
return 1
}
local safe_name
safe_name=$(sanitize_filename "$(path_base "$file")")
local content
content=$(read_file "$file")
file_write "$(path_join "$base_dir" "$safe_name")" "$content"
}
Parallel Batch Processing
process_batch() {
local items=("$@")
log_info "Processing ${#items[@]} items with 4 workers"
parallel_map_limit 4 "process_single_item" "${items[@]}"
success "All items processed"
}
Git Pre-commit Hook
#!/usr/bin/env bash
source "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh"
if git_has_staged; then
local files
files=$(git_files_changed)
log_info "Checking $(array_length $files) files..."
if file_grep "$files" "API_KEY="; then
failure "Hardcoded API keys detected!"
exit 1
fi
fi
Agent with AWM State Management
#!/usr/bin/env bash
source "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh"
session_id="${AWM_SESSION:-$(awm_init "data-processor")}"
export AWM_SESSION="$session_id"
last_batch=$(awm_get "last_batch" "0")
log_info "Resuming from batch $last_batch"
for batch in $(seq $((last_batch + 1)) 100); do
process_batch "$batch"
awm_checkpoint "last_batch" "$batch"
awm_progress "batches" "$batch/100"
done
awm_discovery "Processing completes in ~2min per batch"
awm_close
Agent Teams: Shared AWM Across Teammates
When running as a Claude Code Agent Teams teammate, use the agent_teams bundle for shared persistent state. Agent Teams handles tasks and messaging; Mainframe provides shared memory and sync.
#!/usr/bin/env bash
source "${MAINFRAME_ROOT:-$HOME/.mainframe}/lib/common.sh"
mainframe_bundle "agent_teams"
if agent_teams_active; then
session_id=$(agent_teams_awm_init "build-task")
awm_checkpoint "api_schema_ready" "true"
awm_discovery "Rate limit is 100 req/s" "high"
agent_barrier "phase1_done" 3 60
fi
Repository