@aigne/doc-smith 0.8.14 → 0.8.15-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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.15-beta.1](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.15-beta...v0.8.15-beta.1) (2025-10-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * support openapi datasource, generate better api docs ([#212](https://github.com/AIGNE-io/aigne-doc-smith/issues/212)) ([6a683f1](https://github.com/AIGNE-io/aigne-doc-smith/commit/6a683f117aa94f265383be6e1b3b957803004032))
9
+
10
+ ## [0.8.15-beta](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.14...v0.8.15-beta) (2025-10-21)
11
+
12
+
13
+ ### Features
14
+
15
+ * update document publish prompt for better user experience ([#208](https://github.com/AIGNE-io/aigne-doc-smith/issues/208)) ([0726c23](https://github.com/AIGNE-io/aigne-doc-smith/commit/0726c23ed2d47b2126c0896d71922860b7436895))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * enforce stricter rules for x-field component and markdown ([#210](https://github.com/AIGNE-io/aigne-doc-smith/issues/210)) ([d69ee53](https://github.com/AIGNE-io/aigne-doc-smith/commit/d69ee53dbd113859a822d3391cc6cbf2f02bb75e))
21
+
3
22
  ## [0.8.14](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.14-beta.2...v0.8.14) (2025-10-19)
4
23
 
5
24
  ## [0.8.14-beta.2](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.8.14-beta.1...v0.8.14-beta.2) (2025-10-19)
@@ -2,6 +2,7 @@ import { access, readFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { TeamAgent } from "@aigne/core";
5
+
5
6
  import checkDetailResult from "../utils/check-detail-result.mjs";
6
7
 
7
8
  // Get current script directory
@@ -102,6 +103,19 @@ export default async function checkDocument(
102
103
  options.context.agents["saveSingleDoc"],
103
104
  ],
104
105
  });
106
+ let openAPISpec = null;
107
+
108
+ if (options.context?.userContext?.openAPISpec?.sourceId) {
109
+ const matchingDocument = originalDocumentStructure.find((item) => {
110
+ if (item.path === path) {
111
+ return item.sourceIds.find((x) => x === options.context.userContext.openAPISpec.sourceId);
112
+ }
113
+ return false;
114
+ });
115
+ if (matchingDocument) {
116
+ openAPISpec = options.context.userContext.openAPISpec;
117
+ }
118
+ }
105
119
 
106
120
  const result = await options.context.invoke(teamAgent, {
107
121
  ...rest,
@@ -112,6 +126,7 @@ export default async function checkDocument(
112
126
  originalDocumentStructure,
113
127
  documentStructure,
114
128
  detailFeedback: contentValidationFailed ? validationResult.detailFeedback : "",
129
+ openAPISpec,
115
130
  });
116
131
 
117
132
  return {
@@ -36,7 +36,7 @@ skills:
36
36
  - ../update/save-and-translate-document.mjs
37
37
  - url: ../utils/action-success.mjs
38
38
  default_input:
39
- action: "✅ Documents updated successfully"
39
+ action: "✅ Documents updated successfully!\n💡 Looks good? Run `aigne doc publish` to go live."
40
40
  input_schema:
41
41
  type: object
42
42
  properties:
@@ -50,7 +50,12 @@ export default async function chooseDocs(
50
50
  });
51
51
 
52
52
  // Use title if available, otherwise fall back to filename
53
- const displayName = docItem?.title || file;
53
+ let displayName = docItem?.title;
54
+ if (displayName) {
55
+ displayName = `${displayName} (${file})`;
56
+ } else {
57
+ displayName = file;
58
+ }
54
59
 
55
60
  return {
56
61
  name: displayName,
@@ -1,4 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { statSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import imageSize from "image-size";
4
5
  import {
@@ -7,6 +8,7 @@ import {
7
8
  loadFilesFromPaths,
8
9
  readFileContents,
9
10
  getMimeType,
11
+ checkIsRemoteFile,
10
12
  } from "../../utils/file-utils.mjs";
11
13
  import {
12
14
  getCurrentGitHead,
@@ -18,28 +20,63 @@ import {
18
20
  DEFAULT_EXCLUDE_PATTERNS,
19
21
  DEFAULT_INCLUDE_PATTERNS,
20
22
  } from "../../utils/constants/index.mjs";
21
-
22
- export default async function loadSources({
23
- sources = [],
24
- sourcesPath = [],
25
- includePatterns,
26
- excludePatterns,
27
- outputDir,
28
- docsDir,
29
- "doc-path": docPath,
30
- boardId,
31
- useDefaultPatterns = true,
32
- lastGitHead,
33
- reset = false,
34
- media,
35
- } = {}) {
23
+ import { isOpenAPISpecFile } from "../../utils/openapi/index.mjs";
24
+
25
+ export default async function loadSources(
26
+ {
27
+ sources = [],
28
+ sourcesPath = [],
29
+ includePatterns,
30
+ excludePatterns,
31
+ outputDir,
32
+ docsDir,
33
+ "doc-path": docPath,
34
+ boardId,
35
+ useDefaultPatterns = true,
36
+ lastGitHead,
37
+ reset = false,
38
+ media,
39
+ } = {},
40
+ options,
41
+ ) {
36
42
  let files = Array.isArray(sources) ? [...sources] : [];
37
43
  const { minImageWidth } = media || { minImageWidth: 800 };
38
44
 
39
45
  if (sourcesPath) {
40
- const allFiles = await loadFilesFromPaths(sourcesPath, {
46
+ const sourcesPathList = Array.isArray(sourcesPath) ? sourcesPath : [sourcesPath];
47
+ const pickSourcesPath = [];
48
+ const omitSourcesPath = [];
49
+ sourcesPathList.forEach((x) => {
50
+ if (typeof x !== "string" || !x) {
51
+ return;
52
+ }
53
+ if (x.startsWith("!")) {
54
+ omitSourcesPath.push(x.substring(1));
55
+ } else {
56
+ pickSourcesPath.push(x);
57
+ }
58
+ });
59
+
60
+ const customExcludePatterns = omitSourcesPath
61
+ .map((x) => {
62
+ try {
63
+ const stats = statSync(x);
64
+ if (stats.isFile()) {
65
+ return x;
66
+ }
67
+ if (stats.isDirectory()) {
68
+ return `${x}/**`;
69
+ }
70
+ return null;
71
+ } catch (error) {
72
+ console.warn(`Failed to stat path ${x}: ${error.message}`);
73
+ return null;
74
+ }
75
+ })
76
+ .filter(Boolean);
77
+ const allFiles = await loadFilesFromPaths(pickSourcesPath, {
41
78
  includePatterns,
42
- excludePatterns,
79
+ excludePatterns: [...new Set([...(excludePatterns || []), ...customExcludePatterns])],
43
80
  useDefaultPatterns,
44
81
  defaultIncludePatterns: DEFAULT_INCLUDE_PATTERNS,
45
82
  defaultExcludePatterns: DEFAULT_EXCLUDE_PATTERNS,
@@ -50,9 +87,6 @@ export default async function loadSources({
50
87
 
51
88
  files = [...new Set(files)];
52
89
 
53
- // all files path
54
- const allFilesPaths = files.map((file) => `- ${toRelativePath(file)}`).join("\n");
55
-
56
90
  // Define media file extensions
57
91
  const mediaExtensions = [
58
92
  ".jpg",
@@ -110,7 +144,7 @@ export default async function loadSources({
110
144
  files.map(async (file) => {
111
145
  const ext = path.extname(file).toLowerCase();
112
146
 
113
- if (mediaExtensions.includes(ext)) {
147
+ if (mediaExtensions.includes(ext) && !checkIsRemoteFile(file)) {
114
148
  // This is a media file
115
149
  const relativePath = path.relative(docsDir, file);
116
150
  const fileName = path.basename(file);
@@ -161,7 +195,7 @@ export default async function loadSources({
161
195
  }
162
196
 
163
197
  // Read all source files using the utility function
164
- const sourceFiles = await readFileContents(sourceFilesPaths, process.cwd());
198
+ let sourceFiles = await readFileContents(sourceFilesPaths, process.cwd());
165
199
 
166
200
  // Count tokens and lines using utility function
167
201
  const { totalTokens, totalLines } = calculateFileStats(sourceFiles);
@@ -169,8 +203,32 @@ export default async function loadSources({
169
203
  // check if totalTokens is too large
170
204
  const isLargeContext = totalTokens > INTELLIGENT_SUGGESTION_TOKEN_THRESHOLD;
171
205
 
206
+ // filter OpenAPI doc should after check isLargeContext
207
+ sourceFiles = sourceFiles.filter((file) => {
208
+ if (options?.context?.userContext.openAPISpec) return true;
209
+
210
+ const isOpenAPI = isOpenAPISpecFile(file.content);
211
+ if (isOpenAPI && options?.context?.userContext) {
212
+ options.context.userContext.openAPISpec = file;
213
+ }
214
+ return !isOpenAPI;
215
+ });
216
+
217
+ const httpFileList = [];
218
+
219
+ sourceFiles.forEach((file) => {
220
+ if (checkIsRemoteFile(file.sourceId)) {
221
+ httpFileList.push(file);
222
+ }
223
+ });
224
+ if (options?.context?.userContext) {
225
+ options.context.userContext.httpFileList = httpFileList;
226
+ }
227
+
172
228
  // Build allSources string using utility function
173
229
  const allSources = buildSourcesContent(sourceFiles, isLargeContext);
230
+ // all files path
231
+ const allFilesPaths = sourceFiles.map((x) => `- ${toRelativePath(x.sourceId)}`).join("\n");
174
232
 
175
233
  // Get the last documentation structure
176
234
  let originalDocumentStructure;
@@ -1,11 +1,30 @@
1
1
  import fs from "node:fs";
2
2
  import { normalizePath, toRelativePath } from "../../utils/utils.mjs";
3
+ import { checkIsRemoteFile } from "../../utils/file-utils.mjs";
3
4
 
4
- export default function transformDetailDatasources({ sourceIds }) {
5
+ export default function transformDetailDatasources({ sourceIds }, options = {}) {
5
6
  // Read file content for each sourceId, ignoring failures
7
+ let openAPISpec;
8
+ const httpFileList = options?.context?.userContext?.httpFileList || [];
6
9
  const contents = (sourceIds || [])
10
+ .filter((id) => {
11
+ const openApiSourceId = options?.context?.userContext?.openAPISpec?.sourceId;
12
+ if (openApiSourceId !== undefined && openApiSourceId === id) {
13
+ openAPISpec = options.context.userContext.openAPISpec;
14
+ return false;
15
+ }
16
+ return true;
17
+ })
7
18
  .map((id) => {
8
19
  try {
20
+ if (checkIsRemoteFile(id)) {
21
+ const findFile = httpFileList.find((f) => f.sourceId === id);
22
+ if (findFile) {
23
+ return `// sourceId: ${id}\n${findFile.content}\n`;
24
+ }
25
+ return null;
26
+ }
27
+
9
28
  const normalizedId = normalizePath(id);
10
29
  const content = fs.readFileSync(normalizedId, "utf8");
11
30
  const relativeId = toRelativePath(id);
@@ -17,7 +36,10 @@ export default function transformDetailDatasources({ sourceIds }) {
17
36
  })
18
37
  .filter(Boolean);
19
38
 
20
- return { detailDataSources: contents.join("") };
39
+ return {
40
+ detailDataSources: contents.join(""),
41
+ openAPISpec,
42
+ };
21
43
  }
22
44
 
23
45
  transformDetailDatasources.task_render_mode = "hide";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.8.14",
3
+ "version": "0.8.15-beta.1",
4
4
  "description": "AI-driven documentation generation tool built on the AIGNE Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"