Skip to content

feat: add global vault config fallback via ~/.config/napkin/config.json - #15

Open
cad0p wants to merge 2 commits into
Michaelliv:mainfrom
cad0p:feat/global-config-fallback
Open

feat: add global vault config fallback via ~/.config/napkin/config.json#15
cad0p wants to merge 2 commits into
Michaelliv:mainfrom
cad0p:feat/global-config-fallback

Conversation

@cad0p

@cad0p cad0p commented May 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Add XDG-compliant global config fallback to findVault() resolution order. When no .napkin/ directory is found walking up from cwd, the CLI now checks $XDG_CONFIG_HOME/napkin/config.json (defaults to ~/.config/napkin/config.json) for a configured vault path before creating a bare vault.

Motivation

Agent frameworks like pi-napkin need a way to configure a default vault for projects that don't have a local .napkin/ directory. Currently, pi-napkin has its own vault-resolve.ts that duplicates napkin's walk-up logic and adds a \~/.pi/agent/napkin.json fallback. Moving this into napkin-ai eliminates the duplication and gives all agent frameworks a consistent, standard way to configure a default vault.

Resolution order

  1. Walk up from cwd for .napkin/ (or .obsidian/.napkin/)
  2. Read vault field from $XDG_CONFIG_HOME/napkin/config.json
  3. Create bare vault at cwd (last resort)

Config format

// ~/.config/napkin/config.json
{
  "vault": "~/path/to/vault"
}

Changes

  • src/utils/vault.ts: Added getGlobalConfigVault() between walk-up loop and createBareVault()
    • Respects XDG_CONFIG_HOME env var (defaults to ~/.config)
    • Tilde expansion: only ~ and ~/... (not \~user)
    • Relative paths resolve relative to config directory (not cwd)
  • src/utils/vault.test.ts: Added tests for global config fallback, test isolation via XDG_CONFIG_HOME override

Companion PR

  • pi-napkin#3 — removes duplicated vault-resolve.ts, uses napkin-ai's native resolution

cad0p added 2 commits May 3, 2026 21:54
Add XDG-compliant global config fallback to findVault() resolution.
When no .napkin/ directory is found walking up from cwd, check
$XDG_CONFIG_HOME/napkin/config.json (defaults to ~/.config/napkin/config.json)
for a configured vault path before creating a bare vault.

Resolution order:
1. Walk up from cwd for .napkin/ (or .obsidian/.napkin/)
2. Read vault path from ~/.config/napkin/config.json
3. Create bare vault at cwd (last resort)

This enables tools like pi-napkin to configure a default vault
without per-project .napkin/ directories, and removes the need
for framework-specific vault resolution wrappers.
- Fix tilde expansion to only match ~ and ~/ (not ~user)
- Resolve relative vault paths relative to config directory (not cwd)
- Remove redundant type annotation
- Add test isolation for auto-create test (set XDG_CONFIG_HOME)
- Add tests for global config fallback, invalid config, and vault resolution

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an XDG-style global config fallback to vault discovery so the CLI/SDK can resolve a default vault from ~/.config/napkin/config.json when no local .napkin directory is found. It extends the existing vault resolution logic and adds tests around the new fallback behavior.

Changes:

  • Added global config lookup in findVault() using $XDG_CONFIG_HOME/napkin/config.json with ~ expansion and config-relative path resolution.
  • Updated vault discovery flow so the global config is consulted before auto-creating a bare vault.
  • Added tests for XDG fallback behavior and invalid global config handling; lockfile now includes @types/node.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/utils/vault.ts Adds the global-config fallback path into vault resolution and introduces config parsing helpers.
src/utils/vault.test.ts Adds tests for global fallback behavior and isolates some cases from host XDG_CONFIG_HOME.
package-lock.json Records the new @types/node dev dependency and its transitive type package.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/vault.ts
Comment on lines +50 to +53
// Fall back to global vault from user config
const globalVault = getGlobalConfigVault();
if (globalVault) {
return resolveVaultLayout(globalVault, path.dirname(globalVault));
Comment thread src/utils/vault.test.ts
Comment on lines +52 to +74
test("falls back to global vault from XDG config", () => {
const globalDir = fs.mkdtempSync(path.join(os.tmpdir(), "napkin-global-vault-"));
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "napkin-global-config-"));
const napkinConfigDir = path.join(configDir, "napkin");
fs.mkdirSync(napkinConfigDir, { recursive: true });
// Create a vault in globalDir
const napkinDir = path.join(globalDir, ".napkin");
fs.mkdirSync(path.join(napkinDir, ".obsidian"), { recursive: true });
fs.writeFileSync(
path.join(napkinDir, "config.json"),
JSON.stringify({ vault: { root: "..", obsidian: "../.obsidian" } }),
);
// Write global config pointing to globalDir
fs.writeFileSync(
path.join(napkinConfigDir, "config.json"),
JSON.stringify({ vault: globalDir }),
);
const origXdg = process.env.XDG_CONFIG_HOME;
process.env.XDG_CONFIG_HOME = configDir;
try {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "napkin-fallback-"));
const result = findVault(tmpDir);
expect(result.configPath).toBe(path.join(globalDir, ".napkin"));
Comment thread src/utils/vault.ts
Comment on lines +82 to +83
const napkinDir = path.join(vaultPath, ".napkin");
if (fs.existsSync(napkinDir)) return napkinDir;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants