@aigne/doc-smith 0.9.6-beta → 0.9.6-beta.2
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.
- package/CHANGELOG.md +15 -0
- package/agents/create/document-structure-tools/delete-document.mjs +53 -9
- package/agents/create/update-document-structure.yaml +1 -1
- package/agents/create/user-add-document/add-documents-to-structure.mjs +96 -0
- package/agents/create/user-add-document/find-documents-to-add-links.yaml +47 -0
- package/agents/create/user-add-document/index.yaml +46 -0
- package/agents/create/user-add-document/prepare-documents-to-translate.mjs +22 -0
- package/agents/create/user-add-document/print-add-document-summary.mjs +63 -0
- package/agents/create/user-add-document/review-documents-with-new-links.mjs +110 -0
- package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +78 -0
- package/agents/create/user-remove-document/index.yaml +41 -0
- package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +22 -0
- package/agents/create/user-remove-document/print-remove-document-summary.mjs +53 -0
- package/agents/create/user-remove-document/remove-documents-from-structure.mjs +99 -0
- package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +119 -0
- package/agents/create/user-review-document-structure.mjs +1 -40
- package/agents/create/utils/init-current-content.mjs +38 -0
- package/agents/init/index.mjs +3 -4
- package/agents/update/document-tools/update-document-content.mjs +12 -12
- package/agents/update/update-document-detail.yaml +5 -1
- package/agents/update/update-single/update-single-document-detail.mjs +21 -6
- package/agents/update/user-review-document.mjs +10 -13
- package/agents/utils/add-translates-to-structure.mjs +29 -0
- package/agents/utils/{analyze-feedback-intent.yaml → analyze-document-feedback-intent.yaml} +5 -2
- package/agents/utils/analyze-structure-feedback-intent.yaml +29 -0
- package/agents/utils/check-detail-result.mjs +2 -14
- package/agents/utils/load-sources.mjs +36 -46
- package/aigne.yaml +10 -1
- package/package.json +1 -1
- package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +18 -10
- package/prompts/structure/find-documents-to-add-links.md +52 -0
- package/prompts/utils/analyze-document-feedback-intent.md +54 -0
- package/prompts/utils/analyze-structure-feedback-intent.md +43 -0
- package/types/document-schema.mjs +2 -0
- package/types/document-structure-schema.mjs +6 -2
- package/utils/docs-finder-utils.mjs +161 -0
- package/utils/file-utils.mjs +9 -7
- package/utils/load-config.mjs +21 -4
- package/utils/markdown-checker.mjs +50 -5
- package/utils/utils.mjs +103 -0
- package/prompts/utils/analyze-feedback-intent.md +0 -55
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: analyzeStructureFeedbackIntent
|
|
2
|
+
description: Analyze user feedback to determine if data sources are needed for structure modifications
|
|
3
|
+
task_render_mode: hide
|
|
4
|
+
instructions:
|
|
5
|
+
url: ../../prompts/utils/analyze-structure-feedback-intent.md
|
|
6
|
+
input_schema:
|
|
7
|
+
type: object
|
|
8
|
+
properties:
|
|
9
|
+
feedback:
|
|
10
|
+
type: string
|
|
11
|
+
description: User feedback for structure modifications
|
|
12
|
+
required:
|
|
13
|
+
- feedback
|
|
14
|
+
output_schema:
|
|
15
|
+
type: object
|
|
16
|
+
properties:
|
|
17
|
+
needDataSources:
|
|
18
|
+
type: boolean
|
|
19
|
+
description: Whether data sources are needed - true for add/edit operations that need context, false for delete/move/reorder operations
|
|
20
|
+
intentType:
|
|
21
|
+
type: string
|
|
22
|
+
description: The primary type of user intention
|
|
23
|
+
reason:
|
|
24
|
+
type: string
|
|
25
|
+
description: Explanation of why data sources are or aren't needed
|
|
26
|
+
required:
|
|
27
|
+
- needDataSources
|
|
28
|
+
- intentType
|
|
29
|
+
- reason
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkMarkdown } from "../../utils/markdown-checker.mjs";
|
|
2
|
+
import { buildAllowedLinksFromStructure } from "../../utils/docs-finder-utils.mjs";
|
|
2
3
|
|
|
3
4
|
export default async function checkDetailResult({ documentStructure, reviewContent, docsDir }) {
|
|
4
5
|
if (!reviewContent || reviewContent.trim() === "") {
|
|
@@ -12,20 +13,7 @@ export default async function checkDetailResult({ documentStructure, reviewConte
|
|
|
12
13
|
const detailFeedback = [];
|
|
13
14
|
|
|
14
15
|
// Create a set of allowed links, including both original paths and processed .md paths
|
|
15
|
-
const allowedLinks =
|
|
16
|
-
documentStructure.forEach((item) => {
|
|
17
|
-
// Add original path
|
|
18
|
-
allowedLinks.add(item.path);
|
|
19
|
-
|
|
20
|
-
// Add processed .md path (same logic as processContent in utils.mjs)
|
|
21
|
-
let processedPath = item.path;
|
|
22
|
-
if (processedPath.startsWith(".")) {
|
|
23
|
-
processedPath = processedPath.replace(/^\./, "");
|
|
24
|
-
}
|
|
25
|
-
let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
|
|
26
|
-
flatPath = `./${flatPath}.md`;
|
|
27
|
-
allowedLinks.add(flatPath);
|
|
28
|
-
});
|
|
16
|
+
const allowedLinks = buildAllowedLinksFromStructure(documentStructure);
|
|
29
17
|
|
|
30
18
|
// Run comprehensive markdown validation with all checks
|
|
31
19
|
try {
|
|
@@ -1,27 +1,42 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
1
|
import { statSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import imageSize from "image-size";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_EXCLUDE_PATTERNS,
|
|
7
|
+
DEFAULT_INCLUDE_PATTERNS,
|
|
8
|
+
INTELLIGENT_SUGGESTION_TOKEN_THRESHOLD,
|
|
9
|
+
} from "../../utils/constants/index.mjs";
|
|
10
|
+
import { loadDocumentStructure } from "../../utils/docs-finder-utils.mjs";
|
|
5
11
|
import {
|
|
6
12
|
buildSourcesContent,
|
|
7
|
-
|
|
8
|
-
readFileContents,
|
|
13
|
+
calculateTokens,
|
|
9
14
|
getMimeType,
|
|
10
15
|
isRemoteFile,
|
|
11
|
-
|
|
16
|
+
loadFilesFromPaths,
|
|
17
|
+
readFileContents,
|
|
12
18
|
} from "../../utils/file-utils.mjs";
|
|
19
|
+
import { isOpenAPISpecFile } from "../../utils/openapi/index.mjs";
|
|
13
20
|
import {
|
|
14
21
|
getCurrentGitHead,
|
|
15
22
|
getModifiedFilesBetweenCommits,
|
|
16
23
|
toRelativePath,
|
|
17
24
|
} from "../../utils/utils.mjs";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".heic", ".heif"];
|
|
27
|
+
const videoExts = [
|
|
28
|
+
".mp4",
|
|
29
|
+
".mpeg",
|
|
30
|
+
".mpg",
|
|
31
|
+
".mov",
|
|
32
|
+
".avi",
|
|
33
|
+
".flv",
|
|
34
|
+
".mkv",
|
|
35
|
+
".webm",
|
|
36
|
+
".wmv",
|
|
37
|
+
".m4v",
|
|
38
|
+
".3gpp",
|
|
39
|
+
];
|
|
25
40
|
|
|
26
41
|
export default async function loadSources(
|
|
27
42
|
{
|
|
@@ -77,7 +92,13 @@ export default async function loadSources(
|
|
|
77
92
|
.filter(Boolean);
|
|
78
93
|
const allFiles = await loadFilesFromPaths(pickSourcesPath, {
|
|
79
94
|
includePatterns,
|
|
80
|
-
excludePatterns: [
|
|
95
|
+
excludePatterns: [
|
|
96
|
+
...new Set([
|
|
97
|
+
...(excludePatterns || []),
|
|
98
|
+
...customExcludePatterns,
|
|
99
|
+
...videoExts.map((x) => `**/*${x}`),
|
|
100
|
+
]),
|
|
101
|
+
],
|
|
81
102
|
useDefaultPatterns,
|
|
82
103
|
defaultIncludePatterns: DEFAULT_INCLUDE_PATTERNS,
|
|
83
104
|
defaultExcludePatterns: DEFAULT_EXCLUDE_PATTERNS,
|
|
@@ -90,26 +111,9 @@ export default async function loadSources(
|
|
|
90
111
|
|
|
91
112
|
// Define media file extensions
|
|
92
113
|
const mediaExtensions = [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
".gif",
|
|
97
|
-
".bmp",
|
|
98
|
-
".webp",
|
|
99
|
-
".svg",
|
|
100
|
-
".heic",
|
|
101
|
-
".heif",
|
|
102
|
-
".mp4",
|
|
103
|
-
".mpeg",
|
|
104
|
-
".mpg",
|
|
105
|
-
".mov",
|
|
106
|
-
".avi",
|
|
107
|
-
".flv",
|
|
108
|
-
".mkv",
|
|
109
|
-
".webm",
|
|
110
|
-
".wmv",
|
|
111
|
-
".m4v",
|
|
112
|
-
".3gpp",
|
|
114
|
+
...imageExts,
|
|
115
|
+
// ignore video temporary
|
|
116
|
+
// ...videoExts
|
|
113
117
|
];
|
|
114
118
|
|
|
115
119
|
// Separate source files from media files
|
|
@@ -119,20 +123,6 @@ export default async function loadSources(
|
|
|
119
123
|
// Helper function to determine file type from extension
|
|
120
124
|
const getFileType = (filePath) => {
|
|
121
125
|
const ext = path.extname(filePath).toLowerCase();
|
|
122
|
-
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".heic", ".heif"];
|
|
123
|
-
const videoExts = [
|
|
124
|
-
".mp4",
|
|
125
|
-
".mpeg",
|
|
126
|
-
".mpg",
|
|
127
|
-
".mov",
|
|
128
|
-
".avi",
|
|
129
|
-
".flv",
|
|
130
|
-
".mkv",
|
|
131
|
-
".webm",
|
|
132
|
-
".wmv",
|
|
133
|
-
".m4v",
|
|
134
|
-
".3gpp",
|
|
135
|
-
];
|
|
136
126
|
|
|
137
127
|
if (imageExts.includes(ext)) return "image";
|
|
138
128
|
if (videoExts.includes(ext)) return "video";
|
package/aigne.yaml
CHANGED
|
@@ -68,6 +68,7 @@ agents:
|
|
|
68
68
|
- ./agents/clear/clear-media-description.mjs
|
|
69
69
|
|
|
70
70
|
# Utilities
|
|
71
|
+
- ./agents/utils/add-translates-to-structure.mjs
|
|
71
72
|
- ./agents/utils/load-sources.mjs
|
|
72
73
|
- ./agents/utils/post-generate.mjs
|
|
73
74
|
- ./agents/utils/save-sidebar.mjs
|
|
@@ -79,7 +80,8 @@ agents:
|
|
|
79
80
|
- ./agents/utils/find-item-by-path.mjs
|
|
80
81
|
- ./agents/utils/check-feedback-refiner.mjs
|
|
81
82
|
- ./agents/utils/feedback-refiner.yaml
|
|
82
|
-
- ./agents/utils/analyze-feedback-intent.yaml
|
|
83
|
+
- ./agents/utils/analyze-structure-feedback-intent.yaml
|
|
84
|
+
- ./agents/utils/analyze-document-feedback-intent.yaml
|
|
83
85
|
|
|
84
86
|
- ./agents/utils/document-title-streamline.yaml
|
|
85
87
|
- ./agents/utils/streamline-document-titles-if-needed.mjs
|
|
@@ -126,6 +128,13 @@ cli:
|
|
|
126
128
|
- url: ./agents/history/view.mjs
|
|
127
129
|
name: view
|
|
128
130
|
alias: ["log", "list"]
|
|
131
|
+
- name: add-document
|
|
132
|
+
alias: ["add"]
|
|
133
|
+
url: ./agents/create/user-add-document/index.yaml
|
|
134
|
+
- name: remove-document
|
|
135
|
+
alias: ["remove", "rm"]
|
|
136
|
+
url: ./agents/create/user-remove-document/index.yaml
|
|
137
|
+
- ./agents/clear/index.yaml
|
|
129
138
|
mcp_server:
|
|
130
139
|
agents:
|
|
131
140
|
- ./docs-mcp/get-docs-structure.mjs
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ XCards is multiple `<x-card>` container, suitable for displaying multiple links
|
|
|
5
5
|
|
|
6
6
|
### Attributes
|
|
7
7
|
|
|
8
|
-
- `data-columns` (
|
|
8
|
+
- `data-columns` (required): must be an integer ≥ 2; no upper bound.
|
|
9
9
|
|
|
10
10
|
### Children
|
|
11
11
|
|
|
@@ -20,20 +20,21 @@ XCards is multiple `<x-card>` container, suitable for displaying multiple links
|
|
|
20
20
|
|
|
21
21
|
### Good Examples
|
|
22
22
|
|
|
23
|
-
- Example 1:
|
|
23
|
+
- Example 1: Two-column cards with images
|
|
24
24
|
```md
|
|
25
|
-
<x-cards data-columns="
|
|
26
|
-
<x-card data-title="
|
|
27
|
-
<x-card data-title="
|
|
28
|
-
<x-card data-title="Feature 3" data-icon="material-symbols:rocket-outline">Description of Feature 3.</x-card>
|
|
25
|
+
<x-cards data-columns="2">
|
|
26
|
+
<x-card data-title="Card A" data-image="https://picsum.photos/id/10/300/300">Content A</x-card>
|
|
27
|
+
<x-card data-title="Card B" data-image="https://picsum.photos/id/11/300/300">Content B</x-card>
|
|
29
28
|
</x-cards>
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
- Example 2:
|
|
31
|
+
- Example 2: Four-column cards with icons
|
|
33
32
|
```md
|
|
34
|
-
<x-cards data-columns="
|
|
35
|
-
<x-card data-title="
|
|
36
|
-
<x-card data-title="
|
|
33
|
+
<x-cards data-columns="4">
|
|
34
|
+
<x-card data-title="Feature 1" data-icon="lucide:rocket">Description of Feature 1.</x-card>
|
|
35
|
+
<x-card data-title="Feature 2" data-icon="lucide:bolt">Description of Feature 2.</x-card>
|
|
36
|
+
<x-card data-title="Feature 3" data-icon="material-symbols:rocket-outline">Description of Feature 3.</x-card>
|
|
37
|
+
<x-card data-title="Feature 4" data-icon="lucide:star">Description of Feature 4.</x-card>
|
|
37
38
|
</x-cards>
|
|
38
39
|
```
|
|
39
40
|
|
|
@@ -72,4 +73,11 @@ XCards is multiple `<x-card>` container, suitable for displaying multiple links
|
|
|
72
73
|
- [Using the Blog](./blog.md)
|
|
73
74
|
- [Using Chat](./chat.md)
|
|
74
75
|
```
|
|
76
|
+
|
|
77
|
+
- Example 4: Missing `data-columns` attribute (required)
|
|
78
|
+
<x-cards>
|
|
79
|
+
<x-card data-title="Feature 1" data-icon="lucide:rocket">Description of Feature 1.</x-card>
|
|
80
|
+
<x-card data-title="Feature 2" data-icon="lucide:bolt">Description of Feature 2.</x-card>
|
|
81
|
+
</x-cards>
|
|
82
|
+
|
|
75
83
|
</x-card-usage-rules>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Task: Find Documents to Add Links
|
|
2
|
+
|
|
3
|
+
Determine which existing documents should link to newly added documents.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
|
|
7
|
+
<documentStructure>
|
|
8
|
+
{{originalDocumentStructure}}
|
|
9
|
+
</documentStructure>
|
|
10
|
+
|
|
11
|
+
<newDocuments>
|
|
12
|
+
{{newDocuments}}
|
|
13
|
+
</newDocuments>
|
|
14
|
+
|
|
15
|
+
<userFeedback>
|
|
16
|
+
{{allFeedback}}
|
|
17
|
+
</userFeedback>
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
1. **Check <userFeedback> first.**
|
|
22
|
+
- If users explicitly specify linking (e.g., “link FAQ from About”), follow exactly.
|
|
23
|
+
2. **Analyze <documentStructure>.**
|
|
24
|
+
Identify existing documents that should link to those in <newDocuments> using the rules below.
|
|
25
|
+
3. For each qualifying document, add a non-empty `newLinks` array containing new document paths.
|
|
26
|
+
4. Output only these updated documents (subset of <documentStructure>) as `documentsWithNewLinks`.
|
|
27
|
+
|
|
28
|
+
Each item in `documentsWithNewLinks` must:
|
|
29
|
+
- Be an existing document from <documentStructure>
|
|
30
|
+
- Retain all original properties (`path`, `title`, `description`, `parentId`, `icon`, `sourceIds`)
|
|
31
|
+
- Include `newLinks: string[]`
|
|
32
|
+
|
|
33
|
+
## Linking Rules (in priority order)
|
|
34
|
+
|
|
35
|
+
1. **User Instructions** — Follow explicit <userFeedback>.
|
|
36
|
+
2. **Parent–Child** — If a new document’s `parentId` equals a document’s `path`, the parent links to it.
|
|
37
|
+
3. **Semantic Similarity** — Link thematically related documents (e.g., “About” ↔ “Team”).
|
|
38
|
+
4. **Navigation Context** — Documents in the same navigation group may link.
|
|
39
|
+
5. **Hierarchy** — Sibling or section documents may cross-link.
|
|
40
|
+
6. **Relevance** — Add links only when it improves navigation logically.
|
|
41
|
+
|
|
42
|
+
## Output Format
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"documentsWithNewLinks": [
|
|
47
|
+
{
|
|
48
|
+
"path": "/existing-document",
|
|
49
|
+
"newLinks": ["/new-document-1", "/new-document-2"]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<role>
|
|
2
|
+
You are a feedback intent analyzer for **document content modifications**. Your task is to determine the intent type of user feedback regarding content-level operations inside a document, and whether external data sources are needed.
|
|
3
|
+
</role>
|
|
4
|
+
|
|
5
|
+
<input>
|
|
6
|
+
feedback: {{feedback}}
|
|
7
|
+
</input>
|
|
8
|
+
|
|
9
|
+
<analysis_rules>
|
|
10
|
+
If the feedback contains any document-level (structure) operations, return an error (document content edits cannot include structure changes).
|
|
11
|
+
|
|
12
|
+
Scope: Only analyze feedback related to document content (e.g. sections, text, images).
|
|
13
|
+
|
|
14
|
+
**Intent types:**
|
|
15
|
+
|
|
16
|
+
1. add - Adding new sections or content inside a document
|
|
17
|
+
2. edit - Modifying existing content, titles, descriptions, components
|
|
18
|
+
3. delete - Removing sections or content
|
|
19
|
+
4. move - Moving sections to different positions within the document
|
|
20
|
+
5. reorder - Changing the order of sections at the same level
|
|
21
|
+
6. mixed - Combination of multiple intent types
|
|
22
|
+
|
|
23
|
+
**Data source rules:**
|
|
24
|
+
|
|
25
|
+
- add/edit -> needDataSources = true
|
|
26
|
+
- delete/move/reorder -> needDataSources = false
|
|
27
|
+
- mixed -> needDataSources = true if any add/edit is included
|
|
28
|
+
|
|
29
|
+
**Decision logic:**
|
|
30
|
+
|
|
31
|
+
- Only consider document content operations.
|
|
32
|
+
- If any add or edit operation exists -> needDataSources = true
|
|
33
|
+
- If only delete, move, or reorder operations exist -> needDataSources = false
|
|
34
|
+
- When uncertain, default to needDataSources = true
|
|
35
|
+
</analysis_rules>
|
|
36
|
+
|
|
37
|
+
<output_rules>
|
|
38
|
+
Normal output:
|
|
39
|
+
|
|
40
|
+
{
|
|
41
|
+
"error": false,
|
|
42
|
+
"needDataSources": boolean,
|
|
43
|
+
"intentType": "add" | "edit" | "delete" | "move" | "reorder" | "mixed",
|
|
44
|
+
"reason": "Explanation of why data sources are or aren't needed based on page content operations."
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Error output (if document-level operations are detected):
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
"error": true,
|
|
51
|
+
"needDataSources": false,
|
|
52
|
+
"reason": "Feedback mixes document-content edits with document-structure operations. When analyzing document content, structure changes are not allowed. Please split into separate feedback items."
|
|
53
|
+
}
|
|
54
|
+
</output_rules>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<role>
|
|
2
|
+
You are a feedback intent analyzer for **document structure modifications**. Your task is to determine the intent type of user feedback regarding document-level operations and whether external data sources are needed.
|
|
3
|
+
</role>
|
|
4
|
+
|
|
5
|
+
<input>
|
|
6
|
+
feedback: {{feedback}}
|
|
7
|
+
</input>
|
|
8
|
+
|
|
9
|
+
<analysis_rules>
|
|
10
|
+
Scope: Only analyze feedback related to document structure. Ignore any content-level operations inside document (e.g. sections, text, images).
|
|
11
|
+
|
|
12
|
+
**intent types:**
|
|
13
|
+
|
|
14
|
+
1. add - Adding new documents
|
|
15
|
+
2. edit - Modifying document-level properties (e.g., path, parentId, title of the document itself)
|
|
16
|
+
3. delete - Removing documents
|
|
17
|
+
4. move - Moving documents to different positions or parent sections
|
|
18
|
+
5. reorder - Changing the order of documents
|
|
19
|
+
6. mixed - Combination of multiple intent types
|
|
20
|
+
|
|
21
|
+
**Data source rules:**
|
|
22
|
+
|
|
23
|
+
- add/edit -> needDataSources = true
|
|
24
|
+
- delete/move/reorder -> needDataSources = false
|
|
25
|
+
- mixed -> needDataSources = true if any add/edit is included
|
|
26
|
+
|
|
27
|
+
**Decision logic:**
|
|
28
|
+
|
|
29
|
+
- Only consider document-level operations in the feedback.
|
|
30
|
+
- If any add or edit operation exists -> needDataSources = true
|
|
31
|
+
- If only delete, move, or reorder operations exist -> needDataSources = false
|
|
32
|
+
- When uncertain, default to needDataSources = true
|
|
33
|
+
</analysis_rules>
|
|
34
|
+
|
|
35
|
+
<output_rules>
|
|
36
|
+
Return a JSON object:
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
"needDataSources": boolean,
|
|
40
|
+
"intentType": "add" | "edit" | "delete" | "move" | "reorder" | "mixed",
|
|
41
|
+
"reason": "Explanation of why data sources are or aren't needed based on document-level operations."
|
|
42
|
+
}
|
|
43
|
+
</output_rules>
|
|
@@ -4,6 +4,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
|
|
|
4
4
|
// Update document content schemas
|
|
5
5
|
export const updateDocumentContentInputSchema = z.object({
|
|
6
6
|
diffPatch: z.string().min(1, "Diff patch is required"),
|
|
7
|
+
path: z.string().min(1, "Path is required for concurrent document updates"),
|
|
7
8
|
});
|
|
8
9
|
|
|
9
10
|
export const updateDocumentContentOutputSchema = z.object({
|
|
@@ -18,6 +19,7 @@ export const getUpdateDocumentContentInputJsonSchema = () => {
|
|
|
18
19
|
const schema = zodToJsonSchema(updateDocumentContentInputSchema);
|
|
19
20
|
if (schema.properties) {
|
|
20
21
|
schema.properties.diffPatch.description = "Diff patch string to apply to the original content";
|
|
22
|
+
schema.properties.path.description = "Document path";
|
|
21
23
|
}
|
|
22
24
|
return schema;
|
|
23
25
|
};
|
|
@@ -33,12 +33,13 @@ export const addDocumentOutputSchema = z.object({
|
|
|
33
33
|
// Delete document schemas
|
|
34
34
|
export const deleteDocumentInputSchema = z.object({
|
|
35
35
|
path: z.string().min(1, "Path is required"),
|
|
36
|
+
recursive: z.boolean().optional(),
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
export const deleteDocumentOutputSchema = z.object({
|
|
39
40
|
documentStructure: documentStructureSchema,
|
|
40
41
|
message: z.string().optional(),
|
|
41
|
-
|
|
42
|
+
deletedDocuments: z.array(documentItemSchema).optional(),
|
|
42
43
|
error: z.object({ message: z.string() }).optional(),
|
|
43
44
|
});
|
|
44
45
|
|
|
@@ -113,6 +114,8 @@ export const getDeleteDocumentInputJsonSchema = () => {
|
|
|
113
114
|
const schema = zodToJsonSchema(deleteDocumentInputSchema);
|
|
114
115
|
if (schema.properties) {
|
|
115
116
|
schema.properties.path.description = "URL path of the document to delete";
|
|
117
|
+
schema.properties.recursive.description =
|
|
118
|
+
"If true, recursively delete all child documents. If false or not provided, deletion will fail if child documents exist.";
|
|
116
119
|
}
|
|
117
120
|
return schema;
|
|
118
121
|
};
|
|
@@ -123,7 +126,8 @@ export const getDeleteDocumentOutputJsonSchema = () => {
|
|
|
123
126
|
schema.properties.documentStructure.description =
|
|
124
127
|
"Updated documentation structure array with the document removed";
|
|
125
128
|
schema.properties.message.description = "Success message describing the operation result";
|
|
126
|
-
schema.properties.
|
|
129
|
+
schema.properties.deletedDocuments.description =
|
|
130
|
+
"Array of deleted document objects (includes all recursively deleted child documents if recursive=true)";
|
|
127
131
|
schema.properties.error.description =
|
|
128
132
|
"Error object containing error message if operation failed";
|
|
129
133
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import pLimit from "p-limit";
|
|
3
5
|
import { pathExists } from "./file-utils.mjs";
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -324,6 +326,40 @@ export async function loadDocumentStructure(outputDir) {
|
|
|
324
326
|
}
|
|
325
327
|
}
|
|
326
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Build allowed links set from document structure
|
|
331
|
+
* Includes both original paths and processed .md paths for link validation
|
|
332
|
+
* @param {Array} documentStructure - Array of documentation structure items with path property
|
|
333
|
+
* @returns {Set<string>} Set of allowed link paths
|
|
334
|
+
*/
|
|
335
|
+
export function buildAllowedLinksFromStructure(documentStructure) {
|
|
336
|
+
const allowedLinks = new Set();
|
|
337
|
+
|
|
338
|
+
if (!Array.isArray(documentStructure)) {
|
|
339
|
+
return allowedLinks;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
documentStructure.forEach((item) => {
|
|
343
|
+
if (!item?.path) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Add original path
|
|
348
|
+
allowedLinks.add(item.path);
|
|
349
|
+
|
|
350
|
+
// Add processed .md path (same logic as processContent in utils.mjs)
|
|
351
|
+
let processedPath = item.path;
|
|
352
|
+
if (processedPath.startsWith(".")) {
|
|
353
|
+
processedPath = processedPath.replace(/^\./, "");
|
|
354
|
+
}
|
|
355
|
+
let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
|
|
356
|
+
flatPath = `./${flatPath}.md`;
|
|
357
|
+
allowedLinks.add(flatPath);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
return allowedLinks;
|
|
361
|
+
}
|
|
362
|
+
|
|
327
363
|
/**
|
|
328
364
|
* Build a tree structure from a flat document structure array using parentId
|
|
329
365
|
* @param {Array} documentStructure - Flat array of document structure items with path and parentId
|
|
@@ -358,3 +394,128 @@ export function buildDocumentTree(documentStructure) {
|
|
|
358
394
|
|
|
359
395
|
return { rootNodes, nodeMap };
|
|
360
396
|
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Build checkbox choices from tree structure with visual hierarchy
|
|
400
|
+
* @param {Array} nodes - Array of tree nodes
|
|
401
|
+
* @param {string} prefix - Current prefix for indentation
|
|
402
|
+
* @param {number} depth - Current depth level (0 for root)
|
|
403
|
+
* @param {Object} context - Context object containing locale, docsDir, etc.
|
|
404
|
+
* @param {string} context.locale - Main language locale (e.g., 'en', 'zh', 'fr')
|
|
405
|
+
* @param {string} [context.docsDir] - Docs directory path for file existence check
|
|
406
|
+
* @returns {Promise<Array>} Array of choice objects
|
|
407
|
+
*/
|
|
408
|
+
export async function buildChoicesFromTree(nodes, prefix = "", depth = 0, context = {}) {
|
|
409
|
+
const { locale = "en", docsDir } = context;
|
|
410
|
+
const choices = [];
|
|
411
|
+
|
|
412
|
+
// Limit concurrent file checks to 50 per level to avoid overwhelming the file system
|
|
413
|
+
const limit = pLimit(50);
|
|
414
|
+
|
|
415
|
+
// Process nodes with controlled concurrency while maintaining order
|
|
416
|
+
const nodePromises = nodes.map((node, i) =>
|
|
417
|
+
limit(async () => {
|
|
418
|
+
const isLastSibling = i === nodes.length - 1;
|
|
419
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
420
|
+
|
|
421
|
+
// Build the tree prefix - top level nodes don't have ├─ or └─
|
|
422
|
+
const treePrefix = depth === 0 ? "" : prefix + (isLastSibling ? "└─ " : "├─ ");
|
|
423
|
+
const flatName = pathToFlatName(node.path);
|
|
424
|
+
const filename = generateFileName(flatName, locale);
|
|
425
|
+
|
|
426
|
+
// Check file existence if docsDir is provided
|
|
427
|
+
let fileExists = true;
|
|
428
|
+
let missingFileText = "";
|
|
429
|
+
if (docsDir) {
|
|
430
|
+
const filePath = join(docsDir, filename);
|
|
431
|
+
fileExists = await pathExists(filePath);
|
|
432
|
+
if (!fileExists) {
|
|
433
|
+
missingFileText = chalk.red(" - file not found");
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// warningText only shows when file exists, missingFileText has higher priority
|
|
438
|
+
const warningText =
|
|
439
|
+
fileExists && hasChildren ? chalk.yellow(" - will cascade delete all child documents") : "";
|
|
440
|
+
|
|
441
|
+
const displayName = `${treePrefix}${node.title} (${filename})${warningText}${missingFileText}`;
|
|
442
|
+
|
|
443
|
+
const choice = {
|
|
444
|
+
name: displayName,
|
|
445
|
+
value: node.path,
|
|
446
|
+
short: node.title,
|
|
447
|
+
disabled: !fileExists,
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
// Recursively process children
|
|
451
|
+
let childChoices = [];
|
|
452
|
+
if (hasChildren) {
|
|
453
|
+
const childPrefix = depth === 0 ? "" : prefix + (isLastSibling ? " " : "│ ");
|
|
454
|
+
childChoices = await buildChoicesFromTree(node.children, childPrefix, depth + 1, context);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return { choice, childChoices };
|
|
458
|
+
}),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// Wait for all nodes at this level to complete, maintaining order
|
|
462
|
+
const results = await Promise.all(nodePromises);
|
|
463
|
+
|
|
464
|
+
// Build choices array in order
|
|
465
|
+
for (const { choice, childChoices } of results) {
|
|
466
|
+
choices.push(choice);
|
|
467
|
+
if (childChoices.length > 0) {
|
|
468
|
+
choices.push(...childChoices);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return choices;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Format document structure for printing
|
|
477
|
+
* @param {Array} structure - Document structure array
|
|
478
|
+
* @returns {Object} Object containing rootNodes and printNode function
|
|
479
|
+
*/
|
|
480
|
+
function formatDocumentStructure(structure) {
|
|
481
|
+
const { rootNodes } = buildDocumentTree(structure);
|
|
482
|
+
|
|
483
|
+
function printNode(node, depth = 0) {
|
|
484
|
+
const INDENT_SPACES = " ";
|
|
485
|
+
const FOLDER_ICON = " 📁";
|
|
486
|
+
const FILE_ICON = " 📄";
|
|
487
|
+
const indent = INDENT_SPACES.repeat(depth);
|
|
488
|
+
const prefix = depth === 0 ? FOLDER_ICON : FILE_ICON;
|
|
489
|
+
|
|
490
|
+
console.log(`${indent}${prefix} ${node.title}`);
|
|
491
|
+
|
|
492
|
+
if (node.children && node.children.length > 0) {
|
|
493
|
+
node.children.forEach((child) => {
|
|
494
|
+
printNode(child, depth + 1);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return { rootNodes, printNode };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Print document structure in a user-friendly format
|
|
504
|
+
* @param {Array} structure - Document structure array
|
|
505
|
+
*/
|
|
506
|
+
export function printDocumentStructure(structure) {
|
|
507
|
+
console.log(`\n ${"-".repeat(50)}`);
|
|
508
|
+
console.log(" Current Documentation Structure");
|
|
509
|
+
console.log(` ${"-".repeat(50)}`);
|
|
510
|
+
|
|
511
|
+
const { rootNodes, printNode } = formatDocumentStructure(structure);
|
|
512
|
+
|
|
513
|
+
if (rootNodes.length === 0) {
|
|
514
|
+
console.log(" No documentation structure found.");
|
|
515
|
+
} else {
|
|
516
|
+
rootNodes.forEach((node) => {
|
|
517
|
+
printNode(node);
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
console.log();
|
|
521
|
+
}
|