@aigne/doc-smith 0.9.8-alpha.3 → 0.9.8-alpha.5

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 (257) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +94 -250
  3. package/aigne.yaml +5 -146
  4. package/doc-smith/SKILL.md +117 -0
  5. package/doc-smith/references/changeset_schema.md +118 -0
  6. package/doc-smith/references/document_structure_schema.md +139 -0
  7. package/doc-smith/references/document_update_guide.md +193 -0
  8. package/doc-smith/references/structure_confirmation_guide.md +133 -0
  9. package/doc-smith/references/structure_planning_guide.md +146 -0
  10. package/doc-smith/references/user_intent_guide.md +172 -0
  11. package/doc-smith.yaml +114 -0
  12. package/main-system-prompt.md +56 -0
  13. package/package.json +3 -69
  14. package/scripts/README.md +90 -0
  15. package/scripts/install.sh +86 -0
  16. package/scripts/uninstall.sh +52 -0
  17. package/CHANGELOG.md +0 -994
  18. package/LICENSE +0 -93
  19. package/agentic-agents/common/base-info.md +0 -53
  20. package/agentic-agents/common/completer.md +0 -54
  21. package/agentic-agents/common/planner.md +0 -168
  22. package/agentic-agents/common/worker.md +0 -93
  23. package/agentic-agents/create/index.yaml +0 -129
  24. package/agentic-agents/create/objective.md +0 -44
  25. package/agentic-agents/create/set-custom-prompt.mjs +0 -27
  26. package/agentic-agents/detail/index.yaml +0 -95
  27. package/agentic-agents/detail/objective.md +0 -9
  28. package/agentic-agents/detail/set-custom-prompt.mjs +0 -88
  29. package/agentic-agents/predict-resources/index.yaml +0 -44
  30. package/agentic-agents/predict-resources/instructions.md +0 -61
  31. package/agentic-agents/structure/design-rules.md +0 -39
  32. package/agentic-agents/structure/index.yaml +0 -86
  33. package/agentic-agents/structure/objective.md +0 -14
  34. package/agentic-agents/structure/review-criteria.md +0 -55
  35. package/agentic-agents/structure/set-custom-prompt.mjs +0 -78
  36. package/agentic-agents/utils/init-workspace-cache.mjs +0 -171
  37. package/agentic-agents/utils/load-base-sources.mjs +0 -20
  38. package/agentic-agents/workspace-cache-sharing-design.md +0 -671
  39. package/agents/chat/chat-system.md +0 -38
  40. package/agents/chat/index.mjs +0 -59
  41. package/agents/chat/skills/generate-document.yaml +0 -15
  42. package/agents/chat/skills/list-documents.mjs +0 -15
  43. package/agents/chat/skills/update-document.yaml +0 -24
  44. package/agents/clear/choose-contents.mjs +0 -192
  45. package/agents/clear/clear-auth-tokens.mjs +0 -88
  46. package/agents/clear/clear-deployment-config.mjs +0 -49
  47. package/agents/clear/clear-document-config.mjs +0 -36
  48. package/agents/clear/clear-document-structure.mjs +0 -102
  49. package/agents/clear/clear-generated-docs.mjs +0 -142
  50. package/agents/clear/clear-media-description.mjs +0 -129
  51. package/agents/clear/index.yaml +0 -26
  52. package/agents/create/analyze-diagram-type-llm.yaml +0 -160
  53. package/agents/create/analyze-diagram-type.mjs +0 -297
  54. package/agents/create/check-document-structure.yaml +0 -30
  55. package/agents/create/check-need-generate-structure.mjs +0 -105
  56. package/agents/create/document-structure-tools/add-document.mjs +0 -85
  57. package/agents/create/document-structure-tools/delete-document.mjs +0 -116
  58. package/agents/create/document-structure-tools/move-document.mjs +0 -109
  59. package/agents/create/document-structure-tools/update-document.mjs +0 -84
  60. package/agents/create/generate-diagram-image.yaml +0 -60
  61. package/agents/create/generate-structure.yaml +0 -117
  62. package/agents/create/index.yaml +0 -49
  63. package/agents/create/refine-document-structure.yaml +0 -12
  64. package/agents/create/replace-d2-with-image.mjs +0 -625
  65. package/agents/create/update-document-structure.yaml +0 -54
  66. package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
  67. package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
  68. package/agents/create/user-add-document/index.yaml +0 -46
  69. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
  70. package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
  71. package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
  72. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
  73. package/agents/create/user-remove-document/index.yaml +0 -40
  74. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
  75. package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
  76. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
  77. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
  78. package/agents/create/user-review-document-structure.mjs +0 -140
  79. package/agents/create/utils/init-current-content.mjs +0 -34
  80. package/agents/create/utils/merge-document-structures.mjs +0 -30
  81. package/agents/evaluate/code-snippet.mjs +0 -97
  82. package/agents/evaluate/document-structure.yaml +0 -67
  83. package/agents/evaluate/document.yaml +0 -82
  84. package/agents/evaluate/generate-report.mjs +0 -85
  85. package/agents/evaluate/index.yaml +0 -46
  86. package/agents/history/index.yaml +0 -6
  87. package/agents/history/view.mjs +0 -78
  88. package/agents/init/check.mjs +0 -16
  89. package/agents/init/index.mjs +0 -275
  90. package/agents/init/validate.mjs +0 -16
  91. package/agents/localize/choose-language.mjs +0 -107
  92. package/agents/localize/index.yaml +0 -58
  93. package/agents/localize/record-translation-history.mjs +0 -23
  94. package/agents/localize/translate-document.yaml +0 -24
  95. package/agents/localize/translate-multilingual.yaml +0 -51
  96. package/agents/media/batch-generate-media-description.yaml +0 -46
  97. package/agents/media/generate-media-description.yaml +0 -50
  98. package/agents/media/load-media-description.mjs +0 -256
  99. package/agents/prefs/index.mjs +0 -203
  100. package/agents/publish/index.yaml +0 -26
  101. package/agents/publish/publish-docs.mjs +0 -356
  102. package/agents/publish/translate-meta.mjs +0 -103
  103. package/agents/schema/document-structure-item.yaml +0 -26
  104. package/agents/schema/document-structure-refine-item.yaml +0 -23
  105. package/agents/schema/document-structure.yaml +0 -29
  106. package/agents/update/batch-generate-document.yaml +0 -27
  107. package/agents/update/batch-update-document.yaml +0 -7
  108. package/agents/update/check-diagram-flag.mjs +0 -116
  109. package/agents/update/check-document.mjs +0 -162
  110. package/agents/update/check-generate-diagram.mjs +0 -106
  111. package/agents/update/check-sync-image-flag.mjs +0 -55
  112. package/agents/update/check-update-is-single.mjs +0 -53
  113. package/agents/update/document-tools/update-document-content.mjs +0 -303
  114. package/agents/update/generate-diagram.yaml +0 -63
  115. package/agents/update/generate-document.yaml +0 -70
  116. package/agents/update/handle-document-update.yaml +0 -103
  117. package/agents/update/index.yaml +0 -79
  118. package/agents/update/pre-check-generate-diagram.yaml +0 -44
  119. package/agents/update/save-and-translate-document.mjs +0 -76
  120. package/agents/update/sync-images-and-exit.mjs +0 -148
  121. package/agents/update/update-document-detail.yaml +0 -71
  122. package/agents/update/update-single/update-single-document-detail.mjs +0 -280
  123. package/agents/update/update-single-document.yaml +0 -7
  124. package/agents/update/user-review-document.mjs +0 -272
  125. package/agents/utils/action-success.mjs +0 -16
  126. package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
  127. package/agents/utils/analyze-feedback-intent.mjs +0 -136
  128. package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
  129. package/agents/utils/check-detail-result.mjs +0 -38
  130. package/agents/utils/check-feedback-refiner.mjs +0 -81
  131. package/agents/utils/choose-docs.mjs +0 -293
  132. package/agents/utils/document-icon-generate.yaml +0 -52
  133. package/agents/utils/document-title-streamline.yaml +0 -48
  134. package/agents/utils/ensure-document-icons.mjs +0 -129
  135. package/agents/utils/exit.mjs +0 -6
  136. package/agents/utils/feedback-refiner.yaml +0 -50
  137. package/agents/utils/find-item-by-path.mjs +0 -114
  138. package/agents/utils/find-user-preferences-by-path.mjs +0 -37
  139. package/agents/utils/format-document-structure.mjs +0 -35
  140. package/agents/utils/generate-document-or-skip.mjs +0 -41
  141. package/agents/utils/handle-diagram-operations.mjs +0 -263
  142. package/agents/utils/load-all-document-content.mjs +0 -30
  143. package/agents/utils/load-document-all-content.mjs +0 -84
  144. package/agents/utils/load-sources.mjs +0 -405
  145. package/agents/utils/map-reasoning-effort-level.mjs +0 -15
  146. package/agents/utils/post-generate.mjs +0 -144
  147. package/agents/utils/read-current-document-content.mjs +0 -46
  148. package/agents/utils/save-doc-translation.mjs +0 -61
  149. package/agents/utils/save-doc.mjs +0 -88
  150. package/agents/utils/save-output.mjs +0 -26
  151. package/agents/utils/save-sidebar.mjs +0 -51
  152. package/agents/utils/skip-if-content-exists.mjs +0 -27
  153. package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
  154. package/agents/utils/transform-detail-data-sources.mjs +0 -45
  155. package/agents/utils/update-branding.mjs +0 -84
  156. package/assets/report-template/report.html +0 -198
  157. package/docs-mcp/analyze-content-relevance.yaml +0 -50
  158. package/docs-mcp/analyze-docs-relevance.yaml +0 -59
  159. package/docs-mcp/docs-search.yaml +0 -42
  160. package/docs-mcp/get-docs-detail.mjs +0 -41
  161. package/docs-mcp/get-docs-structure.mjs +0 -16
  162. package/docs-mcp/read-doc-content.mjs +0 -119
  163. package/prompts/common/document/content-rules-core.md +0 -20
  164. package/prompts/common/document/markdown-syntax-rules.md +0 -65
  165. package/prompts/common/document/media-file-list-usage-rules.md +0 -18
  166. package/prompts/common/document/openapi-usage-rules.md +0 -189
  167. package/prompts/common/document/role-and-personality.md +0 -16
  168. package/prompts/common/document/user-preferences.md +0 -9
  169. package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
  170. package/prompts/common/document-structure/document-icon-generate.md +0 -116
  171. package/prompts/common/document-structure/document-structure-rules.md +0 -43
  172. package/prompts/common/document-structure/document-title-streamline.md +0 -86
  173. package/prompts/common/document-structure/glossary.md +0 -7
  174. package/prompts/common/document-structure/intj-traits.md +0 -5
  175. package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
  176. package/prompts/common/document-structure/output-constraints.md +0 -18
  177. package/prompts/common/document-structure/user-locale-rules.md +0 -10
  178. package/prompts/common/document-structure/user-preferences.md +0 -9
  179. package/prompts/detail/custom/admonition-usage-rules.md +0 -94
  180. package/prompts/detail/custom/code-block-usage-rules.md +0 -163
  181. package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
  182. package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
  183. package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
  184. package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
  185. package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
  186. package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
  187. package/prompts/detail/diagram/generate-image-system.md +0 -135
  188. package/prompts/detail/diagram/generate-image-user.md +0 -32
  189. package/prompts/detail/diagram/guide.md +0 -29
  190. package/prompts/detail/diagram/official-examples.md +0 -712
  191. package/prompts/detail/diagram/pre-check.md +0 -23
  192. package/prompts/detail/diagram/role-and-personality.md +0 -2
  193. package/prompts/detail/diagram/rules.md +0 -46
  194. package/prompts/detail/diagram/system-prompt.md +0 -1139
  195. package/prompts/detail/diagram/user-prompt.md +0 -43
  196. package/prompts/detail/generate/detail-example.md +0 -457
  197. package/prompts/detail/generate/document-rules.md +0 -45
  198. package/prompts/detail/generate/system-prompt.md +0 -61
  199. package/prompts/detail/generate/user-prompt.md +0 -99
  200. package/prompts/detail/jsx/rules.md +0 -6
  201. package/prompts/detail/update/system-prompt.md +0 -121
  202. package/prompts/detail/update/user-prompt.md +0 -41
  203. package/prompts/evaluate/document-structure.md +0 -93
  204. package/prompts/evaluate/document.md +0 -149
  205. package/prompts/media/media-description/system-prompt.md +0 -43
  206. package/prompts/media/media-description/user-prompt.md +0 -17
  207. package/prompts/structure/check-document-structure.md +0 -93
  208. package/prompts/structure/document-rules.md +0 -21
  209. package/prompts/structure/find-documents-to-add-links.md +0 -52
  210. package/prompts/structure/generate/system-prompt.md +0 -13
  211. package/prompts/structure/generate/user-prompt.md +0 -137
  212. package/prompts/structure/review/structure-review-system.md +0 -81
  213. package/prompts/structure/structure-example.md +0 -89
  214. package/prompts/structure/structure-getting-started.md +0 -10
  215. package/prompts/structure/update/system-prompt.md +0 -93
  216. package/prompts/structure/update/user-prompt.md +0 -43
  217. package/prompts/translate/admonition.md +0 -20
  218. package/prompts/translate/code-block.md +0 -33
  219. package/prompts/translate/glossary.md +0 -6
  220. package/prompts/translate/translate-document.md +0 -305
  221. package/prompts/utils/analyze-document-feedback-intent.md +0 -54
  222. package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
  223. package/prompts/utils/feedback-refiner.md +0 -105
  224. package/types/document-schema.mjs +0 -55
  225. package/types/document-structure-schema.mjs +0 -261
  226. package/utils/auth-utils.mjs +0 -275
  227. package/utils/blocklet.mjs +0 -104
  228. package/utils/check-document-has-diagram.mjs +0 -95
  229. package/utils/conflict-detector.mjs +0 -149
  230. package/utils/constants/index.mjs +0 -620
  231. package/utils/constants/linter.mjs +0 -102
  232. package/utils/d2-utils.mjs +0 -198
  233. package/utils/debug.mjs +0 -3
  234. package/utils/delete-diagram-images.mjs +0 -99
  235. package/utils/deploy.mjs +0 -86
  236. package/utils/docs-finder-utils.mjs +0 -623
  237. package/utils/evaluate/report-utils.mjs +0 -132
  238. package/utils/extract-api.mjs +0 -32
  239. package/utils/file-utils.mjs +0 -960
  240. package/utils/history-utils.mjs +0 -203
  241. package/utils/icon-map.mjs +0 -26
  242. package/utils/image-compress.mjs +0 -75
  243. package/utils/kroki-utils.mjs +0 -173
  244. package/utils/linter/index.mjs +0 -50
  245. package/utils/load-config.mjs +0 -107
  246. package/utils/markdown/index.mjs +0 -26
  247. package/utils/markdown-checker.mjs +0 -694
  248. package/utils/mermaid-validator.mjs +0 -140
  249. package/utils/mermaid-worker-pool.mjs +0 -250
  250. package/utils/mermaid-worker.mjs +0 -233
  251. package/utils/openapi/index.mjs +0 -28
  252. package/utils/preferences-utils.mjs +0 -175
  253. package/utils/request.mjs +0 -10
  254. package/utils/store/index.mjs +0 -45
  255. package/utils/sync-diagram-to-translations.mjs +0 -262
  256. package/utils/upload-files.mjs +0 -231
  257. package/utils/utils.mjs +0 -1354
@@ -1,960 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import { randomBytes } from "node:crypto";
3
- import { access, readFile, stat } from "node:fs/promises";
4
- import path, { join } from "node:path";
5
- import { glob } from "glob";
6
- import fs from "fs-extra";
7
- import { isBinaryFile } from "isbinaryfile";
8
- import { encode } from "gpt-tokenizer";
9
- import { fileTypeFromBuffer } from "file-type";
10
- import { gunzipSync } from "node:zlib";
11
-
12
- import { debug } from "./debug.mjs";
13
- import { isGlobPattern } from "./utils.mjs";
14
- import { uploadFiles } from "./upload-files.mjs";
15
- import { extractApi } from "./extract-api.mjs";
16
- import { minimatch } from "minimatch";
17
-
18
- /**
19
- * Check if a directory is inside a git repository using git command
20
- * @param {string} dir - Directory path to check
21
- * @returns {boolean} True if inside a git repository
22
- */
23
- export function isInGitRepository(dir) {
24
- try {
25
- execSync("git rev-parse --is-inside-work-tree", {
26
- cwd: dir,
27
- stdio: "pipe",
28
- encoding: "utf8",
29
- });
30
- return true;
31
- } catch {
32
- return false;
33
- }
34
- }
35
-
36
- /**
37
- * Find git repository root directory using git command
38
- * @param {string} startDir - Starting directory path
39
- * @returns {string|null} Git repository root path or null if not found
40
- */
41
- function findGitRoot(startDir) {
42
- try {
43
- const gitRoot = execSync("git rev-parse --show-toplevel", {
44
- cwd: startDir,
45
- stdio: "pipe",
46
- encoding: "utf8",
47
- }).trim();
48
- return gitRoot;
49
- } catch {
50
- return null;
51
- }
52
- }
53
-
54
- /**
55
- * Convert gitignore patterns to glob-compatible patterns
56
- * @param {string} pattern - A single gitignore pattern
57
- * @returns {string[]} Array of glob patterns that match gitignore behavior
58
- */
59
- function gitignoreToGlobPatterns(pattern) {
60
- const patterns = [];
61
-
62
- // Remove leading slash (already handled by gitignore parsing)
63
- const cleanPattern = pattern.replace(/^\//, "");
64
-
65
- // If pattern doesn't contain wildcards and doesn't end with /
66
- // it could match both files and directories
67
- if (!cleanPattern.includes("*") && !cleanPattern.includes("?") && !cleanPattern.endsWith("/")) {
68
- // Add patterns to match both file and directory
69
- patterns.push(cleanPattern); // Exact match
70
- patterns.push(`${cleanPattern}/**`); // Directory contents
71
- patterns.push(`**/${cleanPattern}`); // Nested exact match
72
- patterns.push(`**/${cleanPattern}/**`); // Nested directory contents
73
- } else if (cleanPattern.endsWith("/")) {
74
- // Directory-only pattern
75
- const dirPattern = cleanPattern.slice(0, -1);
76
- patterns.push(`${dirPattern}/**`);
77
- patterns.push(`**/${dirPattern}/**`);
78
- } else {
79
- // Pattern with wildcards or specific file
80
- patterns.push(cleanPattern);
81
- if (!cleanPattern.startsWith("**/")) {
82
- patterns.push(`**/${cleanPattern}`);
83
- }
84
- }
85
-
86
- return patterns;
87
- }
88
-
89
- /**
90
- * Parse .gitignore content into patterns
91
- * @param {string} content - .gitignore file content
92
- * @returns {string[]} Array of ignore patterns converted to glob format
93
- */
94
- function parseGitignoreContent(content) {
95
- const lines = content
96
- .split("\n")
97
- .map((line) => line.trim())
98
- .filter((line) => line && !line.startsWith("#"))
99
- .map((line) => line.replace(/^\//, "")); // Remove leading slash
100
-
101
- // Convert each gitignore pattern to glob patterns
102
- const allPatterns = [];
103
- for (const line of lines) {
104
- allPatterns.push(...gitignoreToGlobPatterns(line));
105
- }
106
-
107
- return [...new Set(allPatterns)]; // Remove duplicates
108
- }
109
-
110
- /**
111
- * Load .gitignore patterns from multiple directories (current + all parent directories up to git root)
112
- * @param {string} dir - Directory path (will search up to find all .gitignore files)
113
- * @returns {string[]|null} Array of merged ignore patterns or null if no .gitignore found
114
- */
115
- export async function loadGitignore(dir) {
116
- // First, check if we're in a git repository
117
- const inGitRepo = isInGitRepository(dir);
118
- if (!inGitRepo) {
119
- // Not in a git repository, just check the current directory
120
- const gitignorePath = path.join(dir, ".gitignore");
121
- try {
122
- await access(gitignorePath);
123
- const gitignoreContent = await readFile(gitignorePath, "utf8");
124
- const ignorePatterns = parseGitignoreContent(gitignoreContent);
125
- return ignorePatterns.length > 0 ? ignorePatterns : null;
126
- } catch {
127
- return null;
128
- }
129
- }
130
-
131
- // We're in a git repository, collect all .gitignore files from current dir to git root
132
- const gitRoot = findGitRoot(dir);
133
- if (!gitRoot) {
134
- return null;
135
- }
136
-
137
- const allPatterns = [];
138
- let currentDir = path.resolve(dir);
139
-
140
- // Collect .gitignore patterns from current directory up to git root
141
- while (currentDir.startsWith(gitRoot)) {
142
- const gitignorePath = path.join(currentDir, ".gitignore");
143
- try {
144
- await access(gitignorePath);
145
- const gitignoreContent = await readFile(gitignorePath, "utf8");
146
- const patterns = parseGitignoreContent(gitignoreContent);
147
-
148
- // Add patterns with context of which directory they came from
149
- // Patterns from deeper directories take precedence
150
- allPatterns.unshift(...patterns);
151
- } catch {
152
- // .gitignore doesn't exist in this directory, continue
153
- }
154
-
155
- // Move up one directory
156
- if (currentDir === gitRoot) {
157
- break;
158
- }
159
- currentDir = path.dirname(currentDir);
160
- }
161
-
162
- return allPatterns.length > 0 ? [...new Set(allPatterns)] : null;
163
- }
164
-
165
- /**
166
- * Get files using glob patterns
167
- * @param {string} dir - Directory to search
168
- * @param {string[]} includePatterns - Include patterns
169
- * @param {string[]} excludePatterns - Exclude patterns
170
- * @param {string[]} gitignorePatterns - .gitignore patterns
171
- * @returns {Promise<string[]>} Array of file paths
172
- */
173
- export async function getFilesWithGlob(dir, includePatterns, excludePatterns, gitignorePatterns) {
174
- if (!includePatterns || includePatterns.length === 0) {
175
- console.warn("No include patterns provided");
176
- return [];
177
- }
178
-
179
- // Prepare all ignore patterns
180
- const allIgnorePatterns = [];
181
-
182
- if (excludePatterns) {
183
- allIgnorePatterns.push(...excludePatterns);
184
- }
185
-
186
- if (gitignorePatterns) {
187
- allIgnorePatterns.push(...gitignorePatterns);
188
- }
189
-
190
- // Add default exclusions if not already present
191
- const defaultExclusions = ["node_modules/**", "temp/**"];
192
- for (const exclusion of defaultExclusions) {
193
- if (!allIgnorePatterns.includes(exclusion)) {
194
- allIgnorePatterns.push(exclusion);
195
- }
196
- }
197
-
198
- // Convert patterns to be relative to the directory
199
- const patterns = includePatterns.map((pattern) => {
200
- // If pattern doesn't start with / or **, make it relative to dir
201
- if (!pattern.startsWith("/") && !pattern.startsWith("**")) {
202
- return `**/${pattern}`; // Use ** to search recursively
203
- }
204
- return pattern;
205
- });
206
-
207
- try {
208
- const files = await glob(patterns, {
209
- cwd: dir,
210
- ignore: allIgnorePatterns.length > 0 ? allIgnorePatterns : undefined,
211
- absolute: true,
212
- nodir: true, // Only return files, not directories
213
- dot: false, // Don't include dot files by default
214
- gitignore: true, // Enable .gitignore support
215
- });
216
-
217
- return files;
218
- } catch (error) {
219
- console.warn(`Warning: Error during glob search in ${dir}: ${error.message}`);
220
- return [];
221
- }
222
- }
223
-
224
- /**
225
- * Check if a path exists
226
- * @param {string} targetPath - Path to check
227
- * @returns {Promise<boolean>} True if path exists
228
- */
229
- export async function pathExists(targetPath) {
230
- try {
231
- await stat(targetPath);
232
- return true;
233
- } catch (error) {
234
- if (error.code === "ENOENT") return false;
235
- throw error;
236
- }
237
- }
238
-
239
- /**
240
- * Convert absolute path to display path relative to current working directory
241
- * @param {string} targetPath - Absolute path to convert
242
- * @returns {string} Display path (relative or absolute)
243
- */
244
- export function toDisplayPath(targetPath) {
245
- const rel = path.relative(process.cwd(), targetPath);
246
- return rel.startsWith("..") ? targetPath : rel || ".";
247
- }
248
-
249
- /**
250
- * Resolve path to absolute path
251
- * @param {string} value - Path to resolve
252
- * @returns {string|undefined} Absolute path or undefined if no value provided
253
- */
254
- export function resolveToAbsolute(value) {
255
- if (!value) return undefined;
256
- return path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
257
- }
258
-
259
- /**
260
- * Load files from sourcesPath array
261
- * Supports file paths, directory paths, and glob patterns
262
- * @param {string|string[]} sourcesPath - Single path or array of paths
263
- * @param {object} options - Options for file loading
264
- * @param {string|string[]} options.includePatterns - Include patterns for directories
265
- * @param {string|string[]} options.excludePatterns - Exclude patterns for directories
266
- * @param {boolean} options.useDefaultPatterns - Whether to use default patterns (default: true)
267
- * @param {string[]} options.defaultIncludePatterns - Default include patterns
268
- * @param {string[]} options.defaultExcludePatterns - Default exclude patterns
269
- * @returns {Promise<string[]>} Array of absolute file paths
270
- */
271
- export async function loadFilesFromPaths(sourcesPath, options = {}) {
272
- const {
273
- includePatterns,
274
- excludePatterns,
275
- useDefaultPatterns = true,
276
- defaultIncludePatterns = [],
277
- defaultExcludePatterns = [],
278
- } = options;
279
-
280
- const paths = Array.isArray(sourcesPath) ? sourcesPath : [sourcesPath];
281
- let allFiles = [];
282
-
283
- for (const dir of paths) {
284
- try {
285
- if (typeof dir !== "string") {
286
- console.warn(`Invalid source path: ${dir}`);
287
- continue;
288
- }
289
-
290
- if (isRemoteFile(dir)) {
291
- allFiles.push(dir);
292
- continue;
293
- }
294
-
295
- // First try to access as a file or directory
296
- const stats = await stat(dir);
297
-
298
- if (stats.isFile()) {
299
- // If it's a file, add it directly without filtering
300
- allFiles.push(dir);
301
- } else if (stats.isDirectory()) {
302
- // If it's a directory, use the existing glob logic
303
- // Load .gitignore for this directory
304
- const gitignorePatterns = await loadGitignore(dir);
305
-
306
- // Prepare patterns
307
- let finalIncludePatterns = null;
308
- let finalExcludePatterns = null;
309
-
310
- if (useDefaultPatterns) {
311
- // Merge with default patterns
312
- const userInclude = includePatterns
313
- ? Array.isArray(includePatterns)
314
- ? includePatterns
315
- : [includePatterns]
316
- : [];
317
- const userExclude = excludePatterns
318
- ? Array.isArray(excludePatterns)
319
- ? excludePatterns
320
- : [excludePatterns]
321
- : [];
322
-
323
- finalIncludePatterns = [...defaultIncludePatterns, ...userInclude];
324
- finalExcludePatterns = [
325
- ...defaultExcludePatterns,
326
- ...userExclude.map((x) => {
327
- const prefix = `${dir}/`;
328
- return x.startsWith(prefix) ? x.slice(prefix.length) : x;
329
- }),
330
- ];
331
- } else {
332
- // Use only user patterns
333
- if (includePatterns) {
334
- finalIncludePatterns = Array.isArray(includePatterns)
335
- ? includePatterns
336
- : [includePatterns];
337
- }
338
- if (excludePatterns) {
339
- finalExcludePatterns = Array.isArray(excludePatterns)
340
- ? excludePatterns
341
- : [excludePatterns];
342
- }
343
- }
344
-
345
- // Get files using glob
346
- const filesInDir = await getFilesWithGlob(
347
- dir,
348
- finalIncludePatterns,
349
- finalExcludePatterns,
350
- gitignorePatterns,
351
- );
352
- allFiles = allFiles.concat(filesInDir);
353
- }
354
- } catch (err) {
355
- if (err.code === "ENOENT") {
356
- // Path doesn't exist as file or directory, try as glob pattern
357
- try {
358
- // Check if it looks like a glob pattern
359
- const isGlobPatternResult = isGlobPattern(dir);
360
-
361
- if (isGlobPatternResult) {
362
- // Use glob to find matching files from current working directory
363
- const matchedFiles = await glob(dir, {
364
- absolute: true,
365
- nodir: true, // Only files, not directories
366
- dot: false, // Don't include hidden files
367
- });
368
-
369
- if (matchedFiles.length > 0) {
370
- allFiles = allFiles.concat(matchedFiles);
371
- }
372
- }
373
- } catch (globErr) {
374
- console.warn(`Failed to process glob pattern "${dir}": ${globErr.message}`);
375
- }
376
- } else {
377
- throw err;
378
- }
379
- }
380
- }
381
-
382
- return allFiles;
383
- }
384
-
385
- /**
386
- * Check if a file is likely a text file by checking if it's binary
387
- * @param {string} filePath - File path to check
388
- * @returns {Promise<boolean>} True if file appears to be a text file
389
- */
390
- async function isTextFile(filePath) {
391
- if (isRemoteFile(filePath)) {
392
- return isRemoteTextFile(filePath);
393
- }
394
-
395
- try {
396
- const isBinary = await isBinaryFile(filePath);
397
- return !isBinary;
398
- } catch (_error) {
399
- // If we can't read the file, assume it might be binary to be safe
400
- return false;
401
- }
402
- }
403
-
404
- /**
405
- * Check if a string is an HTTP/HTTPS URL
406
- * @param {string} fileUrl - The string to check
407
- * @returns {boolean} - True if the string starts with http:// or https://
408
- */
409
- export function isRemoteFile(fileUrl) {
410
- if (typeof fileUrl !== "string") return false;
411
-
412
- try {
413
- const url = new URL(fileUrl);
414
- // Only accept http and https url
415
- if (["http:", "https:"].includes(url.protocol)) {
416
- return true;
417
- }
418
- // other protocol will be treated as bad url
419
- return false;
420
- } catch {
421
- return false;
422
- }
423
- }
424
-
425
- export async function isRemoteFileAvailable(fileUrl) {
426
- if (!isRemoteFile(fileUrl)) return false;
427
-
428
- try {
429
- const res = await fetch(fileUrl, {
430
- method: "HEAD",
431
- });
432
- return res.ok;
433
- } catch (error) {
434
- debug(`Failed to check HTTP file availability: ${fileUrl} - ${error.message}`);
435
- return false;
436
- }
437
- }
438
-
439
- export async function isRemoteTextFile(fileUrl) {
440
- try {
441
- const res = await fetch(fileUrl, {
442
- method: "HEAD",
443
- });
444
- const contentType = res.headers.get("content-type") || "";
445
- const textMimeTypes = [
446
- "application/json",
447
- "application/ld+json",
448
- "application/graphql+json",
449
- "application/xml",
450
- "application/xhtml+xml",
451
- "application/javascript",
452
- "application/ecmascript",
453
- "application/x-www-form-urlencoded",
454
- "application/rss+xml",
455
- "application/atom+xml",
456
- ];
457
- if (contentType.startsWith("text/") || textMimeTypes.includes(contentType)) {
458
- return true;
459
- }
460
- return false;
461
- } catch (error) {
462
- debug(`Failed to check HTTP file content type: ${fileUrl} - ${error.message}`);
463
- return null;
464
- }
465
- }
466
-
467
- export async function getRemoteFileContent(fileUrl) {
468
- if (!fileUrl) return null;
469
- try {
470
- const res = await fetch(fileUrl);
471
- const text = await res.text();
472
- return text;
473
- } catch (error) {
474
- debug(`Failed to fetch HTTP file content: ${fileUrl} - ${error.message}`);
475
- return null;
476
- }
477
- }
478
-
479
- /**
480
- * Read and parse file contents from an array of file paths
481
- * @param {string[]} files - Array of file paths to read
482
- * @param {string} baseDir - Base directory for calculating relative paths (defaults to cwd)
483
- * @param {object} options - Options for reading files
484
- * @param {boolean} options.skipBinaryFiles - Whether to skip binary files (default: true)
485
- * @returns {Promise<{sourceId: string, content: string}[]>} Array of file objects with sourceId and content
486
- */
487
- export async function readFileContents(files, baseDir = process.cwd(), options = {}) {
488
- const { skipBinaryFiles = true } = options;
489
-
490
- const results = await Promise.all(
491
- files.map(async (file) => {
492
- // Skip binary files if enabled
493
- if (skipBinaryFiles) {
494
- const isText = await isTextFile(file);
495
- if (!isText) {
496
- return null;
497
- }
498
- }
499
-
500
- try {
501
- if (isRemoteFile(file)) {
502
- const content = await getRemoteFileContent(file);
503
- if (content) {
504
- return {
505
- sourceId: file,
506
- content,
507
- };
508
- }
509
-
510
- return null;
511
- } else {
512
- const content = await extractApi(file);
513
- if (!content) return null;
514
-
515
- const relativePath = path.relative(baseDir, file);
516
- return {
517
- sourceId: relativePath,
518
- content,
519
- };
520
- }
521
- } catch (error) {
522
- // If reading as text fails (e.g., binary file), skip it
523
- console.warn(`Failed to read file as text: ${file} - ${error.message}`);
524
- return null;
525
- }
526
- }),
527
- );
528
-
529
- // Filter out null results
530
- return results.filter((result) => result !== null);
531
- }
532
-
533
- export function calculateTokens(text) {
534
- const tokens = encode(text);
535
- return tokens.length;
536
- }
537
-
538
- /**
539
- * Calculate total lines and tokens from file contents
540
- * @param {Array<{content: string}>} sourceFiles - Array of objects containing content property
541
- * @returns {{totalTokens: number, totalLines: number}} Object with totalTokens and totalLines
542
- */
543
- export function calculateFileStats(sourceFiles) {
544
- let totalTokens = 0;
545
- let totalLines = 0;
546
-
547
- for (const source of sourceFiles) {
548
- const { content } = source;
549
- if (content) {
550
- // Count tokens using gpt-tokenizer
551
- const tokens = encode(content);
552
- totalTokens += tokens.length;
553
-
554
- // Count lines (excluding empty lines)
555
- totalLines += content.split("\n").filter((line) => line.trim() !== "").length;
556
- }
557
- }
558
-
559
- return { totalTokens, totalLines };
560
- }
561
-
562
- /**
563
- * Build sources content string
564
- * @param {Array<{sourceId: string, content: string}>} sourceFiles - Array of source file objects
565
- * @returns {string} Concatenated sources content with sourceId comments
566
- */
567
- export function buildSourcesContent(sourceFiles) {
568
- // Build sources string
569
- let allSources = "";
570
-
571
- // Include all files for normal contexts
572
- for (const source of sourceFiles) {
573
- allSources += `\n// sourceId: ${source.sourceId}\n${source.content}\n`;
574
- }
575
-
576
- return allSources;
577
- }
578
-
579
- /**
580
- * Get doc-smith configuration file path
581
- * @param {string} workDir - Working directory (defaults to current directory)
582
- * @returns {string} Absolute path to config.yaml
583
- */
584
- export function getConfigFilePath(workDir) {
585
- const cwd = workDir || process.cwd();
586
- return path.join(cwd, ".aigne", "doc-smith", "config.yaml");
587
- }
588
-
589
- /**
590
- * Get doc-smith structure plan file path
591
- * @param {string} workDir - Working directory (defaults to current directory)
592
- * @returns {string} Absolute path to structure-plan.json
593
- */
594
- export function getStructurePlanPath(workDir) {
595
- const cwd = workDir || process.cwd();
596
- return path.join(cwd, ".aigne", "doc-smith", "output", "structure-plan.json");
597
- }
598
-
599
- // Shared extension → MIME type mapping table
600
- const EXT_TO_MIME = {
601
- ".jpg": "image/jpeg",
602
- ".jpeg": "image/jpeg",
603
- ".png": "image/png",
604
- ".gif": "image/gif",
605
- ".bmp": "image/bmp",
606
- ".webp": "image/webp",
607
- ".svg": "image/svg+xml",
608
- ".heic": "image/heic",
609
- ".heif": "image/heif",
610
- ".mp4": "video/mp4",
611
- ".mpeg": "video/mpeg",
612
- ".mpg": "video/mpg",
613
- ".mov": "video/mov",
614
- ".avi": "video/avi",
615
- ".flv": "video/x-flv",
616
- ".mkv": "video/x-matroska",
617
- ".webm": "video/webm",
618
- ".wmv": "video/wmv",
619
- ".m4v": "video/x-m4v",
620
- ".3gpp": "video/3gpp",
621
- ".mp3": "audio/mpeg",
622
- ".wav": "audio/wav",
623
- ".pdf": "application/pdf",
624
- ".doc": "application/msword",
625
- ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
626
- ".xls": "application/vnd.ms-excel",
627
- ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
628
- ".ppt": "application/vnd.ms-powerpoint",
629
- ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
630
- ".txt": "text/plain",
631
- ".json": "application/json",
632
- ".xml": "application/xml",
633
- ".html": "text/html",
634
- ".css": "text/css",
635
- ".js": "application/javascript",
636
- ".zip": "application/zip",
637
- ".rar": "application/x-rar-compressed",
638
- ".7z": "application/x-7z-compressed",
639
- };
640
-
641
- // Build reverse mapping: MIME → extensions
642
- const MIME_TO_EXTS = Object.entries(EXT_TO_MIME).reduce((acc, [ext, mime]) => {
643
- const key = mime.toLowerCase();
644
- if (!acc[key]) {
645
- acc[key] = [];
646
- }
647
- acc[key].push(ext);
648
- return acc;
649
- }, {});
650
-
651
- /**
652
- * Get MIME type from file path based on extension
653
- * @param {string} filePath - File path
654
- * @returns {string} MIME type
655
- */
656
- export function getMimeType(filePath) {
657
- const ext = path.extname(filePath || "").toLowerCase();
658
- return EXT_TO_MIME[ext] || "application/octet-stream";
659
- }
660
-
661
- /**
662
- * Get file extension (without dot) from content type
663
- * Handles content types with parameters (e.g., "image/jpeg; charset=utf-8")
664
- * @param {string} contentType - Content type string
665
- * @returns {string} File extension without dot
666
- */
667
- export function getExtnameFromContentType(contentType) {
668
- if (!contentType) return "";
669
- const base = String(contentType).split(";")[0].trim().toLowerCase();
670
- const exts = MIME_TO_EXTS[base];
671
- if (exts?.length) return exts[0].slice(1); // Remove leading dot
672
- const parts = base.split("/");
673
- return parts[1] || "";
674
- }
675
-
676
- /**
677
- * Get media description cache file path
678
- * @returns {string} Absolute path to media-description.yaml
679
- */
680
- export function getMediaDescriptionCachePath() {
681
- const cwd = process.cwd();
682
- return path.join(cwd, ".aigne", "doc-smith", "media-description.yaml");
683
- }
684
-
685
- /**
686
- * Detect file type from buffer with comprehensive fallback strategy
687
- * @param {Buffer} buffer - File buffer
688
- * @param {string} contentType - HTTP Content-Type header
689
- * @param {string} url - Original URL (for fallback)
690
- * @returns {Promise<{ext: string, mime: string}>} File extension and MIME type
691
- */
692
- export async function detectFileType(buffer, contentType, url = "") {
693
- // 1. Try file-type for binary images (PNG, JPG, WebP, GIF, etc.)
694
- try {
695
- const fileType = await fileTypeFromBuffer(buffer);
696
- if (fileType) {
697
- return {
698
- ext: fileType.ext,
699
- mime: fileType.mime,
700
- };
701
- }
702
- } catch (error) {
703
- console.debug("file-type detection failed:", error.message);
704
- }
705
-
706
- // 2. Check for SVG/SVGZ
707
- const svgResult = await detectSvgType(buffer, contentType);
708
- if (svgResult) {
709
- return svgResult;
710
- }
711
-
712
- // 3. Fallback to Content-Type
713
- if (contentType) {
714
- const ext = getExtnameFromContentType(contentType);
715
- if (ext) {
716
- return {
717
- ext,
718
- mime: contentType.split(";")[0].trim(),
719
- };
720
- }
721
- }
722
-
723
- // 4. Fallback to URL extension
724
- if (url) {
725
- const urlExt = path.extname(url).toLowerCase();
726
- if (urlExt) {
727
- return {
728
- ext: urlExt.slice(1), // Remove leading dot
729
- mime: getMimeType(url),
730
- };
731
- }
732
- }
733
-
734
- // 5. Default fallback
735
- return {
736
- ext: "bin",
737
- mime: "application/octet-stream",
738
- };
739
- }
740
-
741
- /**
742
- * Detect SVG/SVGZ file type
743
- * @param {Buffer} buffer - File buffer
744
- * @param {string} contentType - HTTP Content-Type header
745
- * @returns {Promise<{ext: string, mime: string} | null>} SVG info or null
746
- */
747
- async function detectSvgType(buffer, contentType) {
748
- // Check Content-Type first
749
- if (contentType?.includes("image/svg+xml")) {
750
- return {
751
- ext: "svg",
752
- mime: "image/svg+xml",
753
- };
754
- }
755
-
756
- try {
757
- let text = "";
758
-
759
- // Check if it's gzipped (SVGZ)
760
- if (buffer.length >= 2 && buffer[0] === 0x1f && buffer[1] === 0x8b) {
761
- try {
762
- const decompressed = gunzipSync(buffer);
763
- text = decompressed.toString("utf8");
764
- if (isSvgContent(text)) {
765
- return {
766
- ext: "svgz",
767
- mime: "image/svg+xml",
768
- };
769
- }
770
- } catch {
771
- // Not gzipped, continue with regular text detection
772
- }
773
- }
774
-
775
- // Try as regular text
776
- if (!text) {
777
- text = buffer.toString("utf8");
778
- }
779
-
780
- if (isSvgContent(text)) {
781
- return {
782
- ext: "svg",
783
- mime: "image/svg+xml",
784
- };
785
- }
786
- } catch (error) {
787
- console.debug("SVG detection failed:", error.message);
788
- }
789
-
790
- return null;
791
- }
792
-
793
- /**
794
- * Check if text content is SVG
795
- * @param {string} text - Text content
796
- * @returns {boolean} True if SVG
797
- */
798
- function isSvgContent(text) {
799
- if (!text || typeof text !== "string") return false;
800
-
801
- // Remove BOM and trim
802
- const cleanText = text.replace(/^\uFEFF/, "").trim();
803
-
804
- // Check for SVG root element
805
- return /^<\?xml\s+[^>]*>\s*<svg/i.test(cleanText) || /^<svg/i.test(cleanText);
806
- }
807
-
808
- /**
809
- * Download and upload a remote image URL
810
- * @param {string} imageUrl - The remote image URL
811
- * @param {string} docsDir - Directory to save temporary files
812
- * @param {string} appUrl - Application URL for upload
813
- * @param {string} accessToken - Access token for upload
814
- * @returns {Promise<{url: string, downloadFinalPath: string | null}>} The uploaded image URL and final path if failed
815
- */
816
- export async function downloadAndUploadImage(imageUrl, docsDir, appUrl, accessToken) {
817
- let downloadFinalPath = null;
818
-
819
- try {
820
- // 1. Download with timeout control
821
- const controller = new AbortController();
822
- const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
823
-
824
- const response = await fetch(imageUrl, {
825
- signal: controller.signal,
826
- });
827
- clearTimeout(timeoutId);
828
-
829
- if (!response.ok) {
830
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
831
- }
832
-
833
- // 2. Convert to Buffer for file type detection
834
- const blob = await response.blob();
835
- const arrayBuffer = await blob.arrayBuffer();
836
- const buffer = Buffer.from(arrayBuffer);
837
-
838
- // 3. Detect file type with comprehensive fallback
839
- const contentType = response.headers.get("content-type");
840
- const { ext } = await detectFileType(buffer, contentType, imageUrl);
841
-
842
- // 4. Generate random filename and save file
843
- const randomId = randomBytes(16).toString("hex");
844
- const tempFilePath = join(docsDir, `temp-logo-${randomId}`);
845
- downloadFinalPath = ext ? `${tempFilePath}.${ext}` : tempFilePath;
846
- fs.writeFileSync(downloadFinalPath, buffer);
847
-
848
- // 5. Upload and get URL
849
- const { results: uploadResults } = await uploadFiles({
850
- appUrl,
851
- filePaths: [downloadFinalPath],
852
- accessToken,
853
- concurrency: 1,
854
- });
855
-
856
- // 6. Return uploaded URL
857
- return { url: uploadResults?.[0]?.url || imageUrl, downloadFinalPath };
858
- } catch (error) {
859
- console.warn(`Failed to download and upload image from ${imageUrl}: ${error.message}`);
860
- return { url: imageUrl, downloadFinalPath: null };
861
- }
862
- }
863
-
864
- /**
865
- * Extract the path prefix from a glob pattern until the first glob character
866
- */
867
- export function getPathPrefix(pattern) {
868
- const segments = pattern.split("/");
869
- const result = [];
870
-
871
- for (const segment of segments) {
872
- if (isGlobPattern(segment)) {
873
- break;
874
- }
875
- result.push(segment);
876
- }
877
-
878
- return result.join("/") || ".";
879
- }
880
-
881
- /**
882
- * Check if a dir matches any exclude pattern
883
- */
884
- export function isDirExcluded(dir, excludePatterns) {
885
- if (!dir || typeof dir !== "string") {
886
- return false;
887
- }
888
-
889
- let normalizedDir = dir.replace(/\\/g, "/").replace(/^\.\/+/, "");
890
- normalizedDir = normalizedDir.endsWith("/") ? normalizedDir : `${normalizedDir}/`;
891
-
892
- for (const excludePattern of excludePatterns) {
893
- if (minimatch(normalizedDir, excludePattern, { dot: true })) {
894
- return true;
895
- }
896
- }
897
-
898
- return false;
899
- }
900
-
901
- /**
902
- * Return source paths that would be excluded by exclude patterns (files are skipped, directories use minimatch, glob patterns use path prefix heuristic)
903
- * @returns {{excluded: string[], notFound: string[]}} Object with excluded and notFound arrays
904
- */
905
- export async function findInvalidSourcePaths(sourcePaths, excludePatterns) {
906
- if (!Array.isArray(sourcePaths) || sourcePaths.length === 0) {
907
- return { excluded: [], notFound: [] };
908
- }
909
-
910
- if (!Array.isArray(excludePatterns) || excludePatterns.length === 0) {
911
- return { excluded: [], notFound: [] };
912
- }
913
-
914
- const excluded = [];
915
- const notFound = [];
916
-
917
- for (const sourcePath of sourcePaths) {
918
- if (typeof sourcePath !== "string" || !sourcePath) {
919
- continue;
920
- }
921
-
922
- // Skip paths starting with "!" (exclusion patterns)
923
- if (sourcePath.startsWith("!")) {
924
- continue;
925
- }
926
-
927
- // Skip remote URLs
928
- if (isRemoteFile(sourcePath)) {
929
- continue;
930
- }
931
-
932
- // Check glob pattern: use heuristic algorithm
933
- if (isGlobPattern(sourcePath)) {
934
- const representativePath = getPathPrefix(sourcePath);
935
- if (isDirExcluded(representativePath, excludePatterns)) {
936
- excluded.push(sourcePath);
937
- }
938
- continue;
939
- }
940
-
941
- try {
942
- const stats = await stat(sourcePath);
943
- // Skip file
944
- if (stats.isFile()) {
945
- continue;
946
- }
947
- // Check dir with minimatch
948
- if (stats.isDirectory()) {
949
- if (isDirExcluded(sourcePath, excludePatterns)) {
950
- excluded.push(sourcePath);
951
- }
952
- }
953
- } catch {
954
- // Path doesn't exist
955
- notFound.push(sourcePath);
956
- }
957
- }
958
-
959
- return { excluded, notFound };
960
- }