@aigne/doc-smith 0.9.5 → 0.9.6-beta.1

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 (43) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/agents/create/document-structure-tools/delete-document.mjs +32 -9
  3. package/agents/create/update-document-structure.yaml +1 -1
  4. package/agents/create/user-add-document/add-documents-to-structure.mjs +96 -0
  5. package/agents/create/user-add-document/find-documents-to-add-links.yaml +47 -0
  6. package/agents/create/user-add-document/index.yaml +46 -0
  7. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +22 -0
  8. package/agents/create/user-add-document/print-add-document-summary.mjs +56 -0
  9. package/agents/create/user-add-document/review-documents-with-new-links.mjs +73 -0
  10. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +67 -0
  11. package/agents/create/user-remove-document/index.yaml +41 -0
  12. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +22 -0
  13. package/agents/create/user-remove-document/print-remove-document-summary.mjs +56 -0
  14. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +96 -0
  15. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +116 -0
  16. package/agents/create/user-review-document-structure.mjs +1 -40
  17. package/agents/create/utils/init-current-content.mjs +38 -0
  18. package/agents/init/check.mjs +1 -1
  19. package/agents/update/check-generate-diagram.mjs +4 -3
  20. package/agents/update/document-tools/update-document-content.mjs +12 -12
  21. package/agents/update/update-document-detail.yaml +5 -1
  22. package/agents/update/update-single/update-single-document-detail.mjs +155 -0
  23. package/agents/update/user-review-document.mjs +11 -14
  24. package/agents/utils/add-translates-to-structure.mjs +29 -0
  25. package/agents/utils/{analyze-feedback-intent.yaml → analyze-document-feedback-intent.yaml} +5 -2
  26. package/agents/utils/analyze-structure-feedback-intent.yaml +29 -0
  27. package/agents/utils/check-detail-result.mjs +2 -14
  28. package/aigne.yaml +11 -1
  29. package/package.json +1 -1
  30. package/prompts/detail/diagram/user-prompt.md +17 -0
  31. package/prompts/detail/generate/system-prompt.md +0 -1
  32. package/prompts/detail/update/system-prompt.md +40 -32
  33. package/prompts/structure/find-documents-to-add-links.md +52 -0
  34. package/prompts/utils/analyze-document-feedback-intent.md +54 -0
  35. package/prompts/utils/analyze-structure-feedback-intent.md +43 -0
  36. package/types/document-schema.mjs +2 -0
  37. package/types/document-structure-schema.mjs +6 -2
  38. package/utils/auth-utils.mjs +32 -16
  39. package/utils/d2-utils.mjs +13 -0
  40. package/utils/docs-finder-utils.mjs +82 -0
  41. package/utils/markdown-checker.mjs +50 -5
  42. package/utils/utils.mjs +103 -0
  43. 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 = new Set();
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 {
package/aigne.yaml CHANGED
@@ -39,6 +39,7 @@ agents:
39
39
  - ./agents/update/check-update-is-single.mjs
40
40
  - ./agents/update/save-and-translate-document.mjs
41
41
  - ./agents/update/index.yaml
42
+ - ./agents/update/update-single/update-single-document-detail.mjs
42
43
 
43
44
 
44
45
  # Translation
@@ -67,6 +68,7 @@ agents:
67
68
  - ./agents/clear/clear-media-description.mjs
68
69
 
69
70
  # Utilities
71
+ - ./agents/utils/add-translates-to-structure.mjs
70
72
  - ./agents/utils/load-sources.mjs
71
73
  - ./agents/utils/post-generate.mjs
72
74
  - ./agents/utils/save-sidebar.mjs
@@ -78,7 +80,8 @@ agents:
78
80
  - ./agents/utils/find-item-by-path.mjs
79
81
  - ./agents/utils/check-feedback-refiner.mjs
80
82
  - ./agents/utils/feedback-refiner.yaml
81
- - ./agents/utils/analyze-feedback-intent.yaml
83
+ - ./agents/utils/analyze-structure-feedback-intent.yaml
84
+ - ./agents/utils/analyze-document-feedback-intent.yaml
82
85
 
83
86
  - ./agents/utils/document-title-streamline.yaml
84
87
  - ./agents/utils/streamline-document-titles-if-needed.mjs
@@ -125,6 +128,13 @@ cli:
125
128
  - url: ./agents/history/view.mjs
126
129
  name: view
127
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: ["rm"]
136
+ url: ./agents/create/user-remove-document/index.yaml
137
+ - ./agents/clear/index.yaml
128
138
  mcp_server:
129
139
  agents:
130
140
  - ./docs-mcp/get-docs-structure.mjs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.9.5",
3
+ "version": "0.9.6-beta.1",
4
4
  "description": "AI-driven documentation generation tool built on the AIGNE Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -9,6 +9,9 @@ Generate a d2 diagram that represents the following document content:
9
9
  <user_rules>
10
10
 
11
11
  - Output only the diagram labels and text in the {{ locale }} language — keep all variable names, component names, and syntax unchanged.
12
+ {% if previousDiagramContent %}
13
+ - Update the diagram based on `<feedback>` and `<previous_diagram_content>`.
14
+ {% endif %}
12
15
 
13
16
  </user_rules>
14
17
 
@@ -24,3 +27,17 @@ Generate a d2 diagram that represents the following document content:
24
27
 
25
28
  </diagram_check_feedback>
26
29
  {% endif %}
30
+
31
+ {% if previousDiagramContent %}
32
+ <previous_diagram_content>
33
+ {{ previousDiagramContent }}
34
+ </previous_diagram_content>
35
+
36
+ {% if feedback %}
37
+ <feedback>
38
+ {{ feedback }}
39
+ </feedback>
40
+ {% endif %}
41
+
42
+ {% endif %}
43
+
@@ -49,7 +49,6 @@ Documentation content generation rules:
49
49
  </content_generation_rules>
50
50
 
51
51
 
52
-
53
52
  <output_constraints>
54
53
 
55
54
  1. Output the complete Markdown content for {{nodeName}}, only the content itself—no explanations or extra information.
@@ -3,9 +3,35 @@
3
3
  </role_and_goal>
4
4
 
5
5
 
6
- <document_structure>
7
- {{ documentStructureYaml }}
8
- </document_structure>
6
+ <task_instructions>
7
+ Your task is to:
8
+
9
+ Processing workflow:
10
+ - If user feedback is not in English, translate it to English first to better understand user intent
11
+ - Analyze user feedback to understand the exact intent and scope of changes
12
+ - Generate a unified diff patch that implements the requested improvements
13
+ - Use the available tool to apply the changes and get the final content
14
+ - Tool calls only need to return toolCalls information
15
+ - Ensure all modifications maintain document quality and consistency
16
+ - Return 'success' when the latest version of content meets user feedback(Don't add any explanation)
17
+
18
+ Tool usage guidelines:
19
+
20
+ **updateDocumentContent**: Use this tool to apply changes to the document content
21
+ - Generate a precise unified diff patch based on the user feedback
22
+ - Forbidden change diagram source code
23
+ - If user ask to add/update diagram, use `generateDiagram` tool instead.
24
+ - If user ask to remove diagram, should remove diagram content and update document context to get better understanding.
25
+ - The diff should include context lines for accurate application
26
+ - Only consider content within `<page_content>` tag when calculating line numbers, ensure line number calculation is accurate
27
+ - Test the patch application to ensure it works correctly
28
+
29
+ Error handling:
30
+ - If user intent is unclear, ask for clarification
31
+ - If the requested changes conflict with best practices, explain the issues and suggest alternatives
32
+ - If the diff patch fails to apply, revise the approach and try again
33
+ </task_instructions>
34
+
9
35
 
10
36
  <current_document>
11
37
  Current {{nodeName}} information:
@@ -16,6 +42,11 @@ parentId: {{parentId}}
16
42
  </current_document>
17
43
 
18
44
 
45
+ <document_structure>
46
+ {{ documentStructureYaml }}
47
+ </document_structure>
48
+
49
+
19
50
  {% if glossary %}
20
51
  <terms>
21
52
  Glossary of specialized terms. Please ensure correct spelling when using these terms.
@@ -39,7 +70,10 @@ Documentation content optimization rules:
39
70
 
40
71
  {% include "../../common/document/media-file-list-usage-rules.md" %}
41
72
 
42
- {% include "../diagram/rules.md" %}
73
+ {% include "../../common/document/openapi-usage-rules.md" %}
74
+
75
+ ** Update Constraints: **
76
+ - Forbidden change diagram source code
43
77
 
44
78
  </content_optimization_rules>
45
79
 
@@ -75,37 +109,11 @@ Analyze the user feedback to determine the specific improvements needed:
75
109
 
76
110
  </feedback_analysis_guidelines>
77
111
 
78
- <task_instructions>
79
- Your task is to:
80
-
81
- Processing workflow:
82
- - If user feedback is not in English, translate it to English first to better understand user intent
83
- - Analyze user feedback to understand the exact intent and scope of changes
84
- - Generate a unified diff patch that implements the requested improvements
85
- - Use the available tool to apply the changes and get the final content
86
- - Tool calls only need to return toolCalls information
87
- - Ensure all modifications maintain document quality and consistency
88
- - Return 'success' when the latest version of content meets user feedback
89
-
90
- Tool usage guidelines:
91
-
92
- **updateDocumentContent**: Use this tool to apply changes to the document content
93
- - Generate a precise unified diff patch based on the user feedback
94
- - The diff should include context lines for accurate application
95
- - Only consider content within `<page_content>` tag when calculating line numbers, ensure line number calculation is accurate
96
- - Test the patch application to ensure it works correctly
97
-
98
- Error handling:
99
- - If user intent is unclear, ask for clarification
100
- - If the requested changes conflict with best practices, explain the issues and suggest alternatives
101
- - If the diff patch fails to apply, revise the approach and try again
102
- </task_instructions>
103
-
104
112
  {% include "../generate/detail-example.md" %}
105
113
 
106
114
 
107
- <output_format>
115
+ <output_constraints>
108
116
  ** Only output operation execution status **:
109
117
  - Only return 'success' if operation executed successfully
110
118
  - Return brief error message if operation failed
111
- </output_format>
119
+ </output_constraints>
@@ -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
- deletedDocument: documentItemSchema.optional(),
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.deletedDocument.description = "The deleted document object";
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
  }
@@ -21,7 +21,6 @@ import {
21
21
  DISCUSS_KIT_STORE_URL,
22
22
  PAYMENT_KIT_DID,
23
23
  } from "./constants/index.mjs";
24
- import { requestWithAuthToken } from "./request.mjs";
25
24
  import { withQuery } from "ufo";
26
25
 
27
26
  const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
@@ -143,16 +142,15 @@ export async function getAccessToken(appUrl, ltToken = "", locale = "en") {
143
142
 
144
143
  try {
145
144
  const officialBaseUrl = process.env.DOC_SMITH_BASE_URL || CLOUD_SERVICE_URL_PROD;
146
- const officialAccessToken = await getCachedAccessToken(officialBaseUrl);
147
- if (officialAccessToken) {
148
- const mountPoint = await getComponentMountPoint(officialBaseUrl, PAYMENT_KIT_DID);
149
- const data = await requestWithAuthToken(
150
- withQuery(joinURL(officialBaseUrl, mountPoint, "/api/tool/short-url"), {
151
- url: connectUrl,
152
- }),
153
- {},
154
- officialAccessToken,
155
- );
145
+ const mountPoint = await getComponentMountPoint(officialBaseUrl, PAYMENT_KIT_DID);
146
+ const response = await fetch(
147
+ withQuery(joinURL(officialBaseUrl, mountPoint, "/api/tool/short-connect-url"), {
148
+ url: connectUrl,
149
+ locale,
150
+ }),
151
+ );
152
+ const data = await response.json();
153
+ if (data.url) {
156
154
  connectUrl = data.url;
157
155
  }
158
156
  } catch {
@@ -160,7 +158,7 @@ export async function getAccessToken(appUrl, ltToken = "", locale = "en") {
160
158
  }
161
159
 
162
160
  console.log(
163
- "🔗 Please open the following URL in your browser to authorize access: ",
161
+ "🔗 Please open the following URL in your browser to authorize access:",
164
162
  chalk.cyan(connectUrl),
165
163
  "\n",
166
164
  );
@@ -220,16 +218,34 @@ export async function getOfficialAccessToken(baseUrl, openPage = true, locale =
220
218
  fetchInterval: FETCH_INTERVAL,
221
219
  appName: "AIGNE DocSmith",
222
220
  appLogo: "https://docsmith.aigne.io/image-bin/uploads/9645caf64b4232699982c4d940b03b90.svg",
223
- openPage: (pageUrl) => {
221
+ openPage: async (pageUrl) => {
224
222
  const url = new URL(pageUrl);
225
223
  if (locale) {
226
224
  url.searchParams.set("locale", locale);
227
225
  }
228
- open(url.toString());
226
+
227
+ let connectUrl = url.toString();
228
+ open(connectUrl);
229
+ try {
230
+ const officialBaseUrl = process.env.DOC_SMITH_BASE_URL || CLOUD_SERVICE_URL_PROD;
231
+ const mountPoint = await getComponentMountPoint(officialBaseUrl, PAYMENT_KIT_DID);
232
+ const response = await fetch(
233
+ withQuery(joinURL(officialBaseUrl, mountPoint, "/api/tool/short-connect-url"), {
234
+ url: connectUrl,
235
+ locale,
236
+ }),
237
+ );
238
+ const data = await response.json();
239
+ if (data.url) {
240
+ connectUrl = data.url;
241
+ }
242
+ } catch {
243
+ // Ignore error
244
+ }
229
245
 
230
246
  console.log(
231
- "🔗 Please open the following URL in your browser to authorize access: ",
232
- chalk.cyan(url.toString()),
247
+ "🔗 Please open the following URL in your browser to authorize access:",
248
+ chalk.cyan(connectUrl),
233
249
  "\n",
234
250
  );
235
251
  },
@@ -19,6 +19,8 @@ import { getContentHash } from "./utils.mjs";
19
19
 
20
20
  const codeBlockRegex = /```d2.*\n([\s\S]*?)```/g;
21
21
 
22
+ export const DIAGRAM_PLACEHOLDER = "DIAGRAM_PLACEHOLDER";
23
+
22
24
  export async function getChart({ content, strict }) {
23
25
  const d2 = new D2();
24
26
  const iconUrlList = Object.keys(iconMap);
@@ -203,3 +205,14 @@ export function wrapCode({ content }) {
203
205
 
204
206
  return `\`\`\`d2\n${content}\n\`\`\``;
205
207
  }
208
+
209
+ export function replacePlaceholder({ content }) {
210
+ const [firstMatch] = Array.from(content.matchAll(codeBlockRegex));
211
+ if (firstMatch) {
212
+ const matchContent = firstMatch[0];
213
+ const cleanContent = content.replace(matchContent, DIAGRAM_PLACEHOLDER);
214
+ return [cleanContent, matchContent];
215
+ }
216
+
217
+ return [content, ""];
218
+ }
@@ -324,6 +324,40 @@ export async function loadDocumentStructure(outputDir) {
324
324
  }
325
325
  }
326
326
 
327
+ /**
328
+ * Build allowed links set from document structure
329
+ * Includes both original paths and processed .md paths for link validation
330
+ * @param {Array} documentStructure - Array of documentation structure items with path property
331
+ * @returns {Set<string>} Set of allowed link paths
332
+ */
333
+ export function buildAllowedLinksFromStructure(documentStructure) {
334
+ const allowedLinks = new Set();
335
+
336
+ if (!Array.isArray(documentStructure)) {
337
+ return allowedLinks;
338
+ }
339
+
340
+ documentStructure.forEach((item) => {
341
+ if (!item?.path) {
342
+ return;
343
+ }
344
+
345
+ // Add original path
346
+ allowedLinks.add(item.path);
347
+
348
+ // Add processed .md path (same logic as processContent in utils.mjs)
349
+ let processedPath = item.path;
350
+ if (processedPath.startsWith(".")) {
351
+ processedPath = processedPath.replace(/^\./, "");
352
+ }
353
+ let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
354
+ flatPath = `./${flatPath}.md`;
355
+ allowedLinks.add(flatPath);
356
+ });
357
+
358
+ return allowedLinks;
359
+ }
360
+
327
361
  /**
328
362
  * Build a tree structure from a flat document structure array using parentId
329
363
  * @param {Array} documentStructure - Flat array of document structure items with path and parentId
@@ -358,3 +392,51 @@ export function buildDocumentTree(documentStructure) {
358
392
 
359
393
  return { rootNodes, nodeMap };
360
394
  }
395
+
396
+ /**
397
+ * Format document structure for printing
398
+ * @param {Array} structure - Document structure array
399
+ * @returns {Object} Object containing rootNodes and printNode function
400
+ */
401
+ function formatDocumentStructure(structure) {
402
+ const { rootNodes } = buildDocumentTree(structure);
403
+
404
+ function printNode(node, depth = 0) {
405
+ const INDENT_SPACES = " ";
406
+ const FOLDER_ICON = " 📁";
407
+ const FILE_ICON = " 📄";
408
+ const indent = INDENT_SPACES.repeat(depth);
409
+ const prefix = depth === 0 ? FOLDER_ICON : FILE_ICON;
410
+
411
+ console.log(`${indent}${prefix} ${node.title}`);
412
+
413
+ if (node.children && node.children.length > 0) {
414
+ node.children.forEach((child) => {
415
+ printNode(child, depth + 1);
416
+ });
417
+ }
418
+ }
419
+
420
+ return { rootNodes, printNode };
421
+ }
422
+
423
+ /**
424
+ * Print document structure in a user-friendly format
425
+ * @param {Array} structure - Document structure array
426
+ */
427
+ export function printDocumentStructure(structure) {
428
+ console.log(`\n ${"-".repeat(50)}`);
429
+ console.log(" Current Documentation Structure");
430
+ console.log(` ${"-".repeat(50)}`);
431
+
432
+ const { rootNodes, printNode } = formatDocumentStructure(structure);
433
+
434
+ if (rootNodes.length === 0) {
435
+ console.log(" No documentation structure found.");
436
+ } else {
437
+ rootNodes.forEach((node) => {
438
+ printNode(node);
439
+ });
440
+ }
441
+ console.log();
442
+ }