githubinferredactive
obsistant
provenance:github:aturcati/obsistant
An opinionated automation layer for your Obsidian vault that combines deterministic CLI tools and AI agents
README
# Obsistant
obsistant is an opinionated automation layer for your Obsidian vault that combines **deterministic CLI tools** and **AI agents**.
It bulk-organizes notes, normalizes frontmatter, backs up and restores your vault, and runs agentic workflows (deep research, calendar summaries) — all while staying predictable, reversible, and scoped to your vault.
## Highlights
- **AI agents via CLI**: Run deep research or calendar/weekly-summary flows directly from the command line.
- **Deterministic processing**: Tag extraction, folder routing, meeting-note normalization, quick-notes triage.
- **Safety first**: Automatic backups, `--dry-run` previews, idempotent transforms, strict vault confinement.
- **RAG-ready**: Optional Qdrant integration for semantic search over your notes.
---
## Agentic workflows
obsistant ships with CrewAI-powered agent flows. Run them from the CLI:
### Deep Research (`research`)
Turn an open-ended question into a structured research note.
```bash
obsistant research /path/to/vault "How should I structure my Obsidian vault?"
```
- **Output**: `00-Quick Notes/YYYYMMDD_Shortened_Query.md` with `tags: [research]`.
- Requires `OPENAI_API_KEY` in `.obsistant/.env`.
### Calendar / Weekly Summary (`calendar`)
Fetch upcoming calendar events, enrich them, and write a weekly summary.
```bash
# First, authenticate with Google Calendar (one-time setup)
obsistant calendar-login /path/to/vault
# Then generate the weekly summary
obsistant calendar /path/to/vault
```
- **Output**: `10-Meetings/Weekly Summaries/Weekly_Events_Summary.md` with `tags: [weekly-summary, meeting]`.
- Requires Google OAuth credentials in `.obsistant/credentials.json` and `OPENAI_API_KEY` in `.obsistant/.env`.
### Environment setup for agents
Create `.obsistant/.env` in your vault:
```bash
OPENAI_API_KEY=your-openai-key
```
Agent memory is stored in `.obsistant/storage/` (auto-created). You can customize work-event context by editing `.obsistant/storage/work/knowledge/user_preference.md`.
<details>
<summary><strong>Advanced: programmatic invocation</strong></summary>
```python
from pathlib import Path
from obsistant.agents.deep_research_flow.src.deep_research_flow import main as research
from obsistant.agents.calendar_flow.src.calendar_flow import main as calendar
vault = Path("/path/to/vault")
# Deep research
research.kickoff(vault_path=vault, user_query="Your question here")
# Calendar summary
calendar.kickoff(vault_path=vault, meetings_folder="10-Meetings")
```
</details>
---
## CLI commands
All commands share these options (where applicable):
| Option | Description |
|--------|-------------|
| `-n, --dry-run` | Preview changes without writing |
| `-b, --backup-ext` | Backup extension (default `.bak`) |
| `-f, --format` | Format markdown via `mdformat` |
| `-v, --verbose` | Verbose logging |
If no subcommand is provided, `obsistant` falls back to `process`.
### `init` — Initialize a vault
```bash
obsistant init /path/to/vault
```
Creates the folder structure and `.obsistant/config.yaml`.
Options: `--overwrite-config`, `--skip-folders`.
### `process` — Main processing pass
```bash
obsistant process /path/to/vault
```
- Extracts `#tags` into frontmatter.
- Normalizes frontmatter fields (`created`, `modified`, `tags`, etc.).
### `meetings` — Organize meeting notes
```bash
obsistant meetings /path/to/vault
```
- Renames files using `meetings.filename_format` (default: `YYYY-MM-DD Title`).
- Ensures `meeting` tag is present.
- Archives old meetings based on `archive_weeks`.
### `notes` — Organize notes by tags
```bash
obsistant notes /path/to/vault
```
Moves notes into `20-Notes/` subfolders based on their primary tag (e.g., `products/app` → `20-Notes/products/app/`).
### `quick-notes` — Triage quick-capture inbox
```bash
obsistant quick-notes /path/to/vault
```
- Routes `meeting`-tagged files to Meetings folder.
- Routes other tagged files to Notes subfolders.
### `backup` / `restore` / `clear-backups`
```bash
obsistant backup /path/to/vault --backup-name "pre-migration"
obsistant restore /path/to/vault [--file specific.md]
obsistant clear-backups /path/to/vault
```
### `qdrant` — Vector database for RAG
```bash
obsistant qdrant start /path/to/vault # Start Qdrant in Docker
obsistant qdrant stop /path/to/vault # Stop Qdrant
obsistant qdrant ingest /path/to/vault # Ingest notes into Qdrant
```
Ingest options: `--collection`, `--include-pdfs`, `--recreate-collection`, `--dry-run`.
Requires Docker and `OPENAI_API_KEY` in `.obsistant/.env`.
---
## The `.obsistant` folder
All configuration and agent state lives here:
```
Your-Vault/
├── .obsistant/
│ ├── config.yaml # Main configuration
│ ├── .env # API keys and secrets
│ ├── storage/ # CrewAI agent memory (auto-managed)
│ ├── qdrant_storage/ # Qdrant data (auto-managed)
│ ├── credentials.json # Google OAuth credentials
│ └── token.json # Google OAuth token
├── 00-Quick Notes/
├── 10-Meetings/
├── 20-Notes/
├── 30-Guides/
├── 40-Vacations/
└── 50-Files/
```
**Safe to edit**: `config.yaml`, `.env`, crew knowledge files under `storage/*/knowledge/`.
**Auto-managed**: `storage/`, `qdrant_storage/`, `credentials.json`, `token.json`.
You can delete `storage/` or `qdrant_storage/` to reset agent memory or the vector index.
### Configuration (`config.yaml`)
Priority: CLI args > `config.yaml` > built-in defaults.
```yaml
vault:
folders:
quick_notes: "00-Quick Notes"
meetings: "10-Meetings"
notes: "20-Notes"
guides: "30-Guides"
vacations: "40-Vacations"
files: "50-Files"
tags:
target_tags: [ai, hr, software, projects, events]
ignored_tags: [planqc]
tag_regex: "(?<!\\w)#([\\w/-]+)(?=\\s|$)"
meetings:
filename_format: "YYYY-MM-DD Title"
archive_weeks: 2
auto_tag: "meeting"
processing:
backup_ext: ".bak"
date_formats: ["%Y-%m-%d", "%Y/%m/%d"] # ... and more
date_patterns: ["(\\d{4}[-/]\\d{1,2}[-/]\\d{1,2})"] # ... and more
granola:
link_pattern: "Chat with meeting transcript:\\s*\\[([^\\]]+)\\]\\([^\\)]+\\)"
calendar:
calendars: { primary: "primary" }
credentials_path: ".obsistant/credentials.json"
token_path: ".obsistant/token.json"
```
Omit any section to use defaults.
---
## Tags and frontmatter
- **Primary tags**: configurable via `tags.target_tags` (defaults: `ai`, `hr`, `software`, `projects`, `events`).
- **Hierarchies**: `software/tooling` → `20-Notes/software/tooling/`.
- **Special tags**: `meeting` (meeting notes), `research` (research flow output).
Frontmatter field order: `created` → `modified` → `meeting-transcript` → `tags` → other fields.
Tag extraction uses regex `(?<!\w)#([\w/-]+)`, ignores code blocks and malformed tags, and de-duplicates with existing frontmatter tags.
---
## Safety
- **Dry runs**: `--dry-run` on all content-modifying commands.
- **Backups**: Files backed up with `.bak` extension before modification.
- **Idempotent**: Multiple runs converge to stable state.
- **Vault-scoped**: Operations never escape the vault root.
---
## Installation
```bash
# Using uv (recommended)
uv add obsistant
# or globally
uv tool install git+https://github.com/aturcati/obsistant.git
# Using pip
pip install obsistant
# From source
git clone https://github.com/aturcati/obsistant.git && cd obsistant && uv sync --dev
```
Optional extras: `mdformat-gfm` (table formatting), `pdfplumber` (PDF ingestion).
---
## Development
```bash
uv sync --dev
uv run pytest
uv run ruff check .
uv run ruff format .
```
---
## Project structure
```
obsistant/
├── obsistant/
│ ├── cli.py # CLI entrypoint
│ ├── core/ # Frontmatter, tags, dates, formatting
│ ├── vault/ # Vault orchestration and init
│ ├── meetings/ # Meeting processing
│ ├── notes/ # Notes organization
│ ├── backup/ # Backup/restore
│ ├── config/ # Config schema and loading
│ └── agents/ # CrewAI agent flows
├── tests/
├── AGENTS
[truncated…]PUBLIC HISTORY
First discoveredMar 21, 2026
IDENTITY
inferred
Identity inferred from code signals. No PROVENANCE.yml found.
Is this yours? Claim it →METADATA
platformgithub
first seenOct 2, 2025
last updatedMar 3, 2026
last crawled4 days ago
version—
README BADGE
Add to your README:
