@aigne/doc-smith 0.9.7-beta.3 → 0.9.8-alpha.0
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 +22 -0
- package/agents/create/analyze-diagram-type-llm.yaml +160 -0
- package/agents/create/analyze-diagram-type.mjs +297 -0
- package/agents/create/check-need-generate-structure.mjs +1 -34
- package/agents/create/generate-diagram-image.yaml +60 -0
- package/agents/create/index.yaml +9 -5
- package/agents/create/replace-d2-with-image.mjs +625 -0
- package/agents/create/user-review-document-structure.mjs +8 -7
- package/agents/create/utils/init-current-content.mjs +5 -9
- package/agents/evaluate/document.yaml +6 -0
- package/agents/evaluate/index.yaml +1 -0
- package/agents/init/index.mjs +36 -388
- package/agents/localize/index.yaml +4 -4
- package/agents/media/batch-generate-media-description.yaml +2 -0
- package/agents/media/generate-media-description.yaml +3 -0
- package/agents/media/load-media-description.mjs +44 -15
- package/agents/publish/index.yaml +1 -0
- package/agents/publish/publish-docs.mjs +1 -4
- package/agents/update/check-diagram-flag.mjs +116 -0
- package/agents/update/check-document.mjs +0 -1
- package/agents/update/check-generate-diagram.mjs +48 -30
- package/agents/update/check-sync-image-flag.mjs +55 -0
- package/agents/update/check-update-is-single.mjs +11 -0
- package/agents/update/generate-diagram.yaml +43 -9
- package/agents/update/generate-document.yaml +9 -0
- package/agents/update/handle-document-update.yaml +10 -8
- package/agents/update/index.yaml +25 -7
- package/agents/update/sync-images-and-exit.mjs +148 -0
- package/agents/update/update-single/update-single-document-detail.mjs +131 -17
- package/agents/utils/analyze-feedback-intent.mjs +136 -0
- package/agents/utils/choose-docs.mjs +185 -40
- package/agents/utils/generate-document-or-skip.mjs +41 -0
- package/agents/utils/handle-diagram-operations.mjs +263 -0
- package/agents/utils/load-all-document-content.mjs +30 -0
- package/agents/utils/load-sources.mjs +2 -2
- package/agents/utils/post-generate.mjs +14 -3
- package/agents/utils/read-current-document-content.mjs +46 -0
- package/agents/utils/save-doc-translation.mjs +34 -0
- package/agents/utils/save-doc.mjs +42 -0
- package/agents/utils/save-sidebar.mjs +19 -6
- package/agents/utils/skip-if-content-exists.mjs +27 -0
- package/aigne.yaml +15 -3
- package/assets/report-template/report.html +17 -17
- package/docs-mcp/read-doc-content.mjs +30 -1
- package/package.json +8 -7
- package/prompts/detail/diagram/generate-image-system.md +135 -0
- package/prompts/detail/diagram/generate-image-user.md +32 -0
- package/prompts/detail/generate/user-prompt.md +27 -13
- package/prompts/evaluate/document.md +23 -10
- package/prompts/media/media-description/system-prompt.md +10 -2
- package/prompts/media/media-description/user-prompt.md +9 -0
- package/utils/check-document-has-diagram.mjs +95 -0
- package/utils/constants/index.mjs +46 -0
- package/utils/d2-utils.mjs +119 -178
- package/utils/delete-diagram-images.mjs +99 -0
- package/utils/docs-finder-utils.mjs +133 -25
- package/utils/image-compress.mjs +75 -0
- package/utils/kroki-utils.mjs +2 -3
- package/utils/load-config.mjs +29 -0
- package/utils/sync-diagram-to-translations.mjs +262 -0
- package/utils/utils.mjs +24 -0
- package/agents/create/check-diagram.mjs +0 -40
- package/agents/create/draw-diagram.yaml +0 -27
- package/agents/create/merge-diagram.yaml +0 -39
- package/agents/create/wrap-diagram-code.mjs +0 -35
|
@@ -10,6 +10,9 @@ task_title: Evaluate document for '{{ title }}'
|
|
|
10
10
|
input_schema:
|
|
11
11
|
type: object
|
|
12
12
|
properties:
|
|
13
|
+
path:
|
|
14
|
+
type: string
|
|
15
|
+
description: Document path to be evaluated
|
|
13
16
|
content:
|
|
14
17
|
type: string
|
|
15
18
|
description: Document content to be evaluated
|
|
@@ -28,6 +31,9 @@ input_schema:
|
|
|
28
31
|
readerKnowledgeLevel:
|
|
29
32
|
type: string
|
|
30
33
|
description: User-selected reader knowledge level
|
|
34
|
+
allDocumentContentList:
|
|
35
|
+
type: array
|
|
36
|
+
description: All document content list for better evaluate
|
|
31
37
|
required:
|
|
32
38
|
- content
|
|
33
39
|
- description
|
package/agents/init/index.mjs
CHANGED
|
@@ -2,32 +2,16 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { stringify as yamlStringify } from "yaml";
|
|
5
|
-
import { getFilteredOptions } from "../../utils/conflict-detector.mjs";
|
|
6
5
|
import {
|
|
7
6
|
DEFAULT_REASONING_EFFORT_LEVEL,
|
|
8
7
|
DEFAULT_THINKING_EFFORT_LEVEL,
|
|
9
|
-
|
|
10
|
-
DOCUMENT_STYLES,
|
|
11
|
-
DOCUMENTATION_DEPTH,
|
|
12
|
-
PURPOSE_TO_KNOWLEDGE_MAPPING,
|
|
13
|
-
READER_KNOWLEDGE_LEVELS,
|
|
14
|
-
SUPPORTED_LANGUAGES,
|
|
15
|
-
TARGET_AUDIENCES,
|
|
8
|
+
DIAGRAM_STYLES,
|
|
16
9
|
} from "../../utils/constants/index.mjs";
|
|
17
|
-
import { isRemoteFile } from "../../utils/file-utils.mjs";
|
|
18
10
|
import loadConfig from "../../utils/load-config.mjs";
|
|
19
|
-
import {
|
|
20
|
-
detectSystemLanguage,
|
|
21
|
-
getAvailablePaths,
|
|
22
|
-
getProjectInfo,
|
|
23
|
-
isGlobPattern,
|
|
24
|
-
validatePath,
|
|
25
|
-
} from "../../utils/utils.mjs";
|
|
11
|
+
import { detectSystemLanguage, getProjectInfo } from "../../utils/utils.mjs";
|
|
26
12
|
import mapReasoningEffortLevel from "../utils/map-reasoning-effort-level.mjs";
|
|
27
13
|
import { validateDocDir } from "./validate.mjs";
|
|
28
14
|
|
|
29
|
-
const _PRESS_ENTER_TO_FINISH = "Press Enter to finish";
|
|
30
|
-
|
|
31
15
|
/**
|
|
32
16
|
* Guides the user through a multi-turn dialog to generate a YAML configuration file.
|
|
33
17
|
* @param {Object} params
|
|
@@ -58,16 +42,13 @@ export default async function init(input, options) {
|
|
|
58
42
|
};
|
|
59
43
|
}
|
|
60
44
|
|
|
61
|
-
async function _init(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
options,
|
|
70
|
-
) {
|
|
45
|
+
async function _init({
|
|
46
|
+
outputPath = ".aigne/doc-smith",
|
|
47
|
+
fileName = "config.yaml",
|
|
48
|
+
skipIfExists = false,
|
|
49
|
+
appUrl,
|
|
50
|
+
checkOnly = false,
|
|
51
|
+
}) {
|
|
71
52
|
// Check if we're in checkOnly mode
|
|
72
53
|
if (checkOnly) {
|
|
73
54
|
const filePath = join(outputPath, fileName);
|
|
@@ -107,307 +88,24 @@ async function _init(
|
|
|
107
88
|
}
|
|
108
89
|
}
|
|
109
90
|
|
|
110
|
-
console.log("🚀 Welcome to AIGNE DocSmith!");
|
|
111
|
-
console.log("Let's set up your documentation preferences.\n");
|
|
112
|
-
|
|
113
91
|
const input = {};
|
|
114
92
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.map(([key, style]) => ({
|
|
120
|
-
name: `${style.name}`,
|
|
121
|
-
description: style.description,
|
|
122
|
-
value: key,
|
|
123
|
-
})),
|
|
124
|
-
validate: (input) => {
|
|
125
|
-
if (input.length === 0) {
|
|
126
|
-
return "You must choose at least one goal for your documentation.";
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
let prioritizedPurposes = purposeChoices;
|
|
133
|
-
if (purposeChoices.length === 1 && purposeChoices.includes("mixedPurpose")) {
|
|
134
|
-
const topPriorities = await options.prompts.checkbox({
|
|
135
|
-
message: "🎯 Which is most important? (Select up to 2 priorities)",
|
|
136
|
-
choices: Object.entries(DOCUMENT_STYLES)
|
|
137
|
-
.filter(([key]) => key !== "custom" && key !== "mixedPurpose")
|
|
138
|
-
.map(([key, style]) => ({
|
|
139
|
-
name: `${style.name}`,
|
|
140
|
-
description: style.description,
|
|
141
|
-
value: key,
|
|
142
|
-
})),
|
|
143
|
-
validate: (input) => {
|
|
144
|
-
if (input.length === 0) {
|
|
145
|
-
return "You must choose at least one priority.";
|
|
146
|
-
}
|
|
147
|
-
if (input.length > 2) {
|
|
148
|
-
return "You may only choose up to 2 priorities.";
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
prioritizedPurposes = topPriorities;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
input.documentPurpose = prioritizedPurposes;
|
|
158
|
-
|
|
159
|
-
const audienceChoices = await options.prompts.checkbox({
|
|
160
|
-
message: "👥 [2/9]: Who will be reading your documentation?",
|
|
161
|
-
choices: Object.entries(TARGET_AUDIENCES)
|
|
162
|
-
.filter(([key]) => key !== "custom")
|
|
163
|
-
.map(([key, audience]) => ({
|
|
164
|
-
name: `${audience.name}`,
|
|
165
|
-
description: audience.description,
|
|
166
|
-
value: key,
|
|
167
|
-
})),
|
|
168
|
-
validate: (input) => {
|
|
169
|
-
if (input.length === 0) {
|
|
170
|
-
return "You must choose at least one audience.";
|
|
171
|
-
}
|
|
172
|
-
return true;
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
input.targetAudienceTypes = audienceChoices;
|
|
177
|
-
|
|
178
|
-
const mappedPurpose = prioritizedPurposes.find(
|
|
179
|
-
(purpose) => PURPOSE_TO_KNOWLEDGE_MAPPING[purpose],
|
|
180
|
-
);
|
|
181
|
-
const defaultKnowledge = mappedPurpose ? PURPOSE_TO_KNOWLEDGE_MAPPING[mappedPurpose] : null;
|
|
182
|
-
|
|
183
|
-
const { filteredOptions: filteredKnowledgeOptions } = getFilteredOptions(
|
|
184
|
-
"readerKnowledgeLevel",
|
|
185
|
-
{ documentPurpose: prioritizedPurposes, targetAudienceTypes: audienceChoices },
|
|
186
|
-
READER_KNOWLEDGE_LEVELS,
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
const knowledgeChoice = await options.prompts.select({
|
|
190
|
-
message: "🧠 [3/9]: How much do your readers already know about your project?",
|
|
191
|
-
choices: Object.entries(filteredKnowledgeOptions).map(([key, level]) => ({
|
|
192
|
-
name: `${level.name}`,
|
|
193
|
-
description: level.description,
|
|
194
|
-
value: key,
|
|
195
|
-
})),
|
|
196
|
-
default: defaultKnowledge,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// Save reader knowledge level choice as key
|
|
200
|
-
input.readerKnowledgeLevel = knowledgeChoice;
|
|
201
|
-
|
|
202
|
-
// 4. Documentation depth - how comprehensive should the documentation be?
|
|
203
|
-
// Determine default based on priority: Purpose > Audience > Knowledge Level
|
|
204
|
-
const getDepthDefault = () => {
|
|
205
|
-
// Check priority order: purposes -> audiences -> knowledgeLevels
|
|
206
|
-
const checks = [
|
|
207
|
-
() => {
|
|
208
|
-
const purpose = prioritizedPurposes.find((p) => DEPTH_RECOMMENDATION_LOGIC.purposes[p]);
|
|
209
|
-
return purpose ? DEPTH_RECOMMENDATION_LOGIC.purposes[purpose] : null;
|
|
210
|
-
},
|
|
211
|
-
() => {
|
|
212
|
-
const audience = audienceChoices.find((a) => DEPTH_RECOMMENDATION_LOGIC.audiences[a]);
|
|
213
|
-
return audience ? DEPTH_RECOMMENDATION_LOGIC.audiences[audience] : null;
|
|
214
|
-
},
|
|
215
|
-
() => DEPTH_RECOMMENDATION_LOGIC.knowledgeLevels[knowledgeChoice] || null,
|
|
216
|
-
];
|
|
217
|
-
|
|
218
|
-
return checks.find((check) => check())?.() || null;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const defaultDepth = getDepthDefault();
|
|
222
|
-
|
|
223
|
-
// Filter documentation depth options based on all previous selections
|
|
224
|
-
const { filteredOptions: filteredDepthOptions } = getFilteredOptions(
|
|
225
|
-
"documentationDepth",
|
|
226
|
-
{
|
|
227
|
-
documentPurpose: prioritizedPurposes,
|
|
228
|
-
targetAudienceTypes: audienceChoices,
|
|
229
|
-
readerKnowledgeLevel: knowledgeChoice,
|
|
230
|
-
},
|
|
231
|
-
DOCUMENTATION_DEPTH,
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
const depthChoice = await options.prompts.select({
|
|
235
|
-
message: "📊 [4/9]: How detailed should your documentation be?",
|
|
236
|
-
choices: Object.entries(filteredDepthOptions).map(([key, depth]) => ({
|
|
237
|
-
name: `${depth.name}`,
|
|
238
|
-
description: depth.description,
|
|
239
|
-
value: key,
|
|
240
|
-
})),
|
|
241
|
-
default: defaultDepth,
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Save documentation depth choice as key
|
|
245
|
-
input.documentationDepth = depthChoice;
|
|
246
|
-
|
|
247
|
-
// 5. Language settings
|
|
248
|
-
// Detect system language and use as default
|
|
249
|
-
const systemLanguage = detectSystemLanguage();
|
|
250
|
-
|
|
251
|
-
// Let user select primary language from supported list
|
|
252
|
-
const primaryLanguageChoice = await options.prompts.select({
|
|
253
|
-
message: "🌐 [5/9]: What is the main language of your documentation?",
|
|
254
|
-
choices: SUPPORTED_LANGUAGES.map((lang) => ({
|
|
255
|
-
name: `${lang.label} - ${lang.sample}`,
|
|
256
|
-
value: lang.code,
|
|
257
|
-
})),
|
|
258
|
-
default: systemLanguage,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
input.locale = primaryLanguageChoice;
|
|
262
|
-
|
|
263
|
-
// 6. Translation languages
|
|
264
|
-
// Filter out the primary language from available choices
|
|
265
|
-
const availableTranslationLanguages = SUPPORTED_LANGUAGES.filter(
|
|
266
|
-
(lang) => lang.code !== primaryLanguageChoice,
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const translateLanguageChoices = await options.prompts.checkbox({
|
|
270
|
-
message: "🔄 [6/9]: What languages should we translate to?",
|
|
271
|
-
choices: availableTranslationLanguages.map((lang) => ({
|
|
272
|
-
name: `${lang.label} - ${lang.sample}`,
|
|
273
|
-
value: lang.code,
|
|
274
|
-
})),
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
input.translateLanguages = translateLanguageChoices;
|
|
278
|
-
|
|
279
|
-
// 7. Documentation directory
|
|
280
|
-
const docsDirInput = await options.prompts.input({
|
|
281
|
-
message: `📁 [7/9]: Where should we save your documentation?`,
|
|
282
|
-
default: `${outputPath}/docs`,
|
|
283
|
-
validate: validateDocDir,
|
|
284
|
-
});
|
|
285
|
-
input.docsDir = docsDirInput.trim() || `${outputPath}/docs`;
|
|
286
|
-
|
|
287
|
-
// 8. Content sources
|
|
288
|
-
console.log("🔍 [8/9]: Data Sources");
|
|
289
|
-
console.log("Please specify the data source we should analyze to generate your documentation.");
|
|
290
|
-
console.log(
|
|
291
|
-
` 1. Use paths like ${chalk.green("./src")}, ${chalk.green("./README.md")} or ${chalk.green("!./src/private")}.`,
|
|
292
|
-
);
|
|
293
|
-
console.log(
|
|
294
|
-
` 2. Use globs like ${chalk.green("src/**/*.js")} or ${chalk.green(
|
|
295
|
-
"!private/**/*.js",
|
|
296
|
-
)} for more specific file matching.`,
|
|
297
|
-
);
|
|
298
|
-
console.log(` 3. Use URLs like ${chalk.green("https://example.com/openapi.yaml")}.`);
|
|
299
|
-
console.log("💡 If you leave this empty, we will scan the entire directory.");
|
|
300
|
-
|
|
301
|
-
const sourcePaths = [];
|
|
302
|
-
while (true) {
|
|
303
|
-
const selectedPath = await options.prompts.search({
|
|
304
|
-
message: "Please enter a valid data source:",
|
|
305
|
-
source: async (input) => {
|
|
306
|
-
if (!input || input.trim() === "") {
|
|
307
|
-
return [
|
|
308
|
-
{
|
|
309
|
-
name: "",
|
|
310
|
-
value: "",
|
|
311
|
-
description: _PRESS_ENTER_TO_FINISH,
|
|
312
|
-
},
|
|
313
|
-
];
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
let isIgnore = false;
|
|
317
|
-
const searchTerm = input.trim();
|
|
318
|
-
let cleanSearchTerm = searchTerm;
|
|
319
|
-
if (cleanSearchTerm.startsWith("!")) {
|
|
320
|
-
isIgnore = true;
|
|
321
|
-
cleanSearchTerm = searchTerm.slice(1);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Search for matching files and folders in current directory
|
|
325
|
-
const availablePaths = getAvailablePaths(cleanSearchTerm);
|
|
326
|
-
|
|
327
|
-
// Also add option to use as glob pattern
|
|
328
|
-
const options = [...availablePaths].map((x) => ({
|
|
329
|
-
...x,
|
|
330
|
-
name: isIgnore ? `!${x.name}` : x.name,
|
|
331
|
-
value: isIgnore ? `!${x.value}` : x.value,
|
|
332
|
-
}));
|
|
333
|
-
|
|
334
|
-
// Check if input looks like a glob pattern
|
|
335
|
-
const isGlobPatternResult = isGlobPattern(searchTerm);
|
|
336
|
-
if (isGlobPatternResult) {
|
|
337
|
-
// If it looks like a glob pattern, allow direct input
|
|
338
|
-
options.push({
|
|
339
|
-
name: searchTerm,
|
|
340
|
-
value: searchTerm,
|
|
341
|
-
description: "Use this glob pattern for file matching.",
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (!isIgnore && isRemoteFile(searchTerm)) {
|
|
346
|
-
options.push({
|
|
347
|
-
name: searchTerm,
|
|
348
|
-
value: searchTerm,
|
|
349
|
-
description: "Use this remote url for content source.",
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return options;
|
|
354
|
-
},
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
// Check if user chose to exit
|
|
358
|
-
if (!selectedPath || selectedPath.trim() === "" || selectedPath === _PRESS_ENTER_TO_FINISH) {
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const trimmedPath = selectedPath.trim();
|
|
93
|
+
// 5. Language settings - use system language detection as default
|
|
94
|
+
// const systemLanguage = detectSystemLanguage();
|
|
95
|
+
// FIXME: 临时使用中文,框架优化后需要修改
|
|
96
|
+
input.locale = "zh";
|
|
363
97
|
|
|
364
|
-
|
|
365
|
-
|
|
98
|
+
// 6. Translation languages - default to empty
|
|
99
|
+
input.translateLanguages = [];
|
|
366
100
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (sourcePaths.includes(trimmedPath)) {
|
|
370
|
-
console.log(`⚠️ URL already exists: ${trimmedPath}`);
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
sourcePaths.push(trimmedPath);
|
|
374
|
-
} else if (isGlobPatternResult) {
|
|
375
|
-
// For glob patterns, just add them without validation
|
|
376
|
-
if (sourcePaths.includes(trimmedPath)) {
|
|
377
|
-
console.log(`⚠️ Pattern already exists: ${trimmedPath}`);
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
sourcePaths.push(trimmedPath);
|
|
381
|
-
} else {
|
|
382
|
-
const cleanTrimmedPath = trimmedPath.startsWith("!") ? trimmedPath.slice(1) : trimmedPath;
|
|
383
|
-
// Use validatePath to check if path is valid for regular paths
|
|
384
|
-
const validation = validatePath(cleanTrimmedPath);
|
|
385
|
-
|
|
386
|
-
if (!validation.isValid) {
|
|
387
|
-
console.log(`⚠️ ${validation.error}`);
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Avoid duplicate paths
|
|
392
|
-
if (sourcePaths.includes(trimmedPath)) {
|
|
393
|
-
console.log(`⚠️ Path already exists: ${trimmedPath}`);
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
sourcePaths.push(trimmedPath);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
101
|
+
// 7. Documentation directory - use default value
|
|
102
|
+
input.docsDir = `${outputPath}/docs`;
|
|
400
103
|
|
|
401
|
-
//
|
|
402
|
-
input.sourcesPath =
|
|
104
|
+
// 8. Content sources - use "./" as default
|
|
105
|
+
input.sourcesPath = ["./"];
|
|
403
106
|
|
|
404
|
-
// 9. Custom rules -
|
|
405
|
-
|
|
406
|
-
message:
|
|
407
|
-
"📋 [9/9]: Do you have any custom rules or requirements for your documentation? (Optional, press Enter to skip)",
|
|
408
|
-
default: "",
|
|
409
|
-
});
|
|
410
|
-
input.rules = rulesInput.trim();
|
|
107
|
+
// 9. Custom rules - default to empty
|
|
108
|
+
input.rules = "";
|
|
411
109
|
|
|
412
110
|
// Save project info to config
|
|
413
111
|
const projectInfo = await getProjectInfo();
|
|
@@ -468,12 +166,6 @@ export function generateYAML(input) {
|
|
|
468
166
|
effort: input.thinking?.effort || DEFAULT_THINKING_EFFORT_LEVEL,
|
|
469
167
|
},
|
|
470
168
|
|
|
471
|
-
// Documentation configuration
|
|
472
|
-
documentPurpose: input.documentPurpose || [],
|
|
473
|
-
targetAudienceTypes: input.targetAudienceTypes || [],
|
|
474
|
-
readerKnowledgeLevel: input.readerKnowledgeLevel || "",
|
|
475
|
-
documentationDepth: input.documentationDepth || "",
|
|
476
|
-
|
|
477
169
|
// Custom rules and target audience (empty for user to fill)
|
|
478
170
|
rules: input.rules || "",
|
|
479
171
|
targetAudience: "",
|
|
@@ -516,65 +208,6 @@ export function generateYAML(input) {
|
|
|
516
208
|
${modelSection}
|
|
517
209
|
\n`;
|
|
518
210
|
|
|
519
|
-
// Add documentation configuration with comments
|
|
520
|
-
yaml += "# =============================================================================\n";
|
|
521
|
-
yaml += "# Documentation Configuration\n";
|
|
522
|
-
yaml += "# =============================================================================\n\n";
|
|
523
|
-
|
|
524
|
-
// Document Purpose with all available options
|
|
525
|
-
yaml += "# Purpose: What's the main outcome you want readers to achieve?\n";
|
|
526
|
-
yaml += "# Available options (uncomment and modify as needed):\n";
|
|
527
|
-
Object.entries(DOCUMENT_STYLES).forEach(([key, style]) => {
|
|
528
|
-
if (key !== "custom") {
|
|
529
|
-
yaml += `# ${key.padEnd(16)} - ${style.name}: ${style.description}\n`;
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
// Safely serialize documentPurpose
|
|
534
|
-
const documentPurposeSection = yamlStringify({ documentPurpose: config.documentPurpose }).trim();
|
|
535
|
-
yaml += `${documentPurposeSection.replace(/^documentPurpose:/, "documentPurpose:")}\n\n`;
|
|
536
|
-
|
|
537
|
-
// Target Audience Types with all available options
|
|
538
|
-
yaml += "# Target Audience: Who will be reading this most often?\n";
|
|
539
|
-
yaml += "# Available options (uncomment and modify as needed):\n";
|
|
540
|
-
Object.entries(TARGET_AUDIENCES).forEach(([key, audience]) => {
|
|
541
|
-
if (key !== "custom") {
|
|
542
|
-
yaml += `# ${key.padEnd(16)} - ${audience.name}: ${audience.description}\n`;
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
// Safely serialize targetAudienceTypes
|
|
547
|
-
const targetAudienceTypesSection = yamlStringify({
|
|
548
|
-
targetAudienceTypes: config.targetAudienceTypes,
|
|
549
|
-
}).trim();
|
|
550
|
-
yaml += `${targetAudienceTypesSection.replace(/^targetAudienceTypes:/, "targetAudienceTypes:")}\n\n`;
|
|
551
|
-
|
|
552
|
-
// Reader Knowledge Level with all available options
|
|
553
|
-
yaml += "# Reader Knowledge Level: What do readers typically know when they arrive?\n";
|
|
554
|
-
yaml += "# Available options (uncomment and modify as needed):\n";
|
|
555
|
-
Object.entries(READER_KNOWLEDGE_LEVELS).forEach(([key, level]) => {
|
|
556
|
-
yaml += `# ${key.padEnd(20)} - ${level.name}: ${level.description}\n`;
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// Safely serialize readerKnowledgeLevel
|
|
560
|
-
const readerKnowledgeLevelSection = yamlStringify({
|
|
561
|
-
readerKnowledgeLevel: config.readerKnowledgeLevel,
|
|
562
|
-
}).trim();
|
|
563
|
-
yaml += `${readerKnowledgeLevelSection.replace(/^readerKnowledgeLevel:/, "readerKnowledgeLevel:")}\n\n`;
|
|
564
|
-
|
|
565
|
-
// Documentation Depth with all available options
|
|
566
|
-
yaml += "# Documentation Depth: How comprehensive should the documentation be?\n";
|
|
567
|
-
yaml += "# Available options (uncomment and modify as needed):\n";
|
|
568
|
-
Object.entries(DOCUMENTATION_DEPTH).forEach(([key, depth]) => {
|
|
569
|
-
yaml += `# ${key.padEnd(18)} - ${depth.name}: ${depth.description}\n`;
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// Safely serialize documentationDepth
|
|
573
|
-
const documentationDepthSection = yamlStringify({
|
|
574
|
-
documentationDepth: config.documentationDepth,
|
|
575
|
-
}).trim();
|
|
576
|
-
yaml += `${documentationDepthSection.replace(/^documentationDepth:/, "documentationDepth:")}\n\n`;
|
|
577
|
-
|
|
578
211
|
// Custom Documentation Rules and Requirements
|
|
579
212
|
yaml += "# Custom Rules: Define specific documentation generation rules and requirements\n";
|
|
580
213
|
const rulesSection = yamlStringify({ rules: config.rules }).trim();
|
|
@@ -621,6 +254,21 @@ ${modelSection}
|
|
|
621
254
|
}).trim();
|
|
622
255
|
yaml += `# minImageWidth: Only images wider than this value (in pixels) will be used in the page generation.\n${mediaInfoSection}\n`;
|
|
623
256
|
|
|
257
|
+
// Diagramming configuration
|
|
258
|
+
yaml += "\n# Diagramming Configuration\n";
|
|
259
|
+
yaml +=
|
|
260
|
+
"# diagramming.effort: AI effort level for diagramming, 0-10, larger value means fewer diagrams\n";
|
|
261
|
+
yaml += "diagramming:\n";
|
|
262
|
+
yaml += " effort: 5 # AI effort level for diagramming, 0-10, large is less diagram\n";
|
|
263
|
+
yaml +=
|
|
264
|
+
" # Default diagram style: The primary style to use when no style is specified in feedback\n";
|
|
265
|
+
yaml += " # This style will be applied if feedback doesn't specify a different style\n";
|
|
266
|
+
yaml += " # Available options:\n";
|
|
267
|
+
Object.entries(DIAGRAM_STYLES).forEach(([key, style]) => {
|
|
268
|
+
yaml += ` # ${key.padEnd(16)} - ${style.name}: ${style.description}\n`;
|
|
269
|
+
});
|
|
270
|
+
yaml += ' # style: "modern"\n';
|
|
271
|
+
|
|
624
272
|
return yaml;
|
|
625
273
|
}
|
|
626
274
|
|
|
@@ -39,14 +39,14 @@ skills:
|
|
|
39
39
|
input_schema:
|
|
40
40
|
type: object
|
|
41
41
|
properties:
|
|
42
|
-
glossary:
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
# glossary:
|
|
43
|
+
# type: string
|
|
44
|
+
# description: Glossary file for consistent terminology (use @filename.md)
|
|
45
45
|
docs:
|
|
46
46
|
type: array
|
|
47
47
|
items:
|
|
48
48
|
type: string
|
|
49
|
-
description: Documents to
|
|
49
|
+
description: Documents to update, use document path in document structure to specify
|
|
50
50
|
langs:
|
|
51
51
|
type: array
|
|
52
52
|
items:
|
|
@@ -6,6 +6,7 @@ import { parse, stringify } from "yaml";
|
|
|
6
6
|
import { getMediaDescriptionCachePath } from "../../utils/file-utils.mjs";
|
|
7
7
|
|
|
8
8
|
const SIZE_THRESHOLD = 10 * 1024 * 1024; // 10MB
|
|
9
|
+
const SVG_SIZE_THRESHOLD = 50 * 1024; // 50KB for SVG files
|
|
9
10
|
|
|
10
11
|
// Supported MIME types for Gemini AI
|
|
11
12
|
const SUPPORTED_IMAGE_TYPES = new Set([
|
|
@@ -16,6 +17,8 @@ const SUPPORTED_IMAGE_TYPES = new Set([
|
|
|
16
17
|
"image/heif",
|
|
17
18
|
]);
|
|
18
19
|
|
|
20
|
+
const SUPPORTED_SVG_TYPES = new Set(["image/svg+xml"]);
|
|
21
|
+
|
|
19
22
|
const SUPPORTED_VIDEO_TYPES = new Set([
|
|
20
23
|
"video/mp4",
|
|
21
24
|
"video/mpeg",
|
|
@@ -60,10 +63,10 @@ async function calculateMediaHash(absolutePath) {
|
|
|
60
63
|
export default async function loadMediaDescription(input, options) {
|
|
61
64
|
const { mediaFiles = [], docsDir } = input;
|
|
62
65
|
|
|
63
|
-
// Filter to get image and
|
|
66
|
+
// Filter to get image, video and svg files with supported MIME types
|
|
64
67
|
const mediaFilesToProcess = mediaFiles.filter((file) => {
|
|
65
68
|
if (file.type === "image") {
|
|
66
|
-
return SUPPORTED_IMAGE_TYPES.has(file.mimeType);
|
|
69
|
+
return SUPPORTED_IMAGE_TYPES.has(file.mimeType) || SUPPORTED_SVG_TYPES.has(file.mimeType);
|
|
67
70
|
}
|
|
68
71
|
if (file.type === "video") {
|
|
69
72
|
return SUPPORTED_VIDEO_TYPES.has(file.mimeType);
|
|
@@ -101,19 +104,45 @@ export default async function loadMediaDescription(input, options) {
|
|
|
101
104
|
mediaHashMap.set(mediaFile.path, mediaHash);
|
|
102
105
|
|
|
103
106
|
if (!cache[mediaHash]) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
const isSvg = SUPPORTED_SVG_TYPES.has(mediaFile.mimeType);
|
|
108
|
+
|
|
109
|
+
if (isSvg) {
|
|
110
|
+
// For SVG files, check size and read content
|
|
111
|
+
try {
|
|
112
|
+
const stats = await stat(absolutePath);
|
|
113
|
+
if (stats.size > SVG_SIZE_THRESHOLD) {
|
|
114
|
+
console.warn(
|
|
115
|
+
`SVG file ${mediaFile.path} exceeds ${SVG_SIZE_THRESHOLD / 1024}KB limit, skipping`,
|
|
116
|
+
);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const svgContent = await readFile(absolutePath, "utf8");
|
|
121
|
+
mediaToDescribe.push({
|
|
122
|
+
...mediaFile,
|
|
123
|
+
hash: mediaHash,
|
|
124
|
+
path: mediaFile.path,
|
|
125
|
+
svgContent,
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.warn(`Failed to read SVG file ${mediaFile.path}:`, error.message);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// For non-SVG media files, use mediaFile field
|
|
132
|
+
mediaToDescribe.push({
|
|
133
|
+
...mediaFile,
|
|
134
|
+
hash: mediaHash,
|
|
135
|
+
path: mediaFile.path,
|
|
136
|
+
mediaFile: [
|
|
137
|
+
{
|
|
138
|
+
type: "local",
|
|
139
|
+
path: absolutePath,
|
|
140
|
+
filename: mediaFile.name,
|
|
141
|
+
mimeType: mediaFile.mimeType,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
117
146
|
}
|
|
118
147
|
}
|
|
119
148
|
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
TMP_DIR,
|
|
17
17
|
TMP_DOCS_DIR,
|
|
18
18
|
} from "../../utils/constants/index.mjs";
|
|
19
|
-
import {
|
|
19
|
+
import { ensureTmpDir } from "../../utils/d2-utils.mjs";
|
|
20
20
|
import { deploy } from "../../utils/deploy.mjs";
|
|
21
21
|
import { getGithubRepoUrl, loadConfigFromFile, saveValueToConfig } from "../../utils/utils.mjs";
|
|
22
22
|
import updateBranding from "../utils/update-branding.mjs";
|
|
@@ -53,9 +53,6 @@ export default async function publishDocs(
|
|
|
53
53
|
});
|
|
54
54
|
await fs.cp(rawDocsDir, docsDir, { recursive: true });
|
|
55
55
|
|
|
56
|
-
// ----------------- trigger beforePublishHook -----------------------------
|
|
57
|
-
await beforePublishHook({ docsDir });
|
|
58
|
-
|
|
59
56
|
// ----------------- main publish process flow -----------------------------
|
|
60
57
|
// Check if DOC_DISCUSS_KIT_URL is set in environment variables
|
|
61
58
|
const useEnvAppUrl = !!(
|