KS
Killer-Skills

state-directory-manager — how to use state-directory-manager how to use state-directory-manager, state-directory-manager setup guide, what is state-directory-manager, state-directory-manager alternative, state-directory-manager vs git, state-directory-manager install, bash script state management, XDG Base Directory specification, persistent state management

v1.0.0
GitHub

About this Skill

Perfect for CLI Agents needing persistent state management and modular organization across multiple GitHub repositories. state-directory-manager is a centralized management system following XDG Base Directory specification for managing persistent state, configuration, and cache directories in bash scripts.

Features

Manages persistent state, configuration, and cache directories in bash scripts
Follows XDG Base Directory specification for modular organization
Enables data persistence between script runs
Supports storing user preferences and configuration
Caches results for improved performance
Manages log files with rotation for efficient logging

# Core Topics

vamseeachanta vamseeachanta
[0]
[0]
Updated: 3/6/2026

Quality Score

Top 5%
64
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add vamseeachanta/workspace-hub/state-directory-manager

Agent Capability Analysis

The state-directory-manager MCP Server by vamseeachanta is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use state-directory-manager, state-directory-manager setup guide, what is state-directory-manager.

Ideal Agent Persona

Perfect for CLI Agents needing persistent state management and modular organization across multiple GitHub repositories.

Core Value

Empowers agents to manage persistent state, configuration, and cache directories in bash scripts following the XDG Base Directory specification, enabling data persistence and performance caching with log file rotation.

Capabilities Granted for state-directory-manager MCP Server

Automating script persistence for data between runs
Generating portable CLI tools with user preferences
Caching results for performance optimization

! Prerequisites & Limits

  • Requires bash script compatibility
  • Intended for scripts needing state persistence, not one-time or stateless scripts
Project
SKILL.md
17.6 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

State Directory Manager

Patterns for managing persistent state, configuration, and cache directories in bash scripts following XDG Base Directory specification.

When to Use This Skill

Use when:

  • Scripts need to persist data between runs
  • Storing user preferences or configuration
  • Caching results for performance
  • Managing log files with rotation
  • Creating portable CLI tools

Avoid when:

  • One-time scripts that don't need state
  • Scripts that should be purely stateless
  • When environment variables are sufficient

Core Capabilities

1. XDG Base Directory Standard

Follow the XDG specification for directory locations:

bash
1#!/bin/bash 2# ABOUTME: XDG Base Directory compliant state management 3# ABOUTME: Cross-platform directory locations 4 5# XDG Base Directories with fallbacks 6XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" 7XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" 8XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" 9XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}" 10 11# Application-specific directories 12APP_NAME="my-tool" 13CONFIG_DIR="$XDG_CONFIG_HOME/$APP_NAME" 14DATA_DIR="$XDG_DATA_HOME/$APP_NAME" 15STATE_DIR="$XDG_STATE_HOME/$APP_NAME" 16CACHE_DIR="$XDG_CACHE_HOME/$APP_NAME" 17LOG_DIR="$STATE_DIR/logs" 18 19# Initialize directories 20init_directories() { 21 mkdir -p "$CONFIG_DIR" 22 mkdir -p "$DATA_DIR" 23 mkdir -p "$STATE_DIR" 24 mkdir -p "$CACHE_DIR" 25 mkdir -p "$LOG_DIR" 26}

2. Workspace-Hub Pattern

Alternative using home directory (from workspace-hub scripts):

bash
1#!/bin/bash 2# ABOUTME: Workspace-hub style state directory management 3# ABOUTME: Simple $HOME/.app-name pattern 4 5APP_NAME="workspace-hub" 6APP_DIR="${HOME}/.${APP_NAME}" 7 8# Directory structure 9CONFIG_DIR="$APP_DIR/config" 10DATA_DIR="$APP_DIR/data" 11LOGS_DIR="$APP_DIR/logs" 12CACHE_DIR="$APP_DIR/cache" 13TEMP_DIR="$APP_DIR/tmp" 14 15# Initialize with proper permissions 16init_app_dirs() { 17 local dirs=("$CONFIG_DIR" "$DATA_DIR" "$LOGS_DIR" "$CACHE_DIR" "$TEMP_DIR") 18 19 for dir in "${dirs[@]}"; do 20 if [[ ! -d "$dir" ]]; then 21 mkdir -p "$dir" 22 chmod 700 "$dir" # Private by default 23 fi 24 done 25} 26 27# Clean old temp files 28clean_temp() { 29 find "$TEMP_DIR" -type f -mtime +1 -delete 2>/dev/null || true 30}

3. Configuration File Management

Read and write configuration files:

bash
1#!/bin/bash 2# ABOUTME: Configuration file management 3# ABOUTME: Key-value pairs with defaults 4 5CONFIG_FILE="$CONFIG_DIR/config" 6 7# Default configuration 8declare -A DEFAULT_CONFIG=( 9 ["parallel_workers"]="5" 10 ["log_level"]="INFO" 11 ["auto_sync"]="true" 12 ["timeout"]="30" 13) 14 15# Initialize config with defaults 16init_config() { 17 if [[ ! -f "$CONFIG_FILE" ]]; then 18 { 19 echo "# Configuration for $APP_NAME" 20 echo "# Generated: $(date)" 21 echo "" 22 for key in "${!DEFAULT_CONFIG[@]}"; do 23 echo "${key}=${DEFAULT_CONFIG[$key]}" 24 done 25 } > "$CONFIG_FILE" 26 fi 27} 28 29# Read config value 30get_config() { 31 local key="$1" 32 local default="${2:-${DEFAULT_CONFIG[$key]:-}}" 33 34 if [[ -f "$CONFIG_FILE" ]]; then 35 local value 36 value=$(grep "^${key}=" "$CONFIG_FILE" 2>/dev/null | cut -d'=' -f2-) 37 echo "${value:-$default}" 38 else 39 echo "$default" 40 fi 41} 42 43# Write config value 44set_config() { 45 local key="$1" 46 local value="$2" 47 48 init_config 49 50 if grep -q "^${key}=" "$CONFIG_FILE" 2>/dev/null; then 51 # Update existing 52 sed -i "s|^${key}=.*|${key}=${value}|" "$CONFIG_FILE" 53 else 54 # Add new 55 echo "${key}=${value}" >> "$CONFIG_FILE" 56 fi 57} 58 59# Load all config into associative array 60load_config() { 61 declare -gA CONFIG 62 63 # Start with defaults 64 for key in "${!DEFAULT_CONFIG[@]}"; do 65 CONFIG[$key]="${DEFAULT_CONFIG[$key]}" 66 done 67 68 # Override with file values 69 if [[ -f "$CONFIG_FILE" ]]; then 70 while IFS='=' read -r key value; do 71 [[ "$key" =~ ^#.*$ || -z "$key" ]] && continue 72 CONFIG[$key]="$value" 73 done < "$CONFIG_FILE" 74 fi 75} 76 77# Usage 78init_config 79load_config 80echo "Parallel workers: ${CONFIG[parallel_workers]}" 81set_config "parallel_workers" "10"

4. State File Operations

Track persistent state between runs:

bash
1#!/bin/bash 2# ABOUTME: State file operations 3# ABOUTME: Track last run, progress, etc. 4 5STATE_FILE="$STATE_DIR/state.json" 6 7# Initialize state 8init_state() { 9 if [[ ! -f "$STATE_FILE" ]]; then 10 cat > "$STATE_FILE" << EOF 11{ 12 "version": "1.0.0", 13 "created": "$(date -Iseconds)", 14 "last_run": null, 15 "run_count": 0, 16 "last_status": null 17} 18EOF 19 fi 20} 21 22# Get state value (requires jq) 23get_state() { 24 local key="$1" 25 local default="${2:-null}" 26 27 if [[ -f "$STATE_FILE" ]] && command -v jq &>/dev/null; then 28 jq -r ".$key // $default" "$STATE_FILE" 29 else 30 echo "$default" 31 fi 32} 33 34# Update state value (requires jq) 35set_state() { 36 local key="$1" 37 local value="$2" 38 39 init_state 40 41 if command -v jq &>/dev/null; then 42 local temp=$(mktemp) 43 jq ".$key = $value" "$STATE_FILE" > "$temp" && mv "$temp" "$STATE_FILE" 44 fi 45} 46 47# Record run 48record_run() { 49 local status="$1" 50 51 set_state "last_run" "\"$(date -Iseconds)\"" 52 set_state "last_status" "\"$status\"" 53 set_state "run_count" "$(($(get_state run_count 0) + 1))" 54} 55 56# Simple key-value state (no jq required) 57STATE_KV_FILE="$STATE_DIR/state.kv" 58 59get_state_kv() { 60 local key="$1" 61 local default="$2" 62 63 if [[ -f "$STATE_KV_FILE" ]]; then 64 grep "^${key}=" "$STATE_KV_FILE" 2>/dev/null | cut -d'=' -f2- || echo "$default" 65 else 66 echo "$default" 67 fi 68} 69 70set_state_kv() { 71 local key="$1" 72 local value="$2" 73 74 mkdir -p "$(dirname "$STATE_KV_FILE")" 75 76 if [[ -f "$STATE_KV_FILE" ]] && grep -q "^${key}=" "$STATE_KV_FILE"; then 77 sed -i "s|^${key}=.*|${key}=${value}|" "$STATE_KV_FILE" 78 else 79 echo "${key}=${value}" >> "$STATE_KV_FILE" 80 fi 81}

5. Cache Management

Implement caching with expiration:

bash
1#!/bin/bash 2# ABOUTME: Cache management with TTL 3# ABOUTME: Store and retrieve cached data 4 5CACHE_TTL="${CACHE_TTL:-3600}" # 1 hour default 6 7# Get cache file path 8cache_path() { 9 local key="$1" 10 local hash=$(echo -n "$key" | md5sum | cut -c1-16) 11 echo "$CACHE_DIR/${hash}" 12} 13 14# Check if cache is valid 15cache_valid() { 16 local key="$1" 17 local ttl="${2:-$CACHE_TTL}" 18 local path=$(cache_path "$key") 19 20 if [[ -f "$path" ]]; then 21 local age=$(($(date +%s) - $(stat -c %Y "$path" 2>/dev/null || stat -f %m "$path"))) 22 [[ $age -lt $ttl ]] 23 else 24 return 1 25 fi 26} 27 28# Get from cache 29cache_get() { 30 local key="$1" 31 local ttl="${2:-$CACHE_TTL}" 32 local path=$(cache_path "$key") 33 34 if cache_valid "$key" "$ttl"; then 35 cat "$path" 36 return 0 37 fi 38 return 1 39} 40 41# Set cache 42cache_set() { 43 local key="$1" 44 local value="$2" 45 local path=$(cache_path "$key") 46 47 mkdir -p "$CACHE_DIR" 48 echo "$value" > "$path" 49} 50 51# Delete cache 52cache_delete() { 53 local key="$1" 54 local path=$(cache_path "$key") 55 rm -f "$path" 56} 57 58# Clear all cache 59cache_clear() { 60 rm -rf "$CACHE_DIR"/* 61} 62 63# Clean expired cache entries 64cache_clean() { 65 local ttl="${1:-$CACHE_TTL}" 66 find "$CACHE_DIR" -type f -mmin "+$((ttl / 60))" -delete 2>/dev/null || true 67} 68 69# Usage with automatic caching 70get_with_cache() { 71 local key="$1" 72 local command="$2" 73 local ttl="${3:-$CACHE_TTL}" 74 75 if cache_valid "$key" "$ttl"; then 76 cache_get "$key" 77 else 78 local result 79 result=$(eval "$command") 80 cache_set "$key" "$result" 81 echo "$result" 82 fi 83} 84 85# Example 86result=$(get_with_cache "api_response" "curl -s https://api.example.com/data" 300)

6. Log File Management

Manage logs with rotation:

bash
1#!/bin/bash 2# ABOUTME: Log file management with rotation 3# ABOUTME: Automatic cleanup of old logs 4 5LOG_FILE="$LOG_DIR/app.log" 6LOG_MAX_SIZE=$((10 * 1024 * 1024)) # 10MB 7LOG_MAX_FILES=5 8 9# Initialize logging 10init_logging() { 11 mkdir -p "$LOG_DIR" 12 touch "$LOG_FILE" 13} 14 15# Write to log 16log_to_file() { 17 local level="$1" 18 shift 19 local message="$*" 20 local timestamp=$(date '+%Y-%m-%d %H:%M:%S') 21 22 echo "[$timestamp] $level: $message" >> "$LOG_FILE" 23 24 # Check if rotation needed 25 maybe_rotate_logs 26} 27 28# Rotate logs if needed 29maybe_rotate_logs() { 30 if [[ -f "$LOG_FILE" ]]; then 31 local size=$(stat -c %s "$LOG_FILE" 2>/dev/null || stat -f %z "$LOG_FILE") 32 33 if [[ $size -gt $LOG_MAX_SIZE ]]; then 34 rotate_logs 35 fi 36 fi 37} 38 39# Perform log rotation 40rotate_logs() { 41 # Remove oldest 42 rm -f "${LOG_FILE}.${LOG_MAX_FILES}" 43 44 # Shift existing 45 for ((i=LOG_MAX_FILES-1; i>=1; i--)); do 46 if [[ -f "${LOG_FILE}.$i" ]]; then 47 mv "${LOG_FILE}.$i" "${LOG_FILE}.$((i+1))" 48 fi 49 done 50 51 # Rotate current 52 if [[ -f "$LOG_FILE" ]]; then 53 mv "$LOG_FILE" "${LOG_FILE}.1" 54 touch "$LOG_FILE" 55 fi 56} 57 58# Clean old logs 59clean_old_logs() { 60 local days="${1:-30}" 61 find "$LOG_DIR" -name "*.log*" -mtime "+$days" -delete 2>/dev/null || true 62} 63 64# View recent logs 65tail_logs() { 66 local lines="${1:-50}" 67 tail -n "$lines" "$LOG_FILE" 68} 69 70# Search logs 71search_logs() { 72 local pattern="$1" 73 grep -h "$pattern" "$LOG_DIR"/*.log* 2>/dev/null | tail -100 74}

Complete Example: State Manager Module

bash
1#!/bin/bash 2# ABOUTME: Complete state directory manager 3# ABOUTME: Reusable module for bash scripts 4 5# ───────────────────────────────────────────────────────────────── 6# State Directory Manager v1.0.0 7# ───────────────────────────────────────────────────────────────── 8 9# Application identity (override in your script) 10: "${STATE_APP_NAME:=my-app}" 11 12# Directory setup 13STATE_BASE_DIR="${HOME}/.${STATE_APP_NAME}" 14STATE_CONFIG_DIR="$STATE_BASE_DIR/config" 15STATE_DATA_DIR="$STATE_BASE_DIR/data" 16STATE_CACHE_DIR="$STATE_BASE_DIR/cache" 17STATE_LOG_DIR="$STATE_BASE_DIR/logs" 18STATE_TMP_DIR="$STATE_BASE_DIR/tmp" 19 20# File paths 21STATE_CONFIG_FILE="$STATE_CONFIG_DIR/config" 22STATE_STATE_FILE="$STATE_DATA_DIR/state" 23STATE_LOG_FILE="$STATE_LOG_DIR/app.log" 24 25# Settings 26STATE_CACHE_TTL="${STATE_CACHE_TTL:-3600}" 27STATE_LOG_MAX_SIZE="${STATE_LOG_MAX_SIZE:-10485760}" 28STATE_LOG_MAX_FILES="${STATE_LOG_MAX_FILES:-5}" 29 30# ───────────────────────────────────────────────────────────────── 31# Initialization 32# ───────────────────────────────────────────────────────────────── 33 34state_init() { 35 local dirs=( 36 "$STATE_CONFIG_DIR" 37 "$STATE_DATA_DIR" 38 "$STATE_CACHE_DIR" 39 "$STATE_LOG_DIR" 40 "$STATE_TMP_DIR" 41 ) 42 43 for dir in "${dirs[@]}"; do 44 if [[ ! -d "$dir" ]]; then 45 mkdir -p "$dir" 46 chmod 700 "$dir" 47 fi 48 done 49 50 # Initialize files 51 [[ -f "$STATE_CONFIG_FILE" ]] || touch "$STATE_CONFIG_FILE" 52 [[ -f "$STATE_STATE_FILE" ]] || touch "$STATE_STATE_FILE" 53 [[ -f "$STATE_LOG_FILE" ]] || touch "$STATE_LOG_FILE" 54} 55 56# ───────────────────────────────────────────────────────────────── 57# Config Functions 58# ───────────────────────────────────────────────────────────────── 59 60state_config_get() { 61 local key="$1" 62 local default="$2" 63 grep "^${key}=" "$STATE_CONFIG_FILE" 2>/dev/null | cut -d'=' -f2- || echo "$default" 64} 65 66state_config_set() { 67 local key="$1" 68 local value="$2" 69 70 if grep -q "^${key}=" "$STATE_CONFIG_FILE" 2>/dev/null; then 71 sed -i "s|^${key}=.*|${key}=${value}|" "$STATE_CONFIG_FILE" 72 else 73 echo "${key}=${value}" >> "$STATE_CONFIG_FILE" 74 fi 75} 76 77state_config_list() { 78 cat "$STATE_CONFIG_FILE" 2>/dev/null | grep -v '^#' | grep -v '^$' 79} 80 81# ───────────────────────────────────────────────────────────────── 82# State Functions 83# ───────────────────────────────────────────────────────────────── 84 85state_get() { 86 local key="$1" 87 local default="$2" 88 grep "^${key}=" "$STATE_STATE_FILE" 2>/dev/null | cut -d'=' -f2- || echo "$default" 89} 90 91state_set() { 92 local key="$1" 93 local value="$2" 94 95 if grep -q "^${key}=" "$STATE_STATE_FILE" 2>/dev/null; then 96 sed -i "s|^${key}=.*|${key}=${value}|" "$STATE_STATE_FILE" 97 else 98 echo "${key}=${value}" >> "$STATE_STATE_FILE" 99 fi 100} 101 102# ───────────────────────────────────────────────────────────────── 103# Cache Functions 104# ───────────────────────────────────────────────────────────────── 105 106state_cache_key() { 107 echo -n "$1" | md5sum | cut -c1-16 108} 109 110state_cache_get() { 111 local key="$1" 112 local ttl="${2:-$STATE_CACHE_TTL}" 113 local path="$STATE_CACHE_DIR/$(state_cache_key "$key")" 114 115 if [[ -f "$path" ]]; then 116 local age=$(($(date +%s) - $(stat -c %Y "$path" 2>/dev/null || stat -f %m "$path"))) 117 if [[ $age -lt $ttl ]]; then 118 cat "$path" 119 return 0 120 fi 121 fi 122 return 1 123} 124 125state_cache_set() { 126 local key="$1" 127 local value="$2" 128 local path="$STATE_CACHE_DIR/$(state_cache_key "$key")" 129 echo "$value" > "$path" 130} 131 132state_cache_clear() { 133 rm -rf "$STATE_CACHE_DIR"/* 134} 135 136# ───────────────────────────────────────────────────────────────── 137# Log Functions 138# ───────────────────────────────────────────────────────────────── 139 140state_log() { 141 local level="$1" 142 shift 143 local message="$*" 144 echo "[$(date '+%Y-%m-%d %H:%M:%S')] $level: $message" >> "$STATE_LOG_FILE" 145 146 # Auto-rotate 147 local size=$(stat -c %s "$STATE_LOG_FILE" 2>/dev/null || echo 0) 148 if [[ $size -gt $STATE_LOG_MAX_SIZE ]]; then 149 state_log_rotate 150 fi 151} 152 153state_log_rotate() { 154 rm -f "${STATE_LOG_FILE}.${STATE_LOG_MAX_FILES}" 155 for ((i=STATE_LOG_MAX_FILES-1; i>=1; i--)); do 156 [[ -f "${STATE_LOG_FILE}.$i" ]] && mv "${STATE_LOG_FILE}.$i" "${STATE_LOG_FILE}.$((i+1))" 157 done 158 mv "$STATE_LOG_FILE" "${STATE_LOG_FILE}.1" 159 touch "$STATE_LOG_FILE" 160} 161 162state_log_tail() { 163 tail -n "${1:-50}" "$STATE_LOG_FILE" 164} 165 166# ───────────────────────────────────────────────────────────────── 167# Cleanup Functions 168# ───────────────────────────────────────────────────────────────── 169 170state_cleanup() { 171 # Clean temp files older than 1 day 172 find "$STATE_TMP_DIR" -type f -mtime +1 -delete 2>/dev/null || true 173 174 # Clean expired cache 175 find "$STATE_CACHE_DIR" -type f -mmin "+$((STATE_CACHE_TTL / 60))" -delete 2>/dev/null || true 176 177 # Clean old logs 178 find "$STATE_LOG_DIR" -name "*.log.*" -mtime +30 -delete 2>/dev/null || true 179} 180 181state_reset() { 182 rm -rf "$STATE_BASE_DIR" 183 state_init 184} 185 186# ───────────────────────────────────────────────────────────────── 187# Auto-initialize 188# ───────────────────────────────────────────────────────────────── 189 190state_init

Usage in Scripts

bash
1#!/bin/bash 2# Your script that uses the state manager 3 4# Set app name before sourcing 5STATE_APP_NAME="my-tool" 6 7# Source the state manager 8source /path/to/state-manager.sh 9 10# Now use it 11state_config_set "api_key" "abc123" 12api_key=$(state_config_get "api_key") 13 14state_set "last_run" "$(date -Iseconds)" 15state_log "INFO" "Script started" 16 17# Use cache 18if ! result=$(state_cache_get "api_response"); then 19 result=$(curl -s https://api.example.com/data) 20 state_cache_set "api_response" "$result" 21fi

Best Practices

  1. Use Standard Locations - Follow XDG or $HOME/.app-name
  2. Initialize Early - Call init before any operations
  3. Handle Permissions - Use 700 for private data
  4. Clean Up Regularly - Remove old temp/cache files
  5. Rotate Logs - Prevent unbounded growth

Resources


Version History

  • 1.0.0 (2026-01-14): Initial release - extracted from workspace-hub patterns

Related Skills

Looking for an alternative to state-directory-manager or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication