Skip to content

Add headless YAOS CLI (packages/cli)#13

Closed
enieuwy wants to merge 3 commits intokavinsood:mainfrom
enieuwy:feat/headless-cli
Closed

Add headless YAOS CLI (packages/cli)#13
enieuwy wants to merge 3 commits intokavinsood:mainfrom
enieuwy:feat/headless-cli

Conversation

@enieuwy
Copy link
Copy Markdown

@enieuwy enieuwy commented Apr 12, 2026

Closes #11

Adds a headless CLI client that syncs a filesystem directory to a YAOS CRDT room — no Obsidian UI required.

What this does

@yaos/cli is a new npm workspace (packages/cli/) that reuses the existing VaultSync core, replaces the Obsidian Vault API with Node fs + chokidar, and runs as a long-lived daemon or one-shot sync tool.

Commands

yaos-cli daemon --host <url> --token <tok> --vault-id <id> --dir /path/to/vault
yaos-cli sync   --host <url> --token <tok> --vault-id <id> --dir /path/to/vault
yaos-cli status --host <url> --token <tok> --vault-id <id>
  • daemon — connect, reconcile, watch filesystem, stay running
  • sync — one reconciliation pass, then exit
  • status — print connection/cache state as JSON

Architecture

  1. Shared code changes (minimal, non-breaking):

    • Extracted normalizeVaultPath() to src/utils/normalizeVaultPath.ts (replaces runtime obsidian.normalizePath in VaultSync)
    • Made VaultSync accept an optional VaultSyncPersistenceFactory to inject persistence (headless uses a no-op adapter; plugin uses IndexedDB as before)
    • Added logPersistenceOpenError option to suppress expected no-op persistence errors
  2. CLI-only code (packages/cli/):

    • NodeDiskMirror — bidirectional fs-to-CRDT sync with: chokidar watcher, write suppression (content-hash fingerprint), per-path write locks, dirty-path coalescing, debounced write batching, rename inference from create+delete pairs, frontmatter guard
    • HeadlessYaosClient — orchestrates VaultSync + NodeDiskMirror lifecycle, reconnection reconciliation with generation tracking
    • Config precedence: CLI flags > env vars > ~/.config/yaos/cli.json
    • Bundled to a single CJS file via esbuild

What is intentionally deferred (v1 scope)

  • No local CRDT persistence — headless v1 uses a no-op IndexedDB adapter; restart requires server reconnect. Adding y-leveldb or similar is a natural follow-up.
  • Blob/attachment sync — disabled; markdown-only for now.
  • Atomic file writes — uses fs.writeFile directly; write-to-temp-then-rename would be safer.

Verification

  • Root YAOS plugin builds: npm run build pass
  • Root YAOS regression tests pass: npm run test:regressions pass
  • CLI typechecks: npm run typecheck --workspace @yaos/cli pass
  • CLI builds: npm run build --workspace @yaos/cli pass
  • CLI tests pass: npm run test --workspace @yaos/cli pass (2 passing)
  • Offline smoke test: yaos-cli sync with unreachable server produces structured JSON output with conservative reconcile mode

enieuwy added 3 commits April 12, 2026 11:03
Two bugs prevented the filesystem watcher from working:

1. shouldIgnoreNormalizedPath treated null-stats paths as files. When
   chokidar calls _isIgnored for the root directory without stats, the
   path was checked against isMarkdownSyncable (returns false for
   directory names not ending in .md), causing the entire tree to be
   pruned. Fix: when stats are null, only ignore paths that are
   definitively non-markdown files (have an extension but not .md).

2. Chokidar's internal _isIgnored calls .map() on the ignored option.
   When ignored is a bare function, .map() throws TypeError (functions
   don't have .map), which is silently caught — the watcher starts but
   watches nothing. Wrapping in an array preserves the function through
   normalizeIgnored's type check.

Also upgraded chokidar from 4.0.3 to 5.0.0.
@enieuwy
Copy link
Copy Markdown
Author

enieuwy commented Apr 13, 2026

Superseded by #16 (feat/headless-cli-fresh branch with code review fixes)

@enieuwy enieuwy closed this Apr 13, 2026
@enieuwy enieuwy deleted the feat/headless-cli branch April 14, 2026 02:39
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.

Headless CLI client for YAOS

1 participant