Agent skills, system prompts, and AI developer rules for siyuan-note/siyuan
# AGENTS.md
SiYuan repository guide. Module path `github.com/siyuan-note/siyuan`, license AGPL-3.0.
---
## 1. Non-negotiable constraints
### Do not hand-edit
- `app/stage/protyle/js/lute/lute.min.js` (built from upstream `88250/lute`)
- `app/stage/build/**`, `app/src/types/dist/**`
- `app/changelogs/**` (generated by separate tooling)
- `app/kernel/SiYuan-Kernel*`, `*.syso`, `kernel/kernel.aar`
- `app/pandoc/*`
### Verification and prohibited operations
1. **Frontend verification:** Do not use `npx webpack` or `pnpm dev` to verify changes; after changes, run `pnpm run lint` with `app/` as the working directory to check code style
2. **Frontend build:** Do NOT run `pnpm build` â the developer runs `pnpm dev` manually, and `pnpm build` will conflict with it, producing broken bundles
3. **Kernel development:** After modifying Go code, run `gofmt`, but do not compile the kernel binary or restart a running kernel; the developer handles both manually
4. **Git:** **NEVER** run `git commit` / `git push` unless explicitly asked â no exceptions
---
## 2. Project-specific rules
1. **i18n:**
- New keys go at the **top** of each `langs/*.json` object; add to every language file (reference `en.json`)
- Indent `langs/*.json` with tabs, using one tab per nesting level; do not use spaces for indentation
- Exception: inside the `_kernel` object, append new entries at the **end** using the next incremental numeric key
- Each language must be properly translated â do NOT copy the same text across all language files
- Use three ASCII periods (`...`) for ellipses in all localized strings; do not use Unicode ellipsis characters (`âŚ` or `âŚâŚ`)
- Setting description tip strings must not end with a period or equivalent sentence-ending mark (for example `.`, `ă`, or `༤`)
- Domains: `ld246.com` only in `zh-CN.json`; use `liuyun.io` in all other languages
- In `zh-TW` localization and the Traditional Chinese user guide, translate SiYuan's content-model term Block as `ĺĺĄ`; never abbreviate it as `ĺĄ`
- Use `ĺĺĄ` consistently in compound terms, for example `ĺ
§ĺŽšĺĺĄ`, `ĺĺĺĄ`, `çśĺĺĄ`, `ĺľĺ
ĽĺĺĄ`, `ç¨ĺźç˘źĺĺĄ`, `ĺĺĄ ID`, `ĺĺĄć¨`, and `ĺĺĄç´`
- Translate Block Reference as `ĺĺĄĺźç¨` and Blockquote as `ĺźčż°ĺĺĄ`; do not confuse them or reverse the word order
- When counting content blocks, use `ĺĺĺĄ` rather than using `ĺĄ` as a classifier or abbreviation
- Do not mechanically replace `ĺĄ` in ordinary words with `ĺĺĄ`; preserve non-content-block terms such as `ĺĺĄ` for data chunks and `čŚĺćšĺĄ` for Checkbox
- Keep block terminology consistent between the Traditional Chinese interface and user guide
- After modifying i18n files, run `python scripts/check-lang-keys.py` to verify key completeness across all language files
2. **Cross-platform scripting:**
- Do not assume the current shell is Bash, zsh, or PowerShell. Confirm the shell before using shell-specific syntax; otherwise avoid constructs such as `&&`, heredocs, and `/dev/null`
- For simple sequences, use separate command calls and set the command working directory instead of chaining `cd` with another command
- For multi-step logic, write and run a temporary Node.js or Python script. On Windows, avoid PowerShell unless necessary
- Do not pass non-ASCII text through shell pipelines, PowerShell here-strings, `python -c`, or `node -e`; write the text to a UTF-8 file with a file-editing tool and consume that file instead
3. **Icons:** Do not hand-write SVG; use existing icons from `app/appearance/icons/litheness/icon.js` when possible
4. **User guide:** When editing the user guide, follow `docs/SY-FORMAT.md`
- When a feature adds or changes shortcuts, update the shortcut documentation in the user guide in the same change; if the appropriate section is unclear, ask the user where it should be placed
- Represent in-app UI navigation paths as segmented `kbd` text marks: use one `NodeTextMark` with `TextMarkType: "kbd"` per navigation level, and place a plain `NodeText` containing ` - ` between adjacent levels
- When a `kbd` path is embedded in prose, use exactly one ASCII space outside the path on each side when adjacent ordinary text exists; do not add an outer space at the start or end of a block
- Omit the left outer space when the first `kbd` immediately follows full-width punctuation (for example, `ďź` or `ă`); apply this rule to every language, including Chinese and Japanese, but do not apply it to half-width punctuation
- Omit the right outer space when `kbd` is immediately followed by punctuation, whether full-width or half-width; keep the internal ` - ` separators of segmented UI paths unchanged
5. **Git:**
- When explicitly asked to commit, follow the style of recent commits (gitmoji prefix + subject, in English)
- Append the full issue/PR URL to the end of the commit title (e.g. `https://github.com/siyuan-note/siyuan/issues/<NNN>`, not the `#NNN` short form â it is clickable) only when a related issue exists; never put the URL in the commit body, and do not fabricate one
6. **GitHub:** Prefer the GitHub CLI (`gh`) for all GitHub operations, including reading issues, comments, pull requests, commits, statuses, and metadata. If `gh` is unavailable or does not support the operation, fall back to the GitHub API or web interface
- For GitHub write operations containing non-ASCII text on Windows or when shell encoding is uncertain, use this file-based workflow:
1. Create the request payload as UTF-8 JSON with a file-editing tool, not an inline shell command
2. Store it in the operating system's temporary directory with a unique name such as `siyuan-gh-<operation>-<timestamp>.json`; do not leave temporary payloads in the repository
3. Call the appropriate endpoint with `gh api --method <method> "<endpoint>" --input "<absolute-json-path>"`
4. Inspect the returned resource and read it back with `gh api` to verify the published text exactly, including line breaks and non-ASCII characters
5. Delete the temporary JSON file and confirm that it no longer exists
- Example for an issue comment: write `{"body":"<comment text>"}` to the UTF-8 JSON file, run `gh api --method POST "repos/{owner}/{repo}/issues/<number>/comments" --input "<absolute-json-path>"`, then read the returned comment by its `id` before deleting the file
7. **Issue titles:** Whenever the user asks to generate an issue title, provide it in English regardless of the wording of the request, and do not start it with `Fix`
- If the issue is labeled `Bug`, objectively describe the problem or symptom instead of writing from a bug-fix perspective
- If the issue is labeled `Enhancement`:
- For improvements to existing functionality, write the title from an improvement perspective and prefer `Improve ...`
- For capabilities that did not previously exist, write the title from a support perspective and prefer `Support ...`
- If no applicable label is available, infer the perspective from the issue content
8. **LD246:** When accessing `ld246.com`, set the HTTP `User-Agent` header to `SiYuan-Coding-Agent`
9. **Configurable entries:**
- Treat the `data-id` of a configurable desktop menu item and the `data-type` of a configurable dock entry as persisted configuration identifiers. Do not rename or reuse them unless the same change migrates existing visibility and order configuration
- When adding, removing, renaming, or moving a configurable desktop menu item or dock entry, or changing its `data-id` / `data-type`, update `app/src/config/entryVisibility/catalog.ts` in the same change, including its type, hierarchy, label, Simple profile default, and default position, and update the related tests
- Give every configurable desktop menu separator a stable `data-id` and register it in the catalog as a `separator`. Keep the catalog order aligned with the actual menu declaration order because it defines the built-in order and where new entries are merged into existing custom profiles
- Keep parent and child paths aligned with the actual menu hierarchy. Dock entries support visibility only and must not be included in sorting
- Cover catalog consistency, separator placement, order migration, and plugin-slot preservation in the related tests. Configured menus must not produce leading, trailing, or consecutive separators
- The menu `ignore` option controls conditional rendering and must not be used to opt an entry out of visibility or order configuration
---
## 3. Coding conventions
1. **Comments:** Wrap code comments at 120 characters
2. **Comments:** Describe what the code does, not what it replaced â don't reference the old implementation in comments
3. **Comments:** Write comments in Chinese
4. **Punctuation:** Use language-appropriate punctuation (e.g. Chinese punctuation ďźăďźďźďźďźăă for Chinese, not ASCII); do not hard-code it in code â put it in the i18n language files so each locale renders its own. Applies to comments, user guide, `.md` docs, etc.
5. **UI paths:** In all contexts, including code comments, UI text, i18n, user guides, documentation, issue/PR content, and responses, separate navigation levels with a hyphen surrounded by spaces (for example, `莞罎 - 忍ćˇéŽ - éç¨`); do not use arrow symbols such as `â`
6. **Markdown:** Do not hand-wrap; keep each line (paragraphs, table rows, list items, etc.) on a single line
7. **TypeScript/JavaScript:** Semicolons required, use double quotes, indent with spaces
8. **CSS:** Do not use the `:has()` selector because of its performance impact
9. **Go:** Format with `gofmt` after editing
---
## 4. Required toolchain
| Tool | Version | Source of truth |
|---|---|---|
| Go | see `go` directive | `kernel/go.mod` |
| Node (+ pnpm) | see CI matrix | `.github/workflows/cd.yml`, `app/package.json` (`packageManager` field) |
---
## 5. Repository layout
**Architecture:** Go kernel (`kernel/`) + TypeScript frontend (`app/`), plus a separate `export` bundle (global `Protyle`, entry `src/protyle/method.ts`) for rendering rich content in exported HTML / PDF preview. Read versions from `kernel/go.mod`, `app/package.json`, `kernel/util/working.go`.
Top level (repo root):
| Path | Contents |
|---|---|
| `kernel/` | Go backend â server, data engine, API, all domain logic |
| `app/` | TypeScript frontend (Electron/web), built by webpack into `app/stage/build/` |
| `app/appearance/` | Themes, icons, **i18n** (`appearance/langs/*.json`) |
| `app/stage/` | Build output served by the kernel |
| `app/changelogs/` | Per-version changelog markdown |
| `.github/` | `CONTRIBUTING.md` (+zh-CN), `SECURITY.md`, `CODE_OF_CONDUCT.md`, `PULL_REQUEST_TEMPLATE.md`, issue templates, `workflows/` |
| `scripts/` | Release packaging: `win-build.bat`, `darwin-build.sh`, `linux-build.sh`, `parse-changelog.py`, `check-lang-keys.py` |
### Major `kernel/` packages (under `kernel/`)
| Package | Responsibility |
|---|---|
| `main.go` (`//go:build !mobile`) | Desktop entry point â `cli/cmd` |
| `cli/cmd/` | Cobra CLI subcommands (`serve`, `notebook`, `block`, `search`, `sql`, `export`, `repo`, `sync`, âŚ) |
| `model/` | **Core domain** (~70 files): blocks/trees, transactions, indexing, search, attribute views, export, history, sync, flashcards, AI, CalDAV/CardDAV, auth |
| `treenode/` | In-memory tree over the Lute AST + `blocktree.db` (`BlockTree{ID,RootID,ParentID,BoxID,Path,HPath,Type,...}`) |
| `av/` | **Attribute View** (database) engine: values, filters, sorts, layouts (table/kanban/gallery) |
| `sql/` | **Embedded SQLite** (`siyuan.db`, `history.db`, `asset_content.db`) + FTS5; async index queues |
| `search/` | FTS tokenizer helpers, CJK conversion (`hanconv.go`) |
| `bazaar/` | Marketplace: plugins/widgets/themes/icons/templates |
| `filesys/` | Read/write `.sy` files on disk (via `filelock`) |
| `server/` | Gin server bootstrap (`serve.go`): middleware, TLS/cmux, WebDAV/CalDAV/CardDAV, WebSocket, MCP |
| `api/` | HTTP route registration (`router.go::ServeAPI`, ~400 endpoints) + per-area handlers |
| `conf/` | Configuration structs |
| `util/` | Cross-cutting: `working.go` (workspace, `Boot()`), `lute.go`, `i18n.go`, `websocket.go` (melody push), `result.go` (API envelope) |
| `plugin/` | Plugin subsystem (kernel side) |
| `mcp/` | MCP (Model Context Protocol) server |
| `agent/` | AI agent runtime |
| `mobile/`, `harmony/` | `//go:build mobile` gomobile bindings for Android/iOS/HarmonyOS |
### Frontend (`app/src/`) highlights
| Dir | Purpose |
|---|---|
| `index.ts` | Main `App` class â boots SPA, opens main WebSocket, handles WS push events |
| `window/` | Detached Electron window variant |
| `protyle/` | **The block editor** â `wysiwyg/`, `toolbar/`, `gutter/`, `breadcrumb/`, `hint/`, `scroll/`, `undo/`, `preview/`, `render/` (incl. `render/av/`) |
| `editor/`, `layout/`, `menus/`, `dialog/`, `config/`, `mobile/`, `ai/`, `sync/`, `history/`, `search/`, `card/` | Feature modules |
| `util/fetch.ts` | `fetchGet`/`fetchPost` â all kernel calls |
| `layout/Model.ts` | WebSocket client all UI binds to |
| `constants.ts` | Global constants (version, IDs, storage keys) |
Four webpack configs each emit a separate bundle to `app/stage/build/{app,desktop,mobile,export}/`. The kernel's `serveAppearance` picks which bundle to serve based on User-Agent. The `export` bundle is different from the other three: it is not an app UI â it is a client-side library (global `Protyle`, entry `src/protyle/method.ts`) exposing renderers for code highlighting, math (KaTeX), and diagrams (Mermaid/flowchart/graphviz/âŚ). It is loaded by the HTML pages assembled during export (`app/src/protyle/export/index.ts`) â the desktop PDF preview window and standalone exported HTML files â so rich content renders outside the editor.
---
## 6. Related repositories (navigation)
SiYuan spans several repos. This repo (`siyuan`) holds the kernel + Electron/web frontend; the others are separate projects with their own tooling.
| Repo | Role / what to know |
|---|---|
| `siyuan` | **This repo** â kernel + Electron/web/tablet UI |
| `siyuan-android` / `siyuan-ios` / `siyuan-harmony` | Native apps wrapping the gomobile kernel; build steps differ per platform â see each project's README |
| `siyuan-chrome` | Browser extension (web clipper); talks to the running kernel over HTTP only |
| `siyuan-testing` | Playwright end-to-end tests for a running SiYuan instance; test data belongs in the `SiYuan Testing` notebook â see that repository's `AGENTS.md` |
| `petal` | SiYuan Plugin API declaration (the plugin system is named "petal"); consumed by plugins, not a kernel Go dependency |
| `lute` | Markdown/Kramdown AST engine â the editor + `.sy` format; also the source of the bundled `lute.min.js` (a GopherJS build served to the frontend). **Lives under `$GOPATH/src/github.com/88250/lute`, not as a sibling repo** |
| `dejavu` | Data repo / sync engine (encrypted snapshots) |
| `riff` | Spaced-repetition (SRS) flashcard scheduler |
| `gulu` | General Go utility library (`gulu.Ret`, `gulu.JSON`, âŚ) |
| `eventbus` | In-process event bus |
| `filelock` | Cross-platform file locking (`.sy` read/write) |
| `httpclient` | HTTP client wrapper (cloud / sync / bazaar calls) |
| `logging` | Leveled logging used throughout the kernel |
| `go-sqlite3` / `pdfcpu` | Maintainer's forks, pulled in via permanent `replace` in `kernel/go.mod` (keep those) |
| `epub` / `clipboard` / `go-humanize` / `vitess-sqlparser` / `dataparser` / `encryption` | Smaller Go libraries (export / clipboard / formatting / SQL parse / data parse / crypto) |
All Go libraries above are dependencies in `kernel/go.mod`. GitHub org: `siyuan-note/*` for the `siyuan-` apps and most libs; `88250/*` for lute, gulu, and the forks (go-sqlite3 / pdfcpu).
### Cross-repo notes
- **Editing any Go dependency (Lute / dejavu / gulu / eventbus / riff / filelock / httpclient / logging / go-sqlite3 / pdfcpu / epub / âŚ):** these are imported by the kernel as Go modules (`kernel/go.mod`). To test a local change, add a temporary `replace` in `kernel/go.mod` pointing at your local checkout â but **never commit that `replace`**; it breaks builds for everyone else.
- **Rebuilding `lute.min.js`:** it's the JS build of the Go `lute` project â generated upstream and checked into `app/stage/protyle/js/lute/`. Don't edit it here; change `lute`, rebuild, and copy the artifact in.
- **Mobile apps (`siyuan-android` / `siyuan-ios` / `siyuan-harmony`):** each is a separate native app that wraps the kernel built from this repo. For how to build, vendor the kernel binding, and wire everything up, **read each project's own README** â the toolchains and steps differ per platform and aren't documented here.
- **`siyuan-chrome`:** independent TypeScript project; it only interacts with a running SiYuan instance through the public HTTP API documented in `docs/API.md`.
---
## 7. Response style
1. **Language:** Match the user's language; do not mix languages mid-sentence (keep proper nouns / identifiers in their original form)