@aigne/doc-smith 0.9.3-beta.1 → 0.9.3
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 +10 -0
- package/agents/create/document-structure-tools/add-document.mjs +1 -5
- package/agents/create/document-structure-tools/update-document.mjs +0 -11
- package/agents/create/index.yaml +1 -0
- package/agents/publish/index.yaml +1 -0
- package/agents/update/index.yaml +1 -0
- package/agents/utils/ensure-document-icons.mjs +129 -0
- package/aigne.yaml +0 -1
- package/package.json +1 -1
- package/prompts/common/document-structure/document-structure-rules.md +0 -6
- package/agents/utils/generate-document-icon-if-needed.mjs +0 -93
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.3](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.3-beta.2...v0.9.3) (2025-11-13)
|
|
4
|
+
|
|
5
|
+
## [0.9.3-beta.2](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.3-beta.1...v0.9.3-beta.2) (2025-11-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* allow manual triggering of the publish docs process ([#306](https://github.com/AIGNE-io/aigne-doc-smith/issues/306)) ([9a76a49](https://github.com/AIGNE-io/aigne-doc-smith/commit/9a76a491c1511397b32b60db62142896637dfed2))
|
|
11
|
+
* move ensure document icons to sub-agent ([#305](https://github.com/AIGNE-io/aigne-doc-smith/issues/305)) ([f139070](https://github.com/AIGNE-io/aigne-doc-smith/commit/f1390706cda9348de094c9f5e592a4c34a1b1389))
|
|
12
|
+
|
|
3
13
|
## [0.9.3-beta.1](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.9.3-beta...v0.9.3-beta.1) (2025-11-13)
|
|
4
14
|
|
|
5
15
|
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
validateAddDocumentInput,
|
|
5
5
|
} from "../../../types/document-structure-schema.mjs";
|
|
6
6
|
import streamlineDocumentTitlesIfNeeded from "../../utils/streamline-document-titles-if-needed.mjs";
|
|
7
|
-
import generateDocumentIconIfNeeded from "../../utils/generate-document-icon-if-needed.mjs";
|
|
8
7
|
|
|
9
8
|
export default async function addDocument(input, options) {
|
|
10
9
|
// Validate input using Zod schema
|
|
@@ -61,12 +60,9 @@ export default async function addDocument(input, options) {
|
|
|
61
60
|
// Streamline document titles if needed (will streamline the new document if title > 18 characters)
|
|
62
61
|
await streamlineDocumentTitlesIfNeeded({ documentStructure: [newDocument] }, options);
|
|
63
62
|
|
|
64
|
-
// Add the document to the structure
|
|
63
|
+
// Add the document to the structure
|
|
65
64
|
const updatedStructure = [...documentStructure, newDocument];
|
|
66
65
|
|
|
67
|
-
// Generate icon for root-level documents if needed
|
|
68
|
-
await generateDocumentIconIfNeeded({ documentStructure: updatedStructure }, options);
|
|
69
|
-
|
|
70
66
|
const successMessage = `addDocument executed successfully.
|
|
71
67
|
Successfully added document '${title}' with path '${path}'.
|
|
72
68
|
Check if the latest version of documentStructure meets user feedback, if so, just return 'success'.`;
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
validateUpdateDocumentInput,
|
|
5
5
|
} from "../../../types/document-structure-schema.mjs";
|
|
6
6
|
import streamlineDocumentTitlesIfNeeded from "../../utils/streamline-document-titles-if-needed.mjs";
|
|
7
|
-
import generateDocumentIconIfNeeded from "../../utils/generate-document-icon-if-needed.mjs";
|
|
8
7
|
|
|
9
8
|
export default async function updateDocument(input, options) {
|
|
10
9
|
// Validate input using Zod schema
|
|
@@ -56,16 +55,6 @@ export default async function updateDocument(input, options) {
|
|
|
56
55
|
const updatedStructure = [...documentStructure];
|
|
57
56
|
updatedStructure[documentIndex] = updatedDocument;
|
|
58
57
|
|
|
59
|
-
// Generate/update icon for root-level documents if needed
|
|
60
|
-
// Pass original document for comparison to detect title/description changes
|
|
61
|
-
await generateDocumentIconIfNeeded(
|
|
62
|
-
{
|
|
63
|
-
documentStructure: updatedStructure,
|
|
64
|
-
originalItems: [originalDocument],
|
|
65
|
-
},
|
|
66
|
-
options,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
58
|
const updates = [];
|
|
70
59
|
if (title !== undefined) updates.push(`title to '${title}'`);
|
|
71
60
|
if (description !== undefined) updates.push("description");
|
package/agents/create/index.yaml
CHANGED
package/agents/update/index.yaml
CHANGED
|
@@ -31,6 +31,7 @@ skills:
|
|
|
31
31
|
default_input:
|
|
32
32
|
requiredFeedback: false
|
|
33
33
|
- ../utils/format-document-structure.mjs
|
|
34
|
+
- ../utils/ensure-document-icons.mjs
|
|
34
35
|
- ../media/load-media-description.mjs
|
|
35
36
|
- ../update/check-update-is-single.mjs
|
|
36
37
|
- ../update/save-and-translate-document.mjs
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { loadConfigFromFile, processConfigFields } from "../../utils/utils.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ensure all root-level document entries have icons
|
|
7
|
+
* Batch processing: Collects all entries without icons and generates them in a single call
|
|
8
|
+
* Conditionally saves the updated structure if icons were generated
|
|
9
|
+
* This is a reusable agent that can be used in structure planning and before publishing
|
|
10
|
+
*/
|
|
11
|
+
export default async function ensureDocumentIcons(inputOrParams, options) {
|
|
12
|
+
// Handle multiple calling patterns:
|
|
13
|
+
// 1. As function skill: (input, options) where input.documentStructure exists
|
|
14
|
+
// 2. As function skill: (input, options) where input.originalDocumentStructure exists
|
|
15
|
+
// 3. As standalone function: ({ documentStructure }, options)
|
|
16
|
+
const documentStructure =
|
|
17
|
+
inputOrParams?.documentStructure ||
|
|
18
|
+
inputOrParams?.originalDocumentStructure ||
|
|
19
|
+
(Array.isArray(inputOrParams) ? inputOrParams : null);
|
|
20
|
+
|
|
21
|
+
if (!documentStructure || !Array.isArray(documentStructure)) {
|
|
22
|
+
// Return input unchanged if no documentStructure to process
|
|
23
|
+
return inputOrParams || {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Batch collect all root-level items that need icon generation
|
|
27
|
+
// Only process root-level items (parentId is null, undefined, or empty string)
|
|
28
|
+
// Only generate icons for items that don't have one
|
|
29
|
+
const itemsNeedingIcon = documentStructure.filter((item) => {
|
|
30
|
+
// Only process root-level items
|
|
31
|
+
const isRootLevel = !item.parentId || item.parentId === "null" || item.parentId === "";
|
|
32
|
+
if (!isRootLevel) return false;
|
|
33
|
+
|
|
34
|
+
// Must have title and description for icon generation
|
|
35
|
+
if (!item.title || !item.description) return false;
|
|
36
|
+
|
|
37
|
+
// Only generate if icon is missing
|
|
38
|
+
const hasNoIcon = !item.icon || !item.icon.trim();
|
|
39
|
+
return hasNoIcon;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// If all items already have icons, skip
|
|
43
|
+
if (itemsNeedingIcon.length === 0) {
|
|
44
|
+
return inputOrParams || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prepare batch list for icon generation
|
|
48
|
+
const documentList = itemsNeedingIcon.map((item) => ({
|
|
49
|
+
path: item.path,
|
|
50
|
+
title: item.title,
|
|
51
|
+
description: item.description,
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
const iconAgent = options?.context?.agents?.["documentIconGenerate"];
|
|
55
|
+
if (!iconAgent) {
|
|
56
|
+
console.warn("⚠️ documentIconGenerate agent not found. Skipping icon generation.");
|
|
57
|
+
return inputOrParams || {};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let iconsGenerated = false;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Batch generate all missing icons in a single call
|
|
64
|
+
const iconResult = await options.context.invoke(iconAgent, {
|
|
65
|
+
documentList,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Batch update all document items with generated icons using path as the key
|
|
69
|
+
if (iconResult.documentList && Array.isArray(iconResult.documentList)) {
|
|
70
|
+
const iconMap = new Map(iconResult.documentList.map((item) => [item.path, item.icon]));
|
|
71
|
+
|
|
72
|
+
for (const item of documentStructure) {
|
|
73
|
+
const generatedIcon = iconMap.get(item.path);
|
|
74
|
+
if (generatedIcon) {
|
|
75
|
+
item.icon = generatedIcon;
|
|
76
|
+
iconsGenerated = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Conditionally save the updated structure if icons were generated
|
|
82
|
+
// Use the same save pattern as save-output.mjs for consistency
|
|
83
|
+
if (iconsGenerated) {
|
|
84
|
+
// Try to get outputDir from multiple sources (same as create flow)
|
|
85
|
+
let outputDir =
|
|
86
|
+
inputOrParams?.outputDir ||
|
|
87
|
+
options?.context?.userContext?.outputDir ||
|
|
88
|
+
options?.context?.config?.outputDir;
|
|
89
|
+
|
|
90
|
+
// If still not found, load from config file and process defaults
|
|
91
|
+
if (!outputDir) {
|
|
92
|
+
try {
|
|
93
|
+
const config = await loadConfigFromFile();
|
|
94
|
+
const processedConfig = await processConfigFields(config || {});
|
|
95
|
+
outputDir = processedConfig?.outputDir;
|
|
96
|
+
} catch {
|
|
97
|
+
// Ignore config load errors
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (outputDir) {
|
|
102
|
+
try {
|
|
103
|
+
// Use the same save pattern as save-output.mjs
|
|
104
|
+
const savePath = outputDir;
|
|
105
|
+
const fileName = "structure-plan.json";
|
|
106
|
+
const content = JSON.stringify(documentStructure, null, 2);
|
|
107
|
+
|
|
108
|
+
await fs.mkdir(savePath, { recursive: true });
|
|
109
|
+
const filePath = join(savePath, fileName);
|
|
110
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
111
|
+
} catch (saveError) {
|
|
112
|
+
console.warn("⚠️ Failed to save updated structure:", saveError.message);
|
|
113
|
+
// Continue even if save fails
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.warn("⚠️ Failed to generate document icons:", error.message);
|
|
119
|
+
console.warn("Continuing without icons.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Return input unchanged (documentStructure is modified in place)
|
|
123
|
+
return inputOrParams || {};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ensureDocumentIcons.taskTitle = "Ensure document icons";
|
|
127
|
+
ensureDocumentIcons.description =
|
|
128
|
+
"Check each root-level document entry for icons - if missing, generate one; if present, skip";
|
|
129
|
+
ensureDocumentIcons.task_render_mode = "hide";
|
package/aigne.yaml
CHANGED
|
@@ -83,7 +83,6 @@ agents:
|
|
|
83
83
|
- ./agents/utils/document-title-streamline.yaml
|
|
84
84
|
- ./agents/utils/streamline-document-titles-if-needed.mjs
|
|
85
85
|
- ./agents/utils/document-icon-generate.yaml
|
|
86
|
-
- ./agents/utils/generate-document-icon-if-needed.mjs
|
|
87
86
|
|
|
88
87
|
# User Preferences & Chat
|
|
89
88
|
- ./agents/prefs/index.mjs
|
package/package.json
CHANGED
|
@@ -28,12 +28,6 @@ Structural planning rules:
|
|
|
28
28
|
|
|
29
29
|
2. Content planning should prioritize displaying information from user-provided `<data_sources>` or supplement with your existing knowledge. Do not arbitrarily fabricate information.
|
|
30
30
|
|
|
31
|
-
3. **Document Icon Generation:**
|
|
32
|
-
- For root-level nodes (where `parentPath` is null or empty), add an `icon` attribute
|
|
33
|
-
- Format: `lucide:icon-name` (only Lucide icons supported)
|
|
34
|
-
- Choose icons that semantically match the document's purpose based on title and description
|
|
35
|
-
- Common icons: `lucide:book` (documentation), `lucide:rocket` (quick start), `lucide:code` (API), `lucide:settings` (configuration), `lucide:graduation-cap` (tutorials), `lucide:folder-open` (overview), `lucide:users` (user guides), `lucide:shield` (security), `lucide:cloud` (deployment)
|
|
36
|
-
|
|
37
31
|
{% ifAsync docsType == 'general' %}
|
|
38
32
|
{% include "../../structure/document-rules.md" %}
|
|
39
33
|
{% endif %}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate icons for root-level document structure items if they don't have one or if title/description changed
|
|
3
|
-
* Reusable function for generating document icons in various contexts
|
|
4
|
-
* Can be called as a standalone function with { documentStructure, originalItems? } or as a function skill with (input, options)
|
|
5
|
-
* @param {Object|Array} inputOrParams - Either input from previous step (with documentStructure) or params object with documentStructure and optional originalItems
|
|
6
|
-
* @param {Object} options - Agent options with context
|
|
7
|
-
* @param {Array<{path: string, title?: string, description?: string}>} [inputOrParams.originalItems] - Original items for comparison (optional, used to detect title/description changes)
|
|
8
|
-
* @returns {Promise<Object>} - Returns input unchanged (modifies documentStructure in place)
|
|
9
|
-
*/
|
|
10
|
-
export default async function generateDocumentIconIfNeeded(inputOrParams, options) {
|
|
11
|
-
// Handle both calling patterns:
|
|
12
|
-
// 1. As function skill: (input, options) where input.documentStructure exists
|
|
13
|
-
// 2. As standalone function: ({ documentStructure, originalItems? }, options)
|
|
14
|
-
const documentStructure =
|
|
15
|
-
inputOrParams?.documentStructure || (Array.isArray(inputOrParams) ? inputOrParams : null);
|
|
16
|
-
const originalItems = inputOrParams?.originalItems || [];
|
|
17
|
-
|
|
18
|
-
if (!documentStructure || !Array.isArray(documentStructure)) {
|
|
19
|
-
// Return input unchanged if no documentStructure to process
|
|
20
|
-
return inputOrParams || {};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Create a map of original items by path for quick lookup
|
|
24
|
-
const originalItemsMap = new Map(
|
|
25
|
-
originalItems.map((item) => [item.path, { title: item.title, description: item.description }]),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
// Filter root-level items that need icon generation or update
|
|
29
|
-
const itemsNeedingIcon = documentStructure.filter((item) => {
|
|
30
|
-
// Only process root-level items (parentId is null, undefined, or empty string)
|
|
31
|
-
const isRootLevel = !item.parentId || item.parentId === "null" || item.parentId === "";
|
|
32
|
-
if (!isRootLevel) return false;
|
|
33
|
-
|
|
34
|
-
// Must have title and description for icon generation
|
|
35
|
-
if (!item.title || !item.description) return false;
|
|
36
|
-
|
|
37
|
-
// Check if icon is missing
|
|
38
|
-
const hasNoIcon = !item.icon;
|
|
39
|
-
|
|
40
|
-
// Check if title or description changed (if original item exists)
|
|
41
|
-
const originalItem = originalItemsMap.get(item.path);
|
|
42
|
-
const titleChanged = originalItem && originalItem.title !== item.title;
|
|
43
|
-
const descriptionChanged = originalItem && originalItem.description !== item.description;
|
|
44
|
-
const contentChanged = titleChanged || descriptionChanged;
|
|
45
|
-
|
|
46
|
-
// Generate/update icon if: missing icon OR content changed
|
|
47
|
-
return hasNoIcon || contentChanged;
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
if (itemsNeedingIcon.length === 0) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const documentList = itemsNeedingIcon.map((item) => ({
|
|
55
|
-
path: item.path,
|
|
56
|
-
title: item.title,
|
|
57
|
-
description: item.description,
|
|
58
|
-
}));
|
|
59
|
-
|
|
60
|
-
const iconAgent = options?.context?.agents?.["documentIconGenerate"];
|
|
61
|
-
if (!iconAgent) {
|
|
62
|
-
console.warn("⚠️ documentIconGenerate agent not found. Skipping icon generation.");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const iconResult = await options.context.invoke(iconAgent, {
|
|
68
|
-
documentList,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Update the document items with generated icons using path as the key
|
|
72
|
-
if (iconResult.documentList && Array.isArray(iconResult.documentList)) {
|
|
73
|
-
const iconMap = new Map(iconResult.documentList.map((item) => [item.path, item]));
|
|
74
|
-
|
|
75
|
-
for (const item of documentStructure) {
|
|
76
|
-
const iconData = iconMap.get(item.path);
|
|
77
|
-
if (iconData?.icon) {
|
|
78
|
-
item.icon = iconData.icon;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.warn("⚠️ Failed to generate document icons:", error.message);
|
|
84
|
-
console.warn("Continuing without icons.");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Return input unchanged (documentStructure is modified in place)
|
|
88
|
-
return inputOrParams || {};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
generateDocumentIconIfNeeded.taskTitle = "Generate document icons if needed";
|
|
92
|
-
generateDocumentIconIfNeeded.description =
|
|
93
|
-
"Generate appropriate Lucide icons for root-level document structure items based on their title and description, or update icon if title/description changed";
|