diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 628716ad..00000000 --- a/AGENTS.md +++ /dev/null @@ -1,310 +0,0 @@ -# AGENTS.md - -This file guides AI agents (Claude Code, opencode, …) working in this -repository. It has two layers: **how to run a task end-to-end** (the -sections below), and **how the codebase is built** (the technical sections -further down, formerly in `CLAUDE.md`). - -## Жизненный цикл задачи - -### 1. Старт: синхронизация с develop - -Перед началом **любой** работы обнови локальный `develop` и ветвись от него: - -```bash -git checkout develop -git fetch gitea -git pull --ff-only gitea develop -git checkout -b <короткое-имя-фичи> -``` - -Никогда не пилит фичу прямо в `develop` и не ветвись от устаревшего -`develop` — иначе PR будет содержать лишние коммиты или конфликтовать. - -### 2. Реализация - -Веди задачу по workflow из системного промпта (Phase 1 анализ → Phase 3 -реализация → Phase 4 review → Phase 5 верификация → Phase 6 отчёт). Большие -изменения делегируй в general subagent, ревьюй через review subagent. - -**Worktree'ы создавай только внутри папки `.claude`** (например, -`.claude/worktrees/<имя>`). Создавать git worktree где-либо ещё — в корне -репозитория, в соседних каталогах или во временных папках — запрещено. - -### 3. Коммит — ТОЛЬКО в Gitea и ТОЛЬКО от `claude_code` - -Это правило без исключений: - -- **Куда:** единственный remote для коммитов/пушей — **`gitea`** - (`gitea.vvzvlad.xyz`). **Никогда** не пушь в `origin` (GitHub-зеркало) и - тем более в `upstream` (оригинальный Docmost). GitHub-зеркало обновляется - CI-процессом владельца, не агентом. -- **От кого:** коммить **только** от агентского identity. Любой коммит, - у которого author или committer — `vvzvlad`, считается ошибкой и должен - быть переписан. - - **name:** `claude_code` - - **email:** `claude_code@vvzvlad.xyz` - -Используй `--reset-author` при amend, иначе git оставит оригинального -автора (по умолчанию config на этой машине — `vvzvlad`, поэтому проверяй -после каждого коммита): - -```bash -GIT_AUTHOR_NAME="claude_code" \ -GIT_AUTHOR_EMAIL="claude_code@vvzvlad.xyz" \ -GIT_COMMITTER_NAME="claude_code" \ -GIT_COMMITTER_EMAIL="claude_code@vvzvlad.xyz" \ -git commit --amend --no-edit --reset-author -``` - -Для обычного нового коммита достаточно один раз выставить локальный -config ветки и коммитить штатно: - -```bash -git config user.name "claude_code" -git config user.email "claude_code@vvzvlad.xyz" -``` - -Проверка перед push: - -```bash -git log -1 --format='Author: %an <%ae>%nCommitter: %cn <%ce>' -# обе строки должны показать claude_code -``` - -### 4. Push и PR в develop - -PR всегда в `develop`. Пароль `claude_code` лежит в macOS keychain как -**generic password** под service `gitea-claude-code` (не дублируй его как -internet-password для `gitea.vvzvlad.xyz` — это создаст конфликт с учёткой -владельца в git credential helper): - -```bash -AGENT_PASS=$(security find-generic-password -s gitea-claude-code -w) -``` - -Push — через временную подстановку кредов в remote URL, после чего URL -обязательно возвращается в чистый вид (пароль не должен оседать в git -config / reflog): - -```bash -ORIG_URL=$(git remote get-url gitea) -SAFE_PASS=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$AGENT_PASS") -git remote set-url gitea "https://claude_code:${SAFE_PASS}@gitea.vvzvlad.xyz/vvzvlad/gitmost.git" -git push -u gitea -git remote set-url gitea "$ORIG_URL" -unset AGENT_PASS SAFE_PASS -``` - -PR создаётся через Gitea REST API (Basic Auth от `claude_code`): - -```bash -curl -s -X POST \ - -u "claude_code:$(security find-generic-password -s gitea-claude-code -w)" \ - -H "Content-Type: application/json" \ - -d @pr_body.json \ - "https://gitea.vvzvlad.xyz/api/v1/repos/vvzvlad/gitmost/pulls" -``` - -`base: develop`, `head: `. В теле PR — что сделано, что вне scope, -результаты верификации (tsc/lint/tests). - -> Если push падает с `User permission denied for writing` — значит у -> `claude_code` нет коллабораторских прав на репо. Попроси владельца -> добавить (один раз, через Gitea UI или -> `PUT /api/v1/repos/vvzvlad/gitmost/collaborators/claude_code` с -> `{"permission":"write"}` от его учётки). - -### 5. Мерж и cleanup - -- **Мерж PR в develop делает пользователь** (не агент). Агент не жмёт - кнопку merge. -- **После реализации задачи удали её план из `docs/backlog/.md`** — - это часть закрытия задачи, не пользовательская работа. Файлы в - `docs/backlog/` — это очередь работы, выполненное из неё вычищается. - Сделай это в отдельном коммите от того же `claude_code` в той же ветке - (или попроси пользователя удалить, если PR уже открыт и ты не хочешь - его перепушивать). -- Не закоммичен ли мусор в рабочем дереве? Проверь `git status` перед - финальным отчётом. - -## Релизный цикл: набор на новую версию - -Когда в `develop` накопилось достаточно изменений для релиза, запускается -**финальное ревью тремя скиллами-оркестраторами** перед мержем/тегом: - -1. **test-orchestrator** (skill `code-review-orchestrator` с фокусом на - тестовом покрытии) — проверяет, что новый код покрыт тестами и нет - регрессий в существующих. -2. **review-orchestrator** (skill `code-review-orchestrator`) — - мульти-аспектный код-ревью: безопасность, стабильность, соответствие - конвенциям, регрессии, перегруженность. -3. **red-team-orchestrator** (red-team скилл) — адверсариальный анализ - атакующих сценариев на затронутые компоненты. - -Порядок: оркестраторы возвращают списки находок → агент правит всё, что -они нашли (через subagent или сам, по правилам делегирования) → повторно -прогоняет ревью затронутых мест → режет тег по процедуре «Cutting a -release» ниже. - -## Шпаргалка по учёткам и endpoint'ам - -| Что | Значение | -| --- | --- | -| Единственный remote для коммитов | `gitea` → `https://vvzvlad@gitea.vvzvlad.xyz/vvzvlad/gitmost.git` | -| Агентский user (Gitea/git) | `claude_code` | -| Агентский email | `claude_code@vvzvlad.xyz` | -| Пароль в keychain | `security find-generic-password -s gitea-claude-code -w` | -| PR API | `https://gitea.vvzvlad.xyz/api/v1/repos/vvzvlad/gitmost/pulls` (тут `gitmost` — реальный slug репо на сервере) | -| Базовая ветка | `develop` | -| `origin` | GitHub-зеркало `vvzvlad/gitmost` — **не пушить**, обновляется CI владельца | -| `upstream` | Оригинальный Docmost — **не пушить никогда** | - ---- - -# Архитектура и кодовая база - -## What this is - -**Gitmost** is a community fork of [Docmost](https://github.com/docmost/docmost) — an open-source collaborative wiki / documentation app. The fork's defining constraint: **100% open, AGPL-only, with no Enterprise-Edition (EE) code**. The upstream `apps/server/src/ee`, `apps/client/src/ee` and `packages/ee` directories were deleted; there is no license gating or feature-flag wall. Features that upstream hides behind the enterprise license (comment resolution, the embedded `/mcp` server, the AI agent chat) are **re-implemented from scratch** on the community codebase. - -**Naming gotcha:** only the *product* is rebranded. Internal identifiers are still `docmost` everywhere — npm package names (`docmost`, `@docmost/mcp`, `@docmost/editor-ext`), the default DB name, env-var prefixes (`MCP_DOCMOST_*`), and the TS path aliases (`@docmost/db/*`, `@docmost/transactional/*`). Do not "fix" these to `gitmost`; they are load-bearing for Docmost data/image compatibility (the DB schema is a strict superset of Docmost's, so an existing instance migrates by swapping images). - -## Monorepo layout - -pnpm workspace (`pnpm@10.4.0`) orchestrated by **Nx**. Four workspace packages: - -| Path | Name | Stack | Role | -| --- | --- | --- | --- | -| `apps/server` | `server` | NestJS 11 + Fastify, Kysely (Postgres), Redis | Backend API, collaboration, AI | -| `apps/client` | `client` | React 18 + Vite + Mantine 8 + TanStack Query + Jotai | SPA frontend | -| `packages/editor-ext` | `@docmost/editor-ext` | Tiptap/ProseMirror | Shared Tiptap node/mark extensions, imported by both the client and the server | -| `packages/mcp` | `@docmost/mcp` | MCP SDK, Tiptap, Yjs | Standalone MCP server, also bundled into the server at `/mcp`. Does **not** import `editor-ext` — it keeps its own vendored mirror of the schema in `packages/mcp/src/lib/` | - -`build` targets are Nx-cached and dependency-ordered (`dependsOn: ["^build"]`), so `editor-ext` builds before the apps. `nx.json` sets `affected.defaultBase: main`. - -## Commands - -Run from the repo root unless noted. The dev workflow needs **Postgres (with the `pgvector` extension) and Redis** reachable per `.env` (copy `.env.example` → `.env`). - -```bash -pnpm install # install all workspaces (uses pnpm patches; see package.json `pnpm.patchedDependencies`) -pnpm dev # client (Vite) + server (Nest watch) concurrently — primary dev loop -pnpm client:dev # frontend only (Vite proxies /api to APP_URL) -pnpm server:dev # backend only (nest start --watch) -pnpm build # nx run-many -t build (all packages) -pnpm collab:dev # run the collaboration server process standalone (see "Two server processes") -``` - -**Lint** (per package — there is no root lint script): -```bash -pnpm --filter server lint # eslint --fix on server .ts -pnpm --filter client lint # eslint on client -``` - -**Tests** (per package — no root test script): -```bash -pnpm --filter server test # Jest, matches *.spec.ts under src -pnpm --filter server test -- ai-chat.service # single file by name pattern -pnpm --filter server test -- -t "resolves a comment" # single test by name -pnpm --filter client test # Vitest (vitest run) -pnpm --filter client test -- message-list # single Vitest file by name -pnpm --filter @docmost/mcp test # node --test (unit + mock) -pnpm --filter @docmost/mcp test:e2e # MCP end-to-end against a live instance -``` - -**Database migrations** (Kysely, run from `apps/server`; they auto-run on server startup too): -```bash -pnpm --filter server migration:create --name=my_change # new empty migration -pnpm --filter server migration:latest # apply all pending -pnpm --filter server migration:down # revert last -pnpm --filter server migration:codegen # regenerate src/database/types/db.d.ts from the live DB -``` -Migration files live in `apps/server/src/database/migrations/` and are named `YYYYMMDDThhmmss-description.ts`. Fork-specific migrations only **add** tables (`page_embeddings`, `ai_chats`, `ai_chat_messages`, `ai_provider_credentials`, `ai_mcp_servers`, `page_template_references`) and columns (e.g. `pages.is_template`, a `NOT NULL DEFAULT false` boolean) — never drop/rewrite Docmost data. - -**Migration ordering — always check when merging branches/features.** Kysely runs migrations in **alphabetical (= timestamp) order** and refuses to start if a *new* migration sorts **before** one already applied to the DB (`corrupted migrations: ... must always have a name that comes alphabetically after the last executed migration`). When you merge a branch or land a feature, verify your migration's timestamp still sorts **after every migration that may already be applied on the target** (`/bin/ls -1 apps/server/src/database/migrations | sort | tail`). Branches developed in parallel routinely break this: a feature branch adds `…T130000-…`, `main` meanwhile ships and deploys `…T150000-…`, and after the merge the older-timestamped file is rejected at boot. **Fix = rename your migration to a timestamp after the latest one already in the target** (content unchanged — the filename is the ordering key), then rebuild so the compiled `dist/database/migrations/` picks up the new name. - -## Architecture — the big picture - -### Two server processes -`apps/server` builds one codebase but runs as **two distinct entrypoints**, both required in production: -- **API server** — `dist/main` (`apps/server/src/main.ts`), the Fastify HTTP app (`AppModule`). -- **Collaboration server** — `dist/collaboration/server/collab-main` (`pnpm collab`), a Hocuspocus/Yjs WebSocket server (`apps/server/src/collaboration/`) handling real-time document editing, persistence, and page-history snapshots. It listens on `COLLAB_PORT` (default `3001`), separate from the API server's `PORT` (default `3000`), and shares state with the API server through Redis. - -The API server is a Fastify app with a global `/api` prefix (`main.ts` excludes `robots.txt`, public share pages, and `mcp` from the prefix). A `preHandler` hook enforces that a resolved `workspaceId` exists for most `/api` routes (multi-tenant by hostname/subdomain via `DomainMiddleware`). Auth is JWT (cookie + bearer); authorization is **CASL** (`core/casl`) — every data access is scoped to the user's abilities. - -### Module structure (server) -`AppModule` wires integration modules (`integrations/*`: storage [local/S3/Azure], mail, queue [BullMQ on Redis], security, telemetry, throttle, `mcp`, `ai`) plus `CoreModule`, `DatabaseModule`, and `CollaborationModule`. `CoreModule` (`core/*`) holds the domain modules: `page`, `space`, `comment`, `workspace`, `user`, `auth`, `group`, `attachment`, `search`, `share`, `ai-chat`, etc. Each domain module follows NestJS controller → service → repo layering; DB repos live under `database/repos` and are injected app-wide from the global `DatabaseModule`. - -**EE removal artifact:** `app.module.ts` still contains a `try/require('./ee/ee.module')` stub. That path no longer exists, so the require fails and is swallowed (it only hard-exits when `CLOUD === 'true'`). Treat EE as gone — do not add code that depends on it. - -### Persistence -- **Postgres via Kysely** (`nestjs-kysely`), typed by the generated `src/database/types/db.d.ts`. Use the camelCase Kysely query builder, not an ORM. After schema changes, write a migration *and* regenerate the DB types. -- **pgvector is mandatory** — the RAG feature stores embeddings in `page_embeddings`. `docker-compose.yml` uses `pgvector/pgvector:pg18` for this reason; the stock `postgres` image will fail the `CREATE EXTENSION vector` migration. -- **Redis** backs caching, the BullMQ queues, the WebSocket Socket.IO adapter, and collaboration sync. - -### The two AI subsystems (the main fork additions) -1. **Embedded MCP server** (`integrations/mcp/` + `packages/mcp`). The standalone `@docmost/mcp` server (38 agent-native tools: per-block patch/insert/delete by id, scripted `(doc)=>doc` transforms with dry-run diff, table editing, version diff/restore, comments, images, shares) is bundled and served over HTTP at `/mcp`. It writes through Docmost's real-time-collaboration layer so concurrent human edits aren't clobbered. Each request authenticates **per-user** via the `Authorization` header — either HTTP Basic (`base64(email:password)`, the user's own Docmost login, validated through `AuthService`) or a Bearer access JWT (the user's `authToken`) — and the session acts under that user's permissions. `MCP_DOCMOST_EMAIL` / `MCP_DOCMOST_PASSWORD` are an **optional service-account fallback**, used only when a request carries neither Basic nor Bearer credentials (back-compat for CI/scripts). An admin enables MCP with a workspace toggle (Workspace settings → AI). Optionally protected by a shared `MCP_TOKEN`: when set, every `/mcp` request must carry a matching `X-MCP-Token` header (its own header, separate from `Authorization`, which now carries the per-user Basic/Bearer credentials). Note: this changed from the older `Authorization: Bearer ` scheme — see `.env.example` and the CHANGELOG Breaking Changes entry. -2. **AI agent chat** (`core/ai-chat/` server + `apps/client/src/features/ai-chat/` client). A built-in agent over the wiki using the Vercel **AI SDK** (`ai`, `@ai-sdk/*`) against any OpenAI-compatible provider configured per workspace (`integrations/ai/` — credentials encrypted at rest via `integrations/crypto`, stored in `ai_provider_credentials`). Key pieces: - - `core/ai-chat/tools/` — the agent's ~40 read+write tools. Every tool runs under the **calling user's** CASL permissions via a per-user loopback access token (`docmost-client.loader.ts`), so the agent can never exceed what the user could do. Only **reversible** operations are exposed (page history + trash; no permanent delete). Agent edits get an "AI agent" provenance badge in page history (`20260616T130000-agent-provenance` migration). - - `core/ai-chat/embedding/` — RAG indexer + a BullMQ consumer on `AI_QUEUE` that embeds pages into `page_embeddings` (vector search), complementing Postgres full-text search. Pages are (re)indexed on edit; `AI_EMBEDDING_TIMEOUT_MS` bounds a hung embeddings endpoint. - - `core/ai-chat/external-mcp/` — admins can attach external MCP servers (e.g. Tavily) to give the agent web access. **`ssrf-guard.ts` validates outbound MCP URLs against SSRF** — keep that guard in the path when touching external-MCP connection logic. - -### Client structure -Vite SPA. Code is organized by feature under `apps/client/src/features/*` (mirrors the server domains: `page`, `space`, `comment`, `ai-chat`, `editor`, …). Conventions: -- **TanStack Query** for server state (one `queries/` file per feature), **Jotai** atoms for local/shared UI state, **Mantine 8** + CSS modules (`*.module.css`) + `postcss-preset-mantine` for UI. -- The editor is Tiptap; shared node/mark extensions live in `packages/editor-ext` and are imported by **both the client and the server** (collaboration, import/export) — editor schema changes often need to be made in `editor-ext`, not just the client. Note `packages/mcp` does *not* depend on `editor-ext`; it carries its own mirrored copy of the schema, so keep the two in sync manually when the document schema changes. -- API access goes through `apps/client/src/lib/api-client.ts` (axios). The `@` alias maps to `apps/client/src`. -- Runtime config is injected at build time by `vite.config.ts` via `define` (`APP_URL`, `COLLAB_URL`, `APP_VERSION`, …) — these come from the root `.env`, not from `import.meta.env`. - -## Conventions - -- **Code comments must be in English.** -- **Errors must never be swallowed or shown as generic messages.** Every caught error MUST (1) be logged in full to the console/logger — error name, message, stack, `cause`, and (for HTTP/provider failures) the status code and response body — and (2) be surfaced to the user with a *specific, human-readable explanation of what actually went wrong*, never a bare generic string like "Something went wrong" / "Could not start recording" / "Transcription failed". Include the real reason (the underlying error/provider message) in the user-facing text. On the server, wrap third-party/provider failures with `describeProviderError` (or equivalent) and rethrow as a meaningful HTTP status + message — never let them collapse into an opaque 500. On the client, `console.error(, err)` the raw error AND show the extracted reason (e.g. `err.response?.data?.message`, or the error `name: message`) in the notification. -- The version string shown in the UI comes from `APP_VERSION` (CI/Docker) or `git describe --tags --always` (local), resolved in `vite.config.ts` — not from `package.json`. -- Server TS config is permissive (`noImplicitAny: false`, `strictNullChecks: false`, `no-explicit-any` lint disabled). Follow the existing relaxed style rather than tightening types broadly. -- Dependency versions are heavily pinned via `pnpm.overrides` and `pnpm.patchedDependencies` (`scimmy`, `yjs`) in the root `package.json`. Don't bump pinned/patched deps casually; the patches and overrides exist for compatibility/security reasons. - -## CI / release - -- `.github/workflows/develop.yml` — on push to `develop`, builds and pushes `ghcr.io/vvzvlad/gitmost:develop`. -- `.github/workflows/release.yml` — on `v*` tags (or manual dispatch), builds multi-arch (amd64 + arm64) images, pushes a manifest list to GHCR (`latest` + semver tags), and creates a draft GitHub Release with image tarballs. Uses the built-in `GITHUB_TOKEN` (not Docker Hub). -- The `Dockerfile` is a multi-stage pnpm build; `APP_VERSION` is passed as a build arg because `.git` isn't in the build context. - -### Cutting a release - -The git tag is the source of truth for the displayed version (UI reads `git describe --tags`); the `package.json` bump is metadata only. Steps: - -1. Make sure `main` is clean and pushed (`git status`, `git push`). -2. Pick `vX.Y.Z` (SemVer): **minor** bump for a batch of features, **patch** for fixes only. Review what landed with `git log ..HEAD --no-merges`. -3. Bump `"version"` to `X.Y.Z` in the **root** `package.json`, `apps/client/package.json`, and `apps/server/package.json` (keep all three in sync). Leave `packages/mcp` alone — it is versioned independently. Commit with the bare version as the subject, e.g. `0.91.0` (matches past bump commits). -4. Update `CHANGELOG.md` (Keep a Changelog format): add a `## [X.Y.Z] - YYYY-MM-DD` section summarising `git log vPREV..HEAD --no-merges` grouped by type (Breaking / Added / Changed / Fixed / Removed), and add the `compare/vPREV...vX.Y.Z` link at the bottom. Fold the bump + changelog into the release commit. -5. Tag the release commit with a **lightweight** tag (existing release tags are lightweight): `git tag vX.Y.Z`. -6. Push commit and tag: `git push origin main && git push origin vX.Y.Z`. Pushing the `v*` tag triggers `release.yml` (multi-arch GHCR images + a draft GitHub Release). -7. **Back-merge the release into `develop`** so develop builds report the new version: `git checkout develop && git merge --no-ff main && git push origin develop` (push to Gitea as well if that is the canonical remote). - -#### Why develop keeps showing the *previous* version (and why step 7 matters) - -The UI version is `git describe --tags --always` (see `vite.config.ts`), which walks **backwards from the current commit** and picks the **nearest tag reachable in that commit's ancestry**, then appends `--g`. - -The release tag (`vX.Y.Z`) is created on **`main`'s release merge commit**, and that commit is **not** in `develop`'s history. So until the release is back-merged, `git describe` on `develop` cannot see the new tag and falls back to the *previous* reachable tag. Result: every develop build — and the `ghcr.io/vvzvlad/gitmost:develop` image — keeps reporting e.g. `v0.91.0-NNN-g` even though `main` is already tagged `v0.93.0`. This is the classic git-flow pitfall: the version on `develop` does **not** advance just because a release was tagged on `main`. - -Back-merging `main → develop` (step 7) pulls the tagged release commit into `develop`'s ancestry, after which develop builds correctly show `vX.Y.Z-NNN-g`. If `develop` already drifted (release tagged but never back-merged), just run step 7 now — no new tag is needed. - -##### The tag must also exist on the remote that CI builds from (multi-remote gotcha) - -`git describe` names a tag **ref**, not just a commit — so the back-merge is *necessary but not sufficient*. The develop image is built by GitHub Actions (`develop.yml`, `actions/checkout` with `fetch-depth: 0`, then `git describe --tags --always`), so the version it prints depends on which tags exist **on the `github` remote**, not on your local clone or on `gitea`. - -This repo has two writable remotes — `gitea` (canonical, where commits land) and `github` (where the `:develop` and release images are built) — plus `upstream` (docmost, never push). **`git push ` does NOT push tags**; tags must be pushed explicitly and *to each remote separately*. A release tag that only lives on `gitea` is invisible to the GitHub Actions build: even with the tagged commit fully in `develop`'s history (step 7 done), `git describe` on the GitHub runner falls back to the previous tag it *does* have, so the develop image keeps showing e.g. `v0.91.0-NNN` while `git describe` locally already says `v0.93.0-NN`. - -Fix / checklist when develop still shows the old version after a back-merge: - -1. Confirm the tag is missing on github: `git ls-remote --tags github` (compare with `gitea`). -2. Push it there: `git push github vX.Y.Z` (and `git push gitea vX.Y.Z` if it is missing on gitea too). Note: pushing a `v*` tag to `github` also triggers `release.yml` (multi-arch GHCR images + draft Release) — expected, but be aware. -3. Re-run the develop build (`gh workflow run Develop`, or push any commit to `develop`) so `git describe` re-resolves with the tag now present. - -(The `git push origin ...` in steps 6–7 above is shorthand — there is no `origin` remote here; substitute `gitea` **and** `github` as appropriate, and always push release tags to both.) - -## Planning docs - -`docs/*.md` hold design plans for in-progress / planned features (mobile app, offline sync, RAG improvements, voice dictation). Arbitrary HTML embed has **shipped** — it renders inside a sandboxed iframe and, when the `htmlEmbed` workspace toggle is on, is insertable by any member (no longer admin-only); turning the toggle off hides/stops serving existing embeds on public share pages. `docs/backlog/*.md` track known issues / follow-ups (e.g. AI-chat review follow-ups). Consult the relevant plan before working on one of those areas. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 43255596..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,209 +0,0 @@ -# Changelog - -All notable changes to this project are documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -> Releases prior to `0.91.0` predate this changelog; see the -> [git tags](https://github.com/vvzvlad/gitmost/tags) for earlier history. - -## [Unreleased] - -### Changed - -- **Public share AI: default per-workspace hourly assistant cap lowered - 300 → 100.** The limiter falls back to this default whenever - `SHARE_AI_WORKSPACE_MAX_PER_HOUR` is unset, so a `0.93.0` deployment that - never set the env var has its anonymous public-share assistant hourly cap - cut from 300 to 100 on upgrade. Set `SHARE_AI_WORKSPACE_MAX_PER_HOUR` to - keep the previous limit. (#62) - -## [0.93.0] - 2026-06-21 - -This release builds on the 0.91.0 AI foundation: admin-defined AI agent roles, -an anonymous AI assistant on public shares, server-side voice dictation, an -editor footnotes model, live page-template embeds, and sandboxed arbitrary-HTML -embeds — plus a large batch of security hardening and test coverage. - -### Breaking Changes - -- **MCP shared-token auth moved to its own header.** The `/mcp` shared guard - no longer reads `Authorization: Bearer `; it now reads only the - `X-MCP-Token` header. The `Authorization` header is now reserved for per-user - HTTP Basic / Bearer access-JWT credentials, so each `/mcp` request - authenticates as a specific user (the `MCP_DOCMOST_*` service account is only - a fallback). Existing MCP clients (e.g. Claude Desktop) configured with - `Authorization: Bearer ` must be reconfigured to send - `X-MCP-Token: ` instead. See `MCP_TOKEN` in `.env.example`. As a - one-time aid, the server logs a single migration warning when it sees the - old-style header. - -### Added - -- **AI agent roles**: admin-defined assistant personas with an optional - per-role model override, selectable in chat. -- **Anonymous AI assistant on public shares**: public-share visitors can chat - with a selectable agent-role identity that reuses the internal chat - presentation, with per-request output-token caps and a fail-closed Redis - limiter. -- **Voice dictation (STT)**: server-side speech-to-text with a mic button in - the chat and the editor, OpenRouter STT support, an endpoint test, and real - provider-error surfacing. -- **Footnotes**: an editor footnotes model (inline references + a definitions - list). -- **Page templates**: live whole-page embed (MVP) with a template-marker icon - in the page tree and a working Refresh action. -- **Arbitrary HTML/CSS/JS embeds**: a sandboxed-iframe embed block gated by a - per-workspace toggle (default OFF); insertable by any member when the toggle - is on. -- Admin-only **"Analytics / tracker"** workspace setting: a raw HTML/JS snippet - injected into the `` of public share pages only (for analytics such as - Google Analytics or Yandex.Metrika), kept separate from the member-facing - HTML-embed feature. -- **MCP**: a hierarchical tree mode for `list_pages`, and per-user auth for the - embedded `/mcp` endpoint. -- **Page tree**: Expand all / Collapse all for the space tree, and - server-authoritative realtime tree updates. -- **AI chat UX**: a `get_current_page` tool for proxy-robust page context, a - current-context-size readout, an agent step cap raised 8→20 with a forced - final text answer, and auto-collapse of the chat window on page focus. -- **AI settings**: a Clear control inside the API-key field and an endpoint - status dot bound to "configured × enabled". -- **Client**: an always-visible space grid replacing the space-switcher popover, - removal of the sidebar Overview item, tighter comments-panel density, and no - auto-open of the comments panel when adding a comment. - -### Changed - -- HTML embed blocks now render inside a sandboxed iframe (separate origin) and, - when the workspace HTML-embed toggle is on, can be inserted by any member - (previously admin-only). Turning the toggle off hides existing embeds and - stops serving them on public share pages. -- Remove the server-side role-based stripping of HTML-embed blocks from the - write paths (collab/REST/MCP, page create/duplicate, import, transclusion - unsync); sandboxing makes per-write gating unnecessary. The only remaining - server-side strip is the public-share read path, which still honors the - workspace HTML-embed toggle. - -### Fixed - -- AI chat: preserve scroll position during streaming, record chats that fail on - their first turn, and resolve the current page for agent context behind - proxies. -- AI roles: guard `update()` against concurrent soft-delete; harden the model - override, role-name uniqueness, and id validation; sandwich the safety - framework around the role persona. -- Auth: handle null-password (SSO/LDAP-only) accounts without a bcrypt throw. -- Footnotes: survive duplicate-id definitions without collab divergence. -- HTML embed: fix stale iframe height and damp the resize loop; strip embeds at - serve time on authenticated read paths and the plain page-create path. -- Page templates: import `ThrottleModule` so collab boots, never strand an - in-flight page-embed id, and add defense-in-depth workspace checks. -- Pages: `movePage` cycle guard with no phantom `PAGE_MOVED` event. -- Import: surface the real error cause from `/pages/import` instead of a generic - 400. - -### Security - -- MCP: close an SSO/MFA bypass on Basic auth and stop minting non-init sessions; - close a brute-force limiter check-then-act race. -- Public share: block restricted descendants in the anonymous assistant, cap - per-request output, fail closed when Redis is unavailable, and reject non-text - message parts to close a size-cap bypass. -- Make `trustProxy` env-configurable with a safe default. - -### Internal - -- CI: gate the `develop` and release image builds on the test suite, run the - suites on push/PR, and build the `:develop` image on push to `develop`. -- Docs: replace `CLAUDE.md` with `AGENTS.md` codifying the agent workflow and - the release procedure, add migration-ordering guidance, and prune implemented - plans. -- A large batch of new server/client test coverage. - -## [0.91.0] - 2026-06-18 - -Gitmost is a community-focused fork of Docmost. This release drops the -Enterprise-Edition code paths and introduces the in-app AI agent chat, a RAG -knowledge layer, an embedded MCP server, and the Gitmost rebrand. - -### Breaking Changes - -- Remove all frontend Enterprise-Edition code — the project now builds as a pure - community edition. -- AI agent: drop the `updateComment` tool from the agent toolset. - -### Added - -- **AI agent chat**: per-user in-app AI agent with a floating chat window. - Includes live streaming responses, open-page context awareness, a typing - indicator, a Stop control, and copy/export of a conversation as Markdown. -- **AI agent write tools & provenance**: reversible write tools (page - create/update/move/soft-delete, comment reply/resolve) enforced by Docmost - CASL, plus non-spoofable agent provenance signed into access/collab tokens and - recorded on pages and comments. No permanent/force delete. -- **RAG knowledge retrieval**: workspace bulk reindex with a manual "Reindex - now" action, hybrid RRF retrieval with heading-breadcrumb chunks and a merged - search tool, dimension-agnostic embeddings, and RAG indexing coverage shown in - AI settings. -- **MCP**: embedded community MCP server served at `/mcp`; an admin UI to - list/add/edit/delete external MCP servers with per-server enable toggle, Test, - write-only auth headers, a tool allowlist, and a Tavily preset; `insert_image`/ - `replace_image` can now fetch sources from web URLs. -- **AI configuration**: dedicated AI provider settings with separate base URL and - API key for the chat vs. embedding model, and per-endpoint test buttons. -- **Branding**: Gitmost logo, favicon, and app name. -- **Collaboration**: comment resolution for the community build; agent edits are - separated from human edits in page history. -- **Editor / client**: page-tree open/closed state is persisted per - workspace+user; the brand logo shows the current `git describe` version. - -### Changed - -- Move AI settings to a dedicated `/settings/ai` page and redesign it with - per-endpoint test buttons. -- `edit_page_text` now returns verifiable mutation results and refuses - formatting-only edits; the agent tolerates Markdown in - `edit_page_text`/`insert_node` locators. -- Compact large tool outputs before persisting them. -- Reduce the chat window corner radius, shrink the chat message font size, and - shrink the default page-tree indentation from 16px to 8px. - -### Fixed - -- AI chat: stable streaming store id so optimistic and streamed messages render - immediately; provider errors stay visible and surface the real provider - status/message; the composer draft survives the new-chat id-adoption remount; - the workspace AI-chat enable toggle is restored for self-hosted. -- AI providers: use OpenAI Chat Completions for multi-turn requests; self-heal - the stored provider settings JSON; drop the hard output-token cap that - truncated complex tool calls. -- RAG: make the indexer observable and bound hung embedding calls; stop the - coverage bar from sticking below 100% on empty pages. -- Collaboration: use `-` instead of `:` in the agent page-history job id. -- Accessibility fixes (#2275) and table jitter on the edit/read toggle (#2252). - -### Removed - -- Non-functional DOCX / PDF / Confluence import buttons. - -### Documentation - -- README: rebrand to the Gitmost fork with EE-free positioning, an MCP - comparison, a grouped roadmap, a Russian translation, a "Migration from - Docmost" section, and AI agent chat documentation. -- Add plans for mobile app, voice dictation, arbitrary HTML/CSS/JS embeds, and - offline sync & PWA. - -### Internal - -- Add `.claude/worktrees/` to `.gitignore`. -- CI: add a `develop` workflow with `workflow_dispatch`; ignore cache errors in - the develop and release builds. -- Build: drop the private EE submodule, retarget CI to GHCR, and update the - Docker image to the GHCR registry. - -[Unreleased]: https://github.com/vvzvlad/gitmost/compare/v0.93.0...HEAD -[0.93.0]: https://github.com/vvzvlad/gitmost/compare/v0.91.0...v0.93.0 -[0.91.0]: https://github.com/vvzvlad/gitmost/compare/v0.90.1...v0.91.0 diff --git a/README.md b/README.md deleted file mode 100644 index b63b76f5..00000000 --- a/README.md +++ /dev/null @@ -1,225 +0,0 @@ -
-

Gitmost

-

- Open-source collaborative wiki and documentation software. -
- A fully-open community fork of Docmost. -

-
-
- -**English** · [Русский](README.ru.md) - -## About this fork - -**Gitmost** is a community fork of [Docmost](https://github.com/docmost/docmost), an open-source -collaborative wiki and documentation app. - -The goal of the fork is a **100% open, AGPL-only build with no Enterprise-Edition (EE) code**: - -- **No EE code at all.** All proprietary Enterprise-Edition sources were removed — the private - `apps/server/src/ee` submodule, the `apps/client/src/ee` directory (201 files) and the - `packages/ee` package are gone. There is no license gating: every feature is available to everyone. -- **Replacements are written from scratch.** Features that previously lived behind the enterprise - license (e.g. comment resolution, the AI agent chat, the `/mcp` server) were re-implemented from - scratch on top of the community codebase. No EE code is reused, and there is no - entitlement/feature-flag wall. -- **No upsell.** There are no "buy a license" / "upgrade to Enterprise" banners, trial nags, or - locked-feature placeholders anywhere in the UI. -- Authentication is plain email + password (no SSO/LDAP/cloud/billing flows). - -## What's different from Docmost - -| Change | Details | -| --- | --- | -| **EE code removed** | Stripped all client and server Enterprise-Edition code; ships as a clean community/AGPL build with no license checks. | -| **Comment resolution** | Re-implemented from scratch as a community feature (resolve / re-open with Open/Resolved tabs). No EE code reused, available to anyone who can comment. | -| **Embedded MCP server** | A community MCP server (`@docmost/mcp`, 38 tools) is served over HTTP at `/mcp` — no enterprise license required. Replaces the removed license-gated EE MCP. | -| **AI agent chat** | Built-in AI agent chat over your wiki, written from scratch as a community feature — no enterprise license. The agent reads and edits pages on your behalf (scoped to your permissions), with full-text + vector (RAG) search and optional web access via external MCP servers. | -| **Rebranding** | App logo / name changed from *Docmost* to *Gitmost*. | -| **Compact page tree** | Default page-tree indentation reduced from 16px to 8px per nesting level. | -| **Persistent page-tree state** | The sidebar page tree remembers which nodes you expanded/collapsed across reloads — saved in the browser (localStorage), scoped per workspace + user so accounts sharing a browser don't clash. Upstream Docmost forgets the tree on every reload. | -| **CI / images** | Release CI publishes container images to GHCR (`ghcr.io/vvzvlad/gitmost`) using the built-in `GITHUB_TOKEN` instead of Docker Hub. | - -### Embedded MCP server - -Gitmost has **our own MCP server** — [docmost-mcp](https://github.com/vvzvlad/docmost-mcp), -which we wrote — **built directly into the app** and served at `/mcp`. It exposes **38 -agent-native tools**: surgical per-block edits (patch / insert / delete by id), -structure-preserving find/replace, scripted `(doc) => doc` transforms with a dry-run diff, -structured table editing, version history with diff / restore, comments, images and share -links — all applied through Docmost's real-time-collaboration layer, so a write never -clobbers a concurrent human edit. - -**Better than Docmost's own MCP.** Docmost's built-in MCP is an enterprise feature, and its -tools are coarse — read a page as Markdown, create / move / delete pages, replace a whole -page. Ours is built around how an agent actually edits: address one block and patch it, or -*program* the change, instead of round-tripping a ~100 KB document through the model on -every little fix. And it needs no enterprise license. - -| | **Gitmost `/mcp` (our docmost-mcp)** | Docmost's built-in MCP | -| --- | :---: | :---: | -| **Enterprise license** | Not required | Required | -| **Tools** | 38, agent-native | Coarse (read Markdown, page CRUD, replace whole page) | -| **Per-block edits / find-replace / scripted transforms** | ✅ | — | -| **Structured table editing, version diff / restore** | ✅ | — | -| **Comments, images, share links** | ✅ | — | -| **Safe real-time-collab writes (no clobber)** | ✅ | — | - -**Same server as standalone docmost-mcp — just bundled.** This is the exact -[docmost-mcp](https://github.com/vvzvlad/docmost-mcp) you can also run on its own; embedding -it doesn't make it more capable, you simply don't have to install and run a separate -process. An admin flips one toggle in **Workspace settings → AI** and any MCP client -points at `${APP_URL}/mcp`. - -### AI agent chat - -Gitmost ships a **built-in AI agent chat** over your wiki — written from scratch as a -community feature, with no enterprise license. Open it from the page header; the agent can -**read and edit** your workspace on your behalf: - -- **Full read + write toolset (~40 tools).** Search and read pages, make surgical per-block - and table edits, create / rename / move pages, diff and restore page history, and create / - resolve comments — every action runs under *your* permissions (Docmost CASL), so the agent - can never see or change anything you couldn't. -- **Safe by design.** The agent is given only **reversible** operations (page history + - trash); permanent deletion is never exposed. Agent edits are marked in page history with an - "AI agent" badge linking back to the chat. -- **Search over your content.** Full-text search plus optional vector (RAG) semantic search - across pages. -- **Web access via external MCP.** Admins can connect external MCP servers (e.g. Tavily) to - give the agent web search / internet access. -- **Bring your own model.** Configure an OpenAI-compatible endpoint — OpenAI, OpenRouter, a - local Ollama, or any self-hosted server — plus the model and API key in - **Workspace settings → AI**. The key is encrypted and never leaves the server. - -## Roadmap - -### Done - -- ✅ **MCP server** — embedded community MCP server served at `/mcp`. -- ✅ **macOS app** — native macOS app ([gitmost-app](https://github.com/vvzvlad/gitmost-app)) that embeds the UI with multi-server tabs. -- ✅ **AI chat** — built-in AI agent chat over your wiki content (read + write, RAG search, configurable provider, optional web access via external MCP). -- ✅ **Voice dictation** — microphone button in the AI agent chat and the page editor; audio is transcribed server-side (Whisper / OpenAI-compatible STT) via the workspace AI provider, with an admin toggle to show/hide it. -- ✅ **Page templates** — flag a page as a template and embed its whole content live into other pages; edits to the template propagate to every place it is inserted (whole-page transclusion on top of the existing synced blocks). -- ✅ **Public-share AI assistant** — anonymous visitors of a shared page can ask the AI agent, scoped strictly to that share's page tree (read-only, share-scoped search), behind a workspace toggle. -- ✅ **Footnotes** — academic-style footnotes: a numbered superscript reference inline (read it in place via a hover popover), with the note text living as a real, editable block at the bottom of the page; auto-numbered, collaboration-safe, and round-trips through Markdown export/import and the AI agent / MCP. - -### In progress - -- 🚧 **Git synchronization** — two-way sync of pages with a Git repository. - -### Planned - -- 🔭 **Viewer comments** — let read-only viewers leave comments. -- 🔭 **Password-protected pages** — protect individual pages / shares with a password. -- 🔭 **Windows / Linux app** — native desktop app for Windows and Linux. -- 🔭 **Mobile app** — mobile apps (iOS first, Android to follow), reusing the existing responsive web UI and editor via a Capacitor wrapper, with offline planned for later. See [docs/mobile-app-plan.md](docs/mobile-app-plan.md). -- 🔭 **Offline mode** — offline sync & PWA support. -- 🔭 **Editor & UX improvements** — blocks inside tables (lists, to-do items), column layout, additional heading levels, highlight blocks, custom emoji in callouts, floating images, anchor links for page mentions, toggles (shared-page width, aside/sidebar, spellcheck, ligatures), sanitized space-tree export, and mentions in breadcrumbs. - -## Getting started - -Gitmost follows the upstream Docmost setup. See the Docmost -[documentation](https://docmost.com/docs) for self-hosting and development instructions; replace the -`docmost/docmost` image with `ghcr.io/vvzvlad/gitmost` where applicable. - -## Migration from Docmost - -Gitmost's database schema is a **strict superset** of Docmost's. Every Gitmost-specific migration -only **adds** new tables (`page_embeddings`, `ai_chats`, `ai_chat_messages`, -`ai_provider_credentials`, `ai_mcp_servers`) and **nullable** columns — it never drops or rewrites -existing Docmost data. Migrations run automatically on startup, so migrating an existing Docmost -instance is essentially **two image swaps**. - -The only hard requirement is the database image: the AI agent's RAG storage needs the -[pgvector](https://github.com/pgvector/pgvector) extension (`CREATE EXTENSION vector`), which the -stock `postgres` image does not ship. Swap it for `pgvector/pgvector:pgNN` — the same vanilla -Postgres plus pgvector bundled, built on the official `postgres` image and fully data-compatible -with it. - -### From a current Docmost on Postgres 18 - -If your Docmost already runs `postgres:18`, it's a clean **in-place** swap — no dump/restore needed, -the existing data directory is reused as-is: - -```diff - services: - docmost: -- image: docmost/docmost:latest -+ image: ghcr.io/vvzvlad/gitmost:latest - ... - db: -- image: postgres:18 -+ image: pgvector/pgvector:pg18 -``` - -`APP_SECRET`, `DATABASE_URL`, `REDIS_URL` and the storage volume stay unchanged. On the first -start the new migrations apply on top of your existing schema (`CREATE EXTENSION vector` plus the -`page_embeddings` and AI tables); watch the logs for `Migration "..." executed successfully`. - -> ⚠️ **Never change `APP_SECRET` after setup.** It does double duty: it signs JWTs *and* derives the -> AES-256-GCM key that encrypts stored AI-provider credentials (API keys). Rotating it makes every -> saved AI API key undecryptable (you'd have to re-enter them in AI settings) and invalidates all -> existing sessions. Pick it once, keep it stable, and back it up together with your database. - -### Notes - -- **Back up first.** Take a `pg_dump` before swapping — migrations apply in place, and the - container exits if a migration fails. -- **Volume layout is identical.** `pgvector/pgvector` is built on the official `postgres` image and - uses the same `PGDATA`, so keep your existing data volume and its mount path unchanged — the swap - reuses the directory as-is. -- **Match the Postgres major.** A Postgres data directory is not compatible across major versions. - If your Docmost runs an older major (e.g. Postgres 16), use the matching `pgvector/pgvector:pg16` - to keep the in-place swap, or move the data with `pg_dump` / `pg_restore` into the new instance. -- **Managed Postgres.** If you don't use the bundled `db` container, make sure pgvector is available - and your database role is allowed to run `CREATE EXTENSION vector`. -- **AI is opt-in.** The `page_embeddings` table stays empty until you configure an AI provider; - existing pages are indexed on their next edit. pgvector is still required for the migration to - apply at all. - -## Features - -- Real-time collaboration -- Diagrams (Draw.io, Excalidraw and Mermaid) -- Spaces -- Permissions management -- Groups -- Comments (with resolve / re-open) -- Page history -- Search -- File attachments -- Embeds (Airtable, Loom, Miro and more) -- Translations (10+ languages) -- Embedded MCP server (`/mcp`) -- AI agent chat over your wiki (read + write, RAG search, external MCP / web access) - -### Screenshots - -

-AI agent chat -home -editor -

- -### License - -Gitmost is licensed under the open-source AGPL 3.0 license. - -Unlike upstream Docmost, this fork contains **no Enterprise-Edition code** — the `apps/server/src/ee`, -`apps/client/src/ee` and `packages/ee` directories have been removed, so there are no files governed -by an enterprise license. - -### Credits - -Gitmost is based on [Docmost](https://github.com/docmost/docmost) by the Docmost team. Huge thanks to -them for the original open-source project. - -Crowdin - -[Crowdin](https://crowdin.com/) for providing access to their localization platform. - -Algolia-mark-square-white - -[Algolia](https://www.algolia.com/) for providing full-text search to the docs. diff --git a/README.ru.md b/README.ru.md deleted file mode 100644 index cb0d12ad..00000000 --- a/README.ru.md +++ /dev/null @@ -1,212 +0,0 @@ -
-

Gitmost

-

- Совместная вики и система документации с открытым исходным кодом. -
- Полностью открытый community-форк Docmost. -

-
-
- -[English](README.md) · **Русский** - -## Об этом форке - -**Gitmost** — это community-форк [Docmost](https://github.com/docmost/docmost), -открытой системы для совместной работы над вики и документацией. - -Цель форка — **сборка на 100% открытая, только под AGPL, без кода Enterprise-редакции (EE)**: - -- **Никакого EE-кода.** Весь проприетарный код Enterprise-редакции удалён — приватный - сабмодуль `apps/server/src/ee`, каталог `apps/client/src/ee` (201 файл) и пакет - `packages/ee`. Никаких лицензионных ограничений: все функции доступны всем. -- **Замены написаны с нуля.** Функции, которые раньше были скрыты за enterprise-лицензией - (например, резолв комментариев, чат с AI-агентом, сервер `/mcp`), переписаны с нуля поверх - community-кодовой базы. EE-код не переиспользуется, нет проверок entitlement и feature-флагов. -- **Никакого навязывания.** В интерфейсе нигде нет плашек «купите лицензию» / «перейдите на - Enterprise», напоминаний о триале и заглушек на месте заблокированных функций. -- Аутентификация — обычная пара email + пароль (без SSO/LDAP/облака/биллинга). - -## Чем отличается от Docmost - -| Изменение | Подробности | -| --- | --- | -| **Удалён EE-код** | Вырезан весь код Enterprise-редакции на клиенте и сервере; это чистая community/AGPL-сборка без лицензионных проверок. | -| **Резолв комментариев** | Переписан с нуля как community-функция (резолв / переоткрытие с вкладками «Открытые» / «Решённые»). EE-код не используется, доступно любому, кто может комментировать. | -| **Встроенный MCP-сервер** | Community MCP-сервер (`@docmost/mcp`, 38 инструментов) отдаётся по HTTP на `/mcp` — без enterprise-лицензии. Заменяет удалённый лицензируемый EE MCP. | -| **Чат с AI-агентом** | Встроенный чат с AI-агентом по содержимому вики, написанный с нуля как community-функция — без enterprise-лицензии. Агент читает и редактирует страницы от вашего имени (в рамках ваших прав), с полнотекстовым + векторным (RAG) поиском и опциональным доступом в интернет через внешние MCP-серверы. | -| **Ребрендинг** | Логотип / название приложения изменены с *Docmost* на *Gitmost*. | -| **Компактное дерево страниц** | Отступ дерева страниц по умолчанию уменьшен с 16px до 8px на уровень вложенности. | -| **Сохранение состояния дерева страниц** | Дерево страниц в сайдбаре запоминает, какие узлы вы раскрыли/свернули, между перезагрузками — состояние хранится в браузере (localStorage) отдельно для каждой пары воркспейс + пользователь, чтобы аккаунты в одном браузере не пересекались. В оригинальном Docmost дерево сбрасывается при каждой перезагрузке. | -| **CI / образы** | Release-CI публикует контейнерные образы в GHCR (`ghcr.io/vvzvlad/gitmost`) через встроенный `GITHUB_TOKEN` вместо Docker Hub. | - -### Встроенный MCP-сервер - -В Gitmost есть **наш собственный MCP-сервер** — [docmost-mcp](https://github.com/vvzvlad/docmost-mcp), -который мы написали сами, — **встроенный прямо в приложение** и доступный на `/mcp`. Он даёт -**38 agent-native инструментов**: точечное редактирование по блокам (patch / insert / delete -по id), find/replace с сохранением структуры, скриптовые трансформации `(doc) => doc` с -предпросмотром диффа, структурное редактирование таблиц, история версий с диффом / -восстановлением, комментарии, изображения и ссылки на шаринг — всё применяется через слой -real-time-коллаборации Docmost, поэтому запись никогда не затирает параллельную правку -человека. - -**Лучше, чем родной MCP у Docmost.** Встроенный MCP у Docmost — enterprise-функция, и его -инструменты примитивные: прочитать страницу как Markdown, создать / переместить / удалить -страницу, заменить страницу целиком. Наш сделан под то, как агент реально редактирует: -адресовать один блок и пропатчить его или *запрограммировать* изменение, а не гонять -документ на ~100 КБ через модель ради каждой мелкой правки. И enterprise-лицензия не нужна. - -| | **`/mcp` в Gitmost (наш docmost-mcp)** | Родной MCP у Docmost | -| --- | :---: | :---: | -| **Enterprise-лицензия** | Не нужна | Нужна | -| **Инструменты** | 38, agent-native | Примитивные (Markdown, CRUD страниц, замена целиком) | -| **Правки по блокам / find-replace / скриптовые трансформации** | ✅ | — | -| **Структурное редактирование таблиц, дифф / восстановление версий** | ✅ | — | -| **Комментарии, изображения, ссылки на шаринг** | ✅ | — | -| **Безопасная запись через real-time-коллаборацию (без затирания)** | ✅ | — | - -**Это тот же сервер, что и отдельный docmost-mcp, — просто встроенный.** Это ровно тот самый -[docmost-mcp](https://github.com/vvzvlad/docmost-mcp), который можно запускать и отдельно; -от встраивания он не становится «мощнее» — просто не нужно ставить и держать отдельный -процесс. Админ включает его одним переключателем в **Настройки воркспейса → AI**, а -любой MCP-клиент указывает на `${APP_URL}/mcp`. - -### Чат с AI-агентом - -В Gitmost есть **встроенный чат с AI-агентом** по содержимому вики — написанный с нуля -как community-функция, без enterprise-лицензии. Открывается из шапки страницы; агент умеет -**читать и редактировать** ваш воркспейс от вашего имени: - -- **Полный набор инструментов чтения + записи (~40 штук).** Поиск и чтение страниц, - точечные правки по блокам и в таблицах, создание / переименование / перемещение страниц, - дифф и восстановление версий, создание / резолв комментариев — каждое действие - выполняется под *вашими* правами (Docmost CASL), поэтому агент не видит и не меняет - ничего, чего не могли бы вы сами. -- **Безопасность по умолчанию.** Агенту доступны только **обратимые** операции (история - версий + корзина); перманентное удаление не экспонируется. Правки агента помечаются в - истории версий бейджем «AI-агент» со ссылкой на чат. -- **Поиск по вашему контенту.** Полнотекстовый поиск плюс опциональный векторный (RAG) - семантический поиск по страницам. -- **Доступ в интернет через внешние MCP.** Админ может подключить внешние MCP-серверы - (например, Tavily), чтобы дать агенту веб-поиск / доступ в интернет. -- **Своя модель.** OpenAI-совместимый эндпоинт — OpenAI, OpenRouter, локальный Ollama или - любой self-hosted-сервер — плюс модель и API-ключ настраиваются в - **Настройки воркспейса → AI**. Ключ шифруется и никогда не покидает сервер. - -## Дорожная карта - -### Готово - -- ✅ **MCP-сервер** — встроенный community MCP-сервер на `/mcp`. -- ✅ **Приложение для macOS** — нативное приложение для macOS ([gitmost-app](https://github.com/vvzvlad/gitmost-app)), встраивающее UI с вкладками для нескольких серверов. -- ✅ **AI-чат** — встроенный чат с AI-агентом по содержимому вики (чтение + запись, RAG-поиск, настраиваемый провайдер, опциональный доступ в интернет через внешние MCP). -- ✅ **Голосовая диктовка** — кнопка-микрофон в чате AI-агента и в редакторе страниц; аудио распознаётся на сервере (Whisper / OpenAI-совместимый STT) через AI-провайдер воркспейса, с тумблером админа для показа/скрытия. -- ✅ **Шаблоны страниц** — пометить страницу шаблоном и вставлять её содержимое живой ссылкой в другие страницы; правки шаблона распространяются на все места вставки (whole-page-транслюзия поверх существующих synced-блоков). -- ✅ **AI-ассистент на публичных шарах** — анонимный зритель расшаренной страницы может спросить AI-агента, который ищет строго по дереву этой шары (read-only, share-scoped поиск), за тумблером воркспейса. -- ✅ **Сноски** — сноски академического вида: нумерованная ссылка-надстрочник прямо в тексте (читается на месте во всплывающем окне по наведению), а текст сноски живёт реальным редактируемым блоком внизу страницы; авто-нумерация, безопасна для совместного редактирования, переживает экспорт/импорт Markdown и доступна AI-агенту / MCP. - -### В процессе - -- 🚧 **Синхронизация с Git** — двусторонняя синхронизация страниц с Git-репозиторием. - -### В планах - -- 🔭 **Комментарии зрителей** — возможность комментировать для пользователей с доступом только на чтение. -- 🔭 **Защищённые паролем страницы** — защита отдельных страниц / шар паролем. -- 🔭 **Приложение для Windows / Linux** — нативное десктоп-приложение для Windows и Linux. -- 🔭 **Мобильное приложение** — мобильные приложения (iOS обязательно, Android как пойдёт) на базе существующей адаптивной веб-версии и редактора через обёртку Capacitor; оффлайн запланирован на будущее. См. [docs/mobile-app-plan.md](docs/mobile-app-plan.md). -- 🔭 **Офлайн-режим** — офлайн-синхронизация и поддержка PWA. -- 🔭 **Улучшения редактора и UX** — блоки внутри таблиц (списки, чек-листы), колоночная вёрстка, дополнительные уровни заголовков, highlight-блоки, кастомные эмодзи в callout-ах, плавающие изображения, anchor-ссылки на упоминания страниц, тоглы (ширина шары, aside/сайдбар, spellcheck, лигатуры), санитизация экспорта дерева спейса и mentions в хлебных крошках. - -## С чего начать - -Gitmost повторяет процесс установки upstream-Docmost. Инструкции по self-hosting и разработке -смотрите в [документации](https://docmost.com/docs) Docmost; где это применимо, заменяйте образ -`docmost/docmost` на `ghcr.io/vvzvlad/gitmost`. - -## Миграция с Docmost - -Схема БД Gitmost — это **строгий superset** схемы Docmost. Все Gitmost-специфичные миграции только -**добавляют** новые таблицы (`page_embeddings`, `ai_chats`, `ai_chat_messages`, -`ai_provider_credentials`, `ai_mcp_servers`) и **nullable**-колонки — они никогда не удаляют и не -переписывают существующие данные Docmost. Миграции применяются автоматически при старте, поэтому -миграция существующего инстанса Docmost — это по сути **замена двух образов**. - -Единственное жёсткое требование — образ БД: RAG-хранилище AI-агента использует расширение -[pgvector](https://github.com/pgvector/pgvector) (`CREATE EXTENSION vector`), которого нет в -стоковом образе `postgres`. Замените его на `pgvector/pgvector:pgNN` — это тот же ванильный -Postgres со встроенным pgvector, собранный на базе официального образа `postgres` и полностью -data-совместимый с ним. - -### С текущего Docmost на Postgres 18 - -Если ваш Docmost уже работает на `postgres:18`, это чистая замена in-place — без -dump/restore, существующий каталог данных переиспользуется как есть: - -```diff - services: - docmost: -- image: docmost/docmost:latest -+ image: ghcr.io/vvzvlad/gitmost:latest - ... - db: -- image: postgres:18 -+ image: pgvector/pgvector:pg18 -``` - -`APP_SECRET`, `DATABASE_URL`, `REDIS_URL` и том сторейджа остаются прежними. При первом запуске -новые миграции применяются поверх вашей схемы (`CREATE EXTENSION vector` плюс таблицы -`page_embeddings` и AI-таблицы); следите в логах за строками `Migration "..." executed successfully`. - -> ⚠️ **Никогда не меняйте `APP_SECRET` после установки.** Он выполняет двойную роль: подписывает JWT -> *и* служит материалом для ключа AES-256-GCM, которым шифруются сохранённые ключи AI-провайдеров -> (API-ключи). Смена секрета сделает все сохранённые AI-ключи нерасшифровываемыми (придётся вводить -> их заново в настройках AI) и инвалидирует все текущие сессии. Задайте его один раз, держите -> неизменным и бэкапьте вместе с базой данных. - - -## Возможности - -- Совместная работа в реальном времени -- Диаграммы (Draw.io, Excalidraw и Mermaid) -- Пространства (Spaces) -- Управление правами доступа -- Группы -- Комментарии (с резолвом / переоткрытием) -- История страниц -- Поиск -- Вложения файлов -- Встраивания (Airtable, Loom, Miro и другие) -- Переводы (10+ языков) -- Встроенный MCP-сервер (`/mcp`) -- Чат с AI-агентом по вики (чтение + запись, RAG-поиск, внешние MCP / доступ в интернет) - -### Скриншоты - -

-Чат с AI-агентом -home -editor -

- -### Лицензия - -Gitmost распространяется под открытой лицензией AGPL 3.0. - -В отличие от upstream-Docmost, этот форк **не содержит кода Enterprise-редакции** — каталоги -`apps/server/src/ee`, `apps/client/src/ee` и `packages/ee` удалены, поэтому файлов под -enterprise-лицензией здесь нет. - -### Благодарности - -Gitmost основан на [Docmost](https://github.com/docmost/docmost) от команды Docmost. Огромное -спасибо им за оригинальный открытый проект. - -Crowdin - -[Crowdin](https://crowdin.com/) — за доступ к их платформе локализации. - -Algolia-mark-square-white - -[Algolia](https://www.algolia.com/) — за полнотекстовый поиск по документации.