<p align="center">
<img src="assets/logo.png" alt="ShardMind" width="300">
</p>
<h1 align="center">ShardMind</h1>
<p align="center">
<a href="https://www.typescriptlang.org"><img src="https://img.shields.io/badge/typescript-5.0%2B-3178C6" alt="TypeScript"></a>
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-22%2B-339933" alt="Node"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
</p>
<p align="center"><b>A package manager for Obsidian vault templates.</b><br>Install, configure, upgrade, and diagnose AI-augmented vaults.</p>
---
## The Problem
Vault templates ship as monolithic git clones. Fork authors diverge from upstream with no path back. Users who edit rendered files lose the ability to pull template updates. Backstage (Spotify, 30k+ stars) has had "propagate template updates to existing projects" as an open feature request since November 2022. Cookiecutter, create-react-app, and Yeoman all gave up on upgrades.
## The Solution
ShardMind separates templates from values. Users only ever edit their values. Templates upgrade cleanly. The system knows which files the user touched and which are still pristine.
```
shardmind install github:breferrari/obsidian-mind
Quick Setup
Your name: Brenno Ferrari
Organization: Independent
Purpose: Engineering
QMD enabled: Yes
Your vault will include:
brain/ Goals, memories, patterns, decisions
work/ Active projects, archive
reference/ Codebase knowledge, architecture
org/ People, teams
perf/ Brag doc, competencies, reviews
Installed. 51 files. Open in Obsidian and run: claude
```
```
shardmind update
breferrari/obsidian-mind v5.1.0 -> v6.0.0
43 files unchanged (silent re-render)
2 files updated (no conflict)
1 file needs your review:
CLAUDE.md — you added a custom section
[Accept new] [Keep mine] [Open in editor] [Skip]
Updated. 3 replaced · 40 unchanged · 2 merged · 1 reviewed.
```
---
## Get started
```bash
npm install -g shardmind
shardmind --version
```
Node 22+ required.
```bash
# Makes ./obsidian-mind and installs the vault into it, as git clone does
shardmind install github:breferrari/obsidian-mind # interactive wizard
shardmind install --defaults github:breferrari/obsidian-mind # accept all defaults
shardmind install github:breferrari/obsidian-mind my-vault # into ./my-vault instead
shardmind install github:breferrari/obsidian-mind . # into the current folder
# In an existing vault you cloned before shardmind support
shardmind adopt github:breferrari/obsidian-mind
```
Status check at any time: `shardmind` (no args). Upgrade later: `shardmind update`. Full command reference below.
---
## How It Works
### Three-State Model
Adapted from Terraform and chezmoi. Templates define the desired state. The vault is the actual state. A state file tracks what was rendered and when.
- **Managed** files (user never edited) upgrade silently
- **Modified** files (user edited) get a three-way diff in the TUI
- **User** files (created by the user, not from any template) are never touched
### Values vs Modules
**Values** control what goes *inside* files — your name, org, vault purpose. 4 values, 30 seconds.
**Modules** control what files *exist* — perf tracking, incident management, 1:1 notes. Toggle during install. Defaults all included.
### Signals
Classification signals define how the vault routes content. Core signals (DECISION, WIN, PATTERN) always apply. Module-gated signals (INCIDENT, 1:1) only apply if their module is included. The classification hook reads signals from the schema at runtime — fully data-driven.
---
## Commands
Five commands. Three that write (`install`, `update`, `adopt`), two that read (status, `validate`). Status-first: `shardmind` with no args is the diagnostic, not a menu.
```bash
# Status (read-only, default)
shardmind # Quick status + drift summary
shardmind --verbose # Full diagnostics (values, modules, files, env)
shardmind --json # Status as one JSON document (uncapped file lists)
shardmind --version # Print package version
# Install a shard into a new folder named after it, or [folder] ("." for the current one)
shardmind install <shard> [folder]
--values <file> # Prefill answers from YAML
--defaults # Use schema defaults; skip wizard (Invariant 1 mode)
--yes # Skip every prompt and accept its default
--dry-run # Show plan, write nothing
--verbose # Per-file rendering progress
--force # Reinstall over an existing install; overwrite colliding files, no backup
--skip-hooks # Run no hook; each is reported skipped
# Add an overlay shard to this vault, beside the one installed (composition)
shardmind add <shard>
--values <file> # Prefill the overlay's answers from YAML
--defaults # Use the overlay's schema defaults; skip wizard
--yes # Skip every prompt and accept its default
--dry-run # Show plan, write nothing
--verbose # Per-file rendering progress
--skip-hooks # Run no hook; each is reported skipped
# Upgrade the installed shard
shardmind update
--release <tag> # Pin to a specific release tag (stable or prerelease)
--include-prerelease # Widen latest-release resolution to prereleases
--yes # Skip every prompt; keep your version on every conflict
--adopt-preexisting # Track a file you keep at a path the new version adds, as your modified copy
--dry-run # Plan without writing
--verbose # Per-file action history
--skip-hooks # Run no hook; each is reported skipped
--json # One JSON document: what the run did; with --dry-run, the per-file plan
# Retrofit shardmind into an existing shard clone (pre-shardmind era)
shardmind adopt <shard>
--values <file> # Prefill answers from YAML
--yes # Skip every prompt; keep your version of every differing file
--mode <mode> # Settle differing files in bulk: keep-all-mine, use-all-theirs,
# auto-merge (experimental), decide-per-file
--from-version <v> # The release you cloned: follow its renames, and update files you never changed
--dry-run # Preview classification + plan
--verbose # Per-file action history
--skip-hooks # Run no hook; each is reported skipped
--json # One JSON document: what the run did; with --dry-run, the per-file plan
# Check a shard before you publish it (author-facing, read-only)
shardmind validate [dir|shard]
--values <file> # Render the templates with these values
--json # The findings as one JSON document
--verbose # Each finding's hint
# On every command
--no-update-check # Skip the once-a-day check for a newer shardmind on npm
```
`--yes` answers the prompts each command can settle safely on its own, and the answer differs by command, by design:
- `install`: each value takes its schema default. A value with no default needs `--values`, else `VALUES_MISSING`. Reinstalling over an existing install still asks, or needs `--force`.
- `update`: your version wins wherever your edits conflict with the shard's. A new release that adds a required value needs it in `shard-values.yaml` first, else `VALUES_MISSING`.
- `adopt`: your version of every file that differs is kept (`keep-all-mine`, unless `--mode` says otherwise).
Files you never changed still take the new release (`update`, and `adopt --from-version`).
### Driving shardmind from a script or agent
`--values <file>` is enough on its own without a terminal: the answers are already on disk, so the wizard is skipped rather than rendered. Without values and without a TTY the command **refuses** (`INSTALL_/ADOPT_NON_INTERACTIVE_WITHOUT_VALUES`) rather than quietly recording schema defaults as though you had chosen them.
`--json` makes `shardmind` (status), `update`, `adopt` and `validate` emit exactly one JSON document on stdout and nothing else, so `JSON.parse(stdout)` needs no stripping. Every document carries `schemaVersion`, `command`, and `ok`; a failure adds `error` (`code`, `message`, `hint`, `stack` for an unexpected bug and otherwise `null`, and `details` for a `state.json` from a newer ShardMind) and exits non-zero, so `$?` and the body agree. What a document promises across versions is in [`docs/OPERATIONS.md`](docs/OPERATIONS.md#json-documents-and-semver). `install` has no `--json`: run it with `--values` or `--defaults` and read its exit code.
Paired with `--dry-run` you get the **per-file plan** rather than summary counts — path, action or classification, and both hashes where a file diverges — which is what makes choosing a bulk `--mode` safe to automate:
```bash
shardmind adopt <shard> --values v.yaml --dry-run --json |
jq -r '.result.files[] | select(.classification == "differs") | .path'
```
The list is uncapped: the terminal views sample long lists, the document never does.
On `update` and `adopt`, `--dry-run --json` is the plan, and `--json` alone runs the command and answers with what it did: every file's outcome, the backup folder and the hooks. It never prompts: conflicts keep your version (as `--yes` does), and anything else needs the flags its dry run needs (#348, see [`docs/OPERATIONS.md`](docs/OPERATIONS.md)). It always answers with one document: an up-to-date vault gets a document marked `upToDate`, and an update that would need answers (new optional modules, removed files you edited) fails with `UPDATE_JSON_NEEDS_ANSWERS` until you add `--yes`. `shardmind --json` is the status report as a document: whether the vault is managed, installed vs latest version, and every modified, missing and orphaned file (with `--verbose`, line counts per modified file). See [`docs/ARCHITECTURE.md §10.3a`](docs/ARCHITECTURE.md).
`adopt --mode auto-merge` is **experimental**: a best-effort union of your lines and the shard's that keeps lines the shard deleted and can duplicate lines. It sits outside the semver promise and warns on stderr when used.
`adopt` is the migration path for users who cloned a shard repo before shardmind support existed — see [`docs/ARCHITECTURE.md §10.5a`](docs/ARCHITECTURE.md) for the flow. `--defaults` on install is the determinism flag: paired with the same shard ref, two runs on different machines produce byte-equivalent vaults (Invariant 1).
### Shard references
```
breferrari/obsidian-mind # Registry, latest stable
breferrari/obsidian-mind@6.0.0 # Registry, exact version
github:breferrari/obsidian-mind # Direct GitHub, latest stable release
github:breferrari/obsidian-mind@6.0.0 # Direct GitHub, exact tag
github:breferrari/obsidian-mind#main # Branch
github:breferrari/obsidian-mind#a1b2c3d # Commit SHA
```
### Common patterns
```bash
# Deterministic install for CI / fleet rollout — byte-equivalent to git clone
shardmind install --defaults github:breferrari/obsidian-mind
# Pre-canned values for a team
shardmind install --values team-defaults.yaml github:breferrari/obsidian-mind
# Pin an update to a specific release
shardmind update --release 6.0.1
# Pull a beta without affecting the latest dist-tag
shardmind update --include-prerelease
# Convert an existing pre-shardmind clone into a managed shard
shardmind adopt github:breferrari/obsidian-mind
```
### Stop using shardmind on a vault
Delete `.shardmind/` and `shard-values.yaml`. The vault keeps working in Obsidian and your agent exactly as before: shardmind is additive, not load-bearing. Until `shardmind eject` lands (planned, [#83](https://github.com/breferrari/shardmind/issues/83)), those two deletes are the way.
Wrapper scripts, CI pipelines, enterprise deployments — see [`docs/OPERATIONS.md`](docs/OPERATIONS.md) for exit codes, environment variables (`GITHUB_TOKEN`, `SHARDMIND_GITHUB_API_BASE`, `SHARDMIND_REGISTRY_INDEX_URL`), file locations, and signal handling. Every typed error code with cause + remedy: [`docs/ERRORS.md`](docs/ERRORS.md).
---
## Technology
Built with [Pastel](https://github.com/vadimdemedes/pastel) (Next.js for CLIs), [Ink](https://github.com/vadimdemedes/ink) (React for terminals), and [Nunjucks](https://mozilla.github.io/nunjucks/) (Jinja2 for JavaScript).
| Layer | Stack |
|-------|-------|
| Framework | Pastel (file-system routing, zod arg parsing, Commander under the hood) |
| TUI | Ink + @inkjs/ui (Select, TextInput, Spinner, ProgressBar, DiffView) |
| Templates | Nunjucks (`{{ }}` syntax, frontmatter-aware rendering) |
| Validation | zod (shared between CLI args and schema validation) |
| Merge | node-diff3 (Khanna-Myers three-way merge, same algorithm as git) |
| Distribution | GitHub tarballs (no registry server needed) |
### Runtime Module
Hook scripts import `shardmind/runtime` — a thin exported module (~30KB) with zero dependency on Ink, React, or the CLI framework:
```typescript
import { loadValues, loadState, validateFrontmatter } from 'shardmind/runtime';
```
---
## Shard Anatomy
A shard is a packaged vault template. It includes folder structures, markdown templates, agent configurations, and a values schema that drives the install wizard. ShardMind the engine is agent-agnostic — it renders templates and tracks state regardless of which AI reads the output. The shard content is where agent choice lives.
```
my-shard/
.shardmind/ # Engine sidecar (not copied; install writes its own)
shard.yaml # Package identity (name, version, deps)
shard-schema.yaml # Values + modules + signals + frontmatter + migrations
CLAUDE.md # Content lives at its native vault path, copied verbatim
Home.md
brain/
.claude/
commands/ # Slash commands (gated by module)
settings.json.njk # .njk = rendered with values, installs as settings.json
```
The shard repo is itself a working vault, and `shardmind install --defaults` gives the same files as a clone (`.njk` files rendered). See [`docs/SHARD-LAYOUT.md`](docs/SHARD-LAYOUT.md) for the full contract.
Shard authors choose which agents to support. A shard can ship `CLAUDE.md` only, or all three, or any combination. The vault's markdown notes, frontmatter, and folder structure work with any AI — the operational layer (hooks, commands, agent configs) is where specificity lives.
---
## Documentation
### For shard authors
| Document | What |
|----------|------|
| [`docs/AUTHORING.md`](docs/AUTHORING.md) | **Start here.** Every file and concept a shard author needs. |
| [`docs/FORK-TO-SHARD.md`](docs/FORK-TO-SHARD.md) | Turn an obsidian-mind fork into your own installable shard. |
| [`schemas/shard.schema.json`](schemas/shard.schema.json) | JSON Schema for `shard.yaml` — drop into VS Code for autocomplete + validation. |
| [`schemas/shard-schema.schema.json`](schemas/shard-schema.schema.json) | JSON Schema for `shard-schema.yaml`. |
| [`examples/minimal-shard/`](examples/minimal-shard/) | Minimal reference shard — 4 values, 2 modules, signals. |
### For users + contributors
| Document | What |
|----------|------|
| [`docs/OPERATIONS.md`](docs/OPERATIONS.md) | Exit codes, env vars (`GITHUB_TOKEN`, `SHARDMIND_GITHUB_API_BASE`, `SHARDMIND_REGISTRY_INDEX_URL`), file locations, signal handling. |
| [`docs/ERRORS.md`](docs/ERRORS.md) | Every `ShardMindError` code: meaning, cause, remedy. |
| [`VISION.md`](VISION.md) | Origin story, architectural bets, scope guardrails, competitive moat. |
| [`ROADMAP.md`](ROADMAP.md) | The build order in phases, each linked to its issues, and the history of what shipped. |
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | The what and why. 22 sections. Ownership model, values layer, modules, signals. |
| [`docs/IMPLEMENTATION.md`](docs/IMPLEMENTATION.md) | The how, exactly. 10 modules with TypeScript signatures, 17 merge fixtures, 6-day build plan. |
| [`CLAUDE.md`](CLAUDE.md) | Spec-driven development guide for building ShardMind with AI agents. |
---
## Requirements
- [Node.js](https://nodejs.org) 22+ (matches obsidian-mind's hook runtime requirement)
- [Git](https://git-scm.com)
- [Obsidian](https://obsidian.md) 1.12+ (for CLI support)
- [QMD](https://github.com/tobi/qmd) (optional, for semantic search)
### AI Agent Support
ShardMind installs vault templates. Which AI agent you use with the vault is up to the shard:
| Agent | Config File | Hooks | Status |
|-------|-----------|-------|--------|
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `CLAUDE.md` | 5-hook lifecycle via `.claude/settings.json` | First-class (richest hook system) |
| [Codex CLI](https://github.com/openai/codex) | `AGENTS.md` | `.codex/prompts/` | Supported (shard-defined) |
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `GEMINI.md` | `save_memory` / `/memory` | Supported (shard-defined) |
The `shardmind/runtime` module is available to any TypeScript hook script. Claude Code's hook system is the most extensible — it's why obsidian-mind is Claude Code-first. But the vault content (notes, frontmatter, folders, bases) is fully agent-agnostic.
---
## Status
**Published on npm**: `npm install -g shardmind`. The engine installs, updates (three-way merge, migrations, `--release` / `--include-prerelease`), adopts, validates and reports status, and exports `shardmind/runtime` for hook scripts. The shard registry index is published, and lists [obsidian-mind](https://github.com/breferrari/obsidian-mind) and [wiki-mind](https://github.com/breferrari/wiki-mind): two shards of different shapes on the same engine. What is next is in [`ROADMAP.md`](ROADMAP.md).
---
## Author
Created by **[Brenno Ferrari](https://brennoferrari.com)** — Senior iOS Engineer in Berlin. Creator of [obsidian-mind](https://github.com/breferrari/obsidian-mind) (2k+ stars).
---
## License
MIT