name: global-state description: Global state management for cross-session persistence. Reference this skill to read/write global config, history, learnings, and statistics.
// 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 | ~/.claude/autopilot/ |
| Windows | %USERPROFILE%\.claude\autopilot\ |
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 |
|---|---|---|
/autopilot:takeoff | config, history, learnings | history, learnings, statistics |
/autopilot:radar | config, history, learnings | - |
/autopilot:cockpit | config, history | history |
/autopilot:altitude --global | config, history, statistics | - |
/autopilot:config | all | config |
/autopilot:portfolio | history, statistics, learnings | history (archive) |
/autopilot:compare | history, learnings, statistics | - |
/autopilot:estimate | 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