@aigne/doc-smith 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.github/workflows/ci.yml +46 -0
  2. package/.github/workflows/reviewer.yml +2 -1
  3. package/CHANGELOG.md +17 -0
  4. package/agents/chat.yaml +30 -0
  5. package/agents/check-detail-result.mjs +2 -1
  6. package/agents/check-detail.mjs +1 -0
  7. package/agents/check-structure-plan.mjs +1 -1
  8. package/agents/docs-fs.yaml +25 -0
  9. package/agents/exit.mjs +6 -0
  10. package/agents/feedback-refiner.yaml +5 -1
  11. package/agents/find-items-by-paths.mjs +10 -4
  12. package/agents/fs.mjs +60 -0
  13. package/agents/input-generator.mjs +159 -90
  14. package/agents/load-config.mjs +0 -5
  15. package/agents/load-sources.mjs +119 -12
  16. package/agents/publish-docs.mjs +28 -11
  17. package/agents/retranslate.yaml +1 -1
  18. package/agents/team-publish-docs.yaml +2 -2
  19. package/aigne.yaml +1 -0
  20. package/package.json +13 -10
  21. package/prompts/content-detail-generator.md +12 -4
  22. package/prompts/document/custom-components.md +80 -0
  23. package/prompts/document/d2-chart/diy-examples.md +44 -0
  24. package/prompts/document/d2-chart/official-examples.md +708 -0
  25. package/prompts/document/d2-chart/rules.md +48 -0
  26. package/prompts/document/detail-generator.md +13 -15
  27. package/prompts/document/structure-planning.md +1 -3
  28. package/prompts/feedback-refiner.md +81 -60
  29. package/prompts/structure-planning.md +20 -3
  30. package/tests/check-detail-result.test.mjs +50 -2
  31. package/tests/conflict-resolution.test.mjs +237 -0
  32. package/tests/input-generator.test.mjs +940 -0
  33. package/tests/load-sources.test.mjs +627 -3
  34. package/tests/preferences-utils.test.mjs +94 -0
  35. package/tests/save-value-to-config.test.mjs +182 -5
  36. package/tests/utils.test.mjs +49 -0
  37. package/utils/auth-utils.mjs +1 -1
  38. package/utils/conflict-detector.mjs +72 -1
  39. package/utils/constants.mjs +139 -126
  40. package/utils/kroki-utils.mjs +162 -0
  41. package/utils/markdown-checker.mjs +175 -67
  42. package/utils/utils.mjs +97 -29
@@ -0,0 +1,48 @@
1
+ - 使用 d2 图表解释复杂的概念 (```d2``` format),让页面内容展示形式更丰富
2
+ - 使用 d2 展示架构关系、流程与组件交互,节点与连线文案保持简洁
3
+ - d2 代码块必须完整且可渲染,避免使用未闭合的语法与奇异字符
4
+ - d2 图表使用补充说明:
5
+ - 示例:
6
+ {% include "diy-examples.md" %}
7
+ - 官方示例:
8
+ {% include "official-examples.md" %}
9
+ - 其他注意事项:
10
+ - 图表应简洁明了,节点和连线命名准确。
11
+ - 每个 d2 代码块必须完整闭合,避免语法错误。
12
+ - 不要添加注释说明,因为生成的图片无法进行交互。
13
+ - 不要随意更改节点和连线的颜色,这样会破坏配置好的主题。
14
+ - 节点的名称尽量使用 " 进行包裹,避免出现异常。
15
+ - bad: `SDK: @blocklet/js-sdk`
16
+ - good: `SDK: "@blocklet/js-sdk"`
17
+ - d2 中的 `shape` 只有这些值: `rectangle`, `square`, `page`, `parallelogram`, `document`, `cylinder`, `queue`, `package`, `step`, `callout`, `stored_data`, `person`, `diamond`, `oval`, `circle`, `hexagon`, `cloud`, `c4-person`,不要随意创建其他的 shape,会导致图表报错
18
+ - 页面的整体布局使用 `direction: down`,这样能确保图表适合在网页中进行阅读;子图中可以根据情况来使用其他的方向布局,需要确保图表的整体效果看起来不会太宽
19
+ - 如果一个对象中的元素太多了(超过3个),请使用 `grid-columns` 限制一下单行的列数,`grid-columns` 的值不要超过 3,例如
20
+ ```d2
21
+ "Instance": {
22
+ grid-columns: 3
23
+ "A": "A"
24
+ "B": "B"
25
+ "C": "C"
26
+ "D": "D"
27
+ "E": "E"
28
+ }
29
+ ```
30
+ - 尽量保证一个图中,只有一个关联所有元素的图,不要产生多个没有任何连接的子图
31
+ - 确保 `style` 中的值都是可用的,错误的字段会导致图片生成失败
32
+ - 尽量确保每个子元素是有名称的
33
+ - bad:
34
+ ```d2
35
+ "SDK Core Instance": {
36
+ shape: package
37
+ "TokenService": "Manages session and refresh tokens"
38
+ "Services": {
39
+ grid-columns: 2
40
+ "AuthService": ""
41
+ "BlockletService": ""
42
+ "FederatedService": ""
43
+ "UserSessionService": ""
44
+ }
45
+ }
46
+ ```
47
+ - 使用 animate: true 可以让图表增加动画效果,看起来效果更好
48
+ - 针对节点的状态(yes or no),可以给定不同的颜色(error,warning,success)来增强表现力
@@ -1,5 +1,6 @@
1
1
 
2
2
  <document_rules>
3
+
3
4
  文档类信息生成规则:
4
5
  - 如果当前部分是存在子文档,当前文档只展示简要的内容,引导用户到子文档中查看详细的内容
5
6
  - 每个部分文档需要包含:标题、开头介绍、多个 section 介绍、结尾总结
@@ -13,20 +14,17 @@
13
14
  - 确保文档中的内容是完整、连贯的,用户可以跟着文档一步步顺利执行
14
15
  - 说明要尽可能的详细,如果存在配置项或参数,需要解释每个配置项或参数的含义,如果参数有多个可选值,每种可选值需要解释其含义,并尽可能配上代码示例
15
16
  - 参数优先使用 markdown 中的 table 来展示,让内容看上去更整齐,容易阅读
16
- - 接口/方法调用的说明必须包含 **响应数据示例**
17
- - 使用 mermaid 图表解释复杂的概念 (```mermaid``` format),让页面内容展示形式更丰富
18
- - 使用 `flowchart` 图表解释概念之间的关系
19
- - 使用 `sequenceDiagram` 图表解释调用、执行的流程,
20
- - **确保 mermaid `flowchart` 和 `graph` 图表中所有节点的 label 都使用 " 包裹**,使用简单文案描述,不包含任何特殊符号,示例:A["@abc"]、B("AIGNE")、C{"@aigne/core"}
21
- - **确保 mermaid `flowchart` 和 `graph` 图表中所有节点连线上的描述都使用 " 包裹**,使用简单文案描述,不包含任何特殊符号,示例: E -- "Yes, Cache Hit" --> F["Serve Cached HTML"];
22
- - **确保 mermaid`sequenceDiagram` 图表所有节点名不使用 " 包裹,示例:`participant AIGNE as AIGNE Engine`
23
- - 更多的使用 table 、mermaid 图表来解释信息,过长的文本描述会让用户阅读有压力
24
- - 概览部分,必须包含 mermaid 图表,展示产品的架构图
25
- - 对输出的 markdown 进行检查,确认输出内容完整,table 、mermaid 信息完整并且格式正确
26
- - **确保内容完整性**:在生成任何文档内容,特别是代码块(如 Mermaid、JSON、代码等)时,必须确保其是**完整且语法正确**的。在输出完成后,必须进行一次**自我检查**,确认所有的代码块、列表、表格等都已完全闭合且没有中途截断。
27
- - **代码块原子性**:将每个代码块(例如 ```mermaid ... ```)视为一个**不可分割的原子单元**。必须一次性完整生成,从开始标记(```mermaid)到结束标记(```)之间的所有内容都不能省略或截断。
17
+ - 接口/方法调用的说明必须包含 **响应数据示例**
18
+ - 更多的使用 table、d2 图表来解释信息,过长的文本描述会让用户阅读有压力
19
+ - 概览部分,建议包含 d2 图表展示产品架构图
20
+ {% include "d2-chart/rules.md" %}
21
+ - 对输出的 markdown 进行检查,确认输出内容完整,table、d2 信息完整并且格式正确
22
+ - **确保内容完整性**:在生成任何文档内容,特别是代码块(如 d2、JSON、代码等)时,必须确保其是**完整且语法正确**的。在输出完成后,必须进行一次**自我检查**,确认所有的代码块、列表、表格等都已完全闭合且没有中途截断。
23
+ - **代码块原子性**:将每个代码块(例如 ```d2 ... ```)视为一个**不可分割的原子单元**。必须一次性完整生成,从开始标记(```d2)到结束标记(```)之间的所有内容都不能省略或截断。
28
24
  - **确保 Markdown 语法**:Markdown 格式正确,特别是表格的分隔线(例如 `|---|---|---|`),需要与表格数据列数一致。
29
25
  - README 文件只做参考,你需要从代码中获取最新、最完整的信息
26
+ - 忽略详情顶部的标签信息,这是程序处理的,不需要在生成时输出
27
+
30
28
  </document_rules>
31
29
 
32
30
  <TONE_STYLE>
@@ -44,10 +42,10 @@
44
42
  - Be direct: say what happened, why it matters, and how it helps
45
43
 
46
44
  Example Tone Transformations
47
- ❌ "We’re thrilled to announce our most powerful update yet…"
45
+ ❌ "We’re thrilled to announce our most powerful update yet…"
48
46
  ✅ "You can now include location and timestamp metadata for each claim, enabling audit-ready transparency."
49
47
 
50
- ❌ "Unlock the future of verification."
48
+ ❌ "Unlock the future of verification."
51
49
  ✅ "This release makes real-world claims independently verifiable across sectors."
52
50
  </TONE_STYLE>
53
51
 
@@ -90,6 +88,6 @@ Do not use promotional fluff or filler emotion. Avoid the following unless quoti
90
88
  cutting edge
91
89
  </generic-hype-verbs>
92
90
 
93
- ➡️ Instead, focus on concrete outcomes and observable benefits.
91
+ ➡️ Instead, focus on concrete outcomes and observable benefits.
94
92
  Example: “Now includes location and timestamp for each field report” is better than “a powerful new update.”
95
93
  </WORDS_PHRASES_TO_AVOID>
@@ -10,9 +10,7 @@
10
10
  - 如果存在测试相关的代码,可以作为生成文档的参考,**不要为测试代码生成文档**
11
11
  - 总是在一开始包含下列内容:
12
12
  - Overview:简要说明产品能解决什么,产品能提供什么,产品的结构信息,让用户能快速有一个全面的认识,给出下一步的入口,引导用户阅读
13
- - Getting Started:内容包含安装、最小运行示例,让用户用通过一部分文档,在很短的时间就能跑通一个最简单的示例
14
13
  - 标题中不需要包含产品名称,显示更加精简
15
- - 'Overview' 和 'Getting Started' 章节应该引用所有源代码,方便写出准确全面的介绍
16
- - **'Getting Started' 下不需要拆分子章节**
14
+ - 'Overview' 章节应该引用所有源代码,方便写出准确全面的介绍
17
15
  - 每个部分文档都应该尽可能多的引用相关的源代码,来确保生成文档更详细准确,对于你不确定的项,优先添加引用
18
16
  </document_rules>
@@ -1,84 +1,105 @@
1
1
  <role>
2
- 你是"反馈→规则"转换器。将一次性的自然语言反馈提炼为**一条单句**、**可执行**、**可复用**的指令,
3
- 并判断是否需要**持久化保存**,以及作用域(global/structure/document/translation)与是否仅限于"输入 paths 范围"
2
+ You are a "Feedback→Rule" converter. Transform one-time natural language feedback into a **single sentence**, **executable**, **reusable** instruction,
3
+ and determine whether it needs **persistent saving**, along with its scope (global/structure/document/translation) and whether it should be limited to "input paths range".
4
4
  </role>
5
5
 
6
6
  <input>
7
7
  - feedback: {{feedback}}
8
- - stage: {{stage}} # 可取:structure_planning | document_refine | translation_refine
9
- - paths: {{paths}} # 本次命令输入的路径数组(可为空)。仅用于判断是否"限定到这些路径"。不要把它们写进输出。
10
- - existingPreferences: {{existingPreferences}} # 当前已保存的用户偏好规则
8
+ - stage: {{stage}} # Possible values: structure_planning | document_refine | translation_refine
9
+ - paths: {{paths}} # Array of paths input in current command (can be empty). Used only to determine whether to "limit to these paths". Do not include them in output.
10
+ - existingPreferences: {{existingPreferences}} # Currently saved user preference rules
11
11
  </input>
12
12
 
13
13
  <scope_rules>
14
- 作用域判定启发式规则:
14
+ Scope determination heuristic rules:
15
15
 
16
- **按 stage 分类**:
17
- - stage=structure_planning:默认 `scope="structure"`,除非反馈显然是全局写作/语气/排除类政策(则用 `global`)。
18
- - stage=document_refine:默认 `scope="document"`;若反馈是通用写作政策、排除策略且不依赖具体页面,则可提升为 `global`。
19
- - stage=translation_refine:默认 `scope="translation"`;若反馈是翻译阶段的一般政策可保持此 scope
16
+ **Classification by stage**:
17
+ - If stage=structure_planning: Default `scope="structure"`, unless feedback is clearly global writing/tone/exclusion policy (then use `global`).
18
+ - If stage=document_refine: Default `scope="document"`; if feedback is general writing policy or exclusion strategy that doesn't depend on specific pages, can be elevated to `global`.
19
+ - If stage=translation_refine: Default `scope="translation"`; if feedback is general translation policy, maintain this scope.
20
20
 
21
- **路径限制判定**:
22
- - 若用户反馈显著只影响本批 `paths` 指向的范围(例如"examples 目录中的页面精简说明"),将 `limitToInputPaths=true`;否则为 `false`。
23
- - **永远不要**在输出中返回具体的 paths 列表。
21
+ **Path Limitation (`limitToInputPaths`) Determination**:
22
+ - **Set to `true` IF** the feedback explicitly names a specific document, path, or section (e.g., "in the overview", "for the example files") AND the requested change is about the *content or style within* that specific context.
23
+ - **Set to `false` IF** the feedback describes a general policy (e.g., a writing style, a structural rule like 'add Next Steps', a universal exclusion) even if it was triggered by a specific file.
24
+ - **Tie-breaker**: When in doubt, default to `false` to create a more broadly applicable rule.
25
+
26
+ - **Never** return specific paths lists in output.
24
27
  </scope_rules>
25
28
 
26
29
  <save_rules>
27
- 是否保存判定规则:
30
+ Save determination rules:
31
+
32
+ **Primary Goal: Your most critical task is to distinguish between a reusable policy and a one-time fix. Be conservative: when in doubt, default to `save=false`.**
28
33
 
29
- **一次性操作(不保存)**:
30
- - 只修正当下版本/错别字/个别句式/局部事实错误,且无稳定可复用价值 → `save=false`
34
+ **One-time operations (do not save)**:
35
+ - Only corrects current version/typos/individual phrasing/local factual errors with no stable reusable value → `save=false`
36
+ - Fixes that are highly specific to a single line or data point and unlikely to recur (e.g., "change the year from 2020 to 2021") → `save=false`
31
37
 
32
- **可复用政策(保存)**:
33
- - 写作风格、结构约定、包含/排除项、翻译约定等可广泛适用且未来应持续执行 → `save=true`
38
+ **Reusable policies (save)**:
39
+ - Writing styles, structural conventions, inclusion/exclusion items, translation conventions that are broadly applicable and should be consistently executed in the future → `save=true`
34
40
 
35
- **重复性检查(不保存)**:
36
- - `existingPreferences` 中已有**相似或覆盖**本次反馈意图的规则,则 `save=false`
37
- - 检查逻辑:对比反馈意图、规则含义、适用范围。若新反馈已被现有规则充分覆盖,无需重复保存
38
- - 若新反馈是对现有规则的**细化、补充或矛盾修正**,则仍可 `save=true`
41
+ **Duplication check (do not save)**:
42
+ - If `existingPreferences` already contains **similar or covering** rules for current feedback intent, then `save=false`
43
+ - Check logic: Compare feedback intent, rule meaning, and applicable scope. If new feedback is already sufficiently covered by existing rules, no need to save duplicates
44
+ - If new feedback is **refinement, supplement, or conflicting correction** to existing rules, still can be `save=true`
39
45
 
40
- **判定原则**:
41
- - 优先避免重复保存;若难以判定是否重复,优先 `save=false`,以避免规则冗余
46
+ **Determination principle**:
47
+ - Prioritize avoiding duplicate saves; if difficult to determine whether duplicate, prioritize `save=false` to avoid rule redundancy
42
48
  </save_rules>
43
49
 
44
50
  <rule_format>
45
- 规则写法要求:
51
+ Rule writing requirements:
46
52
 
47
- - 面向模型的**单句**指令;允许使用"必须/不得/总是"等明确措辞。
48
- - 不引入具体路径、不绑定具体文件名。
49
- - 例:"写作面向初学者;术语首次出现必须给出简明解释。"
53
+ - Model-oriented **single sentence** instruction; allow using clear wording like "must/must not/always".
54
+ - Do not introduce specific paths or bind to specific file names.
55
+ - **Crucially, preserve specific, domain-related keywords** (e.g., variable names, API endpoints, proprietary terms like 'spaceDid') if they are central to the feedback's intent. Generalize the *action*, not the *subject*.
56
+ - **If the feedback is about deleting or removing content, the resulting rule must be a preventative, forward-looking instruction.** Rephrase it as "Do not generate..." or "Avoid including content about...".
57
+ - Example: "Write for beginners; terms must be given clear explanations on first appearance."
50
58
  </rule_format>
51
59
 
60
+ <output_rule>
61
+ Return a complete JSON object with a `reason` field explaining *why* you are setting `save` to true or false, and how you derived the rule and scope.
62
+ Return the summarized rule in the same language as the feedback in user input.
63
+ </output_rule>
64
+
52
65
  <examples>
53
- 示例1
54
- - 输入:stage=document_refinepaths=["overview.md"]feedback="示例页废话太多,代码要最小可运行。"
55
- - 输出:
56
- {"rule":"示例页面以最小可运行代码为主,移除与主题无关的说明段。","scope":"document","save":true,"limitToInputPaths":true}
57
-
58
- 示例2
59
- - 输入:stage=structure_planningpaths=[]feedback="概览与教程结尾加"下一步"并给出23个链接。"
60
- - 输出:
61
- {"rule":"在概览与教程文档结尾添加"下一步"小节并提供 23 个本仓库内链接。","scope":"structure","save":true,"limitToInputPaths":false}
62
-
63
- 示例3
64
- - 输入:stage=translation_refinepaths=[]feedback="变量名和代码不要翻译。"
65
- - 输出:
66
- {"rule":"翻译时保持代码与标识符原样,不得翻译。","scope":"translation","save":true,"limitToInputPaths":false}
67
-
68
- 示例4
69
- - 输入:stage=document_refinepaths=["overview.md"]feedback="这段话事实有误,改成 2021 年发布。"
70
- - 输出:
71
- {"rule":"更正事实到正确年份。","scope":"document","save":false,"limitToInputPaths":true}
72
-
73
- 示例5(去重案例):
74
- - 输入:stage=document_refinepaths=[]feedback="代码示例太复杂了,简化一下。"existingPreferences="rules:\n - rule: 示例页面以最小可运行代码为主,移除与主题无关的说明段。\n scope: document\n active: true"
75
- - 输出:
76
- {"rule":"简化代码示例的复杂度。","scope":"document","save":false,"limitToInputPaths":false}
77
- # 理由:现有规则已覆盖简化代码示例的意图
78
-
79
- 示例6(非重复案例):
80
- - 输入:stage=document_refine,paths=[],feedback="代码注释要用英文写。",existingPreferences="rules:\n - rule: 示例页面以最小可运行代码为主,移除与主题无关的说明段。\n scope: document\n active: true"
81
- - 输出:
82
- {"rule":"代码注释必须使用英文编写。","scope":"document","save":true,"limitToInputPaths":false}
83
- # 理由:现有规则未涉及注释语言,属于新的规则维度
84
- </examples>
66
+ Example 1 (Keyword Preservation):
67
+ - Input: stage=document_refine, paths=["examples/demo.md"], feedback="Do not use ellipsis in the spaceDid part of endpoint strings used in demo"
68
+ - Output:
69
+ {"rule":"Endpoint strings with 'spaceDid' in code examples should not use ellipsis for abbreviation.","scope":"document","save":true,"limitToInputPaths":true,"reason":"The feedback is about a specific keyword 'spaceDid' in endpoint strings being abbreviated. This is a recurring style issue that should be a policy. It's a reusable rule, so `save` is `true`. The rule preserves the keyword 'spaceDid' as it's the subject of the instruction."}
70
+
71
+ Example 2:
72
+ - Input: stage=structure_planning, paths=[], feedback="Add 'Next Steps' at the end of overview and tutorials with 2-3 links."
73
+ - Output:
74
+ {"rule":"Add 'Next Steps' section at the end of overview and tutorial documents with 2-3 links within the repository.","scope":"structure","save":true,"limitToInputPaths":false,"reason":"This feedback suggests a new structural convention (adding a 'Next Steps' section). This is a classic reusable policy that should be applied to future documents of a certain type. Therefore, `save` is `true` and the scope is `structure`."}
75
+
76
+ Example 3:
77
+ - Input: stage=translation_refine, paths=[], feedback="Don't translate variable names and code."
78
+ - Output:
79
+ {"rule":"Keep code and identifiers unchanged during translation, must not translate them.","scope":"translation","save":true,"limitToInputPaths":false,"reason":"This is a fundamental, reusable policy for all future translations in this project. It's not a one-time fix. So, `save` is `true` and the scope is correctly `translation`."}
80
+
81
+ Example 4 (One-time Fix):
82
+ - Input: stage=document_refine, paths=["overview.md"], feedback="This paragraph has factual errors, change it to released in 2021."
83
+ - Output:
84
+ {"rule":"Correct facts to the accurate year.","scope":"document","save":false,"limitToInputPaths":true,"reason":"The feedback is a one-time factual correction for the current content. It corrects a specific data point and is not a reusable writing policy for the future. Therefore, `save` should be `false`."}
85
+
86
+ Example 5 (Deduplication):
87
+ - Input: stage=document_refine, paths=[], feedback="Code examples are too complex, simplify them.", existingPreferences="rules:\n - rule: Example pages should focus on minimally runnable code, removing explanatory sections unrelated to the topic.\n scope: document\n active: true"
88
+ - Output:
89
+ {"rule":"Simplify the complexity of code examples.","scope":"document","save":false,"limitToInputPaths":false,"reason":"The user wants to simplify code examples. The existing preference rule 'Example pages should focus on minimally runnable code' already covers this intent. Saving a new, similar rule would be redundant. Therefore, `save` should be `false`."}
90
+
91
+ Example 6 (Non-duplication):
92
+ - Input: stage=document_refine, paths=[], feedback="Code comments should be written in English.", existingPreferences="rules:\n - rule: Example pages should focus on minimally runnable code, removing explanatory sections unrelated to the topic.\n scope: document\n active: true"
93
+ - Output:
94
+ {"rule":"Code comments must be written in English.","scope":"document","save":true,"limitToInputPaths":false,"reason":"The feedback is about the language of code comments. The existing rule is about code minimalism and does not cover comment language. This is a new, non-overlapping rule. Thus, it should be saved. `save` is `true`."}
95
+
96
+ Example 7 (Deletion Handling):
97
+ - Input: stage=structure_planning, paths=[], feedback="The 'Legacy API Reference' document is outdated and should be removed."
98
+ - Output:
99
+ {"rule":"Do not generate documents or sections for outdated 'Legacy API Reference'.","scope":"structure","save":true,"limitToInputPaths":false,"reason":"The feedback is about removing outdated content. Following deletion handling rules, this becomes a preventative instruction for future document generation. This is a reusable policy to avoid generating outdated content, so `save` is `true`."}
100
+
101
+ Example 8 (Path-limited Deletion Rule):
102
+ - Input: stage=document_refine, paths=["overview.md"], feedback="Remove contribution-related content from overview"
103
+ - Output:
104
+ {"rule":"Do not include contribution-related content in 'overview' document.","scope":"document","save":true,"limitToInputPaths":true,"reason":"This feedback specifies content that should not appear in a specific document type ('overview'). While it's about removing content, we convert it to a preventative rule. It's worth saving as it defines a clear content boundary for overview documents, but should be limited to overview files only. Therefore `save` is `true` with `limitToInputPaths` also `true`."}
105
+ </examples>
@@ -56,6 +56,23 @@
56
56
  {{ rules }}
57
57
  </user_rules>
58
58
 
59
+ <conflict_resolution_guidance>
60
+ When users select potentially conflicting options, conflict resolution guidance will be provided in user_rules. Please carefully read these guidelines and implement the corresponding resolution strategies in the structure planning.
61
+
62
+ Core principles for conflict resolution:
63
+ 1. **Layered need satisfaction**: Simultaneously satisfy multiple purposes and audiences through reasonable document structure hierarchy
64
+ 2. **Clear navigation paths**: Provide clear document usage paths for users with different needs
65
+ 3. **Avoid content duplication**: Ensure content across different sections is complementary rather than repetitive
66
+ 4. **Progressive disclosure**: From high-level overview to specific details, meeting needs at different depth levels
67
+
68
+ Common conflict resolution patterns:
69
+ - **Purpose conflicts**: Create hierarchical structures
70
+ - **Audience conflicts**: Design role-oriented sections or paths
71
+ - **Depth conflicts**: Adopt progressive structures that allow users to choose appropriate depth levels
72
+
73
+ When planning structure, prioritize conflict resolution strategies to ensure the final structure can harmoniously satisfy all user needs.
74
+ </conflict_resolution_guidance>
75
+
59
76
  <user_preferences>
60
77
  {{userPreferences}}
61
78
 
@@ -94,12 +111,12 @@ DataSources 使用规则:
94
111
  2. 内容规划优先展示用户提供的 DataSources 中的信息,或者使用你拥有的知识进行补充,不可以随意虚构信息。
95
112
 
96
113
  {% ifAsync docsType == 'general' %}
97
- {% include "../prompts/document/structure-planning.md" %}
114
+ {% include "document/structure-planning.md" %}
98
115
 
99
116
  {% endif %}
100
117
 
101
118
  {% ifAsync docsType == 'getting-started' %}
102
- {% include "../prompts/document/structure-getting-started.md" %}
119
+ {% include "document/structure-getting-started.md" %}
103
120
  {% endif %}
104
121
 
105
122
  其他:
@@ -109,7 +126,7 @@ DataSources 使用规则:
109
126
  </rules>
110
127
 
111
128
  {% ifAsync docsType == 'general' %}
112
- {% include "../prompts/document/structure-example.md" %}
129
+ {% include "document/structure-example.md" %}
113
130
  {% endif %}
114
131
 
115
132
  <output_rules>
@@ -45,10 +45,58 @@ describe("checkDetailResult", () => {
45
45
  expect(result.detailFeedback).toContain("dead link");
46
46
  });
47
47
 
48
- test("should approve content with image syntax", async () => {
48
+ test("should approve content with external image syntax", async () => {
49
49
  const structurePlan = [];
50
50
  const reviewContent =
51
- "This is an image ![MCP Go Logo](/logo.png).\n\nThis has proper structure.";
51
+ "This is an image ![MCP Go Logo](https://example.com/logo.png).\n\nThis has proper structure.";
52
+ const result = await checkDetailResult({ structurePlan, reviewContent });
53
+ expect(result.isApproved).toBe(true);
54
+ expect(result.detailFeedback).toBe("");
55
+ });
56
+
57
+ test("should approve content with valid local image path", async () => {
58
+ const structurePlan = [];
59
+ const reviewContent =
60
+ "This is a valid image ![Test Image](./README.md).\n\nThis has proper structure.";
61
+ const docsDir = process.cwd();
62
+ const result = await checkDetailResult({ structurePlan, reviewContent, docsDir });
63
+ expect(result.isApproved).toBe(true);
64
+ expect(result.detailFeedback).toBe("");
65
+ });
66
+
67
+ test("should reject content with invalid local image path", async () => {
68
+ const structurePlan = [];
69
+ const reviewContent =
70
+ "This is an invalid image ![Non-existent Image](./nonexistent.png).\n\nThis has proper structure.";
71
+ const docsDir = process.cwd();
72
+ const result = await checkDetailResult({ structurePlan, reviewContent, docsDir });
73
+ expect(result.isApproved).toBe(false);
74
+ expect(result.detailFeedback).toContain("Found invalid local image");
75
+ expect(result.detailFeedback).toContain("only valid media resources can be used");
76
+ });
77
+
78
+ test("should approve content with absolute image path that exists", async () => {
79
+ const structurePlan = [];
80
+ const reviewContent = `This is an absolute image ![Test Image](${process.cwd()}/README.md).\n\nThis has proper structure.`;
81
+ const result = await checkDetailResult({ structurePlan, reviewContent });
82
+ expect(result.isApproved).toBe(true);
83
+ expect(result.detailFeedback).toBe("");
84
+ });
85
+
86
+ test("should reject content with absolute image path that doesn't exist", async () => {
87
+ const structurePlan = [];
88
+ const reviewContent =
89
+ "This is an invalid absolute image ![Non-existent Image](/path/to/nonexistent.png).\n\nThis has proper structure.";
90
+ const result = await checkDetailResult({ structurePlan, reviewContent });
91
+ expect(result.isApproved).toBe(false);
92
+ expect(result.detailFeedback).toContain("Found invalid local image");
93
+ expect(result.detailFeedback).toContain("only valid media resources can be used");
94
+ });
95
+
96
+ test("should approve content with external image URL", async () => {
97
+ const structurePlan = [];
98
+ const reviewContent =
99
+ "This is an external image ![External Image](https://example.com/image.png).\n\nThis has proper structure.";
52
100
  const result = await checkDetailResult({ structurePlan, reviewContent });
53
101
  expect(result.isApproved).toBe(true);
54
102
  expect(result.detailFeedback).toBe("");
@@ -0,0 +1,237 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ detectResolvableConflicts,
4
+ generateConflictResolutionRules,
5
+ } from "../utils/conflict-detector.mjs";
6
+ import { processConfigFields } from "../utils/utils.mjs";
7
+
8
+ describe("conflict resolution", () => {
9
+ describe("detectResolvableConflicts", () => {
10
+ test("should detect document purpose conflicts", () => {
11
+ const config = {
12
+ documentPurpose: ["getStarted", "findAnswers"],
13
+ targetAudienceTypes: ["developers"],
14
+ };
15
+
16
+ const conflicts = detectResolvableConflicts(config);
17
+
18
+ expect(conflicts).toHaveLength(1);
19
+ expect(conflicts[0]).toMatchObject({
20
+ type: "documentPurpose",
21
+ items: ["getStarted", "findAnswers"],
22
+ strategy: "layered_structure",
23
+ description: "Quick start and API reference conflict, resolved through layered structure",
24
+ });
25
+ });
26
+
27
+ test("should detect target audience conflicts", () => {
28
+ const config = {
29
+ documentPurpose: ["completeTasks"],
30
+ targetAudienceTypes: ["endUsers", "developers"],
31
+ };
32
+
33
+ const conflicts = detectResolvableConflicts(config);
34
+
35
+ expect(conflicts).toHaveLength(1);
36
+ expect(conflicts[0]).toMatchObject({
37
+ type: "targetAudienceTypes",
38
+ items: ["endUsers", "developers"],
39
+ strategy: "separate_user_paths",
40
+ description: "End users and developers conflict, resolved through separate user paths",
41
+ });
42
+ });
43
+
44
+ test("should detect multiple conflicts", () => {
45
+ const config = {
46
+ documentPurpose: ["getStarted", "findAnswers", "understandSystem"],
47
+ targetAudienceTypes: ["endUsers", "developers"],
48
+ };
49
+
50
+ const conflicts = detectResolvableConflicts(config);
51
+
52
+ expect(conflicts).toHaveLength(3);
53
+
54
+ // Check for getStarted vs findAnswers conflict
55
+ expect(
56
+ conflicts.some(
57
+ (c) =>
58
+ c.type === "documentPurpose" &&
59
+ c.items.includes("getStarted") &&
60
+ c.items.includes("findAnswers"),
61
+ ),
62
+ ).toBe(true);
63
+
64
+ // Check for getStarted vs understandSystem conflict
65
+ expect(
66
+ conflicts.some(
67
+ (c) =>
68
+ c.type === "documentPurpose" &&
69
+ c.items.includes("getStarted") &&
70
+ c.items.includes("understandSystem"),
71
+ ),
72
+ ).toBe(true);
73
+
74
+ // Check for endUsers vs developers conflict
75
+ expect(
76
+ conflicts.some(
77
+ (c) =>
78
+ c.type === "targetAudienceTypes" &&
79
+ c.items.includes("endUsers") &&
80
+ c.items.includes("developers"),
81
+ ),
82
+ ).toBe(true);
83
+ });
84
+
85
+ test("should not detect conflicts with single selections", () => {
86
+ const config = {
87
+ documentPurpose: ["getStarted"],
88
+ targetAudienceTypes: ["developers"],
89
+ };
90
+
91
+ const conflicts = detectResolvableConflicts(config);
92
+
93
+ expect(conflicts).toHaveLength(0);
94
+ });
95
+
96
+ test("should not detect conflicts with non-conflicting combinations", () => {
97
+ const config = {
98
+ documentPurpose: ["completeTasks", "solveProblems"],
99
+ targetAudienceTypes: ["developers", "devops"],
100
+ };
101
+
102
+ const conflicts = detectResolvableConflicts(config);
103
+
104
+ expect(conflicts).toHaveLength(0);
105
+ });
106
+
107
+ test("should handle object arrays with value property", () => {
108
+ const config = {
109
+ documentPurpose: [
110
+ { value: "getStarted", label: "Get Started" },
111
+ { value: "findAnswers", label: "Find Answers" },
112
+ ],
113
+ };
114
+
115
+ const conflicts = detectResolvableConflicts(config);
116
+
117
+ expect(conflicts).toHaveLength(1);
118
+ expect(conflicts[0].items).toEqual(["getStarted", "findAnswers"]);
119
+ });
120
+ });
121
+
122
+ describe("generateConflictResolutionRules", () => {
123
+ test("should generate resolution rules for conflicts", () => {
124
+ const conflicts = [
125
+ {
126
+ type: "documentPurpose",
127
+ items: ["getStarted", "findAnswers"],
128
+ strategy: "layered_structure",
129
+ description: "Quick start and API reference conflict, resolved through layered structure",
130
+ },
131
+ {
132
+ type: "targetAudienceTypes",
133
+ items: ["endUsers", "developers"],
134
+ strategy: "separate_user_paths",
135
+ description: "End users and developers conflict, resolved through separate user paths",
136
+ },
137
+ ];
138
+
139
+ const rules = generateConflictResolutionRules(conflicts);
140
+
141
+ expect(rules).toContain("=== Conflict Resolution Guidelines ===");
142
+ expect(rules).toContain('Detected "getStarted" and "findAnswers" purpose conflict');
143
+ expect(rules).toContain('Detected "endUsers" and "developers" audience conflict');
144
+ expect(rules).toContain('Quick start section: Uses "get started" style');
145
+ expect(rules).toContain('User guide path: Uses "end users" style');
146
+ expect(rules).toContain("Conflict Resolution Principles:");
147
+ expect(rules).toContain(
148
+ "1. Meet diverse needs through intelligent structural design, not simple concatenation",
149
+ );
150
+ });
151
+
152
+ test("should return empty string for no conflicts", () => {
153
+ const conflicts = [];
154
+ const rules = generateConflictResolutionRules(conflicts);
155
+ expect(rules).toBe("");
156
+ });
157
+
158
+ test("should handle unknown strategies gracefully", () => {
159
+ const conflicts = [
160
+ {
161
+ type: "documentPurpose",
162
+ items: ["unknown1", "unknown2"],
163
+ strategy: "unknown_strategy",
164
+ description: "Unknown conflict",
165
+ },
166
+ ];
167
+
168
+ const rules = generateConflictResolutionRules(conflicts);
169
+
170
+ expect(rules).toContain("=== Conflict Resolution Guidelines ===");
171
+ expect(rules).toContain("Conflict Resolution Principles:");
172
+ // Should not contain any strategy-specific text since strategy is unknown
173
+ expect(rules).not.toContain('Detected "unknown1" and "unknown2"');
174
+ });
175
+ });
176
+
177
+ describe("processConfigFields integration", () => {
178
+ test("should integrate conflict resolution into config processing", () => {
179
+ const config = {
180
+ documentPurpose: ["getStarted", "findAnswers"],
181
+ targetAudienceTypes: ["endUsers", "developers"],
182
+ readerKnowledgeLevel: "completeBeginners",
183
+ documentationDepth: "balancedCoverage",
184
+ };
185
+
186
+ const result = processConfigFields(config);
187
+
188
+ // Should detect conflicts
189
+ expect(result.detectedConflicts).toBeDefined();
190
+ expect(result.detectedConflicts).toHaveLength(2);
191
+
192
+ // Should include conflict resolution rules in final rules
193
+ expect(result.rules).toContain("=== Conflict Resolution Guidelines ===");
194
+ expect(result.rules).toContain("Create layered document structure");
195
+ expect(result.rules).toContain("Create separate user paths");
196
+
197
+ // Should also include regular configuration content with enhanced format
198
+ expect(result.rules).toContain("Document Purpose - Get started quickly:");
199
+ expect(result.rules).toContain("Target Audience - End users (non-technical):");
200
+ expect(result.rules).toContain("Target Audience - Developers integrating your product/API:");
201
+ expect(result.rules).toContain("Reader Knowledge Level:");
202
+ });
203
+
204
+ test("should work without conflicts", () => {
205
+ const config = {
206
+ documentPurpose: ["completeTasks"],
207
+ targetAudienceTypes: ["developers"],
208
+ readerKnowledgeLevel: "domainFamiliar",
209
+ };
210
+
211
+ const result = processConfigFields(config);
212
+
213
+ // Should not detect conflicts
214
+ expect(result.detectedConflicts).toBeUndefined();
215
+
216
+ // Should not include conflict resolution rules
217
+ expect(result.rules).not.toContain("=== Conflict Resolution Guidelines ===");
218
+
219
+ // Should still include regular configuration content with enhanced format
220
+ expect(result.rules).toContain("Document Purpose - Complete specific tasks:");
221
+ expect(result.rules).toContain("Target Audience - Developers integrating your product/API:");
222
+ });
223
+
224
+ test("should preserve target audience field processing", () => {
225
+ const config = {
226
+ targetAudienceTypes: ["developers", "devops"],
227
+ targetAudience: "Existing audience description",
228
+ };
229
+
230
+ const result = processConfigFields(config);
231
+
232
+ expect(result.targetAudience).toContain("Existing audience description");
233
+ expect(result.targetAudience).toContain("Developers integrating your product/API");
234
+ expect(result.targetAudience).toContain("DevOps / SRE / Infrastructure teams");
235
+ });
236
+ });
237
+ });