Detect Environment Variables
Overview
The detect-env-vars skill scans a project directory and builds a complete,
structured inventory of every environment variable in use. It reads from
three independent source types and merges the results:
| Source |
Examples |
What is captured |
| dotenv template |
.env.example, .env.sample, .env.dist |
Variable names, example values, comments, required vs. optional |
| config files |
*.yaml, *.yml, *.toml, *.json, *.ini |
${VAR} / $VAR interpolation references |
| source code |
*.py, *.js, *.ts, *.go, *.rb, *.sh |
os.environ, os.getenv, process.env, os.Getenv, ENV[] calls |
Quick Start
from harness_skills.env_var_detector import detect_env_vars
result = detect_env_vars(".") # scan current directory
print(result.unique_var_names) # sorted, de-duplicated names
print(result.total_vars_found) # total occurrences (not unique)
print(result.dotenv_files_found) # .env.example paths scanned
print(result.source_files_scanned) # number of source files inspected
# Granular scanning
from harness_skills.env_var_detector import (
scan_dotenv_file,
scan_config_file,
scan_source_file,
)
from pathlib import Path
root = Path(".")
# Parse a single .env.example
entries = scan_dotenv_file(Path(".env.example"), root)
for e in entries:
status = "required" if e.required else "optional"
print(f" [{status}] {e.name}={e.default_value} # {e.comment}")
# Extract ${VAR} refs from a YAML config
entries = scan_config_file(Path("config/app.yaml"), root)
# Scan a Python file for os.environ references
entries = scan_source_file(Path("app/settings.py"), root, "python")
Function Reference
detect_env_vars(path, *, skip_dirs, include_config, include_source)
Recursively scans path and returns an
EnvVarDetectionResult.
| Parameter |
Type |
Default |
Description |
path |
str | Path |
"." |
Root directory (or single file) to scan |
skip_dirs |
frozenset[str] |
None |
Extra directory names to skip (merged with built-in skip list: .git, .venv, node_modules, __pycache__, …) |
include_config |
bool |
True |
Set False to skip config-file scanning |
include_source |
bool |
True |
Set False to skip source-code scanning |
scan_dotenv_file(path, root)
Parses a single .env.example-style file.
KEY=value lines → required=True
# KEY=value lines → required=False (documented optional variable)
- Preceding comment lines are attached to the next variable as
comment
scan_config_file(path, root)
Scans any text-based config file for ${VAR_NAME} and $VAR_NAME patterns.
scan_source_file(path, root, language)
Scans a single source file. Supported language values:
"python", "javascript", "typescript", "go", "ruby", "shell".
Data Models
EnvVarEntry
class EnvVarEntry(BaseModel):
name: str # e.g. DATABASE_URL
source: EnvVarSource # dotenv_example | config_file | source_code
file_path: str # repo-relative path
line_number: int | None
default_value: str | None
comment: str | None
required: bool # False = commented-out optional variable
EnvVarDetectionResult
class EnvVarDetectionResult(HarnessResponse):
command: str # "detect-env-vars"
status: Status # always "passed"
scanned_path: str
env_vars: list[EnvVarEntry] # one per occurrence
unique_var_names: list[str] # sorted, de-duplicated names
dotenv_files_found: list[str]
config_files_found: list[str]
source_files_scanned: int
total_vars_found: int
Detection Patterns
Python
| Pattern |
Regex |
os.environ['KEY'] |
os\.environ\[['"]KEY['"] |
os.environ.get('KEY') |
os\.environ\.get\(['"]KEY['"] |
os.getenv('KEY') |
os\.getenv\(['"]KEY['"] |
JavaScript / TypeScript
| Pattern |
Regex |
process.env.KEY |
process\.env\.KEY |
process.env['KEY'] |
process\.env\[['"]KEY['"] |
Go
| Pattern |
Regex |
os.Getenv("KEY") |
os\.Getenv\("KEY"\) |
os.LookupEnv("KEY") |
os\.LookupEnv\("KEY"\) |
Ruby
| Pattern |
Regex |
ENV['KEY'] |
ENV\[['"]KEY['"] |
ENV.fetch('KEY') |
ENV\.fetch\(['"]KEY['"] |
Config files
| Pattern |
Notes |
${VAR_NAME} |
Shell-style brace substitution |
$VAR_NAME |
Bare dollar (min 3 chars to reduce false-positives) |
Key Files
| Path |
Purpose |
harness_skills/env_var_detector.py |
Core detection logic — three scanners + detect_env_vars() |
harness_skills/models/env_vars.py |
Pydantic models: EnvVarEntry, EnvVarDetectionResult, EnvVarSource |
tests/test_env_var_detector.py |
Full test suite (40+ test cases) |
.claude/commands/detect-env-vars.md |
Agent slash-command documentation |
Notes
- The skill is read-only — it never modifies project files.
- Variables found in multiple files produce one
EnvVarEntry each (the env_vars
list may contain duplicates across files); unique_var_names is always de-duplicated.
- The built-in skip list prevents scanning
.git, .venv, node_modules,
__pycache__, dist, build, .claw-forge, and .tox.
- Related skills:
/detect-api-style, /module-boundaries, /harness:context.
1---2name: detect-env-vars3description: Codebase analysis skill that detects environment variable patterns from .env.example / .env.sample template files, YAML/TOML/JSON/INI config files that use ${VAR} interpolation, and source code references (os.environ, os.getenv, process.env, os.Getenv, ENV[]) across Python, JavaScript, TypeScript, Go, Ruby, and Shell. Produces a structured, de-duplicated inventory of every environment variable the project depends on. Use when: (1) onboarding to a new project and need to know what env vars to set, (2) auditing which services / files read a given variable, (3) generating .env documentation, (4) validating that .env.example is complete against actual code usage. Triggers on: env var, environment variable, .env.example, process.env, os.environ, os.getenv, config variables, secret inventory, required environment, dotenv.4---56# Detect Environment Variables78## Overview910The `detect-env-vars` skill scans a project directory and builds a complete,11structured inventory of every environment variable in use. It reads from12**three independent source types** and merges the results:1314| Source | Examples | What is captured |15|--------|----------|------------------|16| **dotenv template** | `.env.example`, `.env.sample`, `.env.dist` | Variable names, example values, comments, required vs. optional |17| **config files** | `*.yaml`, `*.yml`, `*.toml`, `*.json`, `*.ini` | `${VAR}` / `$VAR` interpolation references |18| **source code** | `*.py`, `*.js`, `*.ts`, `*.go`, `*.rb`, `*.sh` | `os.environ`, `os.getenv`, `process.env`, `os.Getenv`, `ENV[]` calls |1920---2122## Quick Start2324```python25from harness_skills.env_var_detector import detect_env_vars2627result = detect_env_vars(".") # scan current directory28print(result.unique_var_names) # sorted, de-duplicated names29print(result.total_vars_found) # total occurrences (not unique)30print(result.dotenv_files_found) # .env.example paths scanned31print(result.source_files_scanned) # number of source files inspected32```3334```python35# Granular scanning36from harness_skills.env_var_detector import (37 scan_dotenv_file,38 scan_config_file,39 scan_source_file,40)41from pathlib import Path4243root = Path(".")4445# Parse a single .env.example46entries = scan_dotenv_file(Path(".env.example"), root)47for e in entries:48 status = "required" if e.required else "optional"49 print(f" [{status}] {e.name}={e.default_value} # {e.comment}")5051# Extract ${VAR} refs from a YAML config52entries = scan_config_file(Path("config/app.yaml"), root)5354# Scan a Python file for os.environ references55entries = scan_source_file(Path("app/settings.py"), root, "python")56```5758---5960## Function Reference6162### `detect_env_vars(path, *, skip_dirs, include_config, include_source)`6364Recursively scans *path* and returns an65[`EnvVarDetectionResult`](#envvardetectionresult).6667| Parameter | Type | Default | Description |68|-----------|------|---------|-------------|69| `path` | `str \| Path` | `"."` | Root directory (or single file) to scan |70| `skip_dirs` | `frozenset[str]` | `None` | Extra directory names to skip (merged with built-in skip list: `.git`, `.venv`, `node_modules`, `__pycache__`, …) |71| `include_config` | `bool` | `True` | Set `False` to skip config-file scanning |72| `include_source` | `bool` | `True` | Set `False` to skip source-code scanning |7374### `scan_dotenv_file(path, root)`7576Parses a single `.env.example`-style file.7778- `KEY=value` lines → `required=True`79- `# KEY=value` lines → `required=False` (documented optional variable)80- Preceding comment lines are attached to the next variable as `comment`8182### `scan_config_file(path, root)`8384Scans any text-based config file for `${VAR_NAME}` and `$VAR_NAME` patterns.8586### `scan_source_file(path, root, language)`8788Scans a single source file. Supported `language` values:89`"python"`, `"javascript"`, `"typescript"`, `"go"`, `"ruby"`, `"shell"`.9091---9293## Data Models9495### `EnvVarEntry`9697```python98class EnvVarEntry(BaseModel):99 name: str # e.g. DATABASE_URL100 source: EnvVarSource # dotenv_example | config_file | source_code101 file_path: str # repo-relative path102 line_number: int | None103 default_value: str | None104 comment: str | None105 required: bool # False = commented-out optional variable106```107108### `EnvVarDetectionResult`109110```python111class EnvVarDetectionResult(HarnessResponse):112 command: str # "detect-env-vars"113 status: Status # always "passed"114 scanned_path: str115 env_vars: list[EnvVarEntry] # one per occurrence116 unique_var_names: list[str] # sorted, de-duplicated names117 dotenv_files_found: list[str]118 config_files_found: list[str]119 source_files_scanned: int120 total_vars_found: int121```122123---124125## Detection Patterns126127### Python128| Pattern | Regex |129|---------|-------|130| `os.environ['KEY']` | `os\.environ\[['"]KEY['"]` |131| `os.environ.get('KEY')` | `os\.environ\.get\(['"]KEY['"]` |132| `os.getenv('KEY')` | `os\.getenv\(['"]KEY['"]` |133134### JavaScript / TypeScript135| Pattern | Regex |136|---------|-------|137| `process.env.KEY` | `process\.env\.KEY` |138| `process.env['KEY']` | `process\.env\[['"]KEY['"]` |139140### Go141| Pattern | Regex |142|---------|-------|143| `os.Getenv("KEY")` | `os\.Getenv\("KEY"\)` |144| `os.LookupEnv("KEY")` | `os\.LookupEnv\("KEY"\)` |145146### Ruby147| Pattern | Regex |148|---------|-------|149| `ENV['KEY']` | `ENV\[['"]KEY['"]` |150| `ENV.fetch('KEY')` | `ENV\.fetch\(['"]KEY['"]` |151152### Config files153| Pattern | Notes |154|---------|-------|155| `${VAR_NAME}` | Shell-style brace substitution |156| `$VAR_NAME` | Bare dollar (min 3 chars to reduce false-positives) |157158---159160## Key Files161162| Path | Purpose |163|------|---------|164| `harness_skills/env_var_detector.py` | Core detection logic — three scanners + `detect_env_vars()` |165| `harness_skills/models/env_vars.py` | Pydantic models: `EnvVarEntry`, `EnvVarDetectionResult`, `EnvVarSource` |166| `tests/test_env_var_detector.py` | Full test suite (40+ test cases) |167| `.claude/commands/detect-env-vars.md` | Agent slash-command documentation |168169---170171## Notes172173- The skill is **read-only** — it never modifies project files.174- Variables found in multiple files produce one `EnvVarEntry` each (the `env_vars`175 list may contain duplicates across files); `unique_var_names` is always de-duplicated.176- The built-in skip list prevents scanning `.git`, `.venv`, `node_modules`,177 `__pycache__`, `dist`, `build`, `.claw-forge`, and `.tox`.178- Related skills: `/detect-api-style`, `/module-boundaries`, `/harness:context`.