Claude-skill-registry global-state
Global state management for cross-session persistence. Reference this skill to read/write global config, history, learnings, and statistics.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/global-state" ~/.claude/skills/majiayu000-claude-skill-registry-global-state && rm -rf "$T"
skills/data/global-state/SKILL.md// Project Autopilot - Cross-Session State Management // Copyright (c) 2026 Jeremy McSpadden jeremy@fluxlabs.net
Global State Skill
Manage persistent state across Claude Code sessions. Works on macOS, Linux, and Windows.
Directory Location
Platform-Specific Paths
| Platform | Path |
|---|---|
| macOS/Linux | |
| Windows | |
Resolving the Path
FUNCTION getGlobalStateDir(): IF platform == "windows": RETURN env.USERPROFILE + "\\.claude\\autopilot\\" ELSE: RETURN expandPath("~/.claude/autopilot/")
Or use Node.js style:
const os = require('os'); const path = require('path'); const globalDir = path.join(os.homedir(), '.claude', 'autopilot');
Directory Structure
{globalDir}/ ├── config.json # User preferences and defaults ├── history.json # All projects built with outcomes ├── learnings.json # Cross-project patterns and knowledge └── statistics.json # Aggregate stats and estimation accuracy
File Schemas
config.json
User preferences that persist across sessions.
{ "version": "1.0", "defaults": { "maxCost": 50, "warnCost": 10, "alertCost": 25, "maxTokens": 2000000, "warnTokens": 500000, "alertTokens": 1000000, "preferredModel": "sonnet", "autoApprove": false, "verboseOutput": false }, "ui": { "compactStatus": false, "showEstimates": true, "showVariance": true }, "created": "2026-01-28T00:00:00Z", "updated": "2026-01-28T00:00:00Z" }
history.json
Project history for estimation and resume.
{ "version": "1.0", "projects": [ { "id": "uuid-v4", "name": "my-project", "path": "/Users/user/projects/my-project", "description": "User authentication system", "techStack": ["node", "typescript", "postgres"], "started": "2026-01-25T10:00:00Z", "completed": "2026-01-25T14:30:00Z", "status": "completed", "phases": { "total": 10, "completed": 10 }, "costs": { "estimated": 6.52, "actual": 5.89, "variance": -9.7 }, "tokens": { "input": 1250000, "output": 480000 }, "checkpointPath": ".autopilot/checkpoint.md", "outcome": "success", "notes": "Completed under budget" } ], "totalProjects": 1, "updated": "2026-01-28T00:00:00Z" }
learnings.json
Cross-project knowledge and patterns.
{ "version": "1.0", "techStacks": { "node-typescript-postgres": { "seenCount": 5, "avgPhaseCost": { "setup": 0.12, "database": 0.35, "auth": 0.38, "api": 0.85, "testing": 0.45 }, "commonDependencies": ["express", "prisma", "jest"], "typicalPhaseCount": 8, "notes": ["Always add input validation early", "Tests save time later"] } }, "estimationAccuracy": { "byPhaseType": { "setup": { "avgVariance": -15, "samples": 12 }, "database": { "avgVariance": 8, "samples": 10 }, "auth": { "avgVariance": 12, "samples": 8 }, "api": { "avgVariance": 5, "samples": 15 }, "testing": { "avgVariance": -5, "samples": 11 } }, "overall": { "avgVariance": 3, "samples": 56 } }, "commonPatterns": [ { "pattern": "API with auth", "requiredPhases": ["setup", "database", "auth", "api", "testing"], "avgTotalCost": 3.50, "avgDuration": "4h" } ], "errorPatterns": [ { "error": "Missing environment variables", "frequency": 15, "solution": "Add .env.example and validation on startup", "preventionPhase": "setup" } ], "updated": "2026-01-28T00:00:00Z" }
statistics.json
Aggregate metrics across all projects.
{ "version": "1.0", "totals": { "projects": 25, "successfulProjects": 23, "failedProjects": 2, "totalCost": 89.45, "totalTokens": { "input": 28500000, "output": 11200000 }, "totalPhases": 187, "totalTasks": 1245 }, "averages": { "costPerProject": 3.58, "tokensPerProject": 1588000, "phasesPerProject": 7.5, "tasksPerProject": 49.8, "durationPerProject": "3.5h" }, "accuracy": { "overallEstimateAccuracy": 94.5, "bestPhaseType": "setup", "worstPhaseType": "frontend", "improvementTrend": "+2.3%" }, "timeline": { "firstProject": "2026-01-01T00:00:00Z", "lastProject": "2026-01-28T00:00:00Z" }, "updated": "2026-01-28T00:00:00Z" }
Operations
Initialize Global State
First time setup - create directory and default files.
FUNCTION initializeGlobalState(): dir = expandPath("~/.claude/autopilot/") IF NOT exists(dir): mkdir(dir) # Create default config if missing IF NOT exists(dir + "config.json"): WRITE defaultConfig to dir + "config.json" # Create empty history if missing IF NOT exists(dir + "history.json"): WRITE emptyHistory to dir + "history.json" # Create empty learnings if missing IF NOT exists(dir + "learnings.json"): WRITE emptyLearnings to dir + "learnings.json" # Create empty statistics if missing IF NOT exists(dir + "statistics.json"): WRITE emptyStatistics to dir + "statistics.json" RETURN success
Read Global Config
Load user preferences with defaults fallback.
FUNCTION getGlobalConfig(): path = expandPath("~/.claude/autopilot/config.json") IF NOT exists(path): initializeGlobalState() config = parseJSON(readFile(path)) # Merge with defaults for any missing keys RETURN mergeWithDefaults(config, DEFAULT_CONFIG)
Update Global Config
Save configuration changes.
FUNCTION updateGlobalConfig(updates): config = getGlobalConfig() # Deep merge updates FOR key, value IN updates: config[key] = deepMerge(config[key], value) config.updated = now() WRITE config to "~/.claude/autopilot/config.json" RETURN config
Add Project to History
Record a completed project.
FUNCTION addProjectToHistory(project): path = expandPath("~/.claude/autopilot/history.json") history = parseJSON(readFile(path)) projectEntry = { id: generateUUID(), name: project.name, path: project.path, description: project.description, techStack: project.techStack, started: project.started, completed: now(), status: project.status, phases: { total: project.totalPhases, completed: project.completedPhases }, costs: { estimated: project.estimatedCost, actual: project.actualCost, variance: calculateVariance(project.estimatedCost, project.actualCost) }, tokens: project.tokens, checkpointPath: project.checkpointPath, outcome: project.outcome, notes: project.notes } history.autopilots.push(projectEntry) history.totalProjects = history.autopilots.length history.updated = now() WRITE history to path # Also update statistics updateStatistics(projectEntry) # And learnings updateLearnings(projectEntry) RETURN projectEntry.id
Find Similar Projects
Get historical projects for estimation.
FUNCTION findSimilarProjects(techStack, description): history = getHistory() matches = [] FOR project IN history.autopilots: score = 0 # Tech stack similarity commonTech = intersection(techStack, project.techStack) score += commonTech.length * 20 # Description similarity (simple keyword match) IF hasCommonKeywords(description, project.description): score += 30 IF score >= 40: matches.push({ project: project, similarity: score }) # Sort by similarity descending RETURN matches.sortBy(m => m.similarity).reverse()
Get Estimation Adjustment
Calculate adjustment factor from historical accuracy.
FUNCTION getEstimationAdjustment(phaseType, techStack): learnings = getLearnings() # Check phase-specific accuracy IF learnings.estimationAccuracy.byPhaseType[phaseType]: phaseVariance = learnings.estimationAccuracy.byPhaseType[phaseType].avgVariance ELSE: phaseVariance = 0 # Check tech stack accuracy stackKey = techStack.sort().join("-") IF learnings.techStacks[stackKey]: stackData = learnings.techStacks[stackKey] IF stackData.avgPhaseCost[phaseType]: # Use historical average if we have enough data RETURN { type: "historical", adjustment: 1 + (phaseVariance / 100), confidence: "high", historicalCost: stackData.avgPhaseCost[phaseType] } # Fall back to general phase variance RETURN { type: "estimated", adjustment: 1 + (phaseVariance / 100), confidence: phaseVariance != 0 ? "medium" : "low", historicalCost: null }
Update Learnings
Extract and store knowledge from completed project.
FUNCTION updateLearnings(project): path = expandPath("~/.claude/autopilot/learnings.json") learnings = parseJSON(readFile(path)) # Update tech stack knowledge stackKey = project.techStack.sort().join("-") IF NOT learnings.techStacks[stackKey]: learnings.techStacks[stackKey] = { seenCount: 0, avgPhaseCost: {}, commonDependencies: [], typicalPhaseCount: 0, notes: [] } stack = learnings.techStacks[stackKey] stack.seenCount++ stack.typicalPhaseCount = runningAverage( stack.typicalPhaseCount, project.phases.total, stack.seenCount ) # Update estimation accuracy variance = project.costs.variance overall = learnings.estimationAccuracy.overall overall.avgVariance = runningAverage( overall.avgVariance, variance, overall.samples + 1 ) overall.samples++ learnings.updated = now() WRITE learnings to path
Update Statistics
Aggregate project stats.
FUNCTION updateStatistics(project): path = expandPath("~/.claude/autopilot/statistics.json") stats = parseJSON(readFile(path)) # Update totals stats.totals.autopilots++ IF project.outcome == "success": stats.totals.successfulProjects++ ELSE: stats.totals.failedProjects++ stats.totals.totalCost += project.costs.actual stats.totals.totalTokens.input += project.tokens.input stats.totals.totalTokens.output += project.tokens.output stats.totals.totalPhases += project.phases.total # Update averages n = stats.totals.autopilots stats.averages.costPerProject = stats.totals.totalCost / n stats.averages.tokensPerProject = ( stats.totals.totalTokens.input + stats.totals.totalTokens.output ) / n stats.averages.phasesPerProject = stats.totals.totalPhases / n # Update accuracy IF project.costs.variance != null: accuracy = 100 - Math.abs(project.costs.variance) oldAcc = stats.accuracy.overallEstimateAccuracy stats.accuracy.overallEstimateAccuracy = runningAverage(oldAcc, accuracy, n) # Update timeline stats.timeline.lastProject = now() stats.updated = now() WRITE stats to path
Get Resumable Projects
Find projects that can be resumed.
FUNCTION getResumableProjects(): history = getHistory() resumable = [] FOR project IN history.autopilots: IF project.status == "in_progress" OR project.status == "paused": # Check if checkpoint exists checkpointPath = project.path + "/" + project.checkpointPath IF exists(checkpointPath): resumable.push({ id: project.id, name: project.name, path: project.path, lastActivity: project.updated OR project.started, progress: (project.phases.completed / project.phases.total) * 100, remainingCost: project.costs.estimated - project.costs.actual }) # Sort by last activity (most recent first) RETURN resumable.sortBy(p => p.lastActivity).reverse()
Default Values
DEFAULT_CONFIG
{ "version": "1.0", "defaults": { "maxCost": 50, "warnCost": 10, "alertCost": 25, "maxTokens": 2000000, "warnTokens": 500000, "alertTokens": 1000000, "preferredModel": "sonnet", "autoApprove": false, "verboseOutput": false }, "ui": { "compactStatus": false, "showEstimates": true, "showVariance": true } }
EMPTY_HISTORY
{ "version": "1.0", "projects": [], "totalProjects": 0, "updated": null }
EMPTY_LEARNINGS
{ "version": "1.0", "techStacks": {}, "estimationAccuracy": { "byPhaseType": {}, "overall": { "avgVariance": 0, "samples": 0 } }, "commonPatterns": [], "errorPatterns": [], "updated": null }
EMPTY_STATISTICS
{ "version": "1.0", "totals": { "projects": 0, "successfulProjects": 0, "failedProjects": 0, "totalCost": 0, "totalTokens": { "input": 0, "output": 0 }, "totalPhases": 0, "totalTasks": 0 }, "averages": { "costPerProject": 0, "tokensPerProject": 0, "phasesPerProject": 0, "tasksPerProject": 0, "durationPerProject": "0h" }, "accuracy": { "overallEstimateAccuracy": 0, "bestPhaseType": null, "worstPhaseType": null, "improvementTrend": null }, "timeline": { "firstProject": null, "lastProject": null }, "updated": null }
Integration Points
Commands Using Global State
| Command | Reads | Writes |
|---|---|---|
| config, history, learnings | history, learnings, statistics |
| config, history, learnings | - |
| config, history | history |
| config, history, statistics | - |
| all | config |
| history, statistics, learnings | history (archive) |
| history, learnings, statistics | - |
| history, learnings | - |
Portfolio Queries
Get All Projects
FUNCTION getAllProjects(filters): history = readJSON("~/.claude/autopilot/history.json") projects = history.autopilots # Apply filters IF filters.status: projects = projects.filter(p => p.status == filters.status) IF filters.stack: projects = projects.filter(p => p.techStack.some(t => filters.stack.includes(t)) ) IF filters.archived !== undefined: projects = projects.filter(p => p.archived == filters.archived) # Sort by last activity projects.sort((a, b) => new Date(b.updated || b.completed) - new Date(a.updated || a.completed) ) RETURN projects
Get Portfolio Summary
FUNCTION getPortfolioSummary(): history = readJSON("~/.claude/autopilot/history.json") statistics = readJSON("~/.claude/autopilot/statistics.json") RETURN { totalProjects: history.totalProjects, byStatus: { active: countByStatus(history, "in_progress"), paused: countByStatus(history, "paused"), completed: countByStatus(history, "completed"), failed: countByStatus(history, "failed") }, costs: { total: statistics.totals.totalCost, average: statistics.averages.costPerProject }, accuracy: statistics.accuracy.overallEstimateAccuracy, tokens: statistics.totals.totalTokens }
Get Project By Name
FUNCTION getProjectByName(name): history = readJSON("~/.claude/autopilot/history.json") # Exact match project = history.autopilots.find(p => p.name.toLowerCase() == name.toLowerCase() ) IF NOT project: # Partial match project = history.autopilots.find(p => p.name.toLowerCase().includes(name.toLowerCase()) ) RETURN project
Archive Project
FUNCTION archiveProject(projectId): history = readJSON("~/.claude/autopilot/history.json") project = history.autopilots.find(p => p.id == projectId) IF NOT project: ERROR "Project not found" RETURN false project.archived = true project.archivedAt = now() writeJSON("~/.claude/autopilot/history.json", history) LOG "Project archived: {project.name}" RETURN true
Get Cost Analysis
FUNCTION getCostAnalysis(): history = readJSON("~/.claude/autopilot/history.json") statistics = readJSON("~/.claude/autopilot/statistics.json") analysis = { total: statistics.totals.totalCost, byProject: [], byStatus: {}, byStack: {}, trend: [] } # By project FOR each project IN history.autopilots: analysis.byProject.push({ name: project.name, cost: project.costs.actual, variance: project.costs.variance }) # By status statuses = groupBy(history.autopilots, "status") FOR each status, projects IN statuses: analysis.byStatus[status] = { count: projects.length, total: sum(projects.map(p => p.costs.actual)), average: avg(projects.map(p => p.costs.actual)) } # By tech stack stacks = {} FOR each project IN history.autopilots: stackKey = project.techStack.sort().join("-") IF NOT stacks[stackKey]: stacks[stackKey] = [] stacks[stackKey].push(project) FOR each stack, projects IN stacks: analysis.byStack[stack] = { count: projects.length, total: sum(projects.map(p => p.costs.actual)), average: avg(projects.map(p => p.costs.actual)) } RETURN analysis
When to Read Global State
- Session Start - Load config defaults
- Cost Estimation - Get historical accuracy adjustments
- Project Scan - Find similar projects for comparison
- Resume - Find resumable projects
When to Write Global State
- Project Completion - Add to history, update learnings and stats
- Config Changes - User updates preferences
- Phase Completion - Update estimation accuracy
- Checkpoint Save - Mark project status in history
Error Handling
File Access Errors
TRY: data = readGlobalFile(filename) CATCH FileNotFound: initializeGlobalState() data = readGlobalFile(filename) CATCH ParseError: # Backup corrupted file mv(filename, filename + ".backup." + timestamp) # Create fresh default data = getDefaultFor(filename) writeFile(filename, data) LOG warning: "Corrupted {filename} backed up and reset" CATCH PermissionError: LOG error: "Cannot access ~/.claude/autopilot/ - check permissions" RETURN null
Data Migration
When schema version changes:
FUNCTION migrateIfNeeded(data, filename): currentVersion = SCHEMA_VERSIONS[filename] dataVersion = data.version OR "0.0" IF dataVersion < currentVersion: data = applyMigrations(data, dataVersion, currentVersion) data.version = currentVersion writeFile(filename, data) LOG "Migrated {filename} from v{dataVersion} to v{currentVersion}" RETURN data
Performance Notes
- Global state files are small (<1MB typically)
- Read at session start, cache in memory
- Write atomically (temp file + rename)
- Don't re-read during execution unless explicitly needed