Lock the access-layer decision (REST only) and start implementation per SPEC. - monorepo (npm workspaces): packages/docmost-client = DocmostClient + lib/* copied 1:1 from docmost-mcp/src (backport target), plus bannered sync methods (listTrash, restorePage, listAllSpacePages, exportPageBody, listRecentSince / collectRecentSince cursor scan) - engine stays the root app per AGENTS.md (src/, test/, build/, data/, settings.ts); add roundtrip.ts (SPEC §11 idempotency harness), pull.ts (SPEC §6 read-only Docmost->FS mirror), sanitize.ts (SPEC §12 filenames, path-traversal-safe) - Dockerfile builds the workspace lib before the app; vitest gates CI - exportPageBody never touches /comments (SPEC §3); serializeDocmostMarkdownBody emits meta + body only - SPEC: resolve access-layer (REST), reflect root-engine layout + REST pagination - tests: sanitize (incl. dot-traversal), collectRecentSince (cutoff/dedup/cap), stripBlockIds, markdown round-trip byte-stability Note: raw ProseMirror round-trip is byte-stable in Markdown but not yet attribute- idempotent (SPEC §11 Задача №0, before Phase 2).
3.1 KiB
AGENTS.md
Onboarding notes for agents working on docmost-sync (Node / TypeScript, ESM).
What this is
A daemon that bidirectionally syncs Docmost articles with a local Markdown git vault (git is the state store). It reuses the sibling project docmost-mcp as a library (DocmostClient, ProseMirror ↔ Markdown converter, collab-write).
Status: scaffold only — the sync engine is NOT implemented yet. src/index.ts
is a thin stub that validates config and exits. See SPEC.md for the full design
and the phased plan before adding engine logic.
Project structure
The project is now an npm-workspaces monorepo. packages/docmost-client is
the extracted DocmostClient + lib/ — a verbatim 1:1 copy of docmost-mcp/src/
with the sync-specific methods appended under a clear banner (changes are
backported into docmost-mcp manually). The ROOT remains the engine app
(src/, test/, build/, data/) and depends on docmost-client. npm run build builds the lib first, then compiles the app to build/.
src/— application code.src/settings.ts— the single config entry point (zod schema keyed by the real ENV var names;parseSettingsis pure,loadSettingsreads.env).src/config-errors.ts—loadSettingsOrExitturns a config error into a clear startup message that names the missing/invalid variable, then exits.src/index.ts— thin entry point.
test/— vitest tests (*.test.ts).data/— all mutable runtime state (the git vault lives here). Gitignored; mounted as a docker volume in production. Never put code/static assets here.build/— compiled output (tsc). Gitignored.
Relative imports inside src/ use the .js extension (NodeNext), e.g.
import { loadSettings } from './settings.js'.
Setup
make install— install dependencies (npm ci).make env— create.envfrom.env.example(orcp .env.example .env), then fill in the values.
Running
make test— run the test suite (vitest).make run— build and run the app.make dev— run in watch mode (tsx).
make (or make help) lists all targets.
Conventions
- All mutable state lives ONLY under
data/. Static assets are code, never indata/. - All config and credentials come ONLY from ENV /
.env, read throughsrc/settings.ts. Credentials and the addresses of our own services that the user provides go ONLY into.env(never into code, never as inline env vars on the command line) and are read through settings. - No default/example credentials in code. Addresses of our own services (Docmost) have NO default either. A missing required variable must FAIL AT STARTUP with a clear message that names the variable — no raw stack trace.
- Defaults are allowed only for non-secret tunables (log level, intervals, vault
path under
data/). - All code comments are written in ENGLISH.
- Repeated actions go through the
Makefile. - Tests are required for new code. In CI the
buildjob needstest(tests gate the docker build). - No
EXPOSEin the Dockerfile — this is a daemon with no inbound HTTP port.