{siteConfig.productName} is a high-performance, Rust-first pipeline that synchronizes prompts, rules, and workspace memory into native tool configurations.
+
diff --git a/doc/components/home-version-note.tsx b/doc/components/home-version-note.tsx
new file mode 100644
index 00000000..c7c1c376
--- /dev/null
+++ b/doc/components/home-version-note.tsx
@@ -0,0 +1,17 @@
+import process from 'node:process'
+
+export function HomeVersionNote() {
+ const version = process.env.NEXT_PUBLIC_MEMORY_SYNC_VERSION
+
+ if (version == null || version === '') {
+ return null
+ }
+
+ return (
+
+ Current version:
+ {' '}
+ {version}
+
+ )
+}
diff --git a/doc/content/_meta.ts b/doc/content/_meta.ts
index 88faf40d..1c790d58 100644
--- a/doc/content/_meta.ts
+++ b/doc/content/_meta.ts
@@ -1,9 +1,9 @@
export default {
'index': {
- title: 'Overview'
+ title: '概览'
},
'quick-guide': {
- title: 'Quick Guide'
+ title: '快速指南'
},
'cli': {
title: 'CLI'
@@ -18,9 +18,9 @@ export default {
title: 'GUI'
},
'technical-details': {
- title: 'Technical Details'
+ title: '技术细节'
},
'design-rationale': {
- title: 'Design Rationale'
+ title: '设计缘由'
}
}
diff --git a/doc/content/cli/_meta.ts b/doc/content/cli/_meta.ts
index 9d2c4689..cf92a996 100644
--- a/doc/content/cli/_meta.ts
+++ b/doc/content/cli/_meta.ts
@@ -1,17 +1,17 @@
export default {
- 'index': 'Overview',
- 'install': 'Installation and Requirements',
- 'workspace-setup': 'Workspace and aindex',
- 'first-sync': 'First Sync',
- 'migration': 'Migrating from Older Docs',
- 'cli-commands': 'CLI Commands',
- 'dry-run-and-clean': 'dry-run and clean',
+ 'index': '概览',
+ 'install': '安装与要求',
+ 'workspace-setup': '工作区与 aindex',
+ 'first-sync': '第一次同步',
+ 'migration': '从旧文档迁移',
+ 'cli-commands': 'CLI 命令',
+ 'dry-run-and-clean': 'dry-run 与 clean',
'plugin-config': 'plugin.config.ts',
'schema': 'JSON Schema',
- 'output-scopes': 'Output Scopes',
+ 'output-scopes': '输出范围',
'frontmatter': 'Front Matter',
- 'cleanup-protection': 'Cleanup Protection',
- 'supported-outputs': 'Supported Outputs',
- 'troubleshooting': 'Troubleshooting',
- 'upgrade-notes': 'Upgrade Notes'
+ 'cleanup-protection': '清理保护',
+ 'supported-outputs': '支持的输出',
+ 'troubleshooting': '故障排查',
+ 'upgrade-notes': '升级说明'
}
diff --git a/doc/content/cli/cleanup-protection.mdx b/doc/content/cli/cleanup-protection.mdx
index 0786aa25..9b0ef230 100644
--- a/doc/content/cli/cleanup-protection.mdx
+++ b/doc/content/cli/cleanup-protection.mdx
@@ -1,28 +1,28 @@
---
-title: Cleanup Protection
-description: Explains that `cleanupProtection` is now a legacy `.tnmsc.json` field and how cleanup safety works today.
-sidebarTitle: Cleanup Protection
+title: 清理保护
+description: 说明 `cleanupProtection` 现在已是遗留 `.tnmsc.json` 字段,以及当前清理安全机制如何工作。
+sidebarTitle: 清理保护
status: stable
---
-# Cleanup Protection
+# 清理保护
-`cleanupProtection` should now be treated as a legacy config block in `~/.aindex/.tnmsc.json`.
+`cleanupProtection` 现在应被视为 `~/.aindex/.tnmsc.json` 中的遗留配置块。
-The `clean` command still has protection behavior, but it now comes from fixed runtime guardrails and plugin-declared cleanup boundaries rather than user-authored JSON rules in the global config file.
+`clean` 命令仍然具备保护行为,但它现在来自固定的运行时护栏和插件声明的清理边界,而不是全局配置文件里用户手写的 JSON 规则。
-## What Still Protects Cleanup Today
+## 现在仍在保护清理行为的是什么
-- `tnmsc clean --dry-run` lets you inspect the exact cleanup plan first.
-- Workspace-level reserved roots still stay protected.
-- Output plugins still declare their own cleanup and protection boundaries internally.
+- `tnmsc clean --dry-run` 允许你先检查精确的清理计划。
+- 工作区级保留根目录仍然受保护。
+- 输出插件仍会在内部声明自己的清理与保护边界。
-## What You Should Do Instead
+## 你现在应该怎么做
-- Keep hand-written files out of tnmsc-managed output directories.
-- Use `dry-run` before a real clean.
-- If a target needs different cleanup behavior, change the project/plugin assembly rather than adding a `cleanupProtection` block back into `~/.aindex/.tnmsc.json`.
+- 把手写文件放在 tnmsc 管理的输出目录之外。
+- 在真正清理前先运行 `dry-run`。
+- 如果某个目标需要不同的清理行为,应该调整项目或插件装配,而不是重新把 `cleanupProtection` 加回 `~/.aindex/.tnmsc.json`。
-## What To Remove From Old Configs
+## 旧配置里该移除什么
-If an older config still contains `cleanupProtection`, remove it. It is no longer part of the supported user-facing config surface.
+如果旧配置中仍包含 `cleanupProtection`,请移除它。它已经不再属于受支持的用户侧配置表面。
diff --git a/doc/content/cli/cli-commands.mdx b/doc/content/cli/cli-commands.mdx
index 69d15bef..7def5abb 100644
--- a/doc/content/cli/cli-commands.mdx
+++ b/doc/content/cli/cli-commands.mdx
@@ -1,42 +1,42 @@
---
-title: CLI Commands
-description: Summarizes the commands and behavior currently exposed by tnmsc --help.
-sidebarTitle: CLI Commands
+title: CLI 命令
+description: 汇总 `tnmsc --help` 当前暴露的命令及其行为。
+sidebarTitle: CLI 命令
status: stable
---
-# CLI Commands
+# CLI 命令
-The commands currently exposed by `tnmsc --help` are:
+`tnmsc --help` 当前暴露的命令如下:
-| Command | Description |
+| 命令 | 说明 |
| --- | --- |
-| `tnmsc` | Run the default sync pipeline |
-| `tnmsc help` | Show help |
-| `tnmsc version` | Show the version |
-| `tnmsc dry-run` | Preview the files that would be written |
-| `tnmsc clean` | Delete generated outputs and continue cleaning empty directories from the project source tree |
-| `tnmsc clean --dry-run` | Preview what would be cleaned, including empty directories that would be swept afterward |
+| `tnmsc` | 运行默认同步流水线 |
+| `tnmsc help` | 显示帮助 |
+| `tnmsc version` | 显示版本 |
+| `tnmsc dry-run` | 预览将要写入的文件 |
+| `tnmsc clean` | 删除生成输出,并继续清理项目源码树中的空目录 |
+| `tnmsc clean --dry-run` | 预览将被清理的内容,包括后续会一并移除的空目录 |
-## Key Takeaways
+## 关键信息
-### Global Config Is File-Based
+### 全局配置基于文件
-The global user config lives at `~/.aindex/.tnmsc.json`. Edit that file directly. The authoritative field list and fixed aindex layout now live in [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config).
+全局用户配置位于 `~/.aindex/.tnmsc.json`。请直接编辑该文件。权威字段列表和固定的 aindex 布局现在统一说明在 [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config)。
-That includes the newer `codeStyles` block for lightweight user code-style preferences such as `indent` and `tabSize`.
+其中也包括较新的 `codeStyles` 配置块,用于保存 `indent`、`tabSize` 这类轻量级用户代码风格偏好。
-### `logLevel` Is Also Strictly Enumerated
+### `logLevel` 也有严格枚举值
-It can only be:
+它只能取以下值:
```text
trace / debug / info / warn / error
```
-## Recommended Habits
+## 推荐习惯
-1. Run `tnmsc help` first in a new environment.
-2. Run `tnmsc dry-run` before writing outputs.
-3. Run `tnmsc clean --dry-run` before cleanup.
-4. If you need to change global config, edit `~/.aindex/.tnmsc.json` and keep only supported user-facing fields.
+1. 在新环境里先运行 `tnmsc help`。
+2. 在写出输出前先运行 `tnmsc dry-run`。
+3. 在执行清理前先运行 `tnmsc clean --dry-run`。
+4. 如果需要修改全局配置,编辑 `~/.aindex/.tnmsc.json`,并只保留受支持的用户侧字段。
diff --git a/doc/content/cli/dry-run-and-clean.mdx b/doc/content/cli/dry-run-and-clean.mdx
index 4c3c394d..19e7555e 100644
--- a/doc/content/cli/dry-run-and-clean.mdx
+++ b/doc/content/cli/dry-run-and-clean.mdx
@@ -1,41 +1,41 @@
---
-title: dry-run and clean
-description: Explains how tnmsc previews outputs, then performs cleanup, with dry-run and built-in guardrails used to control risk.
-sidebarTitle: dry-run and clean
+title: dry-run 与 clean
+description: 说明 tnmsc 如何通过 `dry-run` 预览输出,再通过 `clean` 执行清理,并依靠内建护栏控制风险。
+sidebarTitle: dry-run 与 clean
status: stable
---
-# dry-run and clean
+# `dry-run` 与 `clean`
-## What `dry-run` Does
+## `dry-run` 会做什么
-`tnmsc dry-run` previews the files and directories that the current execution would write. It is especially useful in these situations:
+`tnmsc dry-run` 会预览当前执行将要写入的文件和目录。它在以下场景中特别有用:
-- When integrating a project for the first time
-- Right after changing `plugin.config.ts`
-- Right after changing source prompt scope or global config
-- Before validating the impact of a large source-content change
+- 首次接入一个项目时
+- 修改 `plugin.config.ts` 之后
+- 修改源 prompt 作用域或全局配置之后
+- 想验证一次较大源内容变更的影响之前
-## What `clean` Does
+## `clean` 会做什么
-`tnmsc clean` removes generated output files. It is not a blind directory deletion command. It follows the current output model and the cleanup declarations built into the runtime.
+`tnmsc clean` 会移除生成出的输出文件。它不是一个盲删目录的命令,而是遵循当前输出模型和运行时内建的清理声明来执行。
-After normal cleanup finishes, `tnmsc clean` also scans the current project source tree and removes remaining empty directories. That empty-dir sweep explicitly skips Git internals as well as dependency, build-output, and cache directory trees.
+常规清理完成后,`tnmsc clean` 还会扫描当前项目源码树,删除剩余的空目录。这个空目录扫描会明确跳过 Git 内部目录,以及依赖、构建输出和缓存目录树。
-Before running it for real, prefer:
+在真正执行之前,优先先运行:
```sh
tnmsc clean --dry-run
```
-`--dry-run` will also show the empty directories that would be removed afterward.
+`--dry-run` 也会显示后续将被移除的空目录。
-## Risk Boundary
+## 风险边界
-If your output directories also contain hand-written files or outputs from other tools, do not rely on a `cleanupProtection` block in `~/.aindex/.tnmsc.json`. Keep those files out of tnmsc-managed output paths, or adjust the project/plugin assembly before running a real clean.
+如果你的输出目录里还混有手写文件或其他工具生成的内容,不要依赖 `~/.aindex/.tnmsc.json` 中的 `cleanupProtection` 配置块。应把这些文件移出 tnmsc 管理的输出路径,或者在真正执行清理前调整项目或插件装配方式。
-## Recommended Habits
+## 推荐习惯
-1. Run `dry-run` first when you change config, source scope, or plugin assembly.
-2. Run `clean --dry-run` first when you really intend to clean.
-3. If something looks wrong, continue with [Troubleshooting](/docs/cli/troubleshooting).
+1. 当你修改配置、源作用域或插件装配时,先运行 `dry-run`。
+2. 当你确实准备清理时,先运行 `clean --dry-run`。
+3. 如果结果看起来不对,请继续查看 [故障排查](/docs/cli/troubleshooting)。
diff --git a/doc/content/cli/first-sync.mdx b/doc/content/cli/first-sync.mdx
index 7a996b67..a20d1897 100644
--- a/doc/content/cli/first-sync.mdx
+++ b/doc/content/cli/first-sync.mdx
@@ -1,19 +1,19 @@
---
-title: First Sync
-description: Walk through tnmsc help, dry-run, the real sync run, and result verification in the shortest path.
-sidebarTitle: First Sync
+title: 第一次同步
+description: 用最短路径走完 tnmsc help、dry-run、真实同步运行和结果核验。
+sidebarTitle: 第一次同步
status: stable
---
-# First Sync
+# 第一次同步
-## Recommended Order
+## 推荐顺序
-1. Run `tnmsc help` first to confirm that you are looking at the current command set.
-2. Run `tnmsc dry-run` next to see which files would be written.
-3. Only after confirming the scope should you run the default sync pipeline.
+1. 先运行 `tnmsc help`,确认你看到的是当前命令集。
+2. 然后运行 `tnmsc dry-run`,查看哪些文件将会被写入。
+3. 只有在确认范围之后,才运行默认 sync 流水线。
-## Shortest Flow
+## 最短流程
```sh
tnmsc help
@@ -21,14 +21,14 @@ tnmsc dry-run
tnmsc
```
-## Why You Should Not Skip `dry-run`
+## 为什么不要跳过 `dry-run`
-The current system does more than write files. It also applies cleanup boundaries based on configuration. On a first integration, checking the preview is much cheaper than recovering from accidental writes or accidental deletions afterward.
+当前系统做的不只是写文件。它还会根据配置应用清理边界。对于第一次接入来说,先检查预览,比事后处理误写入或误删除的成本低得多。
-If you are unsure about cleanup risk, read [dry-run and clean](/docs/cli/dry-run-and-clean) and [Cleanup Protection](/docs/cli/cleanup-protection) first.
+如果你不确定清理风险,先看 [dry-run 与 clean](/docs/cli/dry-run-and-clean) 和 [清理保护](/docs/cli/cleanup-protection)。
-## What to Verify After Sync
+## 同步后要核对什么
-- Whether the target tool appears in [Supported Outputs](/docs/cli/supported-outputs)
-- Whether the actual write scope matches [Output Scopes](/docs/cli/output-scopes)
-- Whether field behavior matches the [JSON Schema](/docs/cli/schema)
+- 目标工具是否出现在[支持的输出](/docs/cli/supported-outputs)中
+- 实际写入范围是否符合[输出范围](/docs/cli/output-scopes)
+- 字段行为是否符合 [JSON Schema](/docs/cli/schema)
diff --git a/doc/content/cli/frontmatter.mdx b/doc/content/cli/frontmatter.mdx
index ff3e23fe..84aa54ec 100644
--- a/doc/content/cli/frontmatter.mdx
+++ b/doc/content/cli/frontmatter.mdx
@@ -1,30 +1,30 @@
---
title: Front Matter
-description: Explains the difference between the frontMatter config option and content frontmatter in the current system.
+description: 说明当前系统里 `frontMatter` 配置项与内容 frontmatter 之间的区别。
sidebarTitle: Front Matter
status: stable
---
# Front Matter
-Start by separating two different layers of front matter.
+先把两种不同层级的 front matter 区分开。
-## 1. A Docs Page's Own Frontmatter
+## 1. 文档页面自身的 Frontmatter
-Each MDX page in this docs site currently requires at least:
+这个文档站点中的每个 MDX 页面目前至少需要:
- `title`
- `description`
-Optional fields include:
+可选字段包括:
- `sidebarTitle`
- `status`
- `keywords`
-## 2. The Sync System's `frontMatter` Config
+## 2. 同步系统中的 `frontMatter` 配置
-The `frontMatter` block currently exposes only one field:
+`frontMatter` 配置块目前只暴露一个字段:
```json
{
@@ -34,27 +34,27 @@ The `frontMatter` block currently exposes only one field:
}
```
-Its job is not to describe the page itself. It controls whether a blank line is preserved after front matter during output.
+它的职责不是描述页面本身,而是控制输出时是否在 front matter 后保留一个空行。
-If you are looking for lightweight personal indentation preferences such as `tabSize` or `indent`, that is now the separate `codeStyles` block in `~/.aindex/.tnmsc.json`, not `frontMatter`.
+如果你要找的是 `tabSize`、`indent` 这类轻量级个人缩进偏好,那么它们现在位于 `~/.aindex/.tnmsc.json` 中独立的 `codeStyles` 配置块,而不是 `frontMatter`。
-## 3. Source-Content Frontmatter
+## 3. 源内容的 Frontmatter
-Different input types also store description, trigger conditions, tool constraints, and similar fields in their own source-file frontmatter. Multiple output plugins consume those fields to map target metadata.
+不同输入类型也会在各自源文件的 frontmatter 中保存 description、触发条件、工具约束等字段。多个输出插件会读取这些字段来映射目标元数据。
-There is one important exception:
+这里有一个重要例外:
-- `skills` no longer read `name` from frontmatter and instead use the skill directory name directly
-- `subagents` no longer read `name` from frontmatter and instead derive the name from the relative path
-- If an older `name` field still exists, it is ignored and a warning is emitted
+- `skills` 不再从 frontmatter 读取 `name`,而是直接使用 skill 目录名
+- `subagents` 不再从 frontmatter 读取 `name`,而是改为从相对路径推导名称
+- 如果旧的 `name` 字段仍然存在,它会被忽略,并发出警告
-See [Technical Details](/docs/technical-details) for the boundary between those input types.
+这些输入类型之间的边界可参见[技术细节](/docs/technical-details)。
-## Summary
+## 总结
-- Docs frontmatter is for the docs site
-- The `frontMatter` config is for output behavior
-- `codeStyles` is the separate config block for lightweight code-style preferences such as indentation
-- Source-content frontmatter is for the sync system and output plugins, but `skills` and `subagents` now take their names from the path rather than `name`
+- Docs frontmatter 用于文档站点本身
+- `frontMatter` 配置用于控制输出行为
+- `codeStyles` 是单独的配置块,用于保存缩进等轻量级代码风格偏好
+- 源内容 frontmatter 用于同步系统和输出插件,但 `skills` 与 `subagents` 现在从路径而不是 `name` 获取名称
-These are three different concerns.
+这三者关注的是完全不同的问题。
diff --git a/doc/content/cli/index.mdx b/doc/content/cli/index.mdx
index 7a0f2053..6c2a71c0 100644
--- a/doc/content/cli/index.mdx
+++ b/doc/content/cli/index.mdx
@@ -1,30 +1,30 @@
---
title: CLI
-description: Organizes installation, project setup, sync workflow, configuration fields, and troubleshooting around the tnmsc command surface.
-sidebarTitle: Overview
+description: 围绕 tnmsc 命令面组织安装、项目准备、同步流程、配置字段与故障排查内容。
+sidebarTitle: 概览
status: stable
---
# CLI
-This section is organized around the public `tnmsc` command surface. Questions such as how to install, how to prepare a project, how to run sync, or what a configuration field really means should start here first.
+这一部分围绕公开的 `tnmsc` 命令面展开。像“怎么安装”“怎么准备项目”“怎么运行 sync”以及“某个配置字段到底是什么意思”这类问题,都应该先从这里开始。
-## What This Section Covers
+## 本节包含什么
-- [Installation and Requirements](/docs/cli/install): confirm the Node, pnpm, Rust, and higher GUI development-engine boundaries.
-- [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config): prepare the source directories, the config file, and the path mapping from one page.
-- [Workspace and aindex](/docs/cli/workspace-setup): understand the remaining workspace-side separation from `plugin.config.ts`.
-- [First Sync](/docs/cli/first-sync): run `help`, `dry-run`, and the real write flow in the recommended order.
-- [CLI Commands](/docs/cli/cli-commands): check the command surface currently exposed by `tnmsc --help`.
-- [dry-run and clean](/docs/cli/dry-run-and-clean): preview first, write second, clean last.
-- [plugin.config.ts](/docs/cli/plugin-config) and [JSON Schema](/docs/cli/schema): verify runtime assembly and the current `.tnmsc.json` field surface.
-- [Front Matter](/docs/cli/frontmatter): understand what the `frontMatter` block does and what it does not do.
-- [Output Scopes](/docs/cli/output-scopes) and [Cleanup Protection](/docs/cli/cleanup-protection): migrate away from removed legacy config blocks.
-- [Supported Outputs](/docs/cli/supported-outputs), [Troubleshooting](/docs/cli/troubleshooting), and [Upgrade Notes](/docs/cli/upgrade-notes): handle day-to-day usage and version migration.
+- [安装与要求](/docs/cli/install):确认 Node、pnpm、Rust,以及更高版本 GUI 开发引擎的边界。
+- [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config):在一个页面里准备源目录、配置文件和路径映射。
+- [工作区与 aindex](/docs/cli/workspace-setup):理解工作区侧剩余职责与 `plugin.config.ts` 的分工。
+- [第一次同步](/docs/cli/first-sync):按推荐顺序运行 `help`、`dry-run` 和真实写入流程。
+- [CLI 命令](/docs/cli/cli-commands):查看 `tnmsc --help` 当前暴露的命令面。
+- [dry-run 与 clean](/docs/cli/dry-run-and-clean):先预览,再写入,最后再清理。
+- [plugin.config.ts](/docs/cli/plugin-config) 与 [JSON Schema](/docs/cli/schema):核对运行时装配方式和当前 `.tnmsc.json` 字段面。
+- [Front Matter](/docs/cli/frontmatter):理解 `frontMatter` 块能做什么、不能做什么。
+- [输出范围](/docs/cli/output-scopes) 与 [清理保护](/docs/cli/cleanup-protection):从已删除的旧配置块迁移出来。
+- [支持的输出](/docs/cli/supported-outputs)、[故障排查](/docs/cli/troubleshooting) 与 [升级说明](/docs/cli/upgrade-notes):处理日常使用和版本迁移。
-## Recommended Order
+## 推荐阅读顺序
-1. Start with [Installation and Requirements](/docs/cli/install).
-2. Continue with [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config).
-3. Then use [First Sync](/docs/cli/first-sync) to complete one real run.
-4. When you need to verify facts, come back to [CLI Commands](/docs/cli/cli-commands) and [JSON Schema](/docs/cli/schema).
+1. 先看[安装与要求](/docs/cli/install)。
+2. 接着看 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config)。
+3. 然后按[第一次同步](/docs/cli/first-sync)完成一次真实运行。
+4. 需要核对事实时,再回来看 [CLI 命令](/docs/cli/cli-commands) 和 [JSON Schema](/docs/cli/schema)。
diff --git a/doc/content/cli/install.mdx b/doc/content/cli/install.mdx
index 13dec394..9c3b5254 100644
--- a/doc/content/cli/install.mdx
+++ b/doc/content/cli/install.mdx
@@ -1,7 +1,7 @@
---
-title: Installation and Requirements
-description: Explains the current Node, pnpm, Rust, and CLI installation requirements for memory-sync.
-sidebarTitle: Installation and Requirements
+title: 安装与要求
+description: 说明 memory-sync 当前对 Node、pnpm、Rust 和 CLI 安装的要求。
+sidebarTitle: 安装与要求
status: stable
keywords:
- install
@@ -10,22 +10,22 @@ keywords:
- rust
---
-# Installation and Requirements
+# 安装与要求
-## Runtime Requirements
+## 运行时要求
-According to the current repository configuration:
+根据当前仓库配置:
-- The root workspace requires `Node.js >= 22`
-- The root workspace development engine requires `pnpm 10.30.1`
-- The Rust workspace baseline is `rust >= 1.88.0`
-- `gui/` requires higher development engines: currently `rust >= 1.93.1` and `node >= 25.2.1`
+- 根工作区要求 `Node.js >= 22`
+- 根工作区开发引擎要求 `pnpm 10.30.1`
+- Rust 工作区基线为 `rust >= 1.88.0`
+- `gui/` 需要更高版本的开发引擎:当前为 `rust >= 1.93.1` 和 `node >= 25.2.1`
-If you only use the docs site and the CLI, the root workspace requirements are enough. If you also build the desktop GUI, align with the higher versions required by `gui/`.
+如果你只使用文档站点和 CLI,那么根工作区的要求就够了。如果你还要构建桌面 GUI,就需要对齐 `gui/` 要求的更高版本。
-## Install the CLI
+## 安装 CLI
-The README currently recommends these installation commands, depending on the package manager you use:
+README 当前按你使用的包管理器推荐以下安装命令:
-If you are developing inside the monorepo, you will usually run this from the repository root instead:
+如果你是在 monorepo 内部开发,通常会改为在仓库根目录运行:
```sh
pnpm install
pnpm -C cli exec node dist/index.mjs --help
```
-## Confirm That You Installed the Current Command Set
+## 确认你安装的是当前命令集
-The core commands currently visible in CLI help are:
+CLI help 里当前可见的核心命令有:
-- the default sync pipeline
+- 默认 sync 流水线
- `help`
- `version`
- `dry-run`
- `clean`
- `config key=value`
-## First Check
+## 首次检查
-Run this immediately after installation:
+安装后立刻运行:
```sh
tnmsc help
```
-You should see `dry-run`, `clean`, and `config`. If that is not what you see, stop there instead of following the rest of the docs.
+你应该能看到 `dry-run`、`clean` 和 `config`。如果实际看到的不是这些,就先停在这里,不要继续照着后面的文档做。
diff --git a/doc/content/cli/migration.mdx b/doc/content/cli/migration.mdx
index f0fabf68..346947c2 100644
--- a/doc/content/cli/migration.mdx
+++ b/doc/content/cli/migration.mdx
@@ -1,30 +1,30 @@
---
-title: Migrating from Older Docs
-description: Explains how older pages and outdated command assumptions map onto the current top-level documentation structure.
-sidebarTitle: Migrating from Older Docs
+title: 从旧文档迁移
+description: 说明旧页面和过时命令假设如何映射到当前顶层文档结构。
+sidebarTitle: 从旧文档迁移
status: stable
---
-# Migrating from Older Docs
+# 从旧文档迁移
-## Structural Changes
+## 结构变化
-Older docs mixed quick start, concepts, authoring, reference, and operations into the same layer. The new top level is:
+旧文档把 quick start、concepts、authoring、reference 和 operations 混在同一层。新的顶层结构是:
- `CLI`
- `MCP`
- `GUI`
-- `Technical Details`
-- `Design Rationale`
+- `技术细节`
+- `设计缘由`
-This is not just a rename. It is a responsibility-based split between reading entrypoints and writing categories.
+这不只是改名,而是按职责把阅读入口和写作分类重新拆开。
-## Where Older Entry Points Go Now
+## 旧入口现在放在哪里
-- Older `quick-start`, `reference`, and `operations` content now belongs mainly under [CLI](/docs/cli).
-- Older `concepts` and `authoring` content now belongs mainly under [Technical Details](/docs/technical-details).
-- Manifesto-style pages now live only under [Design Rationale](/docs/design-rationale).
+- 旧的 `quick-start`、`reference` 和 `operations` 内容,现在主要归到 [CLI](/docs/cli)。
+- 旧的 `concepts` 和 `authoring` 内容,现在主要归到[技术细节](/docs/technical-details)。
+- manifesto 风格页面现在只放在[设计缘由](/docs/design-rationale)。
-## Command Expectations Must Move Too
+## 对命令的预期也要一起迁移
-The most misleading part of the older docs is that they still present `tnmsc init` as the recommended entrypoint. The usage path should follow [CLI Commands](/docs/cli/cli-commands) instead.
+旧文档里最容易误导人的一点,是它们仍然把 `tnmsc init` 当作推荐入口。现在的使用路径应该改为遵循 [CLI 命令](/docs/cli/cli-commands)。
diff --git a/doc/content/cli/output-scopes.mdx b/doc/content/cli/output-scopes.mdx
index d32ae8ce..90fd4bd4 100644
--- a/doc/content/cli/output-scopes.mdx
+++ b/doc/content/cli/output-scopes.mdx
@@ -1,33 +1,33 @@
---
-title: Output Scopes
-description: Explains that `outputScopes` is now a legacy `.tnmsc.json` field and where output placement is controlled instead.
-sidebarTitle: Output Scopes
+title: 输出范围
+description: 说明 `outputScopes` 现在已是遗留 `.tnmsc.json` 字段,以及输出落点现在由什么控制。
+sidebarTitle: 输出范围
status: stable
---
-# Output Scopes
+# 输出范围
-`outputScopes` should now be treated as a legacy config block in `~/.aindex/.tnmsc.json`.
+`outputScopes` 现在应被视为 `~/.aindex/.tnmsc.json` 里的遗留配置块。
-The current runtime no longer documents or supports per-plugin output-scope overrides from the global user config file.
+当前运行时已经不再记录或支持通过全局用户配置文件按插件覆写输出作用域。
-## What Controls Output Placement Now
+## 现在由什么控制输出位置
-Output placement is now determined by the combination of:
+输出落点现在由以下因素共同决定:
-- which output plugins are assembled in [plugin.config.ts](/docs/cli/plugin-config)
-- whether the source asset itself is project-scoped or global-scoped
-- plugin-specific routing and remap behavior
+- 哪些输出插件被装配进了 [plugin.config.ts](/docs/cli/plugin-config)
+- 源资产本身是 project-scoped 还是 global-scoped
+- 插件自身的路由与重映射行为
-That is the place to debug first when content lands in the wrong destination.
+如果内容落到了错误的位置,首先就应该从这里开始排查。
-## What To Do With Older Config Files
+## 旧配置文件该怎么处理
-If an older config still contains `outputScopes`, remove it from `~/.aindex/.tnmsc.json` instead of expanding it further.
+如果旧配置里还保留着 `outputScopes`,请直接从 `~/.aindex/.tnmsc.json` 中移除,而不是继续扩展它。
-## Migration Checklist
+## 迁移检查清单
-- Remove `outputScopes` from the global config.
-- Check the assembled plugins in [plugin.config.ts](/docs/cli/plugin-config).
-- Check the source item scope you authored.
-- Use `tnmsc dry-run` to verify the real write targets before a full sync.
+- 从全局配置中移除 `outputScopes`。
+- 检查 [plugin.config.ts](/docs/cli/plugin-config) 中实际装配的插件。
+- 检查你编写的源条目作用域。
+- 在完整同步前使用 `tnmsc dry-run` 验证真实写入目标。
diff --git a/doc/content/cli/plugin-config.mdx b/doc/content/cli/plugin-config.mdx
index da299843..b32c231d 100644
--- a/doc/content/cli/plugin-config.mdx
+++ b/doc/content/cli/plugin-config.mdx
@@ -1,31 +1,31 @@
---
title: plugin.config.ts
-description: Explains how the project-level plugin.config.ts assembles default output plugins and runtime options.
+description: 说明项目级 `plugin.config.ts` 如何装配默认输出插件和运行时选项。
sidebarTitle: plugin.config.ts
status: stable
---
# `plugin.config.ts`
-## What It Does
+## 它负责什么
-`plugin.config.ts` is the project-level assembly entrypoint. It passes:
+`plugin.config.ts` 是项目级装配入口。它会把以下内容传给:
-- pipeline options
-- the plugin list
-- any programmatic overrides
+- pipeline 选项
+- 插件列表
+- 任何编程式覆写
-into `defineConfig()`, which produces the `PipelineConfig` used for real execution.
+也就是传入 `defineConfig()`,由它生成实际执行时使用的 `PipelineConfig`。
-This file defines the assembled integration surface. It does not, by itself, mean every assembled plugin will emit outputs.
+这个文件定义了已经装配好的集成表面,但它本身并不意味着每个已装配插件都会真正输出文件。
-## Current Default Output Plugins
+## 当前默认装配的输出插件
-The default output plugins currently assembled in `cli/src/plugin.config.ts` include:
+当前在 `cli/src/plugin.config.ts` 中装配的默认输出插件包括:
- `AgentsOutputPlugin`
- `ClaudeCodeCLIOutputPlugin`
-- `CodexCLIOutputPlugin` (now also emits Codex skill bundles and per-skill `mcp.json` files)
+- `CodexCLIOutputPlugin`(现在也会输出 Codex skill bundle 和每个 skill 的 `mcp.json` 文件)
- `JetBrainsAIAssistantCodexOutputPlugin`
- `DroidCLIOutputPlugin`
- `GeminiCLIOutputPlugin`
@@ -39,26 +39,27 @@ The default output plugins currently assembled in `cli/src/plugin.config.ts` inc
- `GitExcludeOutputPlugin`
- `JetBrainsIDECodeStyleConfigOutputPlugin`
- `VisualStudioCodeIDEConfigOutputPlugin`
-- `ReadmeMdConfigFileOutputPlugin` (also emits `.editorconfig`)
+- `ReadmeMdConfigFileOutputPlugin`(也会输出 `.editorconfig`)
-## Assembly vs Enablement
+## 装配与启用的区别
-`plugin.config.ts` answers:
+`plugin.config.ts` 回答的是:
-- which plugin implementations are assembled into the runtime
-- which targets the project knows how to generate
+- 哪些插件实现被装配进运行时
+- 项目已知可以生成哪些目标
-`~/.aindex/.tnmsc.json` answers:
+`~/.aindex/.tnmsc.json` 回答的是:
-- which of those assembled plugins are currently allowed to emit outputs
+- 这些已装配插件中,当前哪些被允许实际输出
-The current built-in default behavior is:
+当前内建默认行为是:
-- `plugins.git` is enabled by default
-- `plugins.readme` is enabled by default and also covers `.editorconfig` output
-- the other assembled output plugins stay off until the user explicitly enables them in `~/.aindex/.tnmsc.json`
+- `plugins.git` 默认启用
+- `plugins.readme` 默认启用,同时也覆盖 `.editorconfig` 输出
+- 其他已装配的输出插件默认保持关闭,直到用户在 `~/.aindex/.tnmsc.json` 中显式启用
+- `~/.aindex/.tnmsc.json` 里的 `plugins` 配置块只能使用 [JSON Schema](/docs/cli/schema) 中受支持的键;不受支持的键现在会触发配置错误并中止运行
-Example:
+示例:
```json
{
@@ -70,14 +71,14 @@ Example:
}
```
-## What That Means
+## 这意味着什么
-This configuration is not illustrative documentation. It is the current assembled runtime surface. If a plugin appears here, the runtime knows how to target it. Whether it actually writes files depends on the user config enablement rules described above.
+这份配置不是展示性质的示例文档,而是当前真实装配后的运行时表面。只要某个插件出现在这里,运行时就知道如何面向它输出;至于它是否真的写文件,则取决于上面提到的用户配置启用规则。
-## Minimal Mental Model
+## 最小心智模型
-You can think of `plugin.config.ts` as the file that:
+你可以把 `plugin.config.ts` 理解为这样一个文件:
-- chooses which targets are assembled into the runtime
-- decides whether to append programmatic config at the project level
-- keeps project config merged with global user config rather than bypassing it entirely
+- 选择哪些目标被装配进运行时
+- 决定是否在项目级追加编程式配置
+- 让项目配置与全局用户配置合并,而不是完全绕过后者
diff --git a/doc/content/cli/schema.mdx b/doc/content/cli/schema.mdx
index d0079bef..f1902364 100644
--- a/doc/content/cli/schema.mdx
+++ b/doc/content/cli/schema.mdx
@@ -1,32 +1,32 @@
---
title: JSON Schema
-description: Summarizes the stable `.tnmsc.json` config surface and the compatibility notes you should keep in mind when reading tnmsc.schema.json.
+description: 汇总稳定的 `.tnmsc.json` 配置表面,以及阅读 `tnmsc.schema.json` 时需要注意的兼容性说明。
sidebarTitle: JSON Schema
status: stable
---
# JSON Schema
-The `.aindex` and `.tnmsc.json` setup details are now centralized in [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config).
+`.aindex` 和 `.tnmsc.json` 的配置细节现在统一收敛在 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config)。
-The generated `tnmsc.schema.json` is currently more permissive than the small stable runtime config surface. For authoring new `~/.aindex/.tnmsc.json` files, use the stable fields below and treat older compatibility keys as legacy.
+当前生成的 `tnmsc.schema.json` 比稳定且精简的运行时配置表面更宽松。编写新的 `~/.aindex/.tnmsc.json` 时,请使用下列稳定字段,并把旧兼容键视为遗留项。
-## Stable runtime fields
+## 稳定运行时字段
-| Field | Type | Meaning |
+| 字段 | 类型 | 含义 |
| --- | --- | --- |
-| `version` | `string` | version marker passed through the runtime |
-| `workspaceDir` | `string` | workspace root |
+| `version` | `string` | 贯穿运行时的版本标记 |
+| `workspaceDir` | `string` | 工作区根目录 |
| `logLevel` | enum | `trace` / `debug` / `info` / `warn` / `error` |
-| `frontMatter` | `object` | front matter output-formatting options |
-| `codeStyles` | `object` | lightweight user code style preferences |
-| `windows` | `object` | Windows and WSL integration options |
-| `profile` | `object` | open user-profile object |
-| `plugins` | `object` | explicit per-plugin output enablement overrides |
+| `frontMatter` | `object` | front matter 输出格式选项 |
+| `codeStyles` | `object` | 轻量级用户代码风格偏好 |
+| `windows` | `object` | Windows 与 WSL 集成选项 |
+| `profile` | `object` | 开放式用户 profile 对象 |
+| `plugins` | `object` | 按插件显式覆写输出启用状态 |
## `frontMatter`
-The `frontMatter` block currently exposes one field:
+`frontMatter` 配置块目前只暴露一个字段:
```json
{
@@ -36,7 +36,7 @@ The `frontMatter` block currently exposes one field:
}
```
-See [Front Matter](/docs/cli/frontmatter) for the distinction.
+具体区别可参见 [Front Matter](/docs/cli/frontmatter)。
## `codeStyles`
@@ -49,14 +49,14 @@ See [Front Matter](/docs/cli/frontmatter) for the distinction.
}
```
-| Field | Type | Meaning |
+| 字段 | 类型 | 含义 |
| --- | --- | --- |
-| `codeStyles.indent` | `"tab" \| "space"` | preferred indentation style |
-| `codeStyles.tabSize` | `integer` | preferred indentation width |
+| `codeStyles.indent` | `"tab" \| "space"` | 偏好的缩进风格 |
+| `codeStyles.tabSize` | `integer` | 偏好的缩进宽度 |
-Additional keys are currently accepted so the block can hold a few more simple personal style preferences without forcing a schema change for every tiny addition.
+当前也接受额外键,这样这个配置块就能容纳更多简单的个人风格偏好,而不必为每一个小增量都修改 schema。
-The block itself is optional. When it is omitted, the merged runtime defaults still resolve to `indent = "space"` and `tabSize = 2`.
+这个配置块本身是可选的。若省略,合并后的运行时默认值仍会解析为 `indent = "space"` 和 `tabSize = 2`。
## `windows`
@@ -70,24 +70,24 @@ The block itself is optional. When it is omitted, the merged runtime defaults st
}
```
-| Field | Type | Meaning |
+| 字段 | 类型 | 含义 |
| --- | --- | --- |
-| `windows.wsl2.instances` | `string \| string[]` | WSL instance name or names used by Windows-specific integration |
+| `windows.wsl2.instances` | `string \| string[]` | Windows 特定集成中使用的 WSL 实例名 |
## `profile`
-`profile` is an open object. The schema explicitly recognizes these optional fields:
+`profile` 是一个开放对象。schema 目前显式识别以下可选字段:
- `name`
- `username`
- `gender`
- `birthday`
-Additional keys are also accepted.
+也接受额外键。
## `plugins`
-The current TypeScript schema also recognizes:
+当前 TypeScript schema 还识别如下写法:
```json
{
@@ -99,12 +99,14 @@ The current TypeScript schema also recognizes:
}
```
-Each plugin key accepts only:
+每个插件键只接受:
- `true`
- `false`
-Known keys are:
+未知键现在不会再被忽略。如果 `plugins` 包含不受支持的键,`tnmsc` 会记录配置校验错误、显示受支持键列表,并直接退出。
+
+已知键包括:
- `agentsMd`
- `claudeCode`
@@ -126,23 +128,42 @@ Known keys are:
- `windsurf`
- `zed`
-When `plugins` is omitted, the current built-in default keeps `git` and `readme` enabled. The other known plugin keys stay off until you explicitly set them to `true`.
+当省略 `plugins` 时,当前内建默认行为会保持 `git` 和 `readme` 启用。其他已知插件键会保持关闭,直到你显式把它们设为 `true`。
+
+`plugins.readme` 同时用于 README 类输出和 `.editorconfig`。
+
+请使用 `plugins.git`,而不是 `plugins.gitExclude`。
-Use `plugins.readme` for both README-like outputs and `.editorconfig`.
+这个配置块就是直接的输出闸门。某个插件即使出现在 [plugin.config.ts](/docs/cli/plugin-config) 里,作为已装配的集成目标存在,只要它的启用状态没有解析为 `true`,它就不会输出文件。
-Use `plugins.git`, not `plugins.gitExclude`.
+> **Warning**
+> 输出插件经常会写入 `.codex/`、`.claude/` 这类由工具接管的目录。如果 tnmsc 需要创建目录的位置上已经存在一个同名文件,这会被视为冲突,因为目录树无法正常创建。
+>
+> 当前运行时的设计是继续向前推进:它会先发出警告,删除阻塞文件,然后重建所需目录路径,以便同步可以完成。
+
+例如,下面这段配置现在会校验失败:
+
+```json
+{
+ "plugins": {
+ "vscode": true,
+ "codex": true,
+ "foo": true
+ }
+}
+```
-This block is the direct output gate. A plugin may still appear in [plugin.config.ts](/docs/cli/plugin-config) as an assembled integration target, but it will not emit files unless its enablement resolves to `true`.
+`foo` 会被拒绝,因为它不在上面列出的受支持插件键之中。
-## Legacy compatibility keys
+## 遗留兼容键
-Do not treat these as part of the supported user-facing config surface anymore:
+不要再把下面这些键视为受支持的用户侧配置表面的一部分:
- `agents`
- `commandSeriesOptions`
- `outputScopes`
- `cleanupProtection`
-- `aindex`, `dir`, and the old `*.src` / `*.dist` path overrides
-- Older migration-era fields such as `fastCommandSeriesOptions`, `externalProjects`, `excludePatterns`, and `shadowSourceProject`
+- `aindex`、`dir`,以及旧的 `*.src` / `*.dist` 路径覆盖
+- 更早迁移时期的字段,如 `fastCommandSeriesOptions`、`externalProjects`、`excludePatterns` 和 `shadowSourceProject`
-If you still see those keys in older examples or transitional schema output, treat them as compatibility leftovers and remove them from new configs.
+如果你仍然在旧示例或过渡期 schema 输出中看到这些键,请把它们视为兼容性残留,并从新配置中移除。
diff --git a/doc/content/cli/supported-outputs.mdx b/doc/content/cli/supported-outputs.mdx
index 90865156..85f98923 100644
--- a/doc/content/cli/supported-outputs.mdx
+++ b/doc/content/cli/supported-outputs.mdx
@@ -1,17 +1,17 @@
---
-title: Supported Outputs
-description: Summarizes the output targets currently integrated by the assembled plugin surface, and clarifies that support is not the same thing as default enablement.
-sidebarTitle: Supported Outputs
+title: 支持的输出
+description: 汇总当前已由装配插件表面集成的输出目标,并说明“受支持”不等于“默认启用”。
+sidebarTitle: 支持的输出
status: stable
---
-# Supported Outputs
+# 支持的输出
-The current assembled plugin surface shows that the project is integrated with at least these output targets:
+当前已装配的插件表面表明,项目至少已集成以下输出目标:
-## AI / IDE / CLI Targets
+## AI / IDE / CLI 目标
-- AGENTS.md-style output
+- AGENTS.md 风格输出
- Claude Code CLI
- OpenAI Codex CLI
- Gemini CLI
@@ -25,31 +25,31 @@ The current assembled plugin surface shows that the project is integrated with a
- Trae CN
- JetBrains AI Assistant Codex
-## Generic Support Outputs
+## 通用支持输出
-- Generic Skills export
+- 通用 Skills 导出
- `.git/info/exclude`
-- VS Code config
-- JetBrains code style config
-- README-like outputs (including `.editorconfig`)
+- VS Code 配置
+- JetBrains 代码风格配置
+- README 类输出(包括 `.editorconfig`)
-## Current Built-in Default
+## 当前内建默认行为
-Supported does not mean enabled by default.
+受支持并不等于默认启用。
-The current built-in default behavior is:
+当前内建默认行为是:
-- `plugins.git` is enabled
-- `plugins.readme` is enabled
-- the other supported outputs remain opt-in until `~/.aindex/.tnmsc.json` explicitly enables them
+- `plugins.git` 已启用
+- `plugins.readme` 已启用
+- 其他受支持输出仍然是 opt-in,直到 `~/.aindex/.tnmsc.json` 显式启用它们
-So this page is a capability list, not a list of outputs that always appear on disk.
+因此,这一页列出的是能力清单,而不是一定会出现在磁盘上的输出清单。
-## One Important Thing to Remember
+## 一个需要记住的重要点
-The "Supported Tools" table in the README is a high-level user-facing description. The real behavior comes from two layers together:
+README 中的 “Supported Tools” 表格只是面向用户的高层说明。真实行为由下面两层共同决定:
-- which plugins are assembled in [plugin.config.ts](/docs/cli/plugin-config)
-- which plugin keys are enabled in `~/.aindex/.tnmsc.json`
+- 哪些插件被装配进了 [plugin.config.ts](/docs/cli/plugin-config)
+- 哪些插件键在 `~/.aindex/.tnmsc.json` 中被启用
-If a target is assembled but disabled in user config, treat it as supported but currently inactive.
+如果某个目标已经装配,但在用户配置中被禁用,那么应把它视为“受支持但当前未激活”。
diff --git a/doc/content/cli/troubleshooting.mdx b/doc/content/cli/troubleshooting.mdx
index 923638db..46a6c521 100644
--- a/doc/content/cli/troubleshooting.mdx
+++ b/doc/content/cli/troubleshooting.mdx
@@ -1,39 +1,56 @@
---
-title: Troubleshooting
-description: Collects the version, command, and output-placement problems you are most likely to run into right now.
-sidebarTitle: Troubleshooting
+title: 故障排查
+description: 汇总你现在最可能遇到的版本、命令和输出落点相关问题。
+sidebarTitle: 故障排查
status: stable
---
-# Troubleshooting
+# 故障排查
-## Symptom: I Expected a Page, but I Got a 404
+## 症状:我本来想打开一个页面,却看到了 404
-The docs have moved from the old mixed grouping into seven top-level sections. Start from [/docs](/docs) or the [Quick Guide](/docs/quick-guide) instead of following older paths.
+文档已经从旧的混合分组迁移到七个顶层分区。请从 [/docs](/docs) 或 [快速指南](/docs/quick-guide) 开始,而不是继续沿用旧路径。
-## Symptom: Rules, Prompts, or MCP-Related Content Was Written to the Wrong Place
+## 症状:Rules、Prompts 或 MCP 相关内容被写到了错误的位置
-Check these first:
+先检查以下几点:
-1. Which output plugins are actually assembled in [plugin.config.ts](/docs/cli/plugin-config).
-2. Whether you authored a global prompt or a workspace prompt.
-3. Whether the target plugin has its own routing behavior for that content type.
+1. [plugin.config.ts](/docs/cli/plugin-config) 里实际装配了哪些输出插件。
+2. 你写的是 global prompt 还是 workspace prompt。
+3. 目标插件是否对该类内容有自己的路由行为。
-## Symptom: `clean` Feels Unsafe or Deleted Too Much
+## 症状:`clean` 看起来不安全,或者删掉了太多内容
-Review [dry-run and clean](/docs/cli/dry-run-and-clean) first. If your directories mix manual files with generated files, move the manual files out of tnmsc-managed output paths before a real clean.
+先查看 [dry-run 与 clean](/docs/cli/dry-run-and-clean)。如果你的目录同时混有手写文件和生成文件,请在真正执行清理前把手写文件移出 tnmsc 管理的输出路径。
-## Symptom: A Docs Page Fails to Build
+> **Warning**
+> 如果 tnmsc 需要的是 `.codex/` 这类目录,但在那里发现了一个同名文件,这会被视为路径类型冲突。恢复行为的目的,就是避免这种冲突阻塞整个同步过程。
+>
+> 当前运行时的处理方式是发出警告、删除阻塞文件,然后继续执行。之所以把该文件视为可丢弃,是因为它占据了一个对生成输出而言必须是目录的路径。
-Run this first:
+## 症状:运行因为不受支持的 `plugins` 键错误而停止
+
+先检查 `~/.aindex/.tnmsc.json`。
+
+现在只要 `plugins` 配置块里包含未知键,`tnmsc` 就会快速失败。日志会告诉你哪个键不受支持,并打印受支持键列表。
+
+可以这样修复:
+
+1. 删除不受支持的键。
+2. 把它改成 [JSON Schema](/docs/cli/schema) 或 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config) 中列出的受支持键之一。
+3. 重新运行命令。
+
+## 症状:某个文档页面构建失败
+
+先运行:
```sh
pnpm -C doc run validate:content
```
-This validation step checks:
+这一步校验会检查:
-- Whether every MDX file has `title` and `description`
-- Whether every content directory has `_meta.ts`
-- Whether `_meta.ts` covers the current files and subdirectories
-- Whether internal docs links point to real pages
+- 每个 MDX 文件是否都有 `title` 和 `description`
+- 每个内容目录是否都有 `_meta.ts`
+- `_meta.ts` 是否覆盖了当前文件和子目录
+- 站内文档链接是否指向真实页面
diff --git a/doc/content/cli/upgrade-notes.mdx b/doc/content/cli/upgrade-notes.mdx
index c25e9956..564b3c1e 100644
--- a/doc/content/cli/upgrade-notes.mdx
+++ b/doc/content/cli/upgrade-notes.mdx
@@ -1,29 +1,29 @@
---
-title: Upgrade Notes
-description: Summarizes the most important upgrades in the current docs structure, command expectations, and runtime boundaries.
-sidebarTitle: Upgrade Notes
+title: 升级说明
+description: 总结当前文档结构、命令预期和运行时边界中最重要的升级点。
+sidebarTitle: 升级说明
status: stable
---
-# Upgrade Notes
+# 升级说明
-## Docs Structure Upgrade
+## 文档结构升级
-The docs site has been reorganized from a mixed structure of quick start, concepts, authoring, reference, and operations into:
+文档站点已经从混合的 quick start、concepts、authoring、reference 和 operations 结构,重组为:
- `CLI`
- `MCP`
- `GUI`
-- `Technical Details`
-- `Design Rationale`
+- `技术细节`
+- `设计缘由`
-If you are maintaining or adding pages, place them under one of those five categories before you start writing.
+如果你要维护或新增页面,请先把它放进这五类之一,再开始写内容。
-## Command Model Upgrade
+## 命令模型升级
-- `dry-run` should be the default validation step
-- `clean --dry-run` should be the default pre-clean check
+- `dry-run` 应该作为默认校验步骤
+- `clean --dry-run` 应该作为默认清理前检查
-## Runtime Boundary Upgrade
+## 运行时边界升级
-The long-term direction of the repository is still Rust-first / NAPI-first. TypeScript is primarily for exposed interfaces, configuration assembly, bridge runtime logic, and declarations rather than the long-term center of the core implementation.
+仓库的长期方向仍然是 Rust-first / NAPI-first。TypeScript 主要用于暴露接口、配置装配、bridge 运行时逻辑和声明,而不是长期承担核心实现的中心位置。
diff --git a/doc/content/cli/workspace-setup.mdx b/doc/content/cli/workspace-setup.mdx
index fc278531..e7d40070 100644
--- a/doc/content/cli/workspace-setup.mdx
+++ b/doc/content/cli/workspace-setup.mdx
@@ -1,43 +1,43 @@
---
-title: Workspace and aindex
-description: Explains how the aindex source directory, global config, and project config entrypoint divide responsibilities in the current repo.
-sidebarTitle: Workspace and aindex
+title: 工作区与 aindex
+description: 说明在当前仓库中,aindex 源目录、全局配置和项目配置入口如何分工。
+sidebarTitle: 工作区与 aindex
status: stable
---
-# Workspace and aindex
+# 工作区与 aindex
-This topic is now centralized in [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config).
+这个主题现在已经集中到 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config) 页面。
-Use that page if you need:
+如果你想了解以下内容,请直接看那一页:
-- where `.aindex` actually lives
-- where `~/.aindex/.tnmsc.json` is loaded from
-- which user config fields are still supported
-- which aindex paths are fixed by convention
-- the current defaults and path rules
+- `.aindex` 实际放在哪里
+- `~/.aindex/.tnmsc.json` 从哪里加载
+- 哪些用户配置字段仍然受支持
+- 哪些 aindex 路径按约定是固定的
+- 当前默认值和路径规则
-## What Still Belongs Here
+## 这里仍然负责什么
-`memory-sync` does not treat target-tool config files as the source of truth. It generates outputs from the aindex source directory and the project configuration entrypoint.
+`memory-sync` 不会把目标工具的配置文件当作事实来源。它会根据 aindex 源目录和项目配置入口生成输出。
-## How Source Content Is Usually Split
+## 源内容通常如何拆分
-The input assets documented separately right now are:
+当前单独说明的输入资产包括:
-- Global prompts and workspace prompts
+- 全局 prompts 和工作区 prompts
- `skills/`
- `commands/`
- `subagents/`
- `rules/`
-Their responsibility boundaries are explained in [Technical Details](/docs/technical-details). The CLI docs focus only on how those assets are read, validated, and written out.
+它们的职责边界在[技术细节](/docs/technical-details)中说明。CLI 文档只关注这些资产如何被读取、校验并写出。
-## Project Configuration Entrypoint
+## 项目配置入口
-The project-level assembly entrypoint is [plugin.config.ts](/docs/cli/plugin-config). It decides which output plugins are assembled and how project-level runtime options are merged into the real execution config. Per-user output enablement then comes from `~/.aindex/.tnmsc.json`.
-`plugins.readme` covers both the README-family outputs and `.editorconfig`.
+项目级装配入口是 [plugin.config.ts](/docs/cli/plugin-config)。它决定要装配哪些输出插件,以及如何把项目级运行时选项合并进真实执行配置。用户级输出启用则来自 `~/.aindex/.tnmsc.json`。
+`plugins.readme` 同时覆盖 README 系列输出和 `.editorconfig`。
-## Next Step
+## 下一步
-Once the directories are ready, continue to [First Sync](/docs/cli/first-sync). Use `dry-run` to validate the output scope before doing a real write.
+目录准备好之后,继续看[第一次同步](/docs/cli/first-sync)。在真实写入前,先用 `dry-run` 校验输出范围。
diff --git a/doc/content/design-rationale/_meta.ts b/doc/content/design-rationale/_meta.ts
index 6425ae35..88fb36b0 100644
--- a/doc/content/design-rationale/_meta.ts
+++ b/doc/content/design-rationale/_meta.ts
@@ -1,4 +1,4 @@
export default {
- index: 'Overview',
- manifesto: 'Why This Tool Exists'
+ index: '概览',
+ manifesto: '为什么会有这个工具'
}
diff --git a/doc/content/design-rationale/index.mdx b/doc/content/design-rationale/index.mdx
index a8e2883d..58ebe642 100644
--- a/doc/content/design-rationale/index.mdx
+++ b/doc/content/design-rationale/index.mdx
@@ -1,21 +1,21 @@
---
-title: Design Rationale
-description: Separates project motivation, design reasoning, and manifesto-style content from usage docs and implementation details.
-sidebarTitle: Overview
+title: 设计缘由
+description: 将项目动机、设计思路和宣言式内容,与使用文档和实现细节分开。
+sidebarTitle: 概览
status: stable
---
-# Design Rationale
+# 设计缘由
-This section answers only why the project is shaped this way. It does not own installation, command, schema, or page-workflow explanations.
+这一部分只回答“为什么项目会被设计成这样”。它不负责安装、命令、schema 或页面工作流的说明。
-## Why This Section Exists Separately
+## 为什么要单独保留这一部分
-Once background narrative is mixed into operational docs, two things get worse at the same time:
+一旦背景叙事混入操作文档,会同时带来两个问题:
-- Users struggle to reach the shortest working path quickly
-- Maintainers start writing idea pages into feature pages, and the information architecture loses focus again
+- 用户很难快速找到最短可行路径
+- 维护者会开始把想法型页面写进功能页面,信息架构又会重新失焦
-## Current Entry
+## 当前入口
-- [Why This Tool Exists](/docs/design-rationale/manifesto)
+- [为什么会有这个工具](/docs/design-rationale/manifesto)
diff --git a/doc/content/design-rationale/manifesto.mdx b/doc/content/design-rationale/manifesto.mdx
index 75e7fdd7..f6f489b4 100644
--- a/doc/content/design-rationale/manifesto.mdx
+++ b/doc/content/design-rationale/manifesto.mdx
@@ -1,41 +1,41 @@
---
-title: Why This Tool Exists
-description: Starts from the README's tool-rat framing and explains the real problem memory-sync is trying to solve.
-sidebarTitle: Why This Tool Exists
+title: 为什么会有这个工具
+description: 从 README 里“tool-rat”的定位出发,解释 memory-sync 真正想解决的问题。
+sidebarTitle: 为什么会有这个工具
status: stable
---
-# Why This Tool Exists
+# 为什么会有这个工具
-The README describes the core positioning of `memory-sync` very directly: this is not a tool that waits for platforms to converge on a shared standard. It is a "tool-rat" that still works in a world where tools are fragmented, interfaces are private, and access is distributed unfairly.
+README 对 `memory-sync` 的核心定位说得很直接:这不是一个等着平台最终收敛到统一标准的工具。它是一个在工具割裂、接口私有、访问机会分配不均的现实世界里依然能工作的 “tool-rat”。
-## What the Project Is Not Promising
+## 这个项目不承诺什么
-It does not promise:
+它不承诺:
-- That a specific IDE or CLI will eventually give you one official unified entrypoint
-- That some platform will preserve your working memory reliably forever
-- That copying and pasting a few prompts is the same thing as a real migration
+- 某个特定 IDE 或 CLI 最终一定会给你一个官方统一入口
+- 某个平台会永远可靠地替你保存工作记忆
+- 复制粘贴几段 prompt 就等同于真正的迁移
-## What the Project Is Actually Trying to Do
+## 这个项目真正想做什么
-Its goal is to:
+它的目标是:
-1. Pull rule sources out of the tools where they are currently scattered.
-2. Give those rule sources one consistent authoring model.
-3. Deliver them back into the native locations required by different tools.
+1. 把当前散落在各个工具里的规则源提取出来。
+2. 为这些规则源提供一套一致的编写模型。
+3. 再把它们投递回不同工具各自要求的原生位置。
-## Why This Must Be a Sync System Instead of a Template Repository
+## 为什么这必须是一个 sync 系统,而不是模板仓库
-With only a template repository, you still have to do all of this by hand:
+如果只有一个模板仓库,你仍然要手动处理这些事情:
-- Decide where each tool should receive output
-- Decide what is global and what is project-specific
-- Decide how to remove old artifacts
-- Decide how to keep multiple configurations from stepping on each other
+- 决定每个工具的输出应该写到哪里
+- 决定哪些内容是全局的,哪些是项目级的
+- 决定如何移除旧产物
+- 决定如何避免多套配置互相踩踏
-The point of `memory-sync` is to move those decisions forward into declarative configuration and source-file structure.
+`memory-sync` 的意义,就在于把这些决策前移到声明式配置和 source 文件结构中。
-## Why the Docs Had to Be Rewritten Too
+## 为什么文档也必须一起重写
-If the docs kept the old loose narrative that could not map back to the real implementation, the docs themselves would become new residue. The docs-site rewrite is really the same discipline applied to docs: make them verifiable against the implementation instead of letting them drift.
+如果文档继续保留那种无法映射回真实实现的松散叙事,文档本身也会变成新的残留物。重写 docs-site,本质上就是把同样的纪律应用到文档上:让文档能够对照实现进行验证,而不是继续漂移。
diff --git a/doc/content/gui/_meta.ts b/doc/content/gui/_meta.ts
index 04f025ea..0546ccb2 100644
--- a/doc/content/gui/_meta.ts
+++ b/doc/content/gui/_meta.ts
@@ -1,4 +1,4 @@
export default {
- 'index': 'Overview',
- 'workflows-and-pages': 'Workflows and Pages'
+ 'index': '概览',
+ 'workflows-and-pages': '工作流与页面'
}
diff --git a/doc/content/gui/index.mdx b/doc/content/gui/index.mdx
index 47f8804d..fa4c2dd5 100644
--- a/doc/content/gui/index.mdx
+++ b/doc/content/gui/index.mdx
@@ -1,43 +1,43 @@
---
title: GUI
-description: Explains the responsibilities and boundaries of the Tauri desktop layer, and how it relates to sdk, the tnmsc crate, and the CLI.
-sidebarTitle: Overview
+description: 说明 Tauri 桌面层的职责与边界,以及它与 sdk、tnmsc crate 和 CLI 的关系。
+sidebarTitle: 概览
status: stable
---
# GUI
> [!danger]
-> The GUI is not the primary maintenance target right now. The CLI is the main focus.
+> GUI 目前不是主要维护目标,当前重点仍然是 CLI。
>
-> If the GUI feels under-maintained, please excuse that. The team is small, and I may not actively maintain the GUI for a while.
+> 如果你觉得 GUI 维护力度不足,还请见谅。团队规模很小,我可能会有一段时间无法积极维护 GUI。
>
-> I expect to come back and continue maintaining the GUI after the core functionality is more complete.
+> 等核心功能更完整之后,我预计会回来继续维护 GUI。
-`gui/` is the desktop invocation layer built with Tauri and React. Its job is not to become the center of the system architecture. Its job is to turn the configuration editing, execution, display, and log inspection exposed by the `tnmsc` crate in `sdk/` into a desktop workflow.
+`gui/` 是基于 Tauri 和 React 构建的桌面调用层。它的职责不是变成系统架构的中心,而是把 `sdk/` 中 `tnmsc` crate 暴露出来的配置编辑、执行、展示和日志检查能力组织成桌面工作流。
-## What This Layer Owns
+## 这一层负责什么
-- Trigger sync, `dry-run`, and cleanup
-- Edit or display config
-- Browse files, plugin results, and logs
-- Provide a page-based desktop workflow
+- 触发 sync、`dry-run` 和 cleanup
+- 编辑或展示 config
+- 浏览文件、plugin 结果和日志
+- 提供基于页面的桌面工作流
-## What This Layer Does Not Own
+## 这一层不负责什么
-- It does not reimplement the sync core
-- It does not re-derive CLI rules in the frontend
-- It does not change the long-term Rust-first / NAPI-first direction
+- 它不会重新实现 sync core
+- 它不会在前端重新推导一套 CLI 规则
+- 它不会改变长期坚持的 Rust-first / NAPI-first 方向
-## Version Boundary
+## 版本边界
-`gui/package.json` currently requires higher development engines than the root workspace:
+`gui/package.json` 当前要求的开发环境版本高于根 workspace:
- `node >= 25.2.1`
- `pnpm >= 10.28.0`
- `rust >= 1.93.1`
-## Recommended Reading
+## 推荐阅读
-- [Workflows and Pages](/docs/gui/workflows-and-pages): see the current navigation, main pages, and desktop workflow
-- [Technical Details / Architecture Boundaries](/docs/technical-details/architecture): understand why the GUI is an invocation layer rather than the core
+- [工作流与页面](/docs/gui/workflows-and-pages):查看当前导航、主要页面和桌面工作流
+- [技术细节 / 架构边界](/docs/technical-details/architecture):理解为什么 GUI 是调用层而不是核心层
diff --git a/doc/content/gui/workflows-and-pages.mdx b/doc/content/gui/workflows-and-pages.mdx
index f833c6ea..cd61d19e 100644
--- a/doc/content/gui/workflows-and-pages.mdx
+++ b/doc/content/gui/workflows-and-pages.mdx
@@ -1,15 +1,15 @@
---
-title: Workflows and Pages
-description: Summarizes the current GUI navigation, main pages, and the desktop surfaces directly tied to the sync flow.
-sidebarTitle: Workflows and Pages
+title: 工作流与页面
+description: 总结当前 GUI 的导航、主要页面,以及与同步流程直接关联的桌面界面。
+sidebarTitle: 工作流与页面
status: stable
---
-# Workflows and Pages
+# 工作流与页面
-## Current Main Navigation
+## 当前主导航
-`gui/src/components/Sidebar.tsx` currently registers these navigation items:
+`gui/src/components/Sidebar.tsx` 当前注册了这些导航项:
- Dashboard
- Pipeline
@@ -19,20 +19,20 @@ status: stable
- Logs
- Settings
-## What These Pages Mean
+## 这些页面分别表示什么
-- Dashboard: stats, quick actions, and a supported-tools overview
-- Pipeline: run sync and `dry-run`, then inspect plugin results and errors
-- Config: view or edit configuration
-- Plugins / Files / Logs: plugin results, file views, and log inspection
-- Settings: desktop-side preferences and settings
+- Dashboard:统计信息、快捷操作,以及受支持工具概览
+- Pipeline:运行 sync 和 `dry-run`,然后检查 plugin 结果与错误
+- Config:查看或编辑配置
+- Plugins / Files / Logs:plugin 结果、文件视图和日志检查
+- Settings:桌面端偏好设置与配置项
-## Relationship to the CLI
+## 与 CLI 的关系
-The desktop pages do not define a separate set of sync rules. They primarily call the lower-level capability through the bridge layer and turn command-style flows into page-style workflows.
+桌面页面并不会定义另一套独立的 sync 规则。它们主要是通过 bridge layer 调用更底层的能力,并把命令式流程转换成页面式工作流。
-That is why you should still return to the CLI docs first when these questions come up:
+所以当你遇到下面这些问题时,仍然应该优先回到 CLI 文档:
-- Has the command surface changed? See [CLI Commands](/docs/cli/cli-commands)
-- Is the output scope correct? See [Output Scopes](/docs/cli/output-scopes)
-- Is the cleanup boundary safe? See [Cleanup Protection](/docs/cli/cleanup-protection)
+- 命令表层是否发生变化?参见 [CLI 命令](/docs/cli/cli-commands)
+- 输出范围是否正确?参见 [输出范围](/docs/cli/output-scopes)
+- cleanup 边界是否安全?参见 [清理保护](/docs/cli/cleanup-protection)
diff --git a/doc/content/index.mdx b/doc/content/index.mdx
index ccc04cb7..af0dcf70 100644
--- a/doc/content/index.mdx
+++ b/doc/content/index.mdx
@@ -1,42 +1,42 @@
---
-title: overview
-description: Entry page for the memory-sync docs, organized into Quick Guide, CLI, SDK, MCP, GUI, Technical Details, and Design Rationale.
-sidebarTitle: overview
+title: 概览
+description: memory-sync 文档站入口,按快速指南、CLI、SDK、MCP、GUI、技术细节与设计缘由组织。
+sidebarTitle: 概览
status: stable
keywords:
- memory-sync
- - docs
- - overview
+ - 文档
+ - 概览
---
-# Docs Overview
+# 文档概览
-This docs site is organized around the current repository as it actually exists. The top-level structure is fixed as `Quick Guide`, `CLI`, `SDK`, `MCP`, `GUI`, `Technical Details`, and `Design Rationale`. That keeps the shortest onboarding path, the user-facing operation surface, the internal mixed core, the integration server, the desktop layer, implementation details, and project motivation from collapsing into one bucket.
+这个文档站围绕仓库当前可验证的真实结构组织。顶层结构固定为“快速指南”“CLI”“SDK”“MCP”“GUI”“技术细节”和“设计缘由”。这样可以避免把最短上手路径、面向用户的操作界面、内部混合核心、集成服务、桌面层、实现细节以及项目动机全部混在一个桶里。
-## The Seven Top-Level Sections
+## 七个顶层部分
-| Section | Main Question | Entry |
+| 部分 | 核心问题 | 入口 |
| --- | --- | --- |
-| Quick Guide | Should I start with CLI, GUI, or MCP, and what is the shortest path for each? | [Quick Guide](/docs/quick-guide) |
-| CLI | How do I install, prepare a project, run sync, and understand the commands and config fields? | [CLI](/docs/cli) |
-| SDK | Why is `sdk/` the mixed core, what does it own, and how should internal consumers depend on it? | [SDK](/docs/sdk) |
-| MCP | What is `memory-sync-mcp`, which tools does it expose, and how should it be integrated? | [MCP](/docs/mcp) |
-| GUI | What does the desktop layer own, which pages exist, and how does it work with `sdk/`, the `tnmsc` crate, and the CLI? | [GUI](/docs/gui) |
-| Technical Details | How are the architecture boundaries, sync pipeline, source-of-truth model, and input assets such as prompts, skills, commands, and rules organized? | [Technical Details](/docs/technical-details) |
-| Design Rationale | Why does this project exist, and why are the docs layered this way? | [Design Rationale](/docs/design-rationale) |
-
-## Where to Start
-
-- If this is your first time opening the docs site, start with the [Quick Guide](/docs/quick-guide) and decide whether you need the CLI, GUI, or MCP path.
-- If you want to use `memory-sync` for real work right away, continue into [CLI](/docs/cli) and get installation, project setup, and the first sync run working.
-- If you need to understand how the internal core is split and why `sdk/` is now the shared center, go to [SDK](/docs/sdk).
-- If you want to integrate `memory-sync-mcp` into an MCP-capable host, jump straight to [MCP](/docs/mcp).
-- If you care about the desktop app rather than the terminal surface, open [GUI](/docs/gui).
-- If you need the Rust-first / NAPI-first direction, the source-of-truth model, and the asset boundaries, go to [Technical Details](/docs/technical-details).
-- If you need the project background, motivation, and design reasoning, finish with [Design Rationale](/docs/design-rationale).
-
-## Documentation Boundaries
-
-- The docs follow the repository's currently verifiable implementation and do not present unshipped automation, pages, or workflows as facts.
-- Command names, API names, and key technical terms remain in English.
-- If the README, older pages, and the current implementation disagree, this site follows `tnmsc --help`, the schema, the directory structure, and the actual code.
+| 快速指南 | 我应该从 CLI、GUI 还是 MCP 开始,每条路径最短的起步方式是什么? | [快速指南](/docs/quick-guide) |
+| CLI | 我该如何安装、准备项目、运行同步,并理解命令和配置字段? | [CLI](/docs/cli) |
+| SDK | 为什么 `sdk/` 是混合核心,它负责什么,内部使用方应该如何依赖它? | [SDK](/docs/sdk) |
+| MCP | `memory-sync-mcp` 是什么,它暴露了哪些工具,应该如何集成? | [MCP](/docs/mcp) |
+| GUI | 桌面层负责什么、有哪些页面,以及它如何与 `sdk/`、`tnmsc` crate 和 CLI 协作? | [GUI](/docs/gui) |
+| 技术细节 | 架构边界、同步流水线、事实来源模型,以及 prompts、skills、commands、rules 这类输入资产是如何组织的? | [技术细节](/docs/technical-details) |
+| 设计缘由 | 这个项目为什么存在,文档为什么按这种方式分层? | [设计缘由](/docs/design-rationale) |
+
+## 从哪里开始
+
+- 如果你是第一次打开这个文档站,先看 [快速指南](/docs/quick-guide),判断自己需要走 CLI、GUI 还是 MCP 路径。
+- 如果你想立刻把 `memory-sync` 用起来,继续进入 [CLI](/docs/cli),先把安装、项目准备和第一次同步跑通。
+- 如果你需要理解内部核心是如何拆分的,以及为什么 `sdk/` 现在成了共享中心,就看 [SDK](/docs/sdk)。
+- 如果你想把 `memory-sync-mcp` 集成到支持 MCP 的宿主里,直接跳到 [MCP](/docs/mcp)。
+- 如果你更关心桌面应用而不是终端界面,就打开 [GUI](/docs/gui)。
+- 如果你需要了解 Rust-first / NAPI-first 方向、事实来源模型以及资产边界,就去看 [技术细节](/docs/technical-details)。
+- 如果你需要项目背景、动机和设计原因,最后再看 [设计缘由](/docs/design-rationale)。
+
+## 文档边界
+
+- 文档以仓库当前可验证的实现为准,不会把尚未发布的自动化、页面或工作流写成既成事实。
+- 命令名、API 名称和关键技术术语保持英文。
+- 如果 README、旧页面与当前实现不一致,这个站点以 `tnmsc --help`、schema、目录结构和实际代码为准。
diff --git a/doc/content/mcp/_meta.ts b/doc/content/mcp/_meta.ts
index 32df850f..af364a0b 100644
--- a/doc/content/mcp/_meta.ts
+++ b/doc/content/mcp/_meta.ts
@@ -1,4 +1,4 @@
export default {
- 'index': 'Overview',
- 'server-tools': 'Server and Tools'
+ 'index': '概览',
+ 'server-tools': '服务端与工具'
}
diff --git a/doc/content/mcp/index.mdx b/doc/content/mcp/index.mdx
index 30ff4835..8b4bc259 100644
--- a/doc/content/mcp/index.mdx
+++ b/doc/content/mcp/index.mdx
@@ -1,28 +1,28 @@
---
title: MCP
-description: Explains the role of memory-sync-mcp, how it runs, and how it relates to the sdk prompt service.
-sidebarTitle: Overview
+description: 说明 memory-sync-mcp 的角色、运行方式,以及它与 sdk prompt service 的关系。
+sidebarTitle: 概览
status: stable
---
# MCP
-`mcp/` is a standalone package in the current repository. It is published as `@truenine/memory-sync-mcp`, and its executable entrypoint is `memory-sync-mcp`. It is not an abstract concept from the docs. It is a real stdio server.
+`mcp/` 是当前仓库中的独立包。它以 `@truenine/memory-sync-mcp` 的形式发布,可执行入口为 `memory-sync-mcp`。它不是文档里的抽象概念,而是一个真实的 stdio server。
-## What It Owns
+## 它负责什么
-- It exposes `memory-sync` prompt-management capability as an MCP stdio server
-- It reuses the prompt service exported by `@truenine/memory-sync-sdk` instead of reimplementing separate logic
-- It lets MCP-capable hosts read, update, and write back prompt assets through tool calls
+- 它把 `memory-sync` 的 prompt 管理能力以 MCP stdio server 的形式暴露出来
+- 它复用 `@truenine/memory-sync-sdk` 导出的 prompt service,而不是重新实现一套独立逻辑
+- 它让支持 MCP 的宿主可以通过 tool call 读取、更新并回写 prompt 资产
-## What It Does Not Own
+## 它不负责什么
-- It is not a new source-of-truth format
-- It is not a replacement for the GUI
-- It is not a privileged path that bypasses CLI rules
+- 它不是新的事实来源格式
+- 它不是 GUI 的替代品
+- 它不是绕过 CLI 规则的特权通道
-## Recommended Reading
+## 推荐阅读
-- [Server and Tools](/docs/mcp/server-tools): see the stdio server entrypoints, tool list, and `workspaceDir` semantics
-- [CLI](/docs/cli): return there when you need project setup, schema details, or output boundaries
-- [Technical Details](/docs/technical-details/source-of-truth): start from the source-of-truth model when you need to understand why prompt assets are modeled this way
+- [服务端与工具](/docs/mcp/server-tools):查看 stdio server 入口、工具列表以及 `workspaceDir` 的语义
+- [CLI](/docs/cli):当你需要项目初始化、schema 细节或输出边界时,回到这里
+- [单一事实来源](/docs/technical-details/source-of-truth):当你需要理解 prompt 资产为何采用这种建模方式时,从事实来源模型开始
diff --git a/doc/content/mcp/server-tools.mdx b/doc/content/mcp/server-tools.mdx
index 73f6db7c..3492221a 100644
--- a/doc/content/mcp/server-tools.mdx
+++ b/doc/content/mcp/server-tools.mdx
@@ -1,69 +1,69 @@
---
-title: Server and Tools
-description: Explains the memory-sync-mcp stdio server entrypoints, the four exposed tools, and the role of workspaceDir.
-sidebarTitle: Server and Tools
+title: 服务端与工具
+description: 说明 memory-sync-mcp 的 stdio server 入口、当前暴露的四个工具,以及 workspaceDir 的作用。
+sidebarTitle: 服务端与工具
status: stable
---
-# Server and Tools
+# 服务端与工具
-## Public Entrypoints
+## 公共入口点
-`mcp/src/index.ts` currently exposes only two entrypoints:
+`mcp/src/index.ts` 当前只暴露两个入口点:
- `createMemorySyncMcpServer`
- `runMemorySyncMcpStdioServer`
-That makes the package boundary very clear: it provides server construction and the stdio runtime entrypoint.
+这让包边界非常清晰:它提供 server 构造能力和 stdio runtime 入口。
-## Currently Exposed Tools
+## 当前暴露的工具
-`mcp/src/server.ts` currently registers these tools:
+`mcp/src/server.ts` 当前注册了这些工具:
- `list_prompts`
- `get_prompt`
- `upsert_prompt_src`
- `apply_prompt_translation`
-## What Each Tool Does at a High Level
+## 每个工具的大致作用
-## What `workspaceDir` Does
+## `workspaceDir` 的作用
-All of these tools accept an optional `workspaceDir`. Its meaning is not "switch directories arbitrarily." It explicitly binds the prompt service project root to a specific project context.
+这些工具都接受可选的 `workspaceDir`。它的含义并不是“随意切换目录”,而是显式地把 prompt service 的项目根目录绑定到某个具体项目上下文。
-If you do not pass it, the server resolves paths from the default current working directory. If you do pass it, the value is applied to both `cwd` and `pluginOptions.workspaceDir`.
+如果你不传,server 会从默认当前工作目录解析路径。如果你传了,这个值会同时应用到 `cwd` 和 `pluginOptions.workspaceDir`。
-## Usage Boundary
+## 使用边界
-- Make sure the CLI-side project structure is correct before integrating MCP
-- MCP exposes only the prompt-management behavior that already exists and does not invent a new schema
-- If you need to confirm prompt types and source-of-truth responsibilities, go back to [Technical Details](/docs/technical-details)
+- 在集成 MCP 之前,先确保 CLI 侧的项目结构是正确的
+- MCP 只暴露已经存在的 prompt 管理行为,并不会发明新的 schema
+- 如果你需要确认 prompt 类型和事实来源的职责划分,请回到 [技术细节](/docs/technical-details)
diff --git a/doc/content/quick-guide/_meta.ts b/doc/content/quick-guide/_meta.ts
index 8de6ddc6..6c958223 100644
--- a/doc/content/quick-guide/_meta.ts
+++ b/doc/content/quick-guide/_meta.ts
@@ -1,5 +1,5 @@
export default {
- 'index': 'Overview',
- 'quick-install': 'Quick Install',
- 'aindex-and-config': 'aindex and Config'
+ 'index': '概览',
+ 'quick-install': '快速安装',
+ 'aindex-and-config': 'aindex 与配置'
}
diff --git a/doc/content/quick-guide/aindex-and-config.mdx b/doc/content/quick-guide/aindex-and-config.mdx
index a555b626..913f814c 100644
--- a/doc/content/quick-guide/aindex-and-config.mdx
+++ b/doc/content/quick-guide/aindex-and-config.mdx
@@ -1,34 +1,34 @@
---
-title: aindex and .tnmsc.json
-description: Explains where `.aindex` lives, how `~/.aindex/.tnmsc.json` is loaded, which fields are active today, and which legacy fields should be removed.
-sidebarTitle: aindex and Config
+title: aindex 与 .tnmsc.json
+description: 说明 `.aindex` 位于哪里、`~/.aindex/.tnmsc.json` 如何加载、当前哪些字段仍然有效,以及哪些遗留字段应该删除。
+sidebarTitle: aindex 与配置
status: stable
---
-# aindex and `.tnmsc.json`
+# aindex 与 `.tnmsc.json`
-If you only need one page for project setup and config, use this one.
+如果你只想用一页搞清项目准备和配置,就看这一页。
-## Two Things to Separate
+## 需要分开的两件事
-`memory-sync` treats these as different layers:
+`memory-sync` 把下面两项视为不同层:
-- The source-content tree under your workspace aindex directory
-- The canonical global user config file at `~/.aindex/.tnmsc.json`
+- 工作区 aindex 目录下的源内容树
+- 位于 `~/.aindex/.tnmsc.json` 的规范全局用户配置文件
-They are related, but they are not the same thing.
+它们彼此相关,但不是同一回事。
-## What Lives Where
+## 各自放在哪里
-### Workspace content
+### 工作区内容
-The aindex content tree lives under:
+aindex 内容树位于:
```text
/aindex
```
-This is where source content usually lives:
+这里通常放这些源内容:
- `skills/`
- `commands/`
@@ -41,32 +41,32 @@ This is where source content usually lives:
- `global.src.mdx`
- `workspace.src.mdx`
-### User config
+### 用户配置
-The only user config file that `tnmsc` auto-loads today is:
+当前 `tnmsc` 唯一会自动加载的用户配置文件是:
```text
~/.aindex/.tnmsc.json
```
-The current loader does not auto-discover `./.tnmsc.json` from the current project directory.
+当前加载器不会从当前项目目录自动发现 `./.tnmsc.json`。
-## Minimal setup
+## 最小准备
-At minimum you need:
+至少需要:
-- A project root directory
-- An aindex-style source tree
+- 一个项目根目录
+- 一棵符合 aindex 约定的源内容树
- `~/.aindex/.tnmsc.json`
-- A project-level [plugin.config.ts](/docs/cli/plugin-config)
+- 一个项目级的 [plugin.config.ts](/docs/cli/plugin-config)
-The separation is intentional:
+这种拆分是有意为之:
-- `~/.aindex/.tnmsc.json` stores user or machine-level config data
-- `plugin.config.ts` stores project-side plugin assembly and programmatic overrides
-- `~/.aindex/.tnmsc.json` also decides which assembled output plugins are actually allowed to write
+- `~/.aindex/.tnmsc.json` 存放用户级或机器级配置数据
+- `plugin.config.ts` 存放项目侧的插件装配和程序化覆盖
+- `~/.aindex/.tnmsc.json` 还决定哪些已装配的输出插件真正被允许写入
-## Minimal example
+## 最小示例
```json
{
@@ -74,30 +74,30 @@ The separation is intentional:
}
```
-With that config:
+在这份配置下:
-- the workspace root is `/repo/demo`
-- the aindex root is `/repo/demo/aindex`
-- `skills` resolves to `/repo/demo/aindex/skills`
-- the default global prompt source resolves to `/repo/demo/aindex/global.src.mdx`
-- `logLevel` falls back to `info` unless you set it explicitly
+- 工作区根目录是 `/repo/demo`
+- aindex 根目录是 `/repo/demo/aindex`
+- `skills` 解析为 `/repo/demo/aindex/skills`
+- 默认的全局 prompt 源文件解析为 `/repo/demo/aindex/global.src.mdx`
+- 除非你显式设置,否则 `logLevel` 会回退到 `info`
-## Stable top-level fields in `~/.aindex/.tnmsc.json`
+## `~/.aindex/.tnmsc.json` 中稳定的顶层字段
-These are the user-facing fields the current runtime actually consumes from the global config file:
+这些是当前运行时真正会从全局配置文件中消费的用户侧字段:
-| Field | Type | Default | Meaning |
+| 字段 | 类型 | 默认值 | 含义 |
| --- | --- | --- | --- |
-| `version` | `string` | `"0.0.0"` in merged runtime options | Version or release marker passed through the runtime |
-| `workspaceDir` | `string` | omitted in config; resolved from the runtime working directory when not supplied | Workspace root directory, typically under `~/workspace/` |
+| `version` | `string` | 合并后运行时选项中的 `"0.0.0"` | 透传给运行时的版本或发布标记 |
+| `workspaceDir` | `string` | 配置里可省略;未提供时从运行时工作目录推导 | 工作区根目录,通常位于 `~/workspace/` 下 |
| `logLevel` | enum | `info` | `trace` / `debug` / `info` / `warn` / `error` |
-| `frontMatter` | `object` | `{ "blankLineAfter": true }` | Front matter formatting behavior |
-| `codeStyles` | `object` | `{ "indent": "space", "tabSize": 2 }` in merged runtime options | User code style preferences exposed to prompt evaluation |
-| `windows` | `object` | `{}` | Windows and WSL-specific runtime options |
-| `profile` | `object` | omitted | User profile metadata |
-| `plugins` | `object` | `{}` in merged runtime options | Explicit per-plugin output enablement overrides |
+| `frontMatter` | `object` | `{ "blankLineAfter": true }` | Front matter 的格式化行为 |
+| `codeStyles` | `object` | 合并后运行时选项中的 `{ "indent": "space", "tabSize": 2 }` | 暴露给 prompt 求值的用户代码风格偏好 |
+| `windows` | `object` | `{}` | Windows 和 WSL 专用运行时选项 |
+| `profile` | `object` | 省略 | 用户资料元数据 |
+| `plugins` | `object` | 合并后运行时选项中的 `{}` | 每个插件输出启用状态的显式覆盖 |
-Everything under the workspace aindex tree keeps a fixed name and fixed relative location. The structure is:
+工作区 aindex 树下的所有内容都保持固定名称和固定相对位置。结构如下:
```text
/
@@ -125,7 +125,7 @@ Everything under the workspace aindex tree keeps a fixed name and fixed relative
workspace.mdx
```
-None of the fixed paths inside that tree are user-configurable in `~/.aindex/.tnmsc.json`.
+这棵树里的固定路径都不能通过 `~/.aindex/.tnmsc.json` 进行用户配置。
### `frontMatter`
@@ -137,7 +137,7 @@ None of the fixed paths inside that tree are user-configurable in `~/.aindex/.tn
}
```
-See [Front Matter](/docs/cli/frontmatter) for the formatting distinction.
+格式差异请参见 [Front Matter](/docs/cli/frontmatter)。
### `codeStyles`
@@ -150,16 +150,16 @@ See [Front Matter](/docs/cli/frontmatter) for the formatting distinction.
}
```
-| Field | Type | Meaning |
+| 字段 | 类型 | 含义 |
| --- | --- | --- |
-| `codeStyles.indent` | `"tab" \| "space"` | Preferred indentation style |
-| `codeStyles.tabSize` | `integer` | Preferred indentation width |
+| `codeStyles.indent` | `"tab" \| "space"` | 首选缩进方式 |
+| `codeStyles.tabSize` | `integer` | 首选缩进宽度 |
-Additional keys are also accepted so you can keep a few lightweight personal style preferences together in one place.
+也接受额外键,这样你可以把一些轻量的个人风格偏好放在同一个地方。
-The block itself is optional. When you omit it, the runtime still injects `codeStyles.indent = "space"` and `codeStyles.tabSize = 2`.
+这个区块本身是可选的。即使你省略它,运行时仍会注入 `codeStyles.indent = "space"` 和 `codeStyles.tabSize = 2`。
-When prompts are compiled, these values are available in MDX expressions such as `{codeStyles.indent}` and `{codeStyles.tabSize}`.
+编译 prompts 时,这些值可在 MDX 表达式中使用,例如 `{codeStyles.indent}` 和 `{codeStyles.tabSize}`。
### `windows`
@@ -173,24 +173,24 @@ When prompts are compiled, these values are available in MDX expressions such as
}
```
-| Field | Type | Meaning |
+| 字段 | 类型 | 含义 |
| --- | --- | --- |
-| `windows.wsl2.instances` | `string \| string[]` | WSL instance name or names used by Windows-specific integration |
+| `windows.wsl2.instances` | `string \| string[]` | Windows 专用集成所使用的 WSL 实例名称 |
### `profile`
-`profile` is an open object. The schema explicitly recognizes these optional fields:
+`profile` 是一个开放对象。schema 明确识别这些可选字段:
- `name`
- `username`
- `gender`
- `birthday`
-Additional keys are also accepted.
+也接受额外键。
## `plugins`
-The current runtime also recognizes a `plugins` block:
+当前运行时也识别 `plugins` 区块:
```json
{
@@ -202,12 +202,18 @@ The current runtime also recognizes a `plugins` block:
}
```
-Each key accepts only a boolean:
+每个键只接受布尔值:
-- `true`: allow that assembled plugin to emit outputs
-- `false`: suppress output generation for that plugin, while still keeping cleanup behavior
+- `true`:允许该已装配插件产出输出
+- `false`:禁止该插件生成输出,但仍保留清理行为
-Known keys are:
+如果 `plugins` 含有任何不受支持的键,`tnmsc` 现在会把它视为硬性配置错误:
+
+- 它会记录哪个键不受支持
+- 它会打印受支持键的列表
+- 它会直接退出,而不是静默忽略这个错误项
+
+已知键包括:
- `agentsMd`
- `claudeCode`
@@ -229,33 +235,52 @@ Known keys are:
- `windsurf`
- `zed`
-Current built-in default behavior is:
+当前内建默认行为是:
+
+- `git` 默认启用
+- `readme` 默认启用
+- 其他已知插件键默认禁用,直到你显式开启
+
+`plugins.readme` 同时控制 README 类输出和 `.editorconfig`。
+
+请使用 `plugins.git`,不要使用 `plugins.gitExclude`。
+
+这个区块控制的是实际输出行为。项目级的 [plugin.config.ts](/docs/cli/plugin-config) 仍然负责装配可用插件集合,但仅仅完成装配并不代表该插件一定会写文件。
-- `git` is enabled by default
-- `readme` is enabled by default
-- the other known plugin keys are disabled by default until you explicitly turn them on
+> **Warning**
+> 某些目标路径,例如 `.codex/`、`.claude/` 或其他工具配置目录,必须是真实目录。如果同名文件占据了该路径,tnmsc 会把它视为目录/文件类型冲突,并阻止输出生成。
+>
+> 这个恢复路径的目标是避免冲突并让同步继续。当前运行时的做法是给出警告、删除阻塞文件,并自动重建预期的目录结构。
-Use `plugins.readme` for both README-like outputs and `.editorconfig`.
+无效配置示例:
-Use `plugins.git`, not `plugins.gitExclude`.
+```json
+{
+ "plugins": {
+ "vscode": true,
+ "codex": true,
+ "foo": true
+ }
+}
+```
-This block controls actual output emission. The project-level [plugin.config.ts](/docs/cli/plugin-config) still assembles the available plugin set, but assembly alone does not mean the plugin will write files.
+这份配置会失败,因为 `foo` 不是受支持的键。
-## Removed legacy fields
+## 已移除的遗留字段
-Do not put these back into new `~/.aindex/.tnmsc.json` files:
+不要把这些字段重新放回新的 `~/.aindex/.tnmsc.json` 文件:
- `agents`
-- `aindex`, `dir`, and the old `*.src` / `*.dist` path-pair overrides such as `skills`, `commands`, `subAgents`, `rules`, `globalPrompt`, `workspacePrompt`, `app`, `ext`, `arch`, and `softwares`
+- `aindex`、`dir`,以及旧的 `*.src` / `*.dist` 路径对覆盖项,例如 `skills`、`commands`、`subAgents`、`rules`、`globalPrompt`、`workspacePrompt`、`app`、`ext`、`arch` 和 `softwares`
- `commandSeriesOptions`
- `outputScopes`
- `cleanupProtection`
-- Older experimental or pre-consolidation fields such as `fastCommandSeriesOptions`, `externalProjects`, `excludePatterns`, and `shadowSourceProject`
+- 更早期的实验性或 consolidation 之前的字段,例如 `fastCommandSeriesOptions`、`externalProjects`、`excludePatterns` 和 `shadowSourceProject`
-Some compatibility layers may still parse or warn on those keys during migration, but they are no longer part of the supported user-facing config surface.
+某些兼容层在迁移期间可能仍会解析这些键或给出警告,但它们已经不再属于受支持的用户侧配置界面。
-## What to read next
+## 接下来读什么
-- If you want the command workflow, continue with [First Sync](/docs/cli/first-sync)
-- If you need plugin assembly, read [plugin.config.ts](/docs/cli/plugin-config)
-- If you need cleanup or output-boundary behavior, continue with [CLI](/docs/cli)
+- 如果你想了解命令工作流,继续看[第一次同步](/docs/cli/first-sync)
+- 如果你需要插件装配说明,阅读 [plugin.config.ts](/docs/cli/plugin-config)
+- 如果你需要清理或输出边界行为,继续看 [CLI](/docs/cli)
diff --git a/doc/content/quick-guide/index.mdx b/doc/content/quick-guide/index.mdx
index ef543d8a..e37937cf 100644
--- a/doc/content/quick-guide/index.mdx
+++ b/doc/content/quick-guide/index.mdx
@@ -1,73 +1,73 @@
---
-title: Quick Guide
-description: Decide whether to enter the memory-sync docs through CLI, GUI, or MCP using the shortest path.
-sidebarTitle: Overview
+title: 快速指南
+description: 用最短路径判断应从 CLI、GUI 还是 MCP 进入 memory-sync 文档。
+sidebarTitle: 概览
status: stable
keywords:
- memory-sync
- - quick guide
- - onboarding
+ - 快速指南
+ - 上手
---
-# Quick Guide
+# 快速指南
-This section does not dive into implementation details. It answers one question only: which path should you read first to get to a working state as quickly as possible?
+这一部分不深入实现细节,只回答一个问题:为了尽快进入可用状态,你应该先读哪条路径?
-## Architecture at a Glance
+## 架构一览
```mermaid
flowchart LR;
- Author["Source Author"] --> CLI["CLI Terminal Entry"];
- Author --> GUI["GUI Desktop Workflow Entry"];
+ Author["源内容作者"] --> CLI["CLI 终端入口"];
+ Author --> GUI["GUI 桌面工作流入口"];
Author --> Host["MCP Host"];
- CLI --> SDK["sdk private mixed core"];
+ CLI --> SDK["sdk 私有混合核心"];
GUI --> SDK;
- Host --> MCP["mcp stdio server"];
+ Host --> MCP["mcp stdio 服务端"];
MCP --> SDK;
- SDK --> Outputs["Native Outputs for Target Tools"];
+ SDK --> Outputs["面向目标工具的原生输出"];
```
-This diagram highlights the current dependency direction only: `sdk/` is the single core layer, and `cli/`, `gui/`, and `mcp/` all work around it instead of maintaining parallel sources of truth.
+这张图只强调当前的依赖方向:`sdk/` 是唯一的核心层,`cli/`、`gui/` 和 `mcp/` 都围绕它工作,而不是各自维护平行的事实来源。
-## Decide Your Goal First
+## 先确定你的目标
-| Your Goal | Where to Go | Why |
+| 你的目标 | 去哪里 | 原因 |
| --- | --- | --- |
-| Sync prompts, rules, skills, commands, or project memory from a terminal | [CLI](/docs/cli) | The real command surface, schema, output scopes, and cleanup boundaries are all verified there. |
-| Edit config, trigger execution, and inspect logs from the desktop app | [GUI](/docs/gui) | `gui/` owns the desktop workflow, but execution still depends on the `tnmsc` crate in `sdk/`. |
-| Connect `memory-sync-mcp` to an MCP-capable host | [MCP](/docs/mcp) | That section focuses on the stdio server, the tool list, and `workspaceDir` semantics. |
-| Understand the repo architecture before using anything | [SDK](/docs/sdk) and [Technical Details](/docs/technical-details) | The first explains the mixed-core boundary, and the second explains the source-of-truth model and the sync pipeline. |
+| 在终端里同步 prompts、rules、skills、commands 或 project memory | [CLI](/docs/cli) | 真实的命令界面、schema、输出范围和清理边界都在那里核实。 |
+| 在桌面应用里编辑配置、触发执行并查看日志 | [GUI](/docs/gui) | `gui/` 负责桌面工作流,但执行仍依赖 `sdk/` 中的 `tnmsc` crate。 |
+| 把 `memory-sync-mcp` 连接到支持 MCP 的宿主 | [MCP](/docs/mcp) | 这一部分重点说明 stdio 服务端、工具列表和 `workspaceDir` 语义。 |
+| 在使用任何东西之前先理解仓库架构 | [SDK](/docs/sdk) 和 [技术细节](/docs/technical-details) | 前者解释混合核心边界,后者解释事实来源模型和同步流水线。 |
-## Quick Install
+## 快速安装
-If you want to install the command first and decide between CLI, GUI, and MCP later, go straight to [Quick Install](/docs/quick-guide/quick-install).
+如果你想先把命令装好,之后再决定使用 CLI、GUI 还是 MCP,直接去看 [快速安装](/docs/quick-guide/quick-install)。
-## Config Setup in One Page
+## 一页看完配置准备
-If you need the real setup and config facts for `.aindex` and `~/.aindex/.tnmsc.json`, use [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config). That page now collects the setup path, config location, supported user fields, and fixed aindex layout in one place.
+如果你需要了解 `.aindex` 和 `~/.aindex/.tnmsc.json` 的真实准备方式与配置事实,请看 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config)。这页把准备路径、配置位置、当前支持的用户字段和固定的 aindex 布局都汇总在一起。
-## Shortest Starting Paths
+## 最短起步路径
-### If You Start with CLI
+### 如果你从 CLI 开始
-1. Read [Installation and Requirements](/docs/cli/install).
-2. Continue with [aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config).
-3. Then use [First Sync](/docs/cli/first-sync) to run through the real flow once.
+1. 阅读 [安装与要求](/docs/cli/install)。
+2. 接着看 [aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config)。
+3. 然后使用 [第一次同步](/docs/cli/first-sync) 实际跑通一次完整流程。
-### If You Start with GUI
+### 如果你从 GUI 开始
-1. Read the [GUI Overview](/docs/gui).
-2. Continue with [Workflows and Pages](/docs/gui/workflows-and-pages).
-3. When command behavior, output scopes, or cleanup boundaries matter, go back to [CLI](/docs/cli) for the authoritative details.
+1. 阅读 [GUI 概览](/docs/gui)。
+2. 接着看 [工作流与页面](/docs/gui/workflows-and-pages)。
+3. 当你需要理解命令行为、输出范围或清理边界时,再回到 [CLI](/docs/cli) 查看权威说明。
-### If You Start with MCP
+### 如果你从 MCP 开始
-1. Read the [MCP Overview](/docs/mcp).
-2. Continue with [Server and Tools](/docs/mcp/server-tools).
-3. If you need project setup, schema details, or output boundaries, go back to [CLI](/docs/cli).
+1. 阅读 [MCP 概览](/docs/mcp)。
+2. 接着看 [服务端与工具](/docs/mcp/server-tools)。
+3. 如果你需要项目准备、schema 细节或输出边界,就回到 [CLI](/docs/cli)。
-## Three Things to Remember Before You Dive In
+## 开始前要记住的三件事
-- `sdk/` is the internal mixed core of the repo. `cli/`, `mcp/`, and `gui/` are consumers, not parallel sources of truth.
-- The public docs follow the current repository. If old pages, the README, and the code disagree, trust the actual directories, command surface, and schema.
-- If you are unsure where to begin, use this page to branch first instead of opening implementation details at random.
+- `sdk/` 是仓库内部的混合核心。`cli/`、`mcp/` 和 `gui/` 都是消费方,不是平行的事实来源。
+- 公共文档以当前仓库为准。如果旧页面、README 和代码彼此不一致,请相信真实目录、命令界面和 schema。
+- 如果你不确定从哪里开始,先用这页分流,不要随机打开实现细节。
diff --git a/doc/content/quick-guide/quick-install.mdx b/doc/content/quick-guide/quick-install.mdx
index 020f2514..fc1b8333 100644
--- a/doc/content/quick-guide/quick-install.mdx
+++ b/doc/content/quick-guide/quick-install.mdx
@@ -1,18 +1,18 @@
---
-title: Quick Install
-description: Install the memory-sync CLI with the shortest possible commands and confirm that you are using the current command set.
-sidebarTitle: Quick Install
+title: 快速安装
+description: 用最短命令安装 memory-sync CLI,并确认你使用的是当前命令集。
+sidebarTitle: 快速安装
status: stable
keywords:
- - quick install
- - install
+ - 快速安装
+ - 安装
- tnmsc
- cli
---
-# Quick Install
+# 快速安装
-If you just want to get the CLI installed first, use your preferred package manager:
+如果你只是想先把 CLI 装上,直接使用你偏好的包管理器:
-Then confirm that the command works:
-Then confirm that the command works:
+然后确认命令可以正常工作:
```sh
tnmsc help
```
-You should see `dry-run`, `clean`, and `config`.
+你应该能看到 `dry-run`、`clean` 和 `config`。
-## Local Monorepo Development
+## 本地 monorepo 开发
-If you are developing inside this monorepo, use:
+如果你正在这个 monorepo 内开发,请使用:
```sh
pnpm install
@@ -41,10 +40,10 @@ pnpm -C cli exec node dist/index.mjs --help
```
> [!warning]
-> `pnpm` is the only recommended package manager for local development right now.
+> 当前本地开发唯一推荐的包管理器是 `pnpm`。
-## What to Read Next
+## 接下来读什么
-- If you want to run through the real flow once, continue with [First Sync](/docs/cli/first-sync).
-- If you still need the Node, pnpm, and Rust version requirements, continue with [Installation and Requirements](/docs/cli/install).
-- If you still have not decided between CLI, GUI, and MCP, go back to the [Quick Guide](/docs/quick-guide).
+- 如果你想把真实流程完整跑一遍,继续看 [第一次同步](/docs/cli/first-sync)。
+- 如果你还需要 Node、pnpm 和 Rust 的版本要求,继续看 [安装与要求](/docs/cli/install)。
+- 如果你还没决定走 CLI、GUI 还是 MCP,回到 [快速指南](/docs/quick-guide)。
diff --git a/doc/content/sdk/_meta.ts b/doc/content/sdk/_meta.ts
index 0b4d35fc..01d60bc6 100644
--- a/doc/content/sdk/_meta.ts
+++ b/doc/content/sdk/_meta.ts
@@ -1,3 +1,3 @@
export default {
- index: 'Overview'
+ index: '概览'
}
diff --git a/doc/content/sdk/index.mdx b/doc/content/sdk/index.mdx
index 0dc8911c..ccf29156 100644
--- a/doc/content/sdk/index.mdx
+++ b/doc/content/sdk/index.mdx
@@ -1,56 +1,56 @@
---
title: SDK
-description: Explains the responsibilities, boundaries, consumers, and public-identity strategy of sdk/ as the private mixed core.
-sidebarTitle: Overview
+description: 说明作为私有混合核心的 sdk/ 所承担的职责、边界、消费者,以及公共标识策略。
+sidebarTitle: 概览
status: stable
---
# SDK
-`sdk/` is the private mixed core in the current repository and the single entrypoint for shared internal capability. It is not a standalone npm package meant for direct external installation. It is the source-of-truth layer created by pulling the old core implementation out of `cli/`.
+`sdk/` 是当前仓库中的私有混合核心,也是共享内部能力的唯一入口。它不是一个供外部直接安装的独立 npm 包。它是将旧核心实现从 `cli/` 中抽离出来后形成的事实来源层。
-## What This Layer Owns
+## 这一层负责什么
-- It contains the private npm package `@truenine/memory-sync-sdk`
-- It contains the actual workspace path for the Rust crate `tnmsc`
-- It owns the TypeScript sync pipeline, prompt service, schema generation, and `plugin-runtime`
-- It owns the Rust library, NAPI build, Node bridge runtime, and embedded runtime logic
-- It is the default dependency point for `mcp/`, `gui/`, and future internal consumers
+- 它包含私有 npm 包 `@truenine/memory-sync-sdk`
+- 它包含 Rust crate `tnmsc` 的实际 workspace 路径
+- 它负责 TypeScript 同步流水线、prompt service、schema 生成和 `plugin-runtime`
+- 它负责 Rust library、NAPI 构建、Node bridge runtime,以及嵌入式 runtime 逻辑
+- 它是 `mcp/`、`gui/` 以及未来内部消费者的默认依赖入口
-## What This Layer Does Not Own
+## 这一层不负责什么
-- It does not directly own the public npm CLI release entrypoint
-- It does not own distribution identity for platform shim packages such as `@truenine/memory-sync-cli-`
-- It does not push shared internal APIs back into `cli/` as a second source of truth
+- 它不直接负责公共 npm CLI 的发布入口
+- 它不负责 `@truenine/memory-sync-cli-` 这类平台 shim 包的分发标识
+- 它不会把共享内部 API 再塞回 `cli/`,从而形成第二个事实来源
-## Public Identities That Stay the Same
+## 保持不变的公共标识
-This layering change did not alter these public identities:
+这次分层调整没有改变下面这些公共标识:
-- The CLI binary is still `tnmsc`
-- The public npm CLI package is still `@truenine/memory-sync-cli`
-- The public platform package is still `@truenine/memory-sync-cli-`
-- The Rust crate name is still `tnmsc`
+- CLI 二进制仍然是 `tnmsc`
+- 公共 npm CLI 包仍然是 `@truenine/memory-sync-cli`
+- 公共平台包仍然是 `@truenine/memory-sync-cli-`
+- Rust crate 名称仍然是 `tnmsc`
-What changed was path and ownership, not the external identities themselves.
+变化的是路径和归属关系,而不是这些对外标识本身。
-## Consumer Direction
+## 消费者依赖方向
-| Consumer | Dependency Style |
+| 消费者 | 依赖方式 |
| --- | --- |
-| `cli/` | A shell and compatibility layer that thinly wraps capability exported from `sdk/` |
-| `mcp/` | Imports the prompt service directly from `@truenine/memory-sync-sdk` |
-| `gui/src-tauri` | Continues to depend on the crate `tnmsc`, whose actual path now lives under `sdk/` |
+| `cli/` | 一个对 `sdk/` 导出能力做轻量封装的 shell 和兼容层 |
+| `mcp/` | 直接从 `@truenine/memory-sync-sdk` 导入 prompt service |
+| `gui/src-tauri` | 继续依赖 crate `tnmsc`,其实际路径现在位于 `sdk/` 下 |
-## Boundary Rules
+## 边界规则
-- New internal repo code should no longer treat `cli/` as the default shared API entrypoint
-- `cli/` keeps only the command entrypoint, compatibility exports, and release packaging duties
-- When you need to explain implementation boundaries, start with `sdk/` before looking at `cli/`, `mcp/`, or `gui/`
+- 仓库中的新内部代码不应再把 `cli/` 视为默认的共享 API 入口
+- `cli/` 只保留命令入口、兼容导出和发布打包职责
+- 当你需要说明实现边界时,应先从 `sdk/` 开始,再看 `cli/`、`mcp/` 或 `gui/`
-## Recommended Reading
+## 推荐阅读
-- [Technical Details / Architecture Boundaries](/docs/technical-details/architecture): see the full layering of `sdk/`, `cli/`, `mcp/`, and `gui/`
-- [CLI](/docs/cli): see the public command entrypoint, installation path, and compatibility-facing release surface
-- [MCP](/docs/mcp): see how the `sdk` prompt service is consumed by the stdio server
-- [GUI](/docs/gui): see how the desktop layer calls the `tnmsc` crate in `sdk/`
+- [技术细节 / 架构边界](/docs/technical-details/architecture):查看 `sdk/`、`cli/`、`mcp/` 和 `gui/` 的完整分层
+- [CLI](/docs/cli):查看公共命令入口、安装路径以及面向兼容性的发布表层
+- [MCP](/docs/mcp):查看 stdio server 如何消费 `sdk` 的 prompt service
+- [GUI](/docs/gui):查看桌面层如何调用 `sdk/` 中的 `tnmsc` crate
diff --git a/doc/content/technical-details/_meta.ts b/doc/content/technical-details/_meta.ts
index f923edab..ea8cb52a 100644
--- a/doc/content/technical-details/_meta.ts
+++ b/doc/content/technical-details/_meta.ts
@@ -1,13 +1,13 @@
export default {
- 'index': 'Overview',
- 'architecture': 'Architecture Boundaries',
- 'pipeline': 'Sync Pipeline',
- 'source-of-truth': 'Single Source of Truth',
- 'documentation-components': 'Documentation Components',
- 'global-and-workspace-prompts': 'Global and Workspace Prompts',
- 'skills': 'Skills',
- 'commands': 'Commands',
- 'subagents': 'Sub-agents',
- 'rules': 'Rules',
- 'libraries': 'Libraries'
+ 'index': '概览',
+ 'architecture': '架构边界',
+ 'pipeline': '同步流水线',
+ 'source-of-truth': '单一事实来源',
+ 'documentation-components': '文档组件',
+ 'global-and-workspace-prompts': '全局与工作区 Prompts',
+ 'skills': '技能',
+ 'commands': '命令',
+ 'subagents': '子代理',
+ 'rules': '规则',
+ 'libraries': '基础库'
}
diff --git a/doc/content/technical-details/architecture.mdx b/doc/content/technical-details/architecture.mdx
index 9e4174c0..bb85a895 100644
--- a/doc/content/technical-details/architecture.mdx
+++ b/doc/content/technical-details/architecture.mdx
@@ -1,36 +1,36 @@
---
-title: Architecture Boundaries
-description: Explains the responsibility boundaries between tnmsc, the crate, npm packages, MCP, and the GUI.
-sidebarTitle: Architecture Boundaries
+title: 架构边界
+description: 说明 tnmsc、crate、npm 包、MCP 与 GUI 之间的职责边界。
+sidebarTitle: 架构边界
status: stable
---
-# Architecture Boundaries
+# 架构边界
-The core direction of the repository is not "keep stacking more pure TypeScript compatibility layers." It is:
+这个仓库的核心方向不是“继续堆叠更多纯 TypeScript 兼容层”,而是:
-- `sdk/` as the private mixed core
-- `cli/` as the public entrypoint and compatibility release layer
-- Rust crate / NAPI as the long-term center of core implementation
-- TypeScript focused mainly on interface exposure, config assembly, bridge runtime logic, and declarations
+- `sdk/` 作为私有混合核心
+- `cli/` 作为公共入口点和兼容性发布层
+- Rust crate / NAPI 作为核心实现的长期中心
+- TypeScript 主要负责接口暴露、配置装配、bridge 运行时逻辑和声明
-## Component Responsibilities
+## 组件职责
-| Component | Responsibility |
+| 组件 | 职责 |
| --- | --- |
-| `sdk/` | Source of truth for the `tnmsc` crate, Node bridge runtime, prompt service, schema, and NAPI |
-| `cli/` | `tnmsc` command entrypoint, public npm CLI package, and compatibility release shell |
-| `mcp/` | MCP stdio server that reuses the sdk prompt service |
-| `gui/` | Tauri desktop invocation and presentation layer |
-| `libraries/` | Rust-first / NAPI-first foundational libraries |
+| `sdk/` | `tnmsc` crate、Node bridge runtime、prompt service、schema 与 NAPI 的事实来源 |
+| `cli/` | `tnmsc` 命令入口、公开 npm CLI 包,以及兼容发布外壳 |
+| `mcp/` | 复用 sdk prompt service 的 MCP stdio server |
+| `gui/` | Tauri 桌面调用与呈现层 |
+| `libraries/` | Rust-first / NAPI-first 基础库 |
-## Key Boundaries
+## 关键边界
-- GUI is not the center of core implementation
-- MCP is not a new source-of-truth model
-- CLI is not the default dependency point for shared internal APIs
-- The docs site is not the only source of architectural truth
+- GUI 不是核心实现的中心
+- MCP 并不是新的事实来源模型
+- CLI 不是共享内部 API 的默认依赖点
+- 文档站并不是架构事实的唯一来源
-If you need the user-facing operational surface, go back to [CLI](/docs/cli) or [GUI](/docs/gui). This page explains only why the implementation is split this way.
+如果你需要面向用户的操作面,请回到 [CLI](/docs/cli) 或 [GUI](/docs/gui)。这一页只解释为什么实现要这样拆分。
-If you need the responsibilities, consumer direction, and identity-preservation strategy of `sdk/` specifically, go straight to [SDK](/docs/sdk).
+如果你具体想看 `sdk/` 的职责、消费者方向和标识保持策略,直接看 [SDK](/docs/sdk)。
diff --git a/doc/content/technical-details/commands.mdx b/doc/content/technical-details/commands.mdx
index 257e36c1..e025699f 100644
--- a/doc/content/technical-details/commands.mdx
+++ b/doc/content/technical-details/commands.mdx
@@ -1,45 +1,45 @@
---
-title: Commands
-description: Introduces the role of commands and explains the current path-derived naming model.
-sidebarTitle: Commands
+title: 命令
+description: 介绍命令的作用并解释当前的路径派生命名模型。
+sidebarTitle: 命令
status: stable
---
-# Commands
+# 命令
-## What Commands Are Good For
+## 命令适合做什么
-Commands are a good fit for operations that:
+命令很适合这类操作:
-- are reused frequently
-- have a clear execution goal
-- are easy to trigger with short text
+- 经常重复使用
+- 有明确的执行目标
+- 很容易用短文本触发
-They work best as concise operation entrypoints.
+它们最适合作为简洁的操作入口。
-Typical examples include generating release notes, rechecking a class of errors, or reshaping context into a specific format.
+典型例子包括生成发布说明、复查某类错误,或把上下文重组为特定格式。
-## Current Path Convention
+## 当前路径约定
-Default input path:
+默认输入路径:
```text
commands/
```
-Default output path:
+默认输出路径:
```text
dist/commands/
```
-## How Naming Works Now
+## 当前命名如何工作
-Command naming now comes primarily from the source path and file name rather than a global config block.
+命令命名现在主要来自源路径和文件名,而不是某个全局配置块。
-Examples:
+示例:
-- `commands/git/status.src.mdx` keeps a path-derived grouping
-- `commands/release_notes.src.mdx` can still derive a prefix from the flat file name
+- `commands/git/status.src.mdx` 保留路径派生的分组
+- `commands/release_notes.src.mdx` 仍然可以从平面文件名派生前缀
-The old `commandSeriesOptions` block in `~/.aindex/.tnmsc.json` is now legacy. New command configuration should start from the source tree shape and the output plugin behavior instead of trying to restore that removed global override layer.
+`~/.aindex/.tnmsc.json` 中旧的 `commandSeriesOptions` 块现在已经属于遗留项。新的命令配置应该从源树形状和输出插件行为出发,而不是试图恢复那个已经删除的全局覆盖层。
diff --git a/doc/content/technical-details/documentation-components.mdx b/doc/content/technical-details/documentation-components.mdx
index 1ec26d1b..1ae3fb6f 100644
--- a/doc/content/technical-details/documentation-components.mdx
+++ b/doc/content/technical-details/documentation-components.mdx
@@ -1,158 +1,158 @@
---
-title: Documentation Components
-description: Provides reusable capability matrices, system support tables, platform cards, and command-reference components for MDX.
-sidebarTitle: Documentation Components
+title: 文档组件
+description: 为 MDX 提供可复用的能力矩阵、系统支持表、平台卡片和命令参考组件。
+sidebarTitle: 文档组件
status: stable
---
-# Documentation Components
+# 文档组件
-`doc/` can now use a shared set of documentation components directly inside MDX instead of hand-writing large Markdown tables. They are registered globally through `doc/mdx-components.tsx`, so any `.mdx` page can use the JSX tags directly.
+`doc/` 现在可以直接在 MDX 里使用一组共享文档组件,而不用手写大段 Markdown 表格。它们通过 `doc/mdx-components.tsx` 全局注册,所以任何 `.mdx` 页面都能直接使用这些 JSX 标签。
-## 1. Multi-Tool Capability Matrix
+## 1. 多工具能力矩阵
-## 2. Multi-System Support Matrix
+## 2. 多系统支持矩阵
-## 3. Platform Cards
+## 3. 平台卡片
-## 4. Tech-Stack Command Reference
+## 4. 技术栈命令参考
-## 5. Package-Manager Install Tabs
+## 5. 包管理器安装标签
-`PackageManagerTabs` works well on install pages so you do not hardcode only one `npm` example. It uses a shared component to switch between `npm`, `pnpm`, and `yarn`.
+`PackageManagerTabs` 很适合安装页,这样你就不用硬编码单一的 `npm` 示例。它通过共享组件在 `npm`、`pnpm` 和 `yarn` 之间切换。
-## Usage
+## 用法
-Write this directly in any MDX page:
+把下面这类代码直接写进任意 MDX 页面即可:
```tsx
@@ -174,9 +174,9 @@ Write this directly in any MDX page:
```
-If you extend this system later, keep moving in this direction instead of falling back to scattered raw Markdown tables:
+如果你以后继续扩展这套系统,建议继续沿着这个方向走,而不是退回到零散的原始 Markdown 表格:
-- Reuse `FeatureMatrix`, `SupportMatrix`, and `CommandReference` for tabular information
-- Reuse `PlatformGrid` for presentation-heavy content
-- Reuse `PackageManagerTabs` for installation command switches
-- Add new card or comparison components only when you truly need a freer layout
+- 对表格型信息复用 `FeatureMatrix`、`SupportMatrix` 和 `CommandReference`
+- 对展示型内容复用 `PlatformGrid`
+- 对安装命令切换复用 `PackageManagerTabs`
+- 只有在你真的需要更自由布局时,再新增卡片类或对比类组件
diff --git a/doc/content/technical-details/global-and-workspace-prompts.mdx b/doc/content/technical-details/global-and-workspace-prompts.mdx
index 65f9c5df..4d3cb82b 100644
--- a/doc/content/technical-details/global-and-workspace-prompts.mdx
+++ b/doc/content/technical-details/global-and-workspace-prompts.mdx
@@ -1,46 +1,46 @@
---
-title: Global and Workspace Prompts
-description: Explains the path responsibilities and usage boundaries of globalPrompt and workspacePrompt.
-sidebarTitle: Global and Workspace Prompts
+title: 全局与工作区 Prompts
+description: 说明 globalPrompt 与 workspacePrompt 的路径职责和使用边界。
+sidebarTitle: 全局与工作区 Prompts
status: stable
---
-# Global and Workspace Prompts
+# 全局与工作区 Prompts
-## Default Paths
+## 默认路径
-In the current default configuration, these two source files are:
+在当前默认配置下,这两个源文件分别是:
```text
global.src.mdx
workspace.src.mdx
```
-Their default output targets are:
+它们的默认输出目标是:
```text
dist/global.mdx
dist/workspace.mdx
```
-## How They Differ
+## 它们的区别
-- `globalPrompt` is for long-lived cross-project memory, such as personal work style, stable preferences, and constraints shared across repositories.
-- `workspacePrompt` is for the current project context, such as project rules, architecture limits, stack boundaries, and delivery constraints.
+- `globalPrompt` 用于长期、跨项目的记忆,比如个人工作风格、稳定偏好,以及跨仓库共享的约束。
+- `workspacePrompt` 用于当前项目上下文,比如项目规则、架构限制、技术栈边界和交付约束。
-## When Not to Put Content into global
+## 什么内容不该放进 global
-If the content clearly depends on a repository's:
+如果内容明显依赖某个仓库的:
-- directory structure
-- domain vocabulary
-- delivery boundary
-- team constraints
+- 目录结构
+- 领域词汇
+- 交付边界
+- 团队约束
-then it does not belong in `globalPrompt`. Otherwise you are just spreading project-specific noise into every future target.
+那它就不属于 `globalPrompt`。否则,你只是把项目特有噪音扩散到未来所有目标里。
-## Writing Principles
+## 写作原则
-1. If something can live in a workspace prompt, do not rush to put it in global.
-2. Do not hardcode target-tool-specific paths or formats into the source of truth.
-3. If a block of content is really a reusable card, prefer extracting it into a skill or rule instead of growing the prompt into a wall of text.
+1. 如果内容可以放在 workspace prompt,就不要急着塞进 global。
+2. 不要把目标工具特有的路径或格式硬编码进事实来源。
+3. 如果某一块内容本质上是可复用卡片,更适合抽成 skill 或 rule,而不是把 prompt 堆成一堵文字墙。
diff --git a/doc/content/technical-details/index.mdx b/doc/content/technical-details/index.mdx
index a9faee86..1e22774a 100644
--- a/doc/content/technical-details/index.mdx
+++ b/doc/content/technical-details/index.mdx
@@ -1,29 +1,29 @@
---
-title: Technical Details
-description: Explains architecture boundaries, the sync pipeline, the source-of-truth model, and the implementation role of each input asset.
-sidebarTitle: Overview
+title: 技术细节
+description: 说明架构边界、同步流水线、事实来源模型,以及每类输入资产在实现中的角色。
+sidebarTitle: 概览
status: stable
---
-# Technical Details
+# 技术细节
-This section does not explain first-time usage. It explains why the current implementation is layered this way and what role prompts, skills, commands, sub-agents, and rules each play inside the system.
+这一部分不解释首次使用。它解释的是:为什么当前实现要按这种方式分层,以及 prompts、skills、commands、sub-agents 和 rules 在系统里分别承担什么角色。
-## Questions This Section Answers
+## 本节回答的问题
-1. Which things are sources of truth, and which are only derived outputs.
-2. Why the repository has clearly moved toward Rust-first / NAPI-first.
-3. Why sync must model output targets, output scopes, and cleanup boundaries explicitly.
-4. Why different input assets should not be collapsed into one giant prompt.
+1. 哪些东西是事实来源,哪些只是派生输出。
+2. 为什么仓库已经明确转向 Rust-first / NAPI-first。
+3. 为什么 sync 必须显式建模输出目标、输出范围和清理边界。
+4. 为什么不同类型的输入资产不应该塌缩成一个巨大的 prompt。
-## Recommended Reading
+## 推荐阅读
-- [Architecture Boundaries](/docs/technical-details/architecture)
-- [Single Source of Truth](/docs/technical-details/source-of-truth)
-- [Sync Pipeline](/docs/technical-details/pipeline)
-- [Global and Workspace Prompts](/docs/technical-details/global-and-workspace-prompts)
-- [Skills](/docs/technical-details/skills)
-- [Commands](/docs/technical-details/commands)
-- [Sub-agents](/docs/technical-details/subagents)
-- [Rules](/docs/technical-details/rules)
-- [Libraries](/docs/technical-details/libraries)
+- [架构边界](/docs/technical-details/architecture)
+- [单一事实来源](/docs/technical-details/source-of-truth)
+- [同步流水线](/docs/technical-details/pipeline)
+- [全局与工作区 Prompts](/docs/technical-details/global-and-workspace-prompts)
+- [技能](/docs/technical-details/skills)
+- [命令](/docs/technical-details/commands)
+- [子代理](/docs/technical-details/subagents)
+- [规则](/docs/technical-details/rules)
+- [基础库](/docs/technical-details/libraries)
diff --git a/doc/content/technical-details/libraries.mdx b/doc/content/technical-details/libraries.mdx
index bc19f761..5c47759c 100644
--- a/doc/content/technical-details/libraries.mdx
+++ b/doc/content/technical-details/libraries.mdx
@@ -1,26 +1,26 @@
---
-title: Libraries
-description: Summarizes the responsibilities and exposed surfaces of the Rust-first / NAPI-first foundational libraries currently under libraries/.
-sidebarTitle: Libraries
+title: 基础库
+description: 总结当前 `libraries/` 下这些 Rust-first / NAPI-first 基础库的职责和暴露面。
+sidebarTitle: 基础库
status: stable
---
-# Libraries
+# 基础库
-`libraries/` is the directory for Rust-first / NAPI-first foundational libraries. The main visible libraries right now are:
+`libraries/` 是 Rust-first / NAPI-first 基础库所在的目录。目前最主要的几个库是:
- `logger`
- `md-compiler`
- `script-runtime`
-## Rough Responsibility of Each One
+## 各自的大致职责
-| Library | Role |
+| 库 | 角色 |
| --- | --- |
-| `logger` | Logging capability with Rust implementation plus TypeScript-facing APIs and NAPI artifacts |
-| `md-compiler` | MDX / Markdown compilation and transformation capability |
-| `script-runtime` | Foundational script-runtime capability consumed by higher-level assembly |
+| `logger` | 以 Rust 实现日志能力,同时暴露 TypeScript API 和 NAPI 制品 |
+| `md-compiler` | 提供 MDX / Markdown 编译与转换能力 |
+| `script-runtime` | 被更高层装配消费的基础 script-runtime 能力 |
-## Why They Deserve Their Own Section
+## 为什么它们值得单独成节
-These libraries are not "extra documentation material." They are evidence that the repository keeps pushing core capability down into shared infrastructure. CLI, MCP, and GUI should all reuse them rather than duplicating their own versions of the same logic.
+这些库不是“额外的文档材料”,而是仓库持续把核心能力下沉进共享基础设施的证据。CLI、MCP 和 GUI 都应该复用它们,而不是各自复制一份相同逻辑。
diff --git a/doc/content/technical-details/pipeline.mdx b/doc/content/technical-details/pipeline.mdx
index 7282d083..28878a69 100644
--- a/doc/content/technical-details/pipeline.mdx
+++ b/doc/content/technical-details/pipeline.mdx
@@ -1,33 +1,33 @@
---
-title: Sync Pipeline
-description: Explains how plugins declare target files, generation flow, scope selection, and cleanup behavior.
-sidebarTitle: Sync Pipeline
+title: 同步流水线
+description: 说明 plugins 如何声明目标文件、生成流程、范围选择和清理行为。
+sidebarTitle: 同步流水线
status: stable
---
-# Sync Pipeline
+# 同步流水线
-Sync is not "copy a few prompt files into a few directories." It is a pipeline where plugins declare target files, output topics, cleanup scopes, and write behavior.
+同步不是“把几个 prompt 文件复制到几个目录里”。它是一条流水线,插件会在其中声明目标文件、输出主题、清理范围和写入行为。
-## What the Pipeline Must Include at Minimum
+## 这条流水线至少要包含什么
-- Read source-of-truth input assets
-- Merge project and global config
-- Decide output targets per plugin
-- Resolve project or global scope from source metadata and plugin behavior
-- Write target files
-- Run cleanup when needed
+- 读取作为事实来源的输入资产
+- 合并项目和全局配置
+- 决定每个插件的输出目标
+- 从源元数据和插件行为解析项目或全局范围
+- 写入目标文件
+- 需要时运行清理
-## Why This Must Be Modeled Explicitly
+## 为什么必须显式建模
-Without explicit modeling:
+如果不显式建模:
-- You cannot know which content belongs in global outputs and which belongs only at the project level
-- You cannot offer reliable deletion boundaries during `clean`
-- You can easily let incidental formatting rules from target tools leak back into the source of truth
+- 你就无法知道哪些内容应该进入全局输出,哪些只属于项目级
+- 你就无法在 `clean` 时提供可靠的删除边界
+- 目标工具里的偶发格式规则也很容易反向污染事实来源
-## Related Pages
+## 相关页面
-- Config-field behavior: see [CLI / JSON Schema](/docs/cli/schema)
-- Global config and fixed path layout: see [Quick Guide / aindex and `.tnmsc.json`](/docs/quick-guide/aindex-and-config)
-- Source-versus-derived relationship: see [Single Source of Truth](/docs/technical-details/source-of-truth)
+- 配置字段行为:参见 [CLI / JSON Schema](/docs/cli/schema)
+- 全局配置和固定路径布局:参见 [快速指南 / aindex 与 `.tnmsc.json`](/docs/quick-guide/aindex-and-config)
+- 源与派生关系:参见 [单一事实来源](/docs/technical-details/source-of-truth)
diff --git a/doc/content/technical-details/rules.mdx b/doc/content/technical-details/rules.mdx
index c770862e..322999c2 100644
--- a/doc/content/technical-details/rules.mdx
+++ b/doc/content/technical-details/rules.mdx
@@ -1,39 +1,39 @@
---
-title: Rules
-description: Explains how rules work at project and global scope, and why they fit ongoing constraints.
-sidebarTitle: Rules
+title: 规则
+description: 解释规则如何在项目和全局范围内发挥作用,以及为什么它们适合持续的约束。
+sidebarTitle: 规则
status: stable
---
-# Rules
+# 规则
-## What Rules Are Good For
+## 规则适合什么
-Rules are better for long-lived constraints such as:
+规则更适合长期约束,比如:
-- directory responsibilities
-- architecture boundaries
-- code-style red lines
-- review and delivery discipline
+- 目录职责
+- 架构边界
+- 代码风格的红线
+- 审查和交付纪律
-They are more stable than commands and more constraint-oriented than skills.
+它们比命令更稳定,也比 skills 更偏约束表达。
-## Current Path Convention
+## 当前路径约定
-Default input path:
+默认输入路径:
```text
rules/
```
-Default output path:
+默认输出路径:
```text
dist/rules/
```
-## How Rules Relate to output scopes
+## 规则与输出范围的关系
-The schema lets you assign `rules` their own `project` or `global` output scope, and that matters a lot. Some tools need global rules, while others should only receive project-local rules.
+当前模型允许你为 `rules` 分配自己的 `project` 或 `global` 输出范围,这非常重要。有些工具需要全局规则,另一些则只应该接收项目本地规则。
-If rules are written where they do not belong, do not blame the rule file first. Check [CLI / Output Scopes](/docs/cli/output-scopes) first.
+如果规则写到了不该去的地方,先别怪规则文件本身。优先检查 [CLI / 输出范围](/docs/cli/output-scopes)。
diff --git a/doc/content/technical-details/skills.mdx b/doc/content/technical-details/skills.mdx
index 506e90a2..5d5bab8b 100644
--- a/doc/content/technical-details/skills.mdx
+++ b/doc/content/technical-details/skills.mdx
@@ -1,44 +1,44 @@
---
-title: Skills
-description: Introduces skills as reusable capability cards and explains how they relate to output plugins.
-sidebarTitle: Skills
+title: 技能
+description: 介绍作为可复用能力卡的 skills,以及它们与输出插件的关系。
+sidebarTitle: 技能
status: stable
---
-# Skills
+# 技能
-## What Skills Are
+## Skills 是什么
-Skills are reusable capability cards split by task theme. They are a good fit for expressing:
+Skills 是按任务主题拆分的可复用能力卡,特别适合表达:
-- how to produce a certain type of deliverable
-- a method for making a certain type of architectural decision
-- step-by-step knowledge for a specific operational flow
+- 如何生成某种类型的可交付成果
+- 做出某种类型的架构决策的方法
+- 特定操作流程的逐步知识
-## Current Path Convention
+## 当前路径约定
-The default input path is:
+默认输入路径为:
```text
skills/
```
-The default export path is:
+默认导出路径为:
```text
dist/skills/
```
-## Why They Are Not the Same as Prompts
+## 为什么它们不同于 Prompts
-Prompts behave more like persistent context, while skills behave more like on-demand modules. Both can influence model behavior, but they are not the same abstraction layer.
+Prompts 更像持续上下文,而 skills 更像按需模块。两者都能影响模型行为,但不是同一个抽象层。
-## What the Output Layer Cares About
+## 输出层真正关心什么
-`tnmsc` now uses the skill directory name as the skill name by default.
+`tnmsc` 现在默认使用 skill 目录名作为 skill 名称。
-- You do not need to put `name` in skill frontmatter
-- If you do add `name`, sync ignores it and emits a warning
-- If you want to change the skill name, rename `skills//`
+- 你不需要在 skill frontmatter 里写 `name`
+- 如果你写了 `name`,sync 会忽略它并发出警告
+- 如果你想改 skill 名称,就重命名 `skills//`
-See [CLI / Front Matter](/docs/cli/frontmatter) for field details.
+字段细节请看 [CLI / Front Matter](/docs/cli/frontmatter)。
diff --git a/doc/content/technical-details/source-of-truth.mdx b/doc/content/technical-details/source-of-truth.mdx
index 248666ab..ff8aa9e8 100644
--- a/doc/content/technical-details/source-of-truth.mdx
+++ b/doc/content/technical-details/source-of-truth.mdx
@@ -1,34 +1,34 @@
---
-title: Single Source of Truth
-description: Explains the relationship between .src.mdx, source directories, and target-tool outputs, and why output files should not be maintained as the source of truth.
-sidebarTitle: Single Source of Truth
+title: 单一事实来源
+description: 解释 .src.mdx、源目录和目标工具输出之间的关系,以及为什么不应将输出文件保留为事实来源。
+sidebarTitle: 单一事实来源
status: stable
---
-# Single Source of Truth
+# 单一事实来源
-The key premise of `memory-sync` is this: the source of truth should be the input assets you maintain, not the derived files consumed by target tools.
+`memory-sync` 的核心前提是:事实来源应该是你维护的输入资产,而不是目标工具消费的派生文件。
-## What Is Usually Source of Truth
+## 通常什么是事实来源
-- Global prompts
-- Workspace prompts
+- 全局 prompts
+- 工作区 prompts
- `skills/`
- `commands/`
- `subagents/`
- `rules/`
-## What Usually Is Not
+## 通常什么不是
-- Native config destinations for different tools
-- Exported dist content
-- Desktop-layer display state
-- Data returned by MCP reads
+- 不同工具的原生配置落点
+- 导出的 dist 内容
+- 桌面层显示状态
+- MCP 读取返回的数据
-## Why Output Files Cannot Be the Source of Truth
+## 为什么输出文件不能成为事实来源
-Because output files naturally carry the path, naming, formatting, and capability constraints of their target tools. Once you treat those as the source of truth, migration cost and cleanup complexity both rise sharply.
+因为输出文件天然带着目标工具的路径、命名、格式和能力约束。一旦把这些当作事实来源,迁移成本和清理复杂度都会迅速上升。
-## Practical Meaning
+## 实际意义
-When behavior needs to change, modify the source-of-truth inputs and config first instead of hot-fixing a derived file inside a target-tool directory.
+当行为需要变化时,应该先修改事实来源输入和配置,而不是在目标工具目录里热修派生文件。
diff --git a/doc/content/technical-details/subagents.mdx b/doc/content/technical-details/subagents.mdx
index 9d6d2b59..a11cee9e 100644
--- a/doc/content/technical-details/subagents.mdx
+++ b/doc/content/technical-details/subagents.mdx
@@ -1,45 +1,45 @@
---
-title: Sub-agents
-description: Explains the role of sub-agents as input assets in memory-sync and the naming constraints around them.
-sidebarTitle: Sub-agents
+title: 子代理
+description: 说明 subagents 作为 memory-sync 输入资产时所承担的角色,以及它们的命名约束。
+sidebarTitle: 子代理
status: stable
---
-# Sub-agents
+# 子代理
-## The Role of Sub-agents
+## 子代理的角色
-Sub-agents split complex work into more specialized roles and boundaries. They are not copies of a normal prompt. They are closer to constrained specialist execution units.
+Subagents 会把复杂工作拆成更专门的角色和边界。它们不是普通 prompt 的拷贝,而更接近受约束的专家执行单元。
-## Current Path Convention
+## 当前路径约定
-Default input path:
+默认输入路径:
```text
subagents/
```
-Default output path:
+默认输出路径:
```text
dist/subagents/
```
-## Why Naming Matters
+## 为什么命名很重要
-`tnmsc` now derives the sub-agent name from its relative path by default:
+`tnmsc` 现在默认从相对路径派生 subagent 名称:
- `subagents/reviewer.src.mdx` -> `reviewer`
- `subagents/qa/boot.src.mdx` -> `qa-boot`
-That means:
+这意味着:
-- You do not need to put `name` in frontmatter
-- If you do add `name`, sync ignores it and emits a warning
-- If you want to change the sub-agent name, rename the directory or filename
+- 你不需要在 frontmatter 里写 `name`
+- 如果你写了 `name`,sync 会忽略它并发出警告
+- 如果你想改 subagent 名称,就去重命名目录或文件名
-## When It Should Be a Sub-agent Instead of a Skill
+## 什么时候它应该是子代理,而不是 Skill
-- You need a long-lived role with a clear responsibility boundary.
-- That role needs its own description of inputs, behavior constraints, and output shape.
-- It is not just a method for doing one thing. It is an agent configuration for owning a class of work.
+- 你需要一个长期存在、职责边界明确的角色。
+- 这个角色需要自己的输入描述、行为约束和输出形状。
+- 它不是“做某一件事的方法”,而是“负责某一类工作的 agent 配置”。
diff --git a/doc/next.config.ts b/doc/next.config.ts
index fe50beca..d5c42521 100644
--- a/doc/next.config.ts
+++ b/doc/next.config.ts
@@ -1,4 +1,5 @@
import type {NextConfig} from 'next'
+import {readFileSync} from 'node:fs'
import nextra from 'nextra'
const mermaidAliasPath = '@/components/mermaid'
@@ -42,7 +43,26 @@ const LEGACY_DOC_REDIRECTS = [
}
] as const
+const DOC_PACKAGE_JSON_PATH = new URL('./package.json', import.meta.url)
+
+function readDocsVersion() {
+ const packageJson = JSON.parse(readFileSync(DOC_PACKAGE_JSON_PATH, 'utf8')) as {
+ version?: string
+ }
+
+ if (packageJson.version == null || packageJson.version === '') {
+ throw new Error('Unable to resolve docs version from doc/package.json')
+ }
+
+ return packageJson.version
+}
+
+const docsVersion = readDocsVersion()
+
const nextConfig: NextConfig = {
+ env: {
+ NEXT_PUBLIC_MEMORY_SYNC_VERSION: docsVersion
+ },
reactStrictMode: true,
pageExtensions: ['tsx', 'ts', 'mdx'],
async redirects() {
diff --git a/doc/package.json b/doc/package.json
index 7f6bb2a2..20f8a76e 100644
--- a/doc/package.json
+++ b/doc/package.json
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-docs",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"description": "Chinese-first manifesto-led documentation site for @truenine/memory-sync.",
"engines": {
diff --git a/gui/package.json b/gui/package.json
index 743787fa..e83eff4c 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync-gui",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"engines": {
"node": ">= 22"
diff --git a/gui/src-tauri/Cargo.toml b/gui/src-tauri/Cargo.toml
index 0f1d9f71..89a1d376 100644
--- a/gui/src-tauri/Cargo.toml
+++ b/gui/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "memory-sync-gui"
-version = "2026.10404.101"
+version = "2026.10406.121"
description = "Memory Sync desktop GUI application"
authors.workspace = true
edition.workspace = true
diff --git a/gui/src-tauri/tauri.conf.json b/gui/src-tauri/tauri.conf.json
index 6198e879..1fe8e6f9 100644
--- a/gui/src-tauri/tauri.conf.json
+++ b/gui/src-tauri/tauri.conf.json
@@ -1,6 +1,6 @@
{
"$schema": "https://schema.tauri.app/config/2",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"productName": "Memory Sync",
"identifier": "org.truenine.memory-sync",
"build": {
diff --git a/gui/src/utils/configValidation.test.ts b/gui/src/utils/configValidation.test.ts
index 587d29f0..fc4687e3 100644
--- a/gui/src/utils/configValidation.test.ts
+++ b/gui/src/utils/configValidation.test.ts
@@ -214,6 +214,15 @@ describe('validateConfig — plugins', () => {
const errors = validateConfig({ plugins: { trae: 'yes' } })
expect(errorFields(errors)).toContain('plugins.trae')
})
+
+ it('rejects unsupported plugin keys and shows the supported list', () => {
+ const errors = validateConfig({ plugins: { vscode: true, codex: true, foo: true } })
+ const pluginError = errors.find((error) => error.field === 'plugins.foo' && error.severity === 'error')
+
+ expect(pluginError).toBeDefined()
+ expect(pluginError?.message).toMatch(/Unsupported plugins key "foo"/)
+ expect(pluginError?.message).toMatch(/Supported keys:/)
+ })
})
// ─── unknown fields → warnings ─────────────────────────────────────────
diff --git a/gui/src/utils/configValidation.ts b/gui/src/utils/configValidation.ts
index 853597b0..f8738a16 100644
--- a/gui/src/utils/configValidation.ts
+++ b/gui/src/utils/configValidation.ts
@@ -49,6 +49,31 @@ const VALID_INDENT_STYLES: ReadonlySet = new Set([
'space',
])
+const SUPPORTED_PLUGIN_KEYS = [
+ 'agentsMd',
+ 'claudeCode',
+ 'codex',
+ 'cursor',
+ 'droid',
+ 'gemini',
+ 'git',
+ 'jetbrains',
+ 'jetbrainsCodeStyle',
+ 'kiro',
+ 'opencode',
+ 'qoder',
+ 'readme',
+ 'trae',
+ 'traeCn',
+ 'vscode',
+ 'warp',
+ 'windsurf',
+ 'zed',
+] as const
+
+const SUPPORTED_PLUGIN_KEY_SET: ReadonlySet = new Set(SUPPORTED_PLUGIN_KEYS)
+const SUPPORTED_PLUGIN_KEYS_MESSAGE = SUPPORTED_PLUGIN_KEYS.join(', ')
+
function isPlainObject(value: unknown): value is Record {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
@@ -137,6 +162,15 @@ export function validateConfig(raw: unknown): readonly ValidationError[] {
const plugins = validateObjectField(obj, 'plugins', errors)
if (plugins != null) {
for (const [pluginName, enabled] of Object.entries(plugins)) {
+ if (!SUPPORTED_PLUGIN_KEY_SET.has(pluginName)) {
+ errors.push({
+ field: `plugins.${pluginName}`,
+ message: `Unsupported plugins key "${pluginName}". Supported keys: ${SUPPORTED_PLUGIN_KEYS_MESSAGE}`,
+ severity: 'error',
+ })
+ continue
+ }
+
if (typeof enabled !== 'boolean') {
errors.push({
field: `plugins.${pluginName}`,
diff --git a/libraries/logger/package.json b/libraries/logger/package.json
index 77a7ad5d..8b9ddc10 100644
--- a/libraries/logger/package.json
+++ b/libraries/logger/package.json
@@ -1,7 +1,7 @@
{
"name": "@truenine/logger",
"type": "module",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"description": "Rust-powered AI-friendly Markdown logger for Node.js via N-API",
"license": "AGPL-3.0-only",
diff --git a/libraries/md-compiler/package.json b/libraries/md-compiler/package.json
index e5fb0663..28b02cf2 100644
--- a/libraries/md-compiler/package.json
+++ b/libraries/md-compiler/package.json
@@ -1,7 +1,7 @@
{
"name": "@truenine/md-compiler",
"type": "module",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"description": "Rust-powered MDX→Markdown compiler for Node.js with pure-TS fallback",
"license": "AGPL-3.0-only",
diff --git a/libraries/script-runtime/package.json b/libraries/script-runtime/package.json
index 53c08321..13a25f7f 100644
--- a/libraries/script-runtime/package.json
+++ b/libraries/script-runtime/package.json
@@ -1,7 +1,7 @@
{
"name": "@truenine/script-runtime",
"type": "module",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"description": "Rust-backed TypeScript proxy runtime for tnmsc",
"license": "AGPL-3.0-only",
diff --git a/mcp/package.json b/mcp/package.json
index 1e07d863..2a3e1eaa 100644
--- a/mcp/package.json
+++ b/mcp/package.json
@@ -1,7 +1,7 @@
{
"name": "@truenine/memory-sync-mcp",
"type": "module",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"description": "MCP stdio server for managing memory-sync prompt sources and translation artifacts",
"author": "TrueNine",
"license": "AGPL-3.0-only",
diff --git a/package.json b/package.json
index c012871b..6be0e147 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@truenine/memory-sync",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"description": "Cross-AI-tool prompt synchronisation toolkit (CLI + Tauri desktop GUI) — one ruleset, multi-target adaptation. Monorepo powered by pnpm + Turbo.",
"license": "AGPL-3.0-only",
"keywords": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 045eaedb..a5db2c6f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -160,8 +160,8 @@ catalogs:
specifier: ^8.0.4
version: 8.0.4
pagefind:
- specifier: ^1.4.0
- version: 1.4.0
+ specifier: ^1.5.0
+ version: 1.5.0
picocolors:
specifier: ^1.1.1
version: 1.1.1
@@ -223,8 +223,8 @@ catalogs:
specifier: ^11.0.5
version: 11.0.5
vite:
- specifier: ^8.0.3
- version: 8.0.3
+ specifier: ^8.0.5
+ version: 8.0.5
vitest:
specifier: ^4.1.2
version: 4.1.2
@@ -244,7 +244,7 @@ importers:
devDependencies:
'@antfu/eslint-config':
specifier: 'catalog:'
- version: 8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
+ version: 8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
'@eslint/js':
specifier: 'catalog:'
version: 10.0.1(eslint@10.2.0(jiti@2.6.1))
@@ -256,7 +256,7 @@ importers:
version: 16.2.2
'@truenine/eslint10-config':
specifier: 'catalog:'
- version: 2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)
+ version: 2026.10402.10314(1da631964a856581b3443921160f73bd)
'@types/node':
specifier: 'catalog:'
version: 25.5.2
@@ -310,16 +310,16 @@ importers:
version: 8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
vite:
specifier: 'catalog:'
- version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ version: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
cli:
devDependencies:
'@truenine/eslint10-config':
specifier: 'catalog:'
- version: 2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)
+ version: 2026.10402.10314(1da631964a856581b3443921160f73bd)
'@truenine/memory-sync-sdk':
specifier: workspace:*
version: link:../sdk
@@ -328,7 +328,7 @@ importers:
version: 25.5.2
'@vitest/coverage-v8':
specifier: 'catalog:'
- version: 4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
+ version: 4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
eslint:
specifier: 'catalog:'
version: 10.2.0(jiti@2.6.1)
@@ -346,7 +346,7 @@ importers:
version: 6.0.2
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
optionalDependencies:
'@truenine/memory-sync-cli-darwin-arm64':
specifier: workspace:*
@@ -415,7 +415,7 @@ importers:
devDependencies:
'@truenine/eslint10-config':
specifier: 'catalog:'
- version: 2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)
+ version: 2026.10402.10314(1da631964a856581b3443921160f73bd)
'@types/node':
specifier: 'catalog:'
version: 25.5.2
@@ -436,7 +436,7 @@ importers:
version: 8.0.4
pagefind:
specifier: 'catalog:'
- version: 1.4.0
+ version: 1.5.0
tsx:
specifier: 'catalog:'
version: 4.21.0
@@ -454,7 +454,7 @@ importers:
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tailwindcss/vite':
specifier: 'catalog:'
- version: 4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.2.2(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
'@tanstack/react-router':
specifier: 'catalog:'
version: 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -463,7 +463,7 @@ importers:
version: 1.166.24
'@tanstack/router-plugin':
specifier: 'catalog:'
- version: 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
'@tauri-apps/api':
specifier: 'catalog:'
version: 2.10.1
@@ -484,7 +484,7 @@ importers:
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: 'catalog:'
- version: 6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 6.0.1(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
class-variance-authority:
specifier: 'catalog:'
version: 0.7.1
@@ -523,7 +523,7 @@ importers:
version: 1.4.0
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
libraries/logger:
devDependencies:
@@ -541,7 +541,7 @@ importers:
version: 6.0.2
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
libraries/md-compiler:
devDependencies:
@@ -589,7 +589,7 @@ importers:
version: 11.0.5
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
yaml:
specifier: 'catalog:'
version: 2.8.3
@@ -601,7 +601,7 @@ importers:
version: 3.6.0(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(node-addon-api@7.1.1)
'@truenine/eslint10-config':
specifier: 'catalog:'
- version: 2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)
+ version: 2026.10402.10314(1da631964a856581b3443921160f73bd)
'@types/node':
specifier: 'catalog:'
version: 25.5.2
@@ -622,7 +622,7 @@ importers:
version: 6.0.2
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
mcp:
dependencies:
@@ -638,7 +638,7 @@ importers:
devDependencies:
'@truenine/eslint10-config':
specifier: 'catalog:'
- version: 2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)
+ version: 2026.10402.10314(1da631964a856581b3443921160f73bd)
'@types/node':
specifier: 'catalog:'
version: 25.5.2
@@ -659,7 +659,7 @@ importers:
version: 6.0.2
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
sdk:
dependencies:
@@ -693,7 +693,7 @@ importers:
version: 4.0.3
'@vitest/coverage-v8':
specifier: 'catalog:'
- version: 4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
+ version: 4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
fast-glob:
specifier: 'catalog:'
version: 3.3.3
@@ -717,7 +717,7 @@ importers:
version: 4.21.0
vitest:
specifier: 'catalog:'
- version: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ version: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
zod-to-json-schema:
specifier: 'catalog:'
version: 3.25.2(zod@4.3.6)
@@ -2271,33 +2271,38 @@ packages:
cpu: [x64]
os: [win32]
- '@pagefind/darwin-arm64@1.4.0':
- resolution: {integrity: sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==}
+ '@pagefind/darwin-arm64@1.5.0':
+ resolution: {integrity: sha512-OXQVlxALU9+Lz/LxkAa+RvaxY1cnRKUDCuwl9o8PY5Lg/znP573y4WIbVOOIz8Bv7uj7r00TGy7pD+NSLMJGBw==}
cpu: [arm64]
os: [darwin]
- '@pagefind/darwin-x64@1.4.0':
- resolution: {integrity: sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==}
+ '@pagefind/darwin-x64@1.5.0':
+ resolution: {integrity: sha512-+LK1Xq5n/B0hHc08DW61SnfIlfLKyXZ1oKcbfZ1MimE7Rn0Q6R0VI/TlC04f/JDPm+67zAOwPGizzvefOi5vqQ==}
cpu: [x64]
os: [darwin]
- '@pagefind/freebsd-x64@1.4.0':
- resolution: {integrity: sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==}
+ '@pagefind/freebsd-x64@1.5.0':
+ resolution: {integrity: sha512-kicDfUF9gn/z06NimTwNlZXF8z3pLsN3BIPPt6N8unuh0n55fr64tVs2p3a5RKYmQkJGjPfOE/C9GI5YTEpURg==}
cpu: [x64]
os: [freebsd]
- '@pagefind/linux-arm64@1.4.0':
- resolution: {integrity: sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==}
+ '@pagefind/linux-arm64@1.5.0':
+ resolution: {integrity: sha512-e5rDB3wPm89bcSLiatKBDTrVTbsMQrrtkXRaAoUJYU0C1suXVvEzZfjmMvrUDvYhZBx/Ls8hGuGxlqSJBz3gDg==}
cpu: [arm64]
os: [linux]
- '@pagefind/linux-x64@1.4.0':
- resolution: {integrity: sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==}
+ '@pagefind/linux-x64@1.5.0':
+ resolution: {integrity: sha512-vh52DcBiF/mRMmq+Rwt3M3RgEWgl00jFk/M5NWhLEHJFq4+papQXwbyKbi7cNlxaeYrKx6wOfW3fm9cftfc/Kg==}
cpu: [x64]
os: [linux]
- '@pagefind/windows-x64@1.4.0':
- resolution: {integrity: sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==}
+ '@pagefind/windows-arm64@1.5.0':
+ resolution: {integrity: sha512-kg+szZwffZdyWn6SL6RHjAYjhSvJ2bT4qkv3KepGsbmD9fuSHUSC+2kydDneDVUA9qEDRf9uSFoEAsXsp1/JKA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@pagefind/windows-x64@1.5.0':
+ resolution: {integrity: sha512-8eOCmB8lnpyvwz+HrcTXLuBxhj7UseAFh6KGEXRe8UCcAfVQih+qPy/4akJRezViI+ONijz9oi7HpMkw9rdtBg==}
cpu: [x64]
os: [win32]
@@ -5252,8 +5257,8 @@ packages:
package-manager-detector@1.6.0:
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
- pagefind@1.4.0:
- resolution: {integrity: sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==}
+ pagefind@1.5.0:
+ resolution: {integrity: sha512-7vQ2xh0ZmjPjsuWONR68nqzb+QNfpPh7pdT6n6YDAthWAQiUkSACVegSswY5zPNONGYFWebFVgdnS5/m/Qqn+w==}
hasBin: true
parse-entities@4.0.2:
@@ -6094,14 +6099,14 @@ packages:
victory-vendor@37.3.6:
resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
- vite@8.0.3:
- resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==}
+ vite@8.0.5:
+ resolution: {integrity: sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
'@vitejs/devtools': ^0.1.0
- esbuild: ^0.27.0
+ esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0'
less: ^4.0.0
sass: ^1.70.0
@@ -6289,7 +6294,7 @@ packages:
snapshots:
- '@antfu/eslint-config@8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
+ '@antfu/eslint-config@8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
dependencies:
'@antfu/install-pkg': 1.1.0
'@clack/prompts': 1.2.0
@@ -6299,7 +6304,7 @@ snapshots:
'@stylistic/eslint-plugin': 5.10.0(eslint@10.2.0(jiti@2.6.1))
'@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
'@typescript-eslint/parser': 8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
- '@vitest/eslint-plugin': 1.6.14(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
+ '@vitest/eslint-plugin': 1.6.14(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
ansis: 4.2.0
cac: 7.0.0
eslint: 10.2.0(jiti@2.6.1)
@@ -7565,22 +7570,25 @@ snapshots:
'@oxfmt/binding-win32-x64-msvc@0.35.0':
optional: true
- '@pagefind/darwin-arm64@1.4.0':
+ '@pagefind/darwin-arm64@1.5.0':
+ optional: true
+
+ '@pagefind/darwin-x64@1.5.0':
optional: true
- '@pagefind/darwin-x64@1.4.0':
+ '@pagefind/freebsd-x64@1.5.0':
optional: true
- '@pagefind/freebsd-x64@1.4.0':
+ '@pagefind/linux-arm64@1.5.0':
optional: true
- '@pagefind/linux-arm64@1.4.0':
+ '@pagefind/linux-x64@1.5.0':
optional: true
- '@pagefind/linux-x64@1.4.0':
+ '@pagefind/windows-arm64@1.5.0':
optional: true
- '@pagefind/windows-x64@1.4.0':
+ '@pagefind/windows-x64@1.5.0':
optional: true
'@parcel/watcher-android-arm64@2.5.6':
@@ -7892,12 +7900,12 @@ snapshots:
'@tailwindcss/oxide-win32-arm64-msvc': 4.2.2
'@tailwindcss/oxide-win32-x64-msvc': 4.2.2
- '@tailwindcss/vite@4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
+ '@tailwindcss/vite@4.2.2(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@tailwindcss/node': 4.2.2
'@tailwindcss/oxide': 4.2.2
tailwindcss: 4.2.2
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ vite: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
'@tanstack/history@1.161.6': {}
@@ -7943,7 +7951,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
+ '@tanstack/router-plugin@1.167.12(@tanstack/react-router@1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
@@ -7960,7 +7968,7 @@ snapshots:
zod: 3.25.76
optionalDependencies:
'@tanstack/react-router': 1.168.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ vite: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
@@ -8052,9 +8060,9 @@ snapshots:
npm-to-yarn: 3.0.1
unist-util-visit: 5.1.0
- '@truenine/eslint10-config@2026.10402.10314(b56a2921cd0832854c1ed6fed0241803)':
+ '@truenine/eslint10-config@2026.10402.10314(1da631964a856581b3443921160f73bd)':
dependencies:
- '@antfu/eslint-config': 8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
+ '@antfu/eslint-config': 8.0.0(@next/eslint-plugin-next@16.2.2)(@typescript-eslint/rule-tester@8.56.1(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@typescript-eslint/typescript-estree@8.58.0(typescript@6.0.2))(@typescript-eslint/utils@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@unocss/eslint-plugin@66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@2.0.1(eslint@10.2.0(jiti@2.6.1)))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))
'@eslint/js': 10.0.1(eslint@10.2.0(jiti@2.6.1))
'@next/eslint-plugin-next': 16.2.2
'@unocss/eslint-config': 66.6.7(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
@@ -8506,12 +8514,12 @@ snapshots:
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
- '@vitejs/plugin-react@6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
+ '@vitejs/plugin-react@6.0.1(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.7
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ vite: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
- '@vitest/coverage-v8@4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
+ '@vitest/coverage-v8@4.1.2(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
dependencies:
'@bcoe/v8-coverage': 1.0.2
'@vitest/utils': 4.1.2
@@ -8523,9 +8531,9 @@ snapshots:
obug: 2.1.1
std-env: 4.0.0
tinyrainbow: 3.1.0
- vitest: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ vitest: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
- '@vitest/eslint-plugin@1.6.14(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
+ '@vitest/eslint-plugin@1.6.14(@typescript-eslint/eslint-plugin@8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)(vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)))':
dependencies:
'@typescript-eslint/scope-manager': 8.58.0
'@typescript-eslint/utils': 8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
@@ -8533,7 +8541,7 @@ snapshots:
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.58.0(@typescript-eslint/parser@8.58.0(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2))(eslint@10.2.0(jiti@2.6.1))(typescript@6.0.2)
typescript: 6.0.2
- vitest: 4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ vitest: 4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- supports-color
@@ -8546,13 +8554,13 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
+ '@vitest/mocker@4.1.2(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
'@vitest/spy': 4.1.2
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ vite: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
'@vitest/pretty-format@4.1.2':
dependencies:
@@ -11052,14 +11060,15 @@ snapshots:
package-manager-detector@1.6.0: {}
- pagefind@1.4.0:
+ pagefind@1.5.0:
optionalDependencies:
- '@pagefind/darwin-arm64': 1.4.0
- '@pagefind/darwin-x64': 1.4.0
- '@pagefind/freebsd-x64': 1.4.0
- '@pagefind/linux-arm64': 1.4.0
- '@pagefind/linux-x64': 1.4.0
- '@pagefind/windows-x64': 1.4.0
+ '@pagefind/darwin-arm64': 1.5.0
+ '@pagefind/darwin-x64': 1.5.0
+ '@pagefind/freebsd-x64': 1.5.0
+ '@pagefind/linux-arm64': 1.5.0
+ '@pagefind/linux-x64': 1.5.0
+ '@pagefind/windows-arm64': 1.5.0
+ '@pagefind/windows-x64': 1.5.0
parse-entities@4.0.2:
dependencies:
@@ -12068,7 +12077,7 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
- vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3):
+ vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -12087,10 +12096,10 @@ snapshots:
- '@emnapi/core'
- '@emnapi/runtime'
- vitest@4.1.2(@types/node@25.5.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)):
+ vitest@4.1.2(@types/node@25.5.2)(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@vitest/expect': 4.1.2
- '@vitest/mocker': 4.1.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
+ '@vitest/mocker': 4.1.2(vite@8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3))
'@vitest/pretty-format': 4.1.2
'@vitest/runner': 4.1.2
'@vitest/snapshot': 4.1.2
@@ -12107,7 +12116,7 @@ snapshots:
tinyexec: 1.0.4
tinyglobby: 0.2.15
tinyrainbow: 3.1.0
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
+ vite: 8.0.5(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.6.1)(sass@1.99.0)(tsx@4.21.0)(yaml@2.8.3)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 25.5.2
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index a40a8269..3465f29b 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -63,7 +63,7 @@ catalog:
nextra: ^4.6.1
nextra-theme-docs: ^4.6.1
npm-run-all2: ^8.0.4
- pagefind: ^1.4.0
+ pagefind: ^1.5.0
picocolors: ^1.1.1
picomatch: ^4.0.4
prettier: ^3.8.1
@@ -84,7 +84,7 @@ catalog:
typescript: 6.0.2
typescript-eslint: ^8.58.0
unified: ^11.0.5
- vite: ^8.0.3
+ vite: ^8.0.5
vitest: ^4.1.2
yaml: ^2.8.3
zod: ^4.3.6
diff --git a/sdk/package.json b/sdk/package.json
index a720002e..3b9bf709 100644
--- a/sdk/package.json
+++ b/sdk/package.json
@@ -1,7 +1,7 @@
{
"name": "@truenine/memory-sync-sdk",
"type": "module",
- "version": "2026.10404.101",
+ "version": "2026.10406.121",
"private": true,
"description": "TrueNine Memory Synchronization SDK",
"author": "TrueNine",
diff --git a/sdk/src/ConfigLoader.test.ts b/sdk/src/ConfigLoader.test.ts
index c2a81ece..8e73ed51 100644
--- a/sdk/src/ConfigLoader.test.ts
+++ b/sdk/src/ConfigLoader.test.ts
@@ -177,4 +177,28 @@ describe('configLoader', () => {
fs.rmSync(tempDir, {recursive: true, force: true})
}
})
+
+ it('throws when plugins contains an unsupported key', () => {
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-config-loader-plugins-invalid-'))
+ const configPath = path.join(tempDir, '.tnmsc.json')
+
+ try {
+ fs.writeFileSync(configPath, JSON.stringify({
+ plugins: {
+ vscode: true,
+ codex: true,
+ foo: true
+ }
+ }), 'utf8')
+
+ const loader = new ConfigLoader()
+
+ expect(() => loader.loadFromFile(configPath)).toThrowError(
+ /Unsupported plugins key "foo"\. Supported keys:/
+ )
+ }
+ finally {
+ fs.rmSync(tempDir, {recursive: true, force: true})
+ }
+ })
})
diff --git a/sdk/src/ConfigLoader.ts b/sdk/src/ConfigLoader.ts
index 2906177e..62e03b64 100644
--- a/sdk/src/ConfigLoader.ts
+++ b/sdk/src/ConfigLoader.ts
@@ -14,9 +14,11 @@ import {
buildConfigDiagnostic,
buildFileOperationDiagnostic,
diagnosticLines,
- splitDiagnosticText
+ splitDiagnosticText,
+ toErrorMessage
} from './diagnostics'
import {
+ getSupportedPluginConfigKeysMessage,
ZUserConfigFile
} from './plugins/plugin-core'
import {
@@ -73,9 +75,9 @@ export class ConfigLoader {
loadFromFile(filePath: string): ConfigLoadResult {
const resolvedPath = this.resolveTilde(filePath)
- try {
- if (!fs.existsSync(resolvedPath)) return {config: {}, source: null, found: false}
+ if (!fs.existsSync(resolvedPath)) return {config: {}, source: null, found: false}
+ try {
const content = fs.readFileSync(resolvedPath, 'utf8')
const config = this.parseConfig(content, resolvedPath)
@@ -83,15 +85,38 @@ export class ConfigLoader {
return {config, source: resolvedPath, found: true}
}
catch (error) {
- this.logger.warn(buildFileOperationDiagnostic({
- code: 'CONFIG_FILE_LOAD_FAILED',
- title: 'Failed to load config file',
- operation: 'read',
- targetKind: 'config file',
- path: resolvedPath,
- error
- }))
- return {config: {}, source: null, found: false}
+ const errorMessage = toErrorMessage(error)
+
+ if (errorMessage.startsWith('Invalid JSON in ') || errorMessage.startsWith('Config validation failed in ')) {
+ this.logger.error(buildConfigDiagnostic({
+ code: 'CONFIG_FILE_VALIDATION_FAILED',
+ title: 'Config file validation failed',
+ reason: splitDiagnosticText(errorMessage),
+ configPath: resolvedPath,
+ exactFix: diagnosticLines(
+ 'Fix the invalid config entries so the file matches the tnmsc schema.'
+ ),
+ possibleFixes: [
+ diagnosticLines(
+ `If the error is under "plugins", only use supported keys: ${getSupportedPluginConfigKeysMessage()}`
+ )
+ ],
+ details: {
+ errorMessage
+ }
+ }))
+ } else {
+ this.logger.error(buildFileOperationDiagnostic({
+ code: 'CONFIG_FILE_LOAD_FAILED',
+ title: 'Failed to load config file',
+ operation: 'read',
+ targetKind: 'config file',
+ path: resolvedPath,
+ error
+ }))
+ }
+
+ throw error instanceof Error ? error : new Error(errorMessage)
}
}
diff --git a/sdk/src/diagnostics.test.ts b/sdk/src/diagnostics.test.ts
index 053d1ec9..ae1729c9 100644
--- a/sdk/src/diagnostics.test.ts
+++ b/sdk/src/diagnostics.test.ts
@@ -1,3 +1,6 @@
+import * as fs from 'node:fs'
+import * as os from 'node:os'
+import * as path from 'node:path'
import {describe, expect, it} from 'vitest'
import {buildFileOperationDiagnostic} from './diagnostics'
@@ -51,4 +54,33 @@ describe('buildFileOperationDiagnostic', () => {
errorMessage: 'ENOENT: no such file or directory'
})
})
+
+ it('explains that a blocking file can be safely deleted when a directory path is occupied', () => {
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-diagnostic-blocking-file-'))
+ const blockingFilePath = path.join(tempDir, '.codex')
+ fs.writeFileSync(blockingFilePath, '', 'utf8')
+
+ try {
+ const diagnostic = buildFileOperationDiagnostic({
+ code: 'CLEANUP_DIRECTORY_DELETE_FAILED',
+ title: 'Cleanup could not delete a directory',
+ operation: 'delete',
+ targetKind: 'directory',
+ path: path.join(tempDir, '.codex', 'skills'),
+ error: 'Not a directory (os error 20)',
+ platform: 'linux'
+ })
+
+ expect(diagnostic.exactFix).toEqual([
+ `Delete the blocking file at "${blockingFilePath}" and rerun tnmsc.`,
+ 'tnmsc expects a directory there, so you do not need to keep that file.'
+ ])
+ expect(diagnostic.details).toMatchObject({
+ blockingPath: blockingFilePath
+ })
+ }
+ finally {
+ fs.rmSync(tempDir, {recursive: true, force: true})
+ }
+ })
})
diff --git a/sdk/src/diagnostics.ts b/sdk/src/diagnostics.ts
index 15634a9b..d33efd86 100644
--- a/sdk/src/diagnostics.ts
+++ b/sdk/src/diagnostics.ts
@@ -5,6 +5,7 @@ import type {
} from './plugins/plugin-core'
import type {ProtectedPathViolation} from './ProtectedDeletionGuard'
import process from 'node:process'
+import {resolveBlockingFilePath} from './path-blocking-file'
export function diagnosticLines(firstLine: string, ...otherLines: string[]): DiagnosticLines {
return [firstLine, ...otherLines]
@@ -78,7 +79,25 @@ function buildFileOperationAdvice(options: {
readonly path: string
readonly error: unknown
readonly platform: NodeJS.Platform
+ readonly blockingPath?: string | undefined
}): FileOperationAdvice {
+ if (options.blockingPath != null) {
+ return {
+ exactFix: diagnosticLines(
+ `Delete the blocking file at "${options.blockingPath}" and rerun tnmsc.`,
+ 'tnmsc expects a directory there, so you do not need to keep that file.'
+ ),
+ possibleFixes: [
+ diagnosticLines(
+ `A file is occupying a directory path required for "${options.path}".`
+ ),
+ diagnosticLines(
+ 'If that file came from an older tool or a mistaken manual edit, remove it and let tnmsc recreate the directory tree.'
+ )
+ ]
+ }
+ }
+
if (isWindowsDirectoryDeletePermissionDenied(options)) {
return {
exactFix: diagnosticLines(
@@ -124,12 +143,16 @@ export function buildFileOperationDiagnostic(options: FileOperationDiagnosticOpt
details
} = options
const errorMessage = toErrorMessage(error)
+ const blockingPath = targetKind === 'file' || targetKind === 'directory'
+ ? resolveBlockingFilePath({path, targetKind, error})
+ : void 0
const advice = buildFileOperationAdvice({
operation,
targetKind,
path,
error,
- platform: platform ?? process.platform
+ platform: platform ?? process.platform,
+ ...blockingPath != null ? {blockingPath} : {}
})
return buildDiagnostic({
@@ -147,6 +170,7 @@ export function buildFileOperationDiagnostic(options: FileOperationDiagnosticOpt
path,
errorMessage,
platform: platform ?? process.platform,
+ ...blockingPath != null ? {blockingPath} : {},
...details ?? {}
}
})
diff --git a/sdk/src/inputs/input-subagent.test.ts b/sdk/src/inputs/input-subagent.test.ts
index 6567c128..b710261f 100644
--- a/sdk/src/inputs/input-subagent.test.ts
+++ b/sdk/src/inputs/input-subagent.test.ts
@@ -221,4 +221,46 @@ describe('subagent input plugin', () => {
fs.rmSync(tempWorkspace, {recursive: true, force: true})
}
})
+
+ it('does not log the legacy agents field name in subagent debug output', async () => {
+ const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-subagent-debug-log-test-'))
+ const aindexDir = path.join(tempWorkspace, 'aindex')
+ const distDir = path.join(aindexDir, 'dist', 'subagents')
+ const debugPayloads: unknown[] = []
+
+ try {
+ fs.mkdirSync(distDir, {recursive: true})
+ fs.writeFileSync(
+ path.join(distDir, 'demo.mdx'),
+ '---\ndescription: dist only\n---\nDist only subagent',
+ 'utf8'
+ )
+
+ const plugin = new SubAgentInputCapability()
+ await plugin.collect({
+ logger: {
+ trace: () => {},
+ debug: (_message: string, payload?: unknown) => debugPayloads.push(payload),
+ info: () => {},
+ warn: () => {},
+ error: () => {},
+ fatal: () => {}
+ },
+ fs,
+ path,
+ glob,
+ userConfigOptions: mergeConfig({workspaceDir: tempWorkspace}),
+ dependencyContext: {}
+ } as InputCapabilityContext)
+
+ expect(debugPayloads).toContainEqual({count: 1})
+ expect(debugPayloads.some(payload =>
+ typeof payload === 'object'
+ && payload !== null
+ && 'agents' in payload)).toBe(false)
+ }
+ finally {
+ fs.rmSync(tempWorkspace, {recursive: true, force: true})
+ }
+ })
})
diff --git a/sdk/src/inputs/input-subagent.ts b/sdk/src/inputs/input-subagent.ts
index 3254c268..7f5b927c 100644
--- a/sdk/src/inputs/input-subagent.ts
+++ b/sdk/src/inputs/input-subagent.ts
@@ -168,8 +168,7 @@ export class SubAgentInputCapability extends AbstractInputCapability {
}
logger.debug('SubAgentInputCapability flattened subAgents', {
- count: flatSubAgents.length,
- agents: flatSubAgents.map(a => a.canonicalName)
+ count: flatSubAgents.length
})
return {
diff --git a/sdk/src/path-blocking-file.ts b/sdk/src/path-blocking-file.ts
new file mode 100644
index 00000000..8480f867
--- /dev/null
+++ b/sdk/src/path-blocking-file.ts
@@ -0,0 +1,65 @@
+import * as fs from 'node:fs'
+import * as path from 'node:path'
+
+function normalizeErrorMessage(error: unknown): string {
+ return (error instanceof Error ? error.message : String(error)).toLowerCase()
+}
+
+export function isDirectoryStructureMismatchError(error: unknown): boolean {
+ const normalizedError = normalizeErrorMessage(error)
+ return normalizedError.includes('enotdir')
+ || normalizedError.includes('not a directory')
+ || normalizedError.includes('eexist')
+ || normalizedError.includes('file exists')
+}
+
+export function findBlockingNonDirectoryPath(expectedDirPath: string): string | undefined {
+ const resolvedDirPath = path.resolve(expectedDirPath)
+ const {root} = path.parse(resolvedDirPath)
+ const relativeSegments = resolvedDirPath
+ .slice(root.length)
+ .split(path.sep)
+ .filter(segment => segment.length > 0)
+
+ let currentPath = root
+ for (const segment of relativeSegments) {
+ currentPath = currentPath.length > 0 ? path.join(currentPath, segment) : segment
+ if (!fs.existsSync(currentPath)) continue
+
+ try {
+ if (!fs.lstatSync(currentPath).isDirectory()) return currentPath
+ }
+ catch {
+ return void 0
+ }
+ }
+
+ return void 0
+}
+
+export function resolveBlockingFilePath(options: {
+ readonly path: string
+ readonly targetKind: 'file' | 'directory'
+ readonly error: unknown
+}): string | undefined {
+ if (!isDirectoryStructureMismatchError(options.error)) return void 0
+
+ const expectedDirPath = options.targetKind === 'file'
+ ? path.dirname(options.path)
+ : options.path
+
+ return findBlockingNonDirectoryPath(expectedDirPath)
+}
+
+export function removeBlockingFile(blockingPath: string): {removed: boolean, error?: unknown} {
+ if (!fs.existsSync(blockingPath)) return {removed: false}
+
+ try {
+ if (fs.lstatSync(blockingPath).isDirectory()) return {removed: false}
+ fs.rmSync(blockingPath, {force: true})
+ return {removed: true}
+ }
+ catch (error) {
+ return {removed: false, error}
+ }
+}
diff --git a/sdk/src/plugins/CodexCLIOutputPlugin.test.ts b/sdk/src/plugins/CodexCLIOutputPlugin.test.ts
index 702d654c..dd27c089 100644
--- a/sdk/src/plugins/CodexCLIOutputPlugin.test.ts
+++ b/sdk/src/plugins/CodexCLIOutputPlugin.test.ts
@@ -476,6 +476,9 @@ describe('codexCLIOutputPlugin command output', () => {
expect(deletePaths).toContain(
path.join(homeDir, '.skills').replaceAll('\\', '/')
)
+ expect(deletePaths).toContain(
+ path.join(homeDir, '.aindex', '.skills').replaceAll('\\', '/')
+ )
})
})
diff --git a/sdk/src/plugins/CodexCLIOutputPlugin.ts b/sdk/src/plugins/CodexCLIOutputPlugin.ts
index cd07c822..42b75485 100644
--- a/sdk/src/plugins/CodexCLIOutputPlugin.ts
+++ b/sdk/src/plugins/CodexCLIOutputPlugin.ts
@@ -21,6 +21,7 @@ const AGENTS_SUBDIR = 'agents'
const SKILLS_SUBDIR = 'skills'
const PRESERVED_SYSTEM_SKILL_DIR = '.system'
const MCP_CONFIG_FILE = 'mcp.json'
+const LEGACY_AINDEX_SKILLS_DIR = '.aindex/.skills'
const CODEX_SUBAGENT_FIELD_ORDER = ['name', 'description', 'developer_instructions'] as const
const CODEX_EXCLUDED_SUBAGENT_FIELDS = ['scope', 'seriName', 'argumentHint', 'color', 'namingCase', 'model'] as const
@@ -85,7 +86,7 @@ const CODEX_OUTPUT_OPTIONS = {
},
global: {
files: ['.codex/AGENTS.md'],
- dirs: ['.codex/prompts', '.agents/skills', '.skills'],
+ dirs: ['.codex/prompts', '.agents/skills', '.skills', LEGACY_AINDEX_SKILLS_DIR],
globs: ['.codex/skills/*']
}
},
diff --git a/sdk/src/plugins/GenericSkillsOutputPlugin.test.ts b/sdk/src/plugins/GenericSkillsOutputPlugin.test.ts
index f669a559..6c3bfe5e 100644
--- a/sdk/src/plugins/GenericSkillsOutputPlugin.test.ts
+++ b/sdk/src/plugins/GenericSkillsOutputPlugin.test.ts
@@ -130,5 +130,8 @@ describe('genericSkillsOutputPlugin cleanup', () => {
expect(deletePaths).toContain(
path.join(homeDir, '.skills').replaceAll('\\', '/')
)
+ expect(deletePaths).toContain(
+ path.join(homeDir, '.aindex', '.skills').replaceAll('\\', '/')
+ )
})
})
diff --git a/sdk/src/plugins/GenericSkillsOutputPlugin.ts b/sdk/src/plugins/GenericSkillsOutputPlugin.ts
index 3ee0cddd..ee1c6607 100644
--- a/sdk/src/plugins/GenericSkillsOutputPlugin.ts
+++ b/sdk/src/plugins/GenericSkillsOutputPlugin.ts
@@ -9,6 +9,7 @@ import {AbstractOutputPlugin, filterByProjectConfig} from './plugin-core'
const PROJECT_SKILLS_DIR = '.agents/skills'
const LEGACY_SKILLS_DIR = '.skills'
+const LEGACY_AINDEX_SKILLS_DIR = '.aindex/.skills'
const SKILL_FILE_NAME = 'SKILL.md'
const MCP_CONFIG_FILE = 'mcp.json'
@@ -43,7 +44,7 @@ export class GenericSkillsOutputPlugin extends AbstractOutputPlugin {
dirs: [PROJECT_SKILLS_DIR, LEGACY_SKILLS_DIR]
},
global: {
- dirs: [PROJECT_SKILLS_DIR, LEGACY_SKILLS_DIR]
+ dirs: [PROJECT_SKILLS_DIR, LEGACY_SKILLS_DIR, LEGACY_AINDEX_SKILLS_DIR]
}
}
},
diff --git a/sdk/src/plugins/plugin-core/ConfigTypes.schema.ts b/sdk/src/plugins/plugin-core/ConfigTypes.schema.ts
index d3b442a3..17916f44 100644
--- a/sdk/src/plugins/plugin-core/ConfigTypes.schema.ts
+++ b/sdk/src/plugins/plugin-core/ConfigTypes.schema.ts
@@ -112,7 +112,53 @@ export const ZWindowsOptions = z.object({
wsl2: ZWindowsWsl2Options.optional()
})
-export const ZPluginsConfig = z.record(z.string(), z.boolean())
+export const SUPPORTED_PLUGIN_CONFIG_KEYS = [
+ 'agentsMd',
+ 'claudeCode',
+ 'codex',
+ 'cursor',
+ 'droid',
+ 'gemini',
+ 'git',
+ 'jetbrains',
+ 'jetbrainsCodeStyle',
+ 'kiro',
+ 'opencode',
+ 'qoder',
+ 'readme',
+ 'trae',
+ 'traeCn',
+ 'vscode',
+ 'warp',
+ 'windsurf',
+ 'zed'
+] as const
+
+export type SupportedPluginConfigKey = (typeof SUPPORTED_PLUGIN_CONFIG_KEYS)[number]
+
+const SUPPORTED_PLUGIN_CONFIG_KEY_SET = new Set(SUPPORTED_PLUGIN_CONFIG_KEYS)
+
+export function isSupportedPluginConfigKey(key: string): key is SupportedPluginConfigKey {
+ return SUPPORTED_PLUGIN_CONFIG_KEY_SET.has(key)
+}
+
+export function getSupportedPluginConfigKeysMessage(): string {
+ return SUPPORTED_PLUGIN_CONFIG_KEYS.join(', ')
+}
+
+export const ZPluginsConfig = z.record(z.string(), z.boolean()).superRefine((plugins, ctx) => {
+ const supportedKeysMessage = getSupportedPluginConfigKeysMessage()
+
+ for (const key of Object.keys(plugins)) {
+ if (isSupportedPluginConfigKey(key)) continue
+
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ path: [key],
+ message: `Unsupported plugins key "${key}". Supported keys: ${supportedKeysMessage}`
+ })
+ }
+})
/**
* Zod schema for user profile information.
diff --git a/sdk/src/plugins/plugin-core/plugin.ts b/sdk/src/plugins/plugin-core/plugin.ts
index cf1529df..24d5b6a6 100644
--- a/sdk/src/plugins/plugin-core/plugin.ts
+++ b/sdk/src/plugins/plugin-core/plugin.ts
@@ -6,6 +6,7 @@ import type {
FrontMatterOptions,
PluginsConfig,
ProtectionMode,
+ SupportedPluginConfigKey,
WindowsOptions
} from './ConfigTypes.schema'
import type {PluginKind} from './enums'
@@ -15,7 +16,13 @@ import type {RuntimeCommand} from '@/runtime-command'
import {Buffer} from 'node:buffer'
import * as fs from 'node:fs'
import * as path from 'node:path'
+import {buildUsageDiagnostic, diagnosticLines} from '@/diagnostics'
import {filterPathScopedEntriesForExecutionPlan} from '@/execution-plan'
+import {
+ findBlockingNonDirectoryPath,
+ isDirectoryStructureMismatchError,
+ removeBlockingFile
+} from '@/path-blocking-file'
export type FastGlobType = typeof import('fast-glob')
@@ -314,8 +321,51 @@ function isNodeBufferLike(value: unknown): value is Buffer {
return Buffer.isBuffer(value)
}
+function normalizeWriteOperationError(targetPath: string, error: unknown): Error {
+ if (!isDirectoryStructureMismatchError(error)) {
+ return error instanceof Error ? error : new Error(String(error))
+ }
+
+ const blockingPath = findBlockingNonDirectoryPath(path.dirname(targetPath))
+ if (blockingPath == null) {
+ return error instanceof Error ? error : new Error(String(error))
+ }
+
+ return new Error(
+ `Cannot write "${targetPath}" because a file is blocking a required directory path at "${blockingPath}". `
+ + 'Delete that file and rerun tnmsc; you do not need to keep it.'
+ )
+}
+
+function tryRecoverBlockingFileForWrite(
+ targetPath: string,
+ error: unknown,
+ logger: Pick
+): boolean {
+ if (!isDirectoryStructureMismatchError(error)) return false
+
+ const blockingPath = findBlockingNonDirectoryPath(path.dirname(targetPath))
+ if (blockingPath == null) return false
+
+ const removal = removeBlockingFile(blockingPath)
+ if (!removal.removed) return false
+
+ logger.warn(buildUsageDiagnostic({
+ code: 'BLOCKING_FILE_REMOVED_FOR_WRITE',
+ title: 'Removed blocking file and continued output write',
+ rootCause: diagnosticLines(
+ `tnmsc deleted the blocking file at "${blockingPath}" so it could continue writing "${targetPath}".`
+ ),
+ details: {
+ targetPath,
+ blockingPath
+ }
+ }))
+ return true
+}
+
interface OutputPluginEnablementRule {
- readonly configKey: string
+ readonly configKey: SupportedPluginConfigKey
readonly defaultEnabled: boolean
}
@@ -459,26 +509,40 @@ export async function executeDeclarativeWriteOutputs(
continue
}
- try {
- const parentDir = path.dirname(declaration.path)
- fs.mkdirSync(parentDir, {recursive: true})
-
- if (declaration.ifExists === 'skip' && fs.existsSync(declaration.path)) {
- fileResults.push({path: declaration.path, success: true, skipped: true})
- continue
+ let recoveredBlockingFile = false
+ for (;;) {
+ try {
+ const parentDir = path.dirname(declaration.path)
+ fs.mkdirSync(parentDir, {recursive: true})
+
+ if (declaration.ifExists === 'skip' && fs.existsSync(declaration.path)) {
+ fileResults.push({path: declaration.path, success: true, skipped: true})
+ break
+ }
+
+ if (declaration.ifExists === 'error' && fs.existsSync(declaration.path)) {
+ throw new Error(`Refusing to overwrite existing file: ${declaration.path}`)
+ }
+
+ const content = await plugin.convertContent(declaration, ctx)
+ if (isNodeBufferLike(content)) fs.writeFileSync(declaration.path, content)
+ else fs.writeFileSync(declaration.path, content, 'utf8')
+ fileResults.push({path: declaration.path, success: true})
+ break
}
-
- if (declaration.ifExists === 'error' && fs.existsSync(declaration.path)) {
- throw new Error(`Refusing to overwrite existing file: ${declaration.path}`)
+ catch (error) {
+ if (!recoveredBlockingFile && tryRecoverBlockingFileForWrite(declaration.path, error, ctx.logger)) {
+ recoveredBlockingFile = true
+ continue
+ }
+
+ fileResults.push({
+ path: declaration.path,
+ success: false,
+ error: normalizeWriteOperationError(declaration.path, error)
+ })
+ break
}
-
- const content = await plugin.convertContent(declaration, ctx)
- if (isNodeBufferLike(content)) fs.writeFileSync(declaration.path, content)
- else fs.writeFileSync(declaration.path, content, 'utf8')
- fileResults.push({path: declaration.path, success: true})
- }
- catch (error) {
- fileResults.push({path: declaration.path, success: false, error: error as Error})
}
}
diff --git a/sdk/src/plugins/plugin-core/plugin.write.test.ts b/sdk/src/plugins/plugin-core/plugin.write.test.ts
new file mode 100644
index 00000000..7f5bf32d
--- /dev/null
+++ b/sdk/src/plugins/plugin-core/plugin.write.test.ts
@@ -0,0 +1,100 @@
+import type {OutputPlugin, OutputWriteContext} from './plugin'
+import * as fs from 'node:fs'
+import * as os from 'node:os'
+import * as path from 'node:path'
+import {afterEach, describe, expect, it} from 'vitest'
+import {PluginKind} from './enums'
+import {executeDeclarativeWriteOutputs} from './plugin'
+
+function createOutputPlugin(outputPath: string): OutputPlugin {
+ return {
+ name: 'TestOutputPlugin',
+ type: PluginKind.Output,
+ log: {
+ trace: () => {},
+ debug: () => {},
+ info: () => {},
+ warn: () => {},
+ error: () => {},
+ fatal: () => {}
+ },
+ declarativeOutput: true,
+ outputCapabilities: {},
+ async declareOutputFiles() {
+ return [{
+ path: outputPath,
+ scope: 'project',
+ source: {kind: 'projectRootMemory', content: 'hello'}
+ }]
+ },
+ async convertContent() {
+ return 'hello'
+ }
+ }
+}
+
+function createWriteContext(workspaceDir: string): OutputWriteContext {
+ return {
+ logger: {
+ trace: () => {},
+ debug: () => {},
+ info: () => {},
+ warn: () => {},
+ error: () => {},
+ fatal: () => {}
+ },
+ collectedOutputContext: {
+ workspace: {
+ directory: {
+ pathKind: 'absolute',
+ path: workspaceDir,
+ getDirectoryName: () => path.basename(workspaceDir)
+ },
+ projects: []
+ }
+ },
+ pluginOptions: {workspaceDir} as OutputWriteContext['pluginOptions'],
+ runtimeTargets: {
+ jetbrainsCodexDirs: []
+ },
+ executionPlan: {
+ scope: 'workspace',
+ cwd: workspaceDir,
+ workspaceDir,
+ projectsBySeries: {
+ app: [],
+ ext: [],
+ arch: [],
+ softwares: []
+ }
+ }
+ }
+}
+
+describe('executeDeclarativeWriteOutputs', () => {
+ const tempDirs: string[] = []
+
+ afterEach(() => {
+ for (const tempDir of tempDirs) {
+ fs.rmSync(tempDir, {recursive: true, force: true})
+ }
+ tempDirs.length = 0
+ })
+
+ it('removes blocking files and continues writing', async () => {
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-plugin-write-'))
+ tempDirs.push(tempDir)
+
+ const blockingFilePath = path.join(tempDir, '.codex')
+ fs.writeFileSync(blockingFilePath, '', 'utf8')
+
+ const outputPath = path.join(tempDir, '.codex', 'skills', 'demo', 'SKILL.md')
+ const plugin = createOutputPlugin(outputPath)
+ const results = await executeDeclarativeWriteOutputs([plugin], createWriteContext(tempDir))
+ const fileResult = results.get(plugin.name)?.files[0]
+
+ expect(fileResult?.success).toBe(true)
+ expect(fs.statSync(blockingFilePath).isDirectory()).toBe(true)
+ expect(fs.readFileSync(outputPath, 'utf8')).toBe('hello')
+ })
+})
diff --git a/sdk/src/runtime/cleanup.execution-scope.test.ts b/sdk/src/runtime/cleanup.execution-scope.test.ts
index 72199b1b..11d5d3c5 100644
--- a/sdk/src/runtime/cleanup.execution-scope.test.ts
+++ b/sdk/src/runtime/cleanup.execution-scope.test.ts
@@ -1,4 +1,6 @@
import type {OutputCleanContext, OutputPlugin, Project} from '../plugins/plugin-core'
+import * as fs from 'node:fs'
+import * as os from 'node:os'
import * as path from 'node:path'
import {afterEach, describe, expect, it} from 'vitest'
import {
@@ -7,7 +9,7 @@ import {
FilePathKind,
PluginKind
} from '../plugins/plugin-core'
-import {collectDeletionTargets} from './cleanup'
+import {collectDeletionTargets, performCleanup} from './cleanup'
function createProject(workspaceDir: string | undefined, name: string, series: Project['promptSeries']): Project {
return {
@@ -339,4 +341,76 @@ describe('cleanup execution scope filtering', () => {
const pluginSnapshot = (capturedSnapshot?.['pluginSnapshots'] as Record[] | undefined)?.[0]
expect(pluginSnapshot?.['outputs']).toEqual([outputPath])
})
+
+ it('removes blocking files reported by cleanup errors and continues', async () => {
+ const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tnmsc-cleanup-blocking-file-'))
+ const blockingFilePath = path.join(workspaceDir, '.codex')
+
+ fs.writeFileSync(blockingFilePath, '', 'utf8')
+
+ const testGlobals = globalThis as typeof globalThis & {__TNMSC_TEST_NATIVE_BINDING__?: object}
+ testGlobals.__TNMSC_TEST_NATIVE_BINDING__ = {
+ performCleanup() {
+ return JSON.stringify({
+ deletedFiles: 0,
+ deletedDirs: 0,
+ errors: [{
+ path: path.join(workspaceDir, '.codex', 'skills'),
+ kind: 'directory',
+ error: 'Not a directory (os error 20)'
+ }],
+ violations: [],
+ conflicts: [],
+ filesToDelete: [],
+ dirsToDelete: [],
+ emptyDirsToDelete: [],
+ excludedScanGlobs: []
+ })
+ },
+ planCleanup() {
+ return JSON.stringify({
+ filesToDelete: [],
+ dirsToDelete: [],
+ emptyDirsToDelete: [],
+ violations: [],
+ conflicts: [],
+ excludedScanGlobs: []
+ })
+ }
+ }
+
+ try {
+ const cleanCtx: OutputCleanContext = {
+ logger: createLogger('cleanup.execution-scope.test', 'error'),
+ collectedOutputContext: {
+ workspace: {
+ directory: {
+ pathKind: FilePathKind.Absolute,
+ path: workspaceDir,
+ getDirectoryName: () => path.basename(workspaceDir)
+ },
+ projects: []
+ }
+ },
+ pluginOptions: createPluginOptions(workspaceDir),
+ runtimeTargets: {jetbrainsCodexDirs: []},
+ executionPlan: {
+ scope: 'workspace',
+ cwd: workspaceDir,
+ workspaceDir,
+ projectsBySeries: createEmptyExecutionPlanProjectsBySeries()
+ },
+ dryRun: false
+ }
+
+ const result = await performCleanup([], cleanCtx, cleanCtx.logger)
+
+ expect(result.errors).toHaveLength(0)
+ expect(result.deletedFiles).toBe(1)
+ expect(fs.existsSync(blockingFilePath)).toBe(false)
+ }
+ finally {
+ fs.rmSync(workspaceDir, {recursive: true, force: true})
+ }
+ })
})
diff --git a/sdk/src/runtime/cleanup.ts b/sdk/src/runtime/cleanup.ts
index dc422c5f..4127eae1 100644
--- a/sdk/src/runtime/cleanup.ts
+++ b/sdk/src/runtime/cleanup.ts
@@ -15,9 +15,11 @@ import {loadAindexProjectConfig} from '@/aindex-config'
import {
buildDiagnostic,
buildFileOperationDiagnostic,
+ buildUsageDiagnostic,
diagnosticLines
} from '@/diagnostics'
import {filterPathScopedEntriesForExecutionPlan} from '@/execution-plan'
+import {removeBlockingFile, resolveBlockingFilePath} from '@/path-blocking-file'
import {getNativeBinding} from '../core/native-binding'
import {collectAllPluginOutputs, isOutputPluginEnabled} from '../plugins/plugin-core'
import {
@@ -430,9 +432,48 @@ function summarizeCleanupSnapshot(snapshot: NativeCleanupSnapshot): {
function logNativeCleanupErrors(
logger: ILogger,
errors: readonly NativeCleanupError[]
-): readonly {path: string, type: 'file' | 'directory', error: string}[] {
- return errors.map(currentError => {
+): {
+ readonly errors: readonly {path: string, type: 'file' | 'directory', error: string}[]
+ readonly recoveredBlockingFiles: readonly string[]
+} {
+ const unresolvedErrors: {path: string, type: 'file' | 'directory', error: string}[] = []
+ const recoveredBlockingFiles: string[] = []
+
+ for (const currentError of errors) {
const type = currentError.kind === 'directory' ? 'directory' : 'file'
+
+ if (recoveredBlockingFiles.some(blockingPath =>
+ currentError.path === blockingPath
+ || currentError.path.startsWith(`${blockingPath}${path.sep}`))) {
+ continue
+ }
+
+ const blockingPath = resolveBlockingFilePath({
+ path: currentError.path,
+ targetKind: type,
+ error: currentError.error
+ })
+
+ if (blockingPath != null) {
+ const removal = removeBlockingFile(blockingPath)
+ if (removal.removed) {
+ recoveredBlockingFiles.push(blockingPath)
+ logger.warn(buildUsageDiagnostic({
+ code: 'BLOCKING_FILE_REMOVED_FOR_CLEANUP',
+ title: 'Removed blocking file and continued cleanup',
+ rootCause: diagnosticLines(
+ `tnmsc deleted the blocking file at "${blockingPath}" while recovering cleanup for "${currentError.path}".`
+ ),
+ details: {
+ phase: 'cleanup',
+ targetPath: currentError.path,
+ blockingPath
+ }
+ }))
+ continue
+ }
+ }
+
logger.warn(
buildFileOperationDiagnostic({
code:
@@ -453,8 +494,13 @@ function logNativeCleanupErrors(
})
)
- return {path: currentError.path, type, error: currentError.error}
- })
+ unresolvedErrors.push({path: currentError.path, type, error: currentError.error})
+ }
+
+ return {
+ errors: unresolvedErrors,
+ recoveredBlockingFiles
+ }
}
async function buildCleanupSnapshot(
@@ -712,21 +758,25 @@ export async function performCleanup(
dirsToDelete: result.dirsToDelete.length + result.emptyDirsToDelete.length,
emptyDirsToDelete: result.emptyDirsToDelete.length
})
- const loggedErrors = logNativeCleanupErrors(logger, result.errors)
+ const {
+ errors: loggedErrors,
+ recoveredBlockingFiles
+ } = logNativeCleanupErrors(logger, result.errors)
+ const deletedFiles = result.deletedFiles + recoveredBlockingFiles.length
logger.debug('cleanup delete execution complete', {
- deletedFiles: result.deletedFiles,
+ deletedFiles,
deletedDirs: result.deletedDirs,
errors: loggedErrors.length
})
logger.info('cleanup execution complete', {
phase: 'cleanup-execute',
- deletedFiles: result.deletedFiles,
+ deletedFiles,
deletedDirs: result.deletedDirs,
errors: loggedErrors.length
})
return {
- deletedFiles: result.deletedFiles,
+ deletedFiles,
deletedDirs: result.deletedDirs,
errors: loggedErrors,
violations: [],