# Where Does Distilly Store Local Data? Directory Structure Explained

> Discover where Distilly stores local data and understand its directory structure. Learn about configuration files and skill metadata storage for efficient management.

- Repository: [Tianyi Zhou/distilly](https://github.com/titanwings/distilly)
- Tags: how-to-guide
- Published: 2026-09-10

---

**Distilly stores configuration files in `~/.distilly/` and skill metadata in hidden [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json) files within each skill's installation directory.**

Distilly, an open-source project under the titanwings organization, persists data across two distinct locations on a user's machine. Understanding where Distilly stores local data is essential for troubleshooting, backups, and manual configuration of its chat platform collectors. The project separates global configuration from skill-specific metadata using a predictable directory structure.

## Configuration Storage: The `~/.distilly/` Directory

All collector tools reference a constant called `CONFIG_PATH` that resolves to the `.distilly` folder in the user's home directory. As implemented in [`tools/slack_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/slack_auto_collector.py), [`tools/feishu_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/feishu_auto_collector.py), and [`tools/dingtalk_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/dingtalk_auto_collector.py), this path stores JSON configuration files for each respective platform.

The typical configuration files include:

- [`slack_config.json`](https://github.com/titanwings/distilly/blob/main/slack_config.json) – API tokens and workspace IDs for Slack
- [`feishu_config.json`](https://github.com/titanwings/distilly/blob/main/feishu_config.json) – Feishu platform credentials
- [`dingtalk_config.json`](https://github.com/titanwings/distilly/blob/main/dingtalk_config.json) – DingTalk integration settings
- [`config.json`](https://github.com/titanwings/distilly/blob/main/config.json) – Global CLI configuration and migration state

The directory is excluded from version control via the `.gitignore` entry `/.distilly/`, ensuring sensitive tokens are never committed.

### Loading Collector Configuration

To load a collector's configuration, the tools use standard path resolution:

```python
from pathlib import Path
import json

# Example: load Slack config

CONFIG_PATH = Path.home() / ".distilly" / "slack_config.json"

def load_slack_config() -> dict:
    if CONFIG_PATH.is_file():
        return json.loads(CONFIG_PATH.read_text())
    raise FileNotFoundError(f"Slack config not found at {CONFIG_PATH}")

slack_cfg = load_slack_config()
print(slack_cfg["bot_token"])

```

*Source:* [`tools/slack_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/slack_auto_collector.py)

## Skill Installation Metadata

When Distilly installs a skill, it writes a hidden metadata file alongside the skill definition. According to [`tools/install_generated_skill_common.py`](https://github.com/titanwings/distilly/blob/main/tools/install_generated_skill_common.py), the installer creates [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json) inside the skill directory (e.g., `~/.agents/skills/distilly/.distilly-install.json`).

This JSON file records:

- **preset**: The generation preset used (e.g., `"distilly.colleague.v1"`)
- **engine**: Object containing `{"name": "distilly"}`
- **generation**: Object tracking the generation engine

The installer also uses a temporary staging folder named `.distilly-install-<pid>` during the copy process, but only the final [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json) persists.

### Reading Skill Metadata

The metadata file enables downstream tools to verify how a skill was generated:

```python
from pathlib import Path
import json

def read_install_metadata(skill_dir: Path) -> dict:
    meta_path = skill_dir / ".distilly-install.json"
    return json.loads(meta_path.read_text())

# Suppose the skill was installed to ~/.agents/skills/distilly

skill_path = Path.home() / ".agents" / "skills" / "distilly"
metadata = read_install_metadata(skill_path)
print(metadata["preset"])               # e.g. "distilly.colleague.v1"

print(metadata["engine"]["name"])       # always "distilly"

```

*Source:* [`tools/install_generated_skill_common.py`](https://github.com/titanwings/distilly/blob/main/tools/install_generated_skill_common.py)

## Global Configuration Migration

During CLI upgrades, Distilly may create a top-level [`config.json`](https://github.com/titanwings/distilly/blob/main/config.json) inside `~/.distilly/` to track migration state. As shown in [`tests/test_config_migration.py`](https://github.com/titanwings/distilly/blob/main/tests/test_config_migration.py), this file contains a source field marking the origin of installed skills:

```json
{"source": "distilly"}

```

This allows the CLI to distinguish Distilly-generated skills from other sources.

## Enumerating Installed Skills

Since skills can reside in multiple locations (e.g., `~/.agents/skills/`, `~/.openclaw/workspace/skills/`, `~/.hermes/skills/openclaw-imports/`), tools scan for the [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json) marker to identify Distilly-managed installations:

```python
from pathlib import Path
import json

def list_distilly_skills(base_dirs):
    """Yield (skill_path, metadata) for each installed Distilly skill."""
    for base in base_dirs:
        for skill_dir in Path(base).glob("*"):
            meta_file = skill_dir / ".distilly-install.json"
            if meta_file.is_file():
                yield skill_dir, json.loads(meta_file.read_text())

bases = [
    Path.home() / ".agents" / "skills",
    Path.home() / ".openclaw" / "workspace" / "skills",
    Path.home() / ".hermes" / "skills" / "openclaw-imports",
]

for path, meta in list_distilly_skills(bases):
    print(f"{path} – preset: {meta['preset']}")

```

*Source:* Logic derived from [`tests/test_install_openclaw_and_codex.py`](https://github.com/titanwings/distilly/blob/main/tests/test_install_openclaw_and_codex.py) and related installer test suites.

## Summary

- Distilly stores **global configuration** in `~/.distilly/` (e.g., API keys for Slack, Feishu, and DingTalk collectors).
- Each installed skill contains a **hidden metadata file** ([`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json)) recording the generation preset and engine details.
- The `~/.distilly/` directory is excluded from Git via `.gitignore` to protect sensitive credentials.
- Skills may be installed to various agent directories, but all are identified by the presence of [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json).

## Frequently Asked Questions

### Where is the Distilly configuration directory located?

Distilly creates a hidden directory at `~/.distilly/` in the user's home folder. This location is defined by the `CONFIG_PATH` constant across collector tools in [`tools/slack_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/slack_auto_collector.py), [`tools/feishu_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/feishu_auto_collector.py), and [`tools/dingtalk_auto_collector.py`](https://github.com/titanwings/distilly/blob/main/tools/dingtalk_auto_collector.py).

### What files are created inside the skill installation directory?

Each skill directory contains [`SKILL.md`](https://github.com/titanwings/distilly/blob/main/SKILL.md) (the skill definition) and a hidden [`.distilly-install.json`](https://github.com/titanwings/distilly/blob/main/.distilly-install.json) file. The metadata file stores the preset used, engine name, and generation settings, as written by [`tools/install_generated_skill_common.py`](https://github.com/titanwings/distilly/blob/main/tools/install_generated_skill_common.py).

### How does Distilly handle configuration for different chat platforms?

Each platform uses a separate JSON file within `~/.distilly/`: [`slack_config.json`](https://github.com/titanwings/distilly/blob/main/slack_config.json), [`feishu_config.json`](https://github.com/titanwings/distilly/blob/main/feishu_config.json), and [`dingtalk_config.json`](https://github.com/titanwings/distilly/blob/main/dingtalk_config.json). These store platform-specific credentials like bot tokens and workspace IDs required by their respective auto-collectors.

### Is the `.distilly` directory tracked in version control?

No. The repository's `.gitignore` explicitly excludes `/.distilly/` to prevent accidental commits of sensitive configuration files containing API tokens and workspace credentials.