configuring
Universal environment variable loader for AI agent environments. Loads secrets and config from Claude.ai, Claude Code, OpenAI Codex, Jules, and standard .env files.
Install
npx skills add https://github.com/oaustegard/claude-skills/tree/main/plugins/environment-and-config/skills/configuring
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oaustegard-claude-skills@llmmart
git clone https://github.com/oaustegard/claude-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oaustegard/claude-skills collection as a plugin from our marketplace. Git is the plain clone.
README
configuring
Universal configuration management for AI agent environments. Loads environment variables, secrets, and config from Claude.ai, Claude Code, OpenAI Codex, Jules, and standard .env files.
Skill manifest
Configuring
Unified configuration management across AI coding environments. Load environment variables, secrets, and other opinionated configuration setups from any AI coding platform.
Quick Start
import sys
sys.path.insert(0, '/path/to/claude-skills') # or wherever skills are installed
from configuring import get_env, detect_environment
# Get a variable (searches all sources automatically)
token = get_env("TURSO_TOKEN", required=True)
# With default
port = get_env("PORT", default="8080")
# What environment are we in?
env = detect_environment() # "claude.ai", "claude-code-desktop", "codex", "jules", etc.
Supported Environments
| Environment | Config Sources |
|---|---|
| Claude.ai Projects | /mnt/project/*.env, /mnt/project/*-token.txt |
| Claude Code | ~/.claude/settings.json (env block), .claude/settings.json |
| OpenAI Codex | ~/.codex/config.toml, setup script → ~/.bashrc, shell_snapshots/*.sh |
| Jules | Environment settings UI, .env in repo |
| Universal | os.environ, .env, .env.local |
API Reference
# Core
get_env(key, default=None, *, required=False, validator=None) -> str | None
load_env(path) -> dict[str, str] # Load specific file
load_all(force_reload=False) -> dict # Load all sources
# Utilities
detect_environment() -> str # Current platform
mask_secret(value, show_chars=4) -> str # Safe logging
debug_info() -> dict # Troubleshooting
get_loaded_sources() -> list[str] # What was checked
Credential File Formats
.env files (KEY=value):
TURSO_TOKEN=eyJhbGciOiJFZERTQSI...
EMBEDDING_API_KEY=sk-svcacct-...
Single-value files (*-token.txt, *-key.txt):
eyJhbGciOiJFZERTQSI...
Filename becomes key: turso-token.txt → TURSO_TOKEN
Claude Code settings.json:
{
"env": {
"TURSO_TOKEN": "eyJhbGciOiJFZERTQSI..."
}
}
Priority Order
Later sources override earlier:
- OS environment variables
- Platform-specific sources (detected automatically)
.envfiles in cwd- OS environment variables (again - explicit exports always win)
Debugging
import sys
sys.path.insert(0, '/path/to/claude-skills')
from configuring import debug_info
print(debug_info())
# {'environment': 'claude.ai', 'sources': ['os.environ', 'claude.ai:/mnt/project/'], ...}
CLI:
cd /path/to/claude-skills/configuring
python scripts/getting_env.py # Show debug info
python scripts/getting_env.py TURSO_TOKEN # Get specific key
Migration from api-credentials / getting-env
Replace:
# Old (api-credentials)
from credentials import get_anthropic_api_key
key = get_anthropic_api_key()
# Old (getting-env)
from getting_env import get_env
key = get_env("ANTHROPIC_API_KEY")
# New (configuring)
import sys
sys.path.insert(0, '/path/to/claude-skills')
from configuring import get_env
key = get_env("ANTHROPIC_API_KEY", required=True)
Files (claude-skills)
-
assets
-
example.env 485 B · in bundle
-
-
examples
-
turso_refactored.py 8.9 KB
""" Turso HTTP API layer for remembering skill. This module handles: - Credential initialization via configuring skill - HTTP request retry logic (_retry_with_backoff) - SQL execution via Turso HTTP API (_exec, _exec_batch) - JSON field parsing (_parse_memory_row) Imports from: state, configuring """ import json import os import sys import time import requests from . import state # Import from configuring skill (must be on Python path) try: sys.path.insert(0, '/path/to/claude-skills') # Adjust path as needed from configuring import detect_environment, get_env except ImportError: # Fallback if configuring skill not available from pathlib import Path def _fallback_get_env(key, default=None, required=False): """Minimal fallback if configuring skill not installed.""" value = os.environ.get(key) if not value: # Try /mnt/project/*.env for env_file in Path("/mnt/project").glob("*.env"): for line in env_file.read_text().splitlines(): if line.strip().startswith(f"{key}="): value = line.partition("=")[2].strip() break if required and not value: raise ValueError(f"Required environment variable '{key}' not found") return value or default get_env = _fallback_get_env detect_environment = lambda: "unknown" def _init(): """Lazy-load credentials and URL using getting-env skill.""" if state._TOKEN is None: # Load TURSO_URL (with default) state._URL = get_env("TURSO_URL", default=state._DEFAULT_URL) # Load TURSO_TOKEN (required) try: state._TOKEN = get_env("TURSO_TOKEN", required=True) except ValueError as e: raise RuntimeError(str(e)) from e # Clean token: remove any whitespace if state._TOKEN: state._TOKEN = state._TOKEN.strip().replace(" ", "") state._HEADERS = {"Authorization": f"Bearer {state._TOKEN}", "Content-Type": "application/json"} def _retry_with_backoff(fn, max_retries=3, base_delay=1.0): """Retry a function with exponential backoff on transient errors. Args: fn: Callable that may raise exceptions max_retries: Maximum number of retry attempts (default 3) base_delay: Initial delay in seconds (default 1.0) Returns: Result of fn() if successful Raises: Last exception if all retries exhausted """ for attempt in range(max_retries): try: return fn() except Exception as e: if attempt == max_retries - 1: # Last attempt failed, re-raise raise # Check if it's a retriable error (503, 429, SSL handshake failures) error_str = str(e) is_retriable = ( '503' in error_str or '429' in error_str or 'Service Unavailable' in error_str or 'SSL' in error_str or 'SSLError' in error_str or 'HANDSHAKE_FAILURE' in error_str ) if is_retriable: delay = base_delay * (2 ** attempt) print(f"Warning: API request failed (attempt {attempt + 1}/{max_retries}), retrying in {delay}s: {e}") time.sleep(delay) else: # Non-retriable error, fail immediately raise def _parse_memory_row(row: dict) -> dict: """Parse JSON fields in a memory row (tags, entities, refs). Args: row: Raw row dict from database Returns: Row dict with parsed JSON fields """ # Parse tags field if 'tags' in row and row['tags'] is not None: if isinstance(row['tags'], str): try: row['tags'] = json.loads(row['tags']) except json.JSONDecodeError: row['tags'] = [] # Parse entities field if 'entities' in row and row['entities'] is not None: if isinstance(row['entities'], str): try: row['entities'] = json.loads(row['entities']) except json.JSONDecodeError: row['entities'] = [] # Parse refs field if 'refs' in row and row['refs'] is not None: if isinstance(row['refs'], str): try: row['refs'] = json.loads(row['refs']) except json.JSONDecodeError: row['refs'] = [] return row def _exec_batch(statements: list) -> list: """Execute multiple SQL statements in a single pipeline request. Args: statements: List of SQL strings or (sql, args) tuples Returns: List of result lists (one per statement) Example: results = _exec_batch([ "SELECT * FROM config WHERE category = 'profile'", ("SELECT * FROM memories WHERE type = ?", ["decision"]) ]) profile_data = results[0] decisions = results[1] """ _init() requests_list = [] for stmt in statements: if isinstance(stmt, tuple): sql, args = stmt else: sql, args = stmt, [] request = {"type": "execute", "stmt": {"sql": sql}} if args: request["stmt"]["args"] = [ {"type": "text", "value": str(v)} if v is not None else {"type": "null"} for v in args ] requests_list.append(request) # Add close request requests_list.append({"type": "close"}) try: resp = requests.post( f"{state._URL}/v2/pipeline", headers=state._HEADERS, json={"requests": requests_list}, timeout=30 ).json() except requests.exceptions.SSLError as e: raise RuntimeError( f"SSL error connecting to Turso database. This often indicates missing or invalid credentials.\n" f"Check that TURSO_TOKEN is set correctly.\n" f"Original error: {e}" ) from e except requests.exceptions.RequestException as e: raise RuntimeError( f"Network error connecting to Turso database at {state._URL}\n" f"Check network connectivity and credentials (TURSO_TOKEN).\n" f"Original error: {e}" ) from e # Parse results (exclude the close response) results = [] for r in resp.get("results", [])[:-1]: # Exclude close result if r["type"] != "ok": error_msg = r.get("error", {}).get("message", "Unknown error") error_code = r.get("error", {}).get("code", "UNKNOWN") raise RuntimeError(f"Database error [{error_code}]: {error_msg}") res = r["response"]["result"] cols = [c["name"] for c in res["cols"]] rows = [ {cols[i]: (row[i].get("value") if row[i].get("type") != "null" else None) for i in range(len(cols))} for row in res["rows"] ] # Parse JSON fields if this is a memory query if rows and 'tags' in rows[0]: rows = [_parse_memory_row(row) for row in rows] results.append(rows) return results def _exec(sql, args=None, parse_json: bool = True): """Execute SQL, return list of dicts. Args: sql: SQL query args: Query arguments parse_json: If True, parse JSON fields (tags, entities, refs) in memory rows """ _init() stmt = {"sql": sql} if args: stmt["args"] = [ {"type": "text", "value": str(v)} if v is not None else {"type": "null"} for v in args ] try: resp = requests.post( f"{state._URL}/v2/pipeline", headers=state._HEADERS, json={"requests": [{"type": "execute", "stmt": stmt}]}, timeout=30 ).json() except requests.exceptions.SSLError as e: raise RuntimeError( f"SSL error connecting to Turso database. This often indicates missing or invalid credentials.\n" f"Check that TURSO_TOKEN is set correctly.\n" f"Original error: {e}" ) from e except requests.exceptions.RequestException as e: raise RuntimeError( f"Network error connecting to Turso database at {state._URL}\n" f"Check network connectivity and credentials (TURSO_TOKEN).\n" f"Original error: {e}" ) from e r = resp["results"][0] if r["type"] != "ok": error_msg = r.get("error", {}).get("message", "Unknown error") error_code = r.get("error", {}).get("code", "UNKNOWN") raise RuntimeError(f"Database error [{error_code}]: {error_msg}") res = r["response"]["result"] cols = [c["name"] for c in res["cols"]] rows = [ {cols[i]: (row[i].get("value") if row[i].get("type") != "null" else None) for i in range(len(cols))} for row in res["rows"] ] # Parse JSON fields if this is a memory query if parse_json and rows and 'tags' in rows[0]: rows = [_parse_memory_row(row) for row in rows] return rows
-
-
scripts
-
getting_env.py 19.4 KB
#!/usr/bin/env python3 """ Universal Configuration Management for AI Agent Environments Loads environment variables, secrets, and configuration from any supported AI coding environment: - Claude.ai Projects (project knowledge files) - Claude Code (desktop & web) - OpenAI Codex - Jules (Google) - Standard environments (.env files, shell env vars) Usage: import sys sys.path.insert(0, '/path/to/claude-skills') from configuring import get_env, load_env, detect_environment # Get a single variable (searches all sources) api_key = get_env("MY_API_KEY") # With default api_key = get_env("MY_API_KEY", default="") # Detect current environment env_type = detect_environment() # "claude.ai", "claude-code", "codex", "jules", "unknown" # Bulk load from a specific file vars = load_env("/path/to/.env") """ import json import os from collections.abc import Callable from pathlib import Path from typing import Any __version__ = "2.0.0" # Module-level cache _cache: dict[str, str] = {} _loaded_sources: list[str] = [] # ============================================================================= # Environment Detection # ============================================================================= # @lat: [[infrastructure#Environment Detection]] def detect_environment() -> str: """ Detect which AI agent environment we're running in. Returns: One of: "claude.ai", "claude-code-desktop", "claude-code-web", "codex", "jules", "unknown" """ # Claude.ai Projects: /mnt/project exists and is populated if Path("/mnt/project").is_dir(): # Claude.ai web chat has /mnt/skills and /mnt/user-data if Path("/mnt/skills").exists() and Path("/mnt/user-data").exists(): return "claude.ai" # Claude Code: ~/.claude directory exists with settings claude_home = Path.home() / ".claude" if claude_home.is_dir(): if (claude_home / "settings.json").exists(): return "claude-code-desktop" # OpenAI Codex: ~/.codex directory or CODEX_HOME env var codex_home = os.environ.get("CODEX_HOME", str(Path.home() / ".codex")) if Path(codex_home).is_dir(): return "codex" # Jules: /home/jules is the standard user if Path("/home/jules").exists() or os.environ.get("JULES_SESSION_ID"): return "jules" # Claude Code on web may not have ~/.claude but has container markers if os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_CODE"): return "claude-code-web" return "unknown" # ============================================================================= # File Parsing Utilities # ============================================================================= def _parse_env_file(path: Path) -> dict[str, str]: """ Parse a .env file into a dict. Handles: - KEY=value - KEY="value with spaces" - KEY='value with spaces' - export KEY=value - # comments - Empty lines Args: path: Path to .env file Returns: Dict of key-value pairs """ env = {} if not path.exists(): return env for line in path.read_text().splitlines(): line = line.strip() # Skip empty lines and comments if not line or line.startswith('#'): continue # Handle 'export KEY=value' format line = line.removeprefix('export ') # Must have an equals sign if '=' not in line: continue key, _, value = line.partition('=') key = key.strip() value = value.strip() # Remove surrounding quotes if (value.startswith('"') and value.endswith('"')) or \ (value.startswith("'") and value.endswith("'")): value = value[1:-1] env[key] = value return env def _parse_shell_exports(path: Path) -> dict[str, str]: """ Parse exported environment variables from a shell script or snapshot. Handles: - export KEY=value - declare -x KEY=value Args: path: Path to a shell file Returns: Dict of key-value pairs """ env = {} if not path.exists(): return env for line in path.read_text().splitlines(): line = line.strip() if line.startswith("export "): line = line[len("export "):] elif line.startswith("declare -x "): line = line[len("declare -x "):] else: continue if '=' not in line: continue key, _, value = line.partition('=') key = key.strip() value = value.strip() if (value.startswith('"') and value.endswith('"')) or \ (value.startswith("'") and value.endswith("'")): value = value[1:-1] env[key] = value return env def _parse_single_value_file(path: Path, key_name: str) -> dict[str, str]: """ Parse a single-value file (like turso-token.txt) into a dict. Handles: - Plain value - KEY=value format (extracts just the value) Args: path: Path to the file key_name: Key name to use if file contains just a value Returns: Dict with the key-value pair """ if not path.exists(): return {} content = path.read_text().strip() # Check if it's already in KEY=value format if '=' in content and '\n' not in content: key, _, value = content.partition('=') return {key.strip(): value.strip()} # Plain value return {key_name: content} def _parse_json_settings(path: Path) -> dict[str, str]: """ Parse env vars from a Claude Code settings.json file. Looks for the "env" block: { "env": { "MY_VAR": "value" } } Args: path: Path to settings.json Returns: Dict of env vars from the "env" block """ if not path.exists(): return {} try: data = json.loads(path.read_text()) return data.get("env", {}) except (OSError, json.JSONDecodeError): return {} def _parse_toml_env(path: Path) -> dict[str, str]: """ Parse environment variables from a Codex config.toml file. Looks for shell_environment_policy.set block. Basic TOML parsing - doesn't handle all TOML features. Args: path: Path to config.toml Returns: Dict of env vars """ if not path.exists(): return {} env = {} try: content = path.read_text() # Simple parsing for common patterns # Look for set = { KEY = "value", ... } import re # Find shell_environment_policy.set or [shell_environment_policy] set = match = re.search(r'set\s*=\s*\{([^}]+)\}', content) if match: pairs = match.group(1) for pair_match in re.finditer(r'(\w+)\s*=\s*"([^"]*)"', pairs): env[pair_match.group(1)] = pair_match.group(2) except OSError: pass return env # ============================================================================= # Source Loading Functions # ============================================================================= def _load_os_environ() -> dict[str, str]: """Load all environment variables from os.environ.""" return dict(os.environ) def _load_claude_ai_project() -> dict[str, str]: """ Load environment variables from Claude.ai project knowledge. Searches /mnt/project for: - *.env files (KEY=value format) - *-token.txt, *-key.txt, *-secret.txt files (single value) """ env = {} project_dir = Path("/mnt/project") if not project_dir.is_dir(): return env # Load all .env files for env_file in project_dir.glob("*.env"): env.update(_parse_env_file(env_file)) # Load single-value credential files patterns = ["*-token.txt", "*-key.txt", "*-secret.txt", "*_TOKEN.txt", "*_KEY.txt"] for pattern in patterns: for cred_file in project_dir.glob(pattern): # Derive key name from filename # e.g., "turso-token.txt" -> "TURSO_TOKEN" stem = cred_file.stem.upper().replace("-", "_") env.update(_parse_single_value_file(cred_file, stem)) return env def _load_claude_code() -> dict[str, str]: """ Load environment variables from Claude Code settings. Searches: - ~/.claude/settings.json (user settings) - ~/.claude/settings.local.json (user local settings) - .claude/settings.json (project settings) - .claude/settings.local.json (project local settings) """ env = {} # User settings (lower priority) user_claude = Path.home() / ".claude" for filename in ["settings.json", "settings.local.json"]: env.update(_parse_json_settings(user_claude / filename)) # Project settings (higher priority) project_claude = Path.cwd() / ".claude" for filename in ["settings.json", "settings.local.json"]: env.update(_parse_json_settings(project_claude / filename)) return env def _load_codex() -> dict[str, str]: """ Load environment variables from OpenAI Codex configuration. Searches: - $CODEX_HOME/config.toml or ~/.codex/config.toml - .codex/config.toml (project) """ env = {} # User config codex_home = Path(os.environ.get("CODEX_HOME", str(Path.home() / ".codex"))) env.update(_parse_toml_env(codex_home / "config.toml")) # Shell setup exports (~/.bashrc, ~/.profile) for path in [Path.home() / ".bashrc", Path.home() / ".profile"]: env.update(_parse_shell_exports(path)) # Shell snapshot exports (used by Codex) snapshot_dir = codex_home / "shell_snapshots" if snapshot_dir.is_dir(): snapshots = sorted(snapshot_dir.glob("*.sh"), key=lambda p: p.stat().st_mtime) if snapshots: env.update(_parse_shell_exports(snapshots[-1])) # Project config (higher priority) env.update(_parse_toml_env(Path.cwd() / ".codex" / "config.toml")) return env def _load_jules() -> dict[str, str]: """ Load environment variables for Jules environment. Jules primarily uses: - Standard environment variables set in Jules config UI - .env files in repo root """ env = {} # Jules mostly relies on env vars set via UI, but check .env in common locations for path in [Path.cwd() / ".env", Path.home() / ".env"]: env.update(_parse_env_file(path)) return env def _load_dotenv_files() -> dict[str, str]: """ Load environment variables from standard .env file locations. Searches (in order, later overrides earlier): - ~/.env - ./.env - ./.env.local """ env = {} paths = [ Path.home() / ".env", Path.cwd() / ".env", Path.cwd() / ".env.local", ] for path in paths: env.update(_parse_env_file(path)) return env # ============================================================================= # Main API # ============================================================================= # @lat: [[infrastructure#Environment Detection]] def load_all(force_reload: bool = False) -> dict[str, str]: """ Load environment variables from all detected sources. Sources are loaded in priority order (later sources override earlier): 1. OS environment variables (lowest - can be overridden) 2. Platform-specific sources (Claude.ai, Claude Code, Codex, Jules) 3. Standard .env files 4. OS environment variables again (highest - explicit env vars win) Results are cached. Use force_reload=True to refresh. Args: force_reload: If True, clear cache and reload from all sources Returns: Dict of all loaded environment variables """ global _cache, _loaded_sources if _cache and not force_reload: return _cache.copy() _cache.clear() _loaded_sources.clear() env_type = detect_environment() # Base: OS environment (can be overridden by file configs) _cache.update(_load_os_environ()) _loaded_sources.append("os.environ") # Platform-specific sources if env_type == "claude.ai": _cache.update(_load_claude_ai_project()) _loaded_sources.append("claude.ai:/mnt/project/") if env_type in ("claude-code-desktop", "claude-code-web"): _cache.update(_load_claude_code()) _loaded_sources.append("claude-code:~/.claude/ + .claude/") if env_type == "codex": _cache.update(_load_codex()) _loaded_sources.append("codex:config.toml + shell exports + shell_snapshots") if env_type == "jules": _cache.update(_load_jules()) _loaded_sources.append("jules:.env") # Standard .env files (can override platform sources) dotenv_vars = _load_dotenv_files() if dotenv_vars: _cache.update(dotenv_vars) _loaded_sources.append("dotenv:.env files") # OS environ again - explicit env vars should win over all file sources # This ensures `export VAR=x && python ...` works as expected _cache.update(_load_os_environ()) return _cache.copy() def get_env( key: str, default: str | None = None, *, required: bool = False, validator: Callable[[str], bool] | None = None, ) -> str | None: """ Get an environment variable from any available source. Searches all sources (cached after first load): 1. OS environment variables 2. Platform-specific sources (Claude.ai project, Claude Code settings, etc.) 3. Standard .env files Args: key: Environment variable name default: Default value if not found required: If True, raise ValueError when not found validator: Optional function to validate the value Returns: The value, or default if not found Raises: ValueError: If required=True and key not found, or if validator fails Example: api_key = get_env("TURSO_TOKEN", required=True) port = get_env("PORT", default="8080") """ env = load_all() value = env.get(key) if value is None: if required: env_type = detect_environment() raise ValueError(_get_missing_key_message(key, env_type)) return default if validator and not validator(value): raise ValueError(f"Environment variable {key} failed validation") return value def load_env(path: str | Path) -> dict[str, str]: """ Load environment variables from a specific file. Automatically detects file format: - .env, .local, no extension: KEY=value format - .json: JSON with "env" block - .toml: TOML with shell_environment_policy.set - .txt: Single value file Args: path: Path to the environment file Returns: Dict of loaded variables (also merged into cache) """ global _cache path = Path(path) if path.suffix in ('.json',): loaded = _parse_json_settings(path) elif path.suffix in ('.toml',): loaded = _parse_toml_env(path) elif path.suffix in ('.txt',): key_name = path.stem.upper().replace("-", "_") loaded = _parse_single_value_file(path, key_name) else: loaded = _parse_env_file(path) _cache.update(loaded) return loaded def mask_secret(value: str, show_chars: int = 4) -> str: """ Mask a secret value for safe logging/display. Args: value: The secret value show_chars: Number of characters to show at start and end Returns: Masked value like "sk-a...xyz" Example: >>> mask_secret("sk-ant-api03-abcdef123456") 'sk-a...3456' """ if len(value) <= show_chars * 2: return "***" return f"{value[:show_chars]}...{value[-show_chars:]}" def get_loaded_sources() -> list[str]: """Return list of sources that were loaded.""" return _loaded_sources.copy() def debug_info() -> dict[str, Any]: """ Get debug information about the current environment. Returns: Dict with environment type, loaded sources, and masked key previews """ env = load_all() # Only show keys that look like credentials credential_patterns = ['KEY', 'TOKEN', 'SECRET', 'PASSWORD', 'CREDENTIAL', 'AUTH'] masked_creds = {} for key, value in env.items(): if any(pattern in key.upper() for pattern in credential_patterns): masked_creds[key] = mask_secret(value) return { "environment": detect_environment(), "sources": get_loaded_sources(), "credential_keys": list(masked_creds.keys()), "credentials_masked": masked_creds, } # ============================================================================= # Error Messages # ============================================================================= def _get_missing_key_message(key: str, env_type: str) -> str: """Generate a helpful error message for a missing environment variable.""" base_msg = f"Required environment variable '{key}' not found." hints = { "claude.ai": f""" {base_msg} In Claude.ai Projects, set this by: 1. Go to Project Settings > Knowledge 2. Upload a file named '{key.lower()}.txt' containing just the value, OR 3. Upload a '{key.lower()}.env' file with {key}=your_value_here """, "claude-code-desktop": f""" {base_msg} In Claude Code, set this by: 1. Add to ~/.claude/settings.json: {{"env": {{"{key}": "your_value_here"}}}} 2. Or set as environment variable: export {key}="your_value_here" """, "claude-code-web": f""" {base_msg} Set as environment variable: export {key}="your_value_here" """, "codex": f""" {base_msg} In OpenAI Codex, set this by: 1. Add to setup script (writes to ~/.bashrc): echo 'export {key}="your_value_here"' >> ~/.bashrc 2. Or in ~/.codex/config.toml: [shell_environment_policy] set = {{ {key} = "your_value_here" }} Note: Secrets are only available during setup phase, not agent phase. """, "jules": f""" {base_msg} In Jules (Google), set this by: 1. Go to Environment Settings in your Jules project 2. Add {key} as an environment variable or secret Alternatively, add to .env file in your repo root: {key}=your_value_here """, "unknown": f""" {base_msg} Set as environment variable: export {key}="your_value_here" Or add to .env file in working directory: {key}=your_value_here """ } return hints.get(env_type, hints["unknown"]) # ============================================================================= # CLI Interface # ============================================================================= if __name__ == "__main__": import sys if len(sys.argv) > 1: # Get specific key key = sys.argv[1] value = get_env(key) if value: print(value) else: print(f"Not found: {key}", file=sys.stderr) sys.exit(1) else: # Print debug info info = debug_info() print(f"Environment: {info['environment']}") print(f"Sources: {', '.join(info['sources'])}") print(f"Credential keys found: {', '.join(info['credential_keys']) or 'none'}") if info['credentials_masked']: print("\nMasked values:") for k, v in info['credentials_masked'].items(): print(f" {k}: {v}") -
__init__.py 0 B
-
-
.gitignore 202 B · in bundle
-
CHANGELOG.md 920 B
# configuring - Changelog All notable changes to the `configuring` skill are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [2.0.0] - 2026-01-22 ### Added - rename getting-env to configuring for Python import compatibility ## [2.0.0] - 2026-01-22 ### Changed - **BREAKING**: Renamed skill from `getting-env` to `configuring` for Python import compatibility - Updated all import examples to use `from configuring import ...` - Expanded scope to cover environment variables, secrets, and other opinionated configuration setups ### Migration Replace imports: ```python # Old from getting_env import get_env # New import sys sys.path.insert(0, '/path/to/claude-skills') from configuring import get_env ``` ## [1.0.1] - 2026-01-22 ### Added - extend env loading for codex and turso ## [1.0.0] - 2026-01-22 ### Added - Add/Update skill: getting-env -
README.md 201 B
# configuring Universal configuration management for AI agent environments. Loads environment variables, secrets, and config from Claude.ai, Claude Code, OpenAI Codex, Jules, and standard .env files. -
SKILL.md 3.3 KB
--- name: configuring description: Universal environment variable loader for AI agent environments. Loads secrets and config from Claude.ai, Claude Code, OpenAI Codex, Jules, and standard .env files. metadata: version: 2.0.0 replaces: api-credentials, getting-env --- # Configuring Unified configuration management across AI coding environments. Load environment variables, secrets, and other opinionated configuration setups from any AI coding platform. ## Quick Start ```python import sys sys.path.insert(0, '/path/to/claude-skills') # or wherever skills are installed from configuring import get_env, detect_environment # Get a variable (searches all sources automatically) token = get_env("TURSO_TOKEN", required=True) # With default port = get_env("PORT", default="8080") # What environment are we in? env = detect_environment() # "claude.ai", "claude-code-desktop", "codex", "jules", etc. ``` ## Supported Environments | Environment | Config Sources | |-------------|----------------| | **Claude.ai Projects** | `/mnt/project/*.env`, `/mnt/project/*-token.txt` | | **Claude Code** | `~/.claude/settings.json` (`env` block), `.claude/settings.json` | | **OpenAI Codex** | `~/.codex/config.toml`, setup script → `~/.bashrc`, `shell_snapshots/*.sh` | | **Jules** | Environment settings UI, `.env` in repo | | **Universal** | `os.environ`, `.env`, `.env.local` | ## API Reference ```python # Core get_env(key, default=None, *, required=False, validator=None) -> str | None load_env(path) -> dict[str, str] # Load specific file load_all(force_reload=False) -> dict # Load all sources # Utilities detect_environment() -> str # Current platform mask_secret(value, show_chars=4) -> str # Safe logging debug_info() -> dict # Troubleshooting get_loaded_sources() -> list[str] # What was checked ``` ## Credential File Formats **`.env` files** (KEY=value): ``` TURSO_TOKEN=eyJhbGciOiJFZERTQSI... EMBEDDING_API_KEY=sk-svcacct-... ``` **Single-value files** (`*-token.txt`, `*-key.txt`): ``` eyJhbGciOiJFZERTQSI... ``` Filename becomes key: `turso-token.txt` → `TURSO_TOKEN` **Claude Code settings.json**: ```json { "env": { "TURSO_TOKEN": "eyJhbGciOiJFZERTQSI..." } } ``` ## Priority Order Later sources override earlier: 1. OS environment variables 2. Platform-specific sources (detected automatically) 3. `.env` files in cwd 4. OS environment variables (again - explicit exports always win) ## Debugging ```python import sys sys.path.insert(0, '/path/to/claude-skills') from configuring import debug_info print(debug_info()) # {'environment': 'claude.ai', 'sources': ['os.environ', 'claude.ai:/mnt/project/'], ...} ``` CLI: ```bash cd /path/to/claude-skills/configuring python scripts/getting_env.py # Show debug info python scripts/getting_env.py TURSO_TOKEN # Get specific key ``` ## Migration from api-credentials / getting-env Replace: ```python # Old (api-credentials) from credentials import get_anthropic_api_key key = get_anthropic_api_key() # Old (getting-env) from getting_env import get_env key = get_env("ANTHROPIC_API_KEY") # New (configuring) import sys sys.path.insert(0, '/path/to/claude-skills') from configuring import get_env key = get_env("ANTHROPIC_API_KEY", required=True) ``` -
__init__.py 681 B
""" configuring: Universal configuration management for AI agent environments. Usage: import sys sys.path.insert(0, '/path/to/claude-skills') from configuring import get_env, detect_environment token = get_env("MY_TOKEN", required=True) env = detect_environment() # "claude.ai", "claude-code-desktop", etc. """ from .scripts.getting_env import ( __version__, debug_info, detect_environment, get_env, get_loaded_sources, load_all, load_env, mask_secret, ) __all__ = [ "__version__", "debug_info", "detect_environment", "get_env", "get_loaded_sources", "load_all", "load_env", "mask_secret", ]
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.