@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.
- package/CHANGELOG.md +20 -0
- package/agents/create/document-structure-tools/delete-document.mjs +32 -9
- package/agents/create/update-document-structure.yaml +1 -1
- package/agents/create/user-add-document/add-documents-to-structure.mjs +96 -0
- package/agents/create/user-add-document/find-documents-to-add-links.yaml +47 -0
- package/agents/create/user-add-document/index.yaml +46 -0
- package/agents/create/user-add-document/prepare-documents-to-translate.mjs +22 -0
- package/agents/create/user-add-document/print-add-document-summary.mjs +56 -0
- package/agents/create/user-add-document/review-documents-with-new-links.mjs +73 -0
- package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +67 -0
- package/agents/create/user-remove-document/index.yaml +41 -0
- package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +22 -0
- package/agents/create/user-remove-document/print-remove-document-summary.mjs +56 -0
- package/agents/create/user-remove-document/remove-documents-from-structure.mjs +96 -0
- package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +116 -0
- package/agents/create/user-review-document-structure.mjs +1 -40
- package/agents/create/utils/init-current-content.mjs +38 -0
- package/agents/init/check.mjs +1 -1
- package/agents/update/check-generate-diagram.mjs +4 -3
- package/agents/update/document-tools/update-document-content.mjs +12 -12
- package/agents/update/update-document-detail.yaml +5 -1
- package/agents/update/update-single/update-single-document-detail.mjs +155 -0
- package/agents/update/user-review-document.mjs +11 -14
- package/agents/utils/add-translates-to-structure.mjs +29 -0
- package/agents/utils/{analyze-feedback-intent.yaml → analyze-document-feedback-intent.yaml} +5 -2
- package/agents/utils/analyze-structure-feedback-intent.yaml +29 -0
- package/agents/utils/check-detail-result.mjs +2 -14
- package/aigne.yaml +11 -1
- package/package.json +1 -1
- package/prompts/detail/diagram/user-prompt.md +17 -0
- package/prompts/detail/generate/system-prompt.md +0 -1
- package/prompts/detail/update/system-prompt.md +40 -32
- package/prompts/structure/find-documents-to-add-links.md +52 -0
- package/prompts/utils/analyze-document-feedback-intent.md +54 -0
- package/prompts/utils/analyze-structure-feedback-intent.md +43 -0
- package/types/document-schema.mjs +2 -0
- package/types/document-structure-schema.mjs +6 -2
- package/utils/auth-utils.mjs +32 -16
- package/utils/d2-utils.mjs +13 -0
- package/utils/docs-finder-utils.mjs +82 -0
- package/utils/markdown-checker.mjs +50 -5
- package/utils/utils.mjs +103 -0
- package/prompts/utils/analyze-feedback-intent.md +0 -55
|
@@ -59,6 +59,9 @@ function countTableColumns(line) {
|
|
|
59
59
|
return columns.length;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const linkPattern = /(?<!!)\[([^\]]+)\]\(([^)]+)\)/g;
|
|
63
|
+
const hrefPattern = /<x-card[^>]*\s+data-href\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+))/gi;
|
|
64
|
+
|
|
62
65
|
/**
|
|
63
66
|
* Check for dead links in markdown content
|
|
64
67
|
* @param {string} markdown - The markdown content
|
|
@@ -67,16 +70,33 @@ function countTableColumns(line) {
|
|
|
67
70
|
* @param {Array} errorMessages - Array to push error messages to
|
|
68
71
|
*/
|
|
69
72
|
function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
|
|
70
|
-
const
|
|
71
|
-
let match;
|
|
73
|
+
const links = [];
|
|
72
74
|
|
|
75
|
+
// Collect Markdown format links: [text](link)
|
|
76
|
+
const linkRegex = new RegExp(linkPattern.source, linkPattern.flags);
|
|
77
|
+
let match;
|
|
73
78
|
while (true) {
|
|
74
79
|
match = linkRegex.exec(markdown);
|
|
75
80
|
if (match === null) break;
|
|
81
|
+
links.push({ trimLink: match[2].trim(), display: `[${match[1]}](${match[2].trim()})` });
|
|
82
|
+
}
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
// Collect data-href attribute values from <x-card> elements
|
|
85
|
+
const hrefRegex = new RegExp(hrefPattern.source, hrefPattern.flags);
|
|
86
|
+
let attrMatch;
|
|
87
|
+
while (true) {
|
|
88
|
+
attrMatch = hrefRegex.exec(markdown);
|
|
89
|
+
if (attrMatch === null) break;
|
|
90
|
+
const attrValue = (attrMatch[1] || attrMatch[2] || attrMatch[3] || "").trim();
|
|
91
|
+
if (attrValue) {
|
|
92
|
+
// Preserve original format with quotes if present
|
|
93
|
+
const originalMatch = attrMatch[0];
|
|
94
|
+
links.push({ trimLink: attrValue, display: originalMatch });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
79
97
|
|
|
98
|
+
// Process all collected links with the same logic
|
|
99
|
+
for (const { trimLink, display } of links) {
|
|
80
100
|
// Only check links that processContent would process
|
|
81
101
|
// Exclude external links and mailto
|
|
82
102
|
if (/^(https?:\/\/|mailto:)/.test(trimLink)) continue;
|
|
@@ -90,12 +110,37 @@ function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
|
|
|
90
110
|
// Check if this link is in the allowed links set
|
|
91
111
|
if (!allowedLinks.has(path)) {
|
|
92
112
|
errorMessages.push(
|
|
93
|
-
`Found a dead link in ${source}:
|
|
113
|
+
`Found a dead link in ${source}: ${display}, ensure the link exists in the documentation structure path`,
|
|
94
114
|
);
|
|
95
115
|
}
|
|
96
116
|
}
|
|
97
117
|
}
|
|
98
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Extract link from error message
|
|
121
|
+
* @param {string} error - The error message
|
|
122
|
+
* @returns {string} - The link
|
|
123
|
+
*/
|
|
124
|
+
export function getLinkFromError(error) {
|
|
125
|
+
if (!error || !error.includes("Found a dead link in")) {
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const linkRegex = new RegExp(linkPattern.source, linkPattern.flags);
|
|
130
|
+
let match = linkRegex.exec(error);
|
|
131
|
+
if (match) {
|
|
132
|
+
return match[2].trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const hrefRegex = new RegExp(hrefPattern.source, hrefPattern.flags);
|
|
136
|
+
match = hrefRegex.exec(error);
|
|
137
|
+
if (match) {
|
|
138
|
+
return (match[1] || match[2] || match[3] || "").trim();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
|
|
99
144
|
/**
|
|
100
145
|
* Check code block content for indentation consistency issues
|
|
101
146
|
* @param {Array} codeBlockContent - Array of {line, lineNumber} objects from the code block
|
package/utils/utils.mjs
CHANGED
|
@@ -1225,3 +1225,106 @@ export function getContentHash(str, { trim = true } = {}) {
|
|
|
1225
1225
|
const input = trim && typeof str === "string" ? str.trim() : str;
|
|
1226
1226
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
1227
1227
|
}
|
|
1228
|
+
|
|
1229
|
+
function toPath(path) {
|
|
1230
|
+
if (Array.isArray(path)) return path;
|
|
1231
|
+
|
|
1232
|
+
const result = [];
|
|
1233
|
+
path.replace(/[^.[\]]+|\[(\d+|(["'])(.*?)\2)\]/g, (match, bracketContent, quote, quotedKey) => {
|
|
1234
|
+
if (quote) {
|
|
1235
|
+
// ["key"] or ['key']
|
|
1236
|
+
result.push(quotedKey);
|
|
1237
|
+
} else if (bracketContent !== undefined) {
|
|
1238
|
+
// [123]
|
|
1239
|
+
result.push(bracketContent);
|
|
1240
|
+
} else {
|
|
1241
|
+
// dot notation
|
|
1242
|
+
result.push(match);
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
return result;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/**
|
|
1249
|
+
* Deeply get the value at a given path from an object, or return a default value if missing.
|
|
1250
|
+
* @param {object} obj - The object to query.
|
|
1251
|
+
* @param {string|Array<string|number>} path - The path to get, as a string or array.
|
|
1252
|
+
* @param {*} defaultValue - The value returned if the resolved value is undefined.
|
|
1253
|
+
* @returns {*} The value at the path or defaultValue.
|
|
1254
|
+
*/
|
|
1255
|
+
export function dget(obj, path, defaultValue) {
|
|
1256
|
+
const parts = toPath(path);
|
|
1257
|
+
|
|
1258
|
+
let current = obj;
|
|
1259
|
+
for (const key of parts) {
|
|
1260
|
+
if (current == null || !(key in current)) return defaultValue;
|
|
1261
|
+
current = current[key];
|
|
1262
|
+
}
|
|
1263
|
+
return current;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Deeply set the value at a given path in an object.
|
|
1268
|
+
* @param {object} obj - The object to modify.
|
|
1269
|
+
* @param {string|Array<string|number>} path - The path to set, as a string or array.
|
|
1270
|
+
* @param {*} value - The value to set.
|
|
1271
|
+
* @returns {object} The modified object.
|
|
1272
|
+
*/
|
|
1273
|
+
export function dset(obj, path, value) {
|
|
1274
|
+
const parts = toPath(path);
|
|
1275
|
+
|
|
1276
|
+
let current = obj;
|
|
1277
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1278
|
+
const key = parts[i];
|
|
1279
|
+
|
|
1280
|
+
if (i === parts.length - 1) {
|
|
1281
|
+
current[key] = value;
|
|
1282
|
+
} else {
|
|
1283
|
+
if (current[key] == null || typeof current[key] !== "object") {
|
|
1284
|
+
current[key] = String(parts[i + 1]).match(/^\d+$/) ? [] : {};
|
|
1285
|
+
}
|
|
1286
|
+
current = current[key];
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return obj;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Create a context path manager that provides get/set/clear operations
|
|
1294
|
+
* @param {object} options - The options object containing user context
|
|
1295
|
+
* @param {string} path - The context path (e.g., 'currentPageDetails./about' or 'lastToolInputs./about')
|
|
1296
|
+
* @returns {object} An object with { get, set } methods and a contextPath method for sub-paths
|
|
1297
|
+
*/
|
|
1298
|
+
export function userContextAt(options, path) {
|
|
1299
|
+
const userContext = options?.context?.userContext || null;
|
|
1300
|
+
if (!userContext) {
|
|
1301
|
+
throw new Error("userContext is not available");
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
return {
|
|
1305
|
+
/**
|
|
1306
|
+
* Get a value from the context path
|
|
1307
|
+
* @param {string} [key] - Optional key for nested access (e.g., 'updateMeta' for lastToolInputs)
|
|
1308
|
+
* @returns {*} The value at the path, or undefined if not found
|
|
1309
|
+
*/
|
|
1310
|
+
get(key) {
|
|
1311
|
+
if (key !== undefined) {
|
|
1312
|
+
return dget(userContext, `${path}.${key}`);
|
|
1313
|
+
}
|
|
1314
|
+
return dget(userContext, path);
|
|
1315
|
+
},
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Set a value in the context path
|
|
1319
|
+
* @param {string|*} key - If key is provided, this is the key; otherwise this is the value
|
|
1320
|
+
* @param {*} [value] - The value to set (required if first param is a key)
|
|
1321
|
+
*/
|
|
1322
|
+
set(key, value) {
|
|
1323
|
+
if (value !== undefined) {
|
|
1324
|
+
dset(userContext, `${path}.${key}`, value);
|
|
1325
|
+
} else {
|
|
1326
|
+
dset(userContext, path, key);
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
<role>
|
|
2
|
-
You are a feedback intent analyzer. Your task is to determine whether data sources are needed to fulfill the user's feedback about content modifications.
|
|
3
|
-
</role>
|
|
4
|
-
|
|
5
|
-
<input>
|
|
6
|
-
- feedback: {{feedback}}
|
|
7
|
-
</input>
|
|
8
|
-
|
|
9
|
-
<analysis_rules>
|
|
10
|
-
**Determining Data Source Necessity:**
|
|
11
|
-
|
|
12
|
-
You need to analyze the user's feedback and categorize it into different intent types. Based on the intent type, determine if data sources are required.
|
|
13
|
-
|
|
14
|
-
This analyzer is generic and can be used for any content modification scenarios (documentation structure, document content, translations, etc.).
|
|
15
|
-
|
|
16
|
-
**Intent Types:**
|
|
17
|
-
|
|
18
|
-
1. **add** - Adding new items, sections, or content
|
|
19
|
-
- Requires data sources: **YES**
|
|
20
|
-
- Reason: Need sufficient context from codebase or related materials to generate accurate new content
|
|
21
|
-
|
|
22
|
-
2. **edit** - Modifying existing content, descriptions, titles, or properties
|
|
23
|
-
- Requires data sources: **YES**
|
|
24
|
-
- Reason: Need context to ensure modifications are accurate and contextually appropriate
|
|
25
|
-
|
|
26
|
-
3. **delete** - Removing items, sections, or content
|
|
27
|
-
- Requires data sources: **NO**
|
|
28
|
-
- Reason: Deletion only needs to identify what to remove, no new content generation needed
|
|
29
|
-
|
|
30
|
-
4. **move** - Moving items to different positions or parent sections
|
|
31
|
-
- Requires data sources: **NO**
|
|
32
|
-
- Reason: Only changing item location in the structure, no content changes needed
|
|
33
|
-
|
|
34
|
-
5. **reorder** - Changing the order of items at the same level
|
|
35
|
-
- Requires data sources: **NO**
|
|
36
|
-
- Reason: Only rearranging sequence, no content generation needed
|
|
37
|
-
|
|
38
|
-
6. **mixed** - Combination of multiple intent types
|
|
39
|
-
- Requires data sources: **Depends on whether add/edit operations are included**
|
|
40
|
-
- Reason: If the feedback includes any add or edit operations, data sources are needed
|
|
41
|
-
|
|
42
|
-
**Decision Logic:**
|
|
43
|
-
- If the feedback contains ANY add or edit operations → `needDataSources = true`
|
|
44
|
-
- If the feedback ONLY contains delete, move, or reorder operations → `needDataSources = false`
|
|
45
|
-
- When in doubt, default to `needDataSources = true` to ensure sufficient context
|
|
46
|
-
</analysis_rules>
|
|
47
|
-
|
|
48
|
-
<output_rules>
|
|
49
|
-
Return a JSON object with:
|
|
50
|
-
- `needDataSources`: boolean indicating if data sources are required
|
|
51
|
-
- `intentType`: the primary intent type (add, edit, delete, move, reorder, or mixed)
|
|
52
|
-
- `reason`: clear explanation of why data sources are or aren't needed
|
|
53
|
-
|
|
54
|
-
Analyze the feedback carefully and be conservative - when uncertain, prefer `needDataSources: true` to ensure sufficient context is available.
|
|
55
|
-
</output_rules>
|