# Using a Per-Project .env Config File in last30days-skill: Complete Setup Guide

> Master per-project .env config files in last30days-skill. Discover how the framework merges settings and prioritizes environment variables for seamless project management.

- Repository: [Matt Van Horn/last30days-skill](https://github.com/mvanhorn/last30days-skill)
- Tags: how-to-guide
- Published: 2026-03-25

---

**The last30days-skill framework automatically discovers `.claude/last30days.env` files by walking up the directory tree from your current working directory, merging per-project settings with global configuration while always allowing environment variables to take precedence.**

Managing API keys and feature flags across multiple projects becomes cumbersome when relying solely on global environment variables. The **last30days-skill** repository solves this by supporting a **per-project .env config file** that lives alongside your code, ensuring credentials stay portable and version-controllable without polluting your system-wide configuration. This approach uses a hierarchical loading system where project-specific values override global settings while remaining subservient to shell-level environment overrides.

## Configuration Priority Hierarchy

The configuration system in [`scripts/lib/env.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/lib/env.py) implements a three-tier precedence model:

- **Environment variables** (`os.environ`) — Highest priority, checked first via `os.getenv`
- **Per-project file** (`.claude/last30days.env`) — Discovered dynamically, overrides global settings
- **Global file** (`~/.config/last30days/.env`) — Lowest priority, loaded from `CONFIG_FILE`

When `get_config()` builds the final dictionary, it resolves each key using `os.environ.get(...) or merged_env.get(...)`, ensuring environment variables automatically supersede both file-based sources.

## How Per-Project Discovery Works

The framework locates your project configuration through directory traversal rather than fixed paths.

### The Discovery Algorithm

When [`scripts/last30days.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/last30days.py) invokes `env.get_config()` (lines 37-38), the following occurs in [`scripts/lib/env.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/lib/env.py):

1. **Directory walking**: `_find_project_env()` (lines 198-211) walks upward from the current working directory through each parent directory
2. **File detection**: It searches for `.claude/last30days.env` in every directory level
3. **Parsing**: Upon finding the file, `load_env_file` (lines 66-87) parses key-value pairs into a dictionary
4. **Merging**: Project values override the global configuration loaded from `~/.config/last30days/.env`
5. **Final resolution**: The configuration dictionary populates each key using environment variables first, then merged file values

This algorithm ensures that running the skill from any subdirectory of your project still locates the root configuration file.

## Creating a Per-Project .env Config File

Place your configuration at the project root using the required path structure:

```text
your-project/
├── .claude/
│   └── last30days.env
└── src/

```

The file supports standard environment variable syntax:

```text
OPENAI_API_KEY=sk-xxxx-your-key
BRAVE_API_KEY=brave-key-123
XAI_API_KEY=xai-key-abc
GEMINI_API_KEY=gemini-key-def
BSKY_HANDLE=alice.bsky.social
BSKY_APP_PASSWORD=super-secret-pw

```

Only define the keys required for that specific project. Missing keys automatically fall back to the global file or environment variables according to the precedence hierarchy.

## Practical Usage Examples

### Running the Skill from a Project Directory

Execute commands from any subdirectory of your repository:

```bash
cd ~/projects/news-analysis
python3 scripts/last30days.py "AI in education" --emit=compact

```

Because [`scripts/last30days.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/last30days.py) calls `env.get_config()` at lines 37-38, the per-project file loads automatically before any API calls execute.

### Accessing Configuration in Custom Code

Import the environment module to retrieve parsed values:

```python
from lib import env

cfg = env.get_config()
print("OpenAI key:", cfg["OPENAI_API_KEY"])

# Prints value from .claude/last30days.env unless overridden by shell variable

```

### Overriding for Single Runs

Temporarily bypass project settings using shell exports:

```bash
OPENAI_API_KEY=sk-override-123 python3 scripts/last30days.py "Quantum computing"

```

The exported value supersedes both per-project and global file configurations immediately.

## Testing the Configuration Behavior

The repository includes [`tests/test_env_project.py`](https://github.com/mvanhorn/last30days-skill/blob/main/tests/test_env_project.py) to certify the loading logic. This test suite validates:

- Directory traversal correctly discovers project files
- Project values override global configuration
- Environment variables override project values
- `_CONFIG_SOURCE` accurately reports the configuration origin (project, global, or environment)

Run the validation suite:

```bash
pytest -q tests/test_env_project.py

```

## Summary

- **Per-project .env files** in `last30days-skill` use the path `.claude/last30days.env` discovered by walking up the directory tree
- **Configuration priority** follows: Environment variables > Per-project file > Global file (`~/.config/last30days/.env`)
- **Core implementation** resides in [`scripts/lib/env.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/lib/env.py), specifically `_find_project_env()` (lines 198-211) and `get_config()`
- **Automatic loading** occurs when [`scripts/last30days.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/last30days.py) initializes via `env.get_config()` (lines 37-38)
- **Override capability** allows temporary environment variables to supersede file-based configuration for debugging or secrets management

## Frequently Asked Questions

### Where should I place the per-project .env config file?

Create a directory named `.claude` in your project root and place the file `last30days.env` inside it. The `_find_project_env()` function in [`scripts/lib/env.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/lib/env.py) searches for this specific path while walking upward from your current working directory, allowing the skill to find configuration even when run from subdirectories.

### Can I use a per-project config file alongside global configuration?

Yes. The system merges both sources, with the per-project file at `.claude/last30days.env` overriding values from the global `~/.config/last30days/.env`. You can define common defaults globally and project-specific overrides locally, reducing duplication while maintaining flexibility.

### How do I temporarily override a value from my per-project file without editing it?

Export the variable in your shell before running the command. The configuration resolution logic in [`scripts/lib/env.py`](https://github.com/mvanhorn/last30days-skill/blob/main/scripts/lib/env.py) checks `os.environ` before falling back to file-based values, so `OPENAI_API_KEY=sk-new-key python3 scripts/last30days.py "query"` immediately supersedes any file-stored API key for that single execution.

### Does the skill tell me which configuration source it used?

Yes. The `get_config()` function populates a `_CONFIG_SOURCE` field in the returned dictionary indicating whether values originated from the project file, global file, or environment only. This metadata field helps debug configuration precedence issues when multiple sources define the same keys.