@aigne/doc-smith 0.8.3 → 0.8.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.
- package/.aigne/doc-smith/config.yaml +3 -3
- package/.aigne/doc-smith/preferences.yml +58 -12
- package/.aigne/doc-smith/upload-cache.yaml +600 -207
- package/CHANGELOG.md +22 -0
- package/README.md +77 -5
- package/agents/input-generator.mjs +12 -6
- package/agents/publish-docs.mjs +53 -4
- package/docs/_sidebar.md +1 -1
- package/docs/advanced-how-it-works.md +55 -60
- package/docs/advanced-how-it-works.zh.md +60 -65
- package/docs/advanced-quality-assurance.md +73 -38
- package/docs/advanced-quality-assurance.zh.md +73 -38
- package/docs/advanced.md +2 -14
- package/docs/advanced.zh.md +5 -17
- package/docs/changelog.md +41 -4
- package/docs/changelog.zh.md +77 -40
- package/docs/cli-reference.md +79 -13
- package/docs/cli-reference.zh.md +92 -26
- package/docs/configuration-interactive-setup.md +102 -49
- package/docs/configuration-interactive-setup.zh.md +102 -49
- package/docs/configuration-language-support.md +69 -39
- package/docs/configuration-language-support.zh.md +68 -38
- package/docs/configuration-llm-setup.md +25 -62
- package/docs/configuration-llm-setup.zh.md +25 -62
- package/docs/configuration-preferences.md +79 -67
- package/docs/configuration-preferences.zh.md +78 -67
- package/docs/configuration.md +122 -109
- package/docs/configuration.zh.md +130 -117
- package/docs/features-generate-documentation.md +44 -24
- package/docs/features-generate-documentation.zh.md +52 -32
- package/docs/features-publish-your-docs.md +41 -40
- package/docs/features-publish-your-docs.zh.md +50 -49
- package/docs/features-translate-documentation.md +73 -17
- package/docs/features-translate-documentation.zh.md +76 -20
- package/docs/features-update-and-refine.md +72 -21
- package/docs/features-update-and-refine.zh.md +80 -29
- package/docs/features.md +24 -28
- package/docs/features.zh.md +25 -29
- package/docs/getting-started.md +87 -38
- package/docs/getting-started.zh.md +88 -39
- package/docs/overview.md +17 -35
- package/docs/overview.zh.md +18 -36
- package/package.json +9 -8
- package/prompts/content-detail-generator.md +1 -0
- package/prompts/document/custom-code-block.md +101 -0
- package/prompts/document/d2-chart/rules.md +941 -1031
- package/prompts/document/detail-generator.md +7 -53
- package/tests/input-generator.test.mjs +2 -2
- package/tests/kroki-utils.test.mjs +88 -17
- package/utils/auth-utils.mjs +9 -2
- package/utils/blocklet.mjs +25 -6
- package/utils/constants.mjs +17 -1
- package/utils/deploy.mjs +404 -0
- package/utils/kroki-utils.mjs +22 -14
- package/utils/markdown-checker.mjs +1 -1
- package/utils/utils.mjs +3 -2
- package/prompts/document/d2-chart/diy-examples.md +0 -44
- package/prompts/document/d2-chart/shape-rules.md +0 -182
|
@@ -15,9 +15,6 @@
|
|
|
15
15
|
- 说明要尽可能的详细,如果存在配置项或参数,需要解释每个配置项或参数的含义,如果参数有多个可选值,每种可选值需要解释其含义,并尽可能配上代码示例
|
|
16
16
|
- 参数优先使用 markdown 中的 table 来展示,让内容看上去更整齐,容易阅读
|
|
17
17
|
- 接口/方法调用的说明必须包含 **响应数据示例**
|
|
18
|
-
- 尽可能少的使用 d2 图表,图表中错误的逻辑会导致文档效果更差,绘制 d2 的图表一定要确保可读性和正确性
|
|
19
|
-
- 概览部分,建议包含 d2 图表展示产品架构图
|
|
20
|
-
{% include "d2-chart/rules.md" %}
|
|
21
18
|
- 对输出的 markdown 进行检查,确认输出内容完整,table、d2 信息完整并且格式正确
|
|
22
19
|
- **确保内容完整性**:在生成任何文档内容,特别是代码块(如 d2、JSON、代码等)时,必须确保其是**完整且语法正确**的。在输出完成后,必须进行一次**自我检查**,确认所有的代码块、列表、表格等都已完全闭合且没有中途截断。
|
|
23
20
|
- **代码块原子性**:将每个代码块(例如 ```d2 ... ```)视为一个**不可分割的原子单元**。必须一次性完整生成,从开始标记(```d2)到结束标记(```)之间的所有内容都不能省略或截断。
|
|
@@ -29,6 +26,13 @@
|
|
|
29
26
|
|
|
30
27
|
</document_rules>
|
|
31
28
|
|
|
29
|
+
<document_rules>
|
|
30
|
+
|
|
31
|
+
D2 Diagram Generation Expert Guide:
|
|
32
|
+
{% include "d2-chart/rules.md" %}
|
|
33
|
+
|
|
34
|
+
</document_rules>
|
|
35
|
+
|
|
32
36
|
<TONE_STYLE>
|
|
33
37
|
- Documentation should be plain, rigorous and accurate, avoiding grandiose or empty vocabulary
|
|
34
38
|
- You are writing for humans, not algorithms
|
|
@@ -42,54 +46,4 @@
|
|
|
42
46
|
- Use contractions and idioms sparingly to maintain an informal, yet credible tone
|
|
43
47
|
- Blend technical precision with relatable language
|
|
44
48
|
- Be direct: say what happened, why it matters, and how it helps
|
|
45
|
-
|
|
46
|
-
Example Tone Transformations
|
|
47
|
-
❌ "We’re thrilled to announce our most powerful update yet…"
|
|
48
|
-
✅ "You can now include location and timestamp metadata for each claim, enabling audit-ready transparency."
|
|
49
|
-
|
|
50
|
-
❌ "Unlock the future of verification."
|
|
51
|
-
✅ "This release makes real-world claims independently verifiable across sectors."
|
|
52
49
|
</TONE_STYLE>
|
|
53
|
-
|
|
54
|
-
<WORDS_PHRASES_TO_AVOID>
|
|
55
|
-
|
|
56
|
-
Do not use promotional fluff or filler emotion. Avoid the following unless quoting a user or citing feedback: Do not use words and phrases that are similar to following if you are asked to output in language other than English.
|
|
57
|
-
|
|
58
|
-
<emotion-words>
|
|
59
|
-
excited
|
|
60
|
-
thrilled
|
|
61
|
-
delighted
|
|
62
|
-
proud to announce
|
|
63
|
-
happy to share
|
|
64
|
-
Overused Adjectives:
|
|
65
|
-
powerful
|
|
66
|
-
seamless
|
|
67
|
-
revolutionary
|
|
68
|
-
robust
|
|
69
|
-
amazing
|
|
70
|
-
significant
|
|
71
|
-
transformative
|
|
72
|
-
innovative
|
|
73
|
-
disruptive
|
|
74
|
-
groundbreaking
|
|
75
|
-
</emotion-words>
|
|
76
|
-
|
|
77
|
-
<generic-hype-verbs>
|
|
78
|
-
unlock
|
|
79
|
-
unleash
|
|
80
|
-
empower
|
|
81
|
-
elevate
|
|
82
|
-
reimagine
|
|
83
|
-
transform
|
|
84
|
-
Empty Marketing Phrases:
|
|
85
|
-
in today's world
|
|
86
|
-
at the end of the day
|
|
87
|
-
best practices
|
|
88
|
-
end-to-end
|
|
89
|
-
game changer
|
|
90
|
-
cutting edge
|
|
91
|
-
</generic-hype-verbs>
|
|
92
|
-
|
|
93
|
-
➡️ Instead, focus on concrete outcomes and observable benefits.
|
|
94
|
-
Example: “Now includes location and timestamp for each field report” is better than “a powerful new update.”
|
|
95
|
-
</WORDS_PHRASES_TO_AVOID>
|
|
@@ -913,7 +913,7 @@ describe("generateYAML", () => {
|
|
|
913
913
|
// Should always include these sections
|
|
914
914
|
expect(result).toContain("# Project information for documentation publishing");
|
|
915
915
|
expect(result).toContain("# Documentation Configuration");
|
|
916
|
-
expect(result).toContain("projectName:
|
|
916
|
+
expect(result).toContain('"projectName":');
|
|
917
917
|
expect(result).toContain("documentPurpose:");
|
|
918
918
|
expect(result).toContain("targetAudienceTypes:");
|
|
919
919
|
expect(result).toContain("locale:");
|
|
@@ -1287,7 +1287,7 @@ describe("init", () => {
|
|
|
1287
1287
|
|
|
1288
1288
|
// Config should be generated since original was empty
|
|
1289
1289
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
1290
|
-
expect(configContent).toContain("projectName:
|
|
1290
|
+
expect(configContent).toContain('"projectName":');
|
|
1291
1291
|
expect(configContent).toContain("locale: en");
|
|
1292
1292
|
} finally {
|
|
1293
1293
|
await cleanupTempDir(tempDir);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { existsSync, mkdtemp, rmdir } from "node:fs";
|
|
3
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
7
|
+
import Debug from "debug";
|
|
8
|
+
|
|
9
|
+
import { TMP_ASSETS_DIR } from "../utils/constants.mjs";
|
|
7
10
|
import {
|
|
8
11
|
beforePublishHook,
|
|
9
12
|
checkD2Content,
|
|
@@ -238,20 +241,33 @@ E -> F
|
|
|
238
241
|
|
|
239
242
|
test("should skip generation if SVG file already exists", async () => {
|
|
240
243
|
const docsDir = path.join(tempDir, "docs");
|
|
241
|
-
|
|
242
|
-
await mkdir(assetsDir, { recursive: true });
|
|
243
|
-
|
|
244
|
+
await mkdir(docsDir, { recursive: true });
|
|
244
245
|
const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
|
|
245
246
|
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
await writeFile(path.join(assetsDir, "test.svg"), testSvgContent);
|
|
247
|
+
// 1. First run to generate the file
|
|
248
|
+
await saveD2Assets({ markdown, docsDir });
|
|
249
249
|
|
|
250
|
-
//
|
|
251
|
-
const
|
|
250
|
+
// 2. Second run to check if cache is used
|
|
251
|
+
const debugLogs = [];
|
|
252
|
+
const originalWrite = process.stderr.write;
|
|
253
|
+
process.stderr.write = (chunk) => {
|
|
254
|
+
debugLogs.push(chunk.toString());
|
|
255
|
+
return true;
|
|
256
|
+
};
|
|
257
|
+
Debug.enable("doc-smith");
|
|
252
258
|
|
|
253
|
-
|
|
254
|
-
|
|
259
|
+
try {
|
|
260
|
+
const result = await saveD2Assets({ markdown, docsDir });
|
|
261
|
+
|
|
262
|
+
expect(typeof result).toBe("string");
|
|
263
|
+
expect(result).toContain(`}`);
|
|
264
|
+
expect(
|
|
265
|
+
debugLogs.some((log) => log.includes("Found assets cache, skipping generation")),
|
|
266
|
+
).toBe(true);
|
|
267
|
+
} finally {
|
|
268
|
+
process.stderr.write = originalWrite;
|
|
269
|
+
Debug.disable();
|
|
270
|
+
}
|
|
255
271
|
});
|
|
256
272
|
|
|
257
273
|
test("should handle D2 generation errors gracefully", async () => {
|
|
@@ -273,6 +289,28 @@ E -> F
|
|
|
273
289
|
global.fetch = originalFetch;
|
|
274
290
|
}
|
|
275
291
|
});
|
|
292
|
+
|
|
293
|
+
test("should write .d2 file when debug is enabled", async () => {
|
|
294
|
+
const docsDir = path.join(tempDir, "docs");
|
|
295
|
+
await mkdir(docsDir, { recursive: true });
|
|
296
|
+
|
|
297
|
+
const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
|
|
298
|
+
|
|
299
|
+
// Enable debug mode
|
|
300
|
+
Debug.enable("doc-smith");
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
await saveD2Assets({ markdown, docsDir });
|
|
304
|
+
|
|
305
|
+
const assetDir = path.join(docsDir, "../", TMP_ASSETS_DIR, "d2");
|
|
306
|
+
const files = await readdir(assetDir);
|
|
307
|
+
const d2File = files.find((file) => file.endsWith(".d2"));
|
|
308
|
+
expect(d2File).toBeDefined();
|
|
309
|
+
} finally {
|
|
310
|
+
// Restore debug mode
|
|
311
|
+
Debug.disable();
|
|
312
|
+
}
|
|
313
|
+
});
|
|
276
314
|
});
|
|
277
315
|
|
|
278
316
|
describe("beforePublishHook", () => {
|
|
@@ -350,13 +388,29 @@ E -> F
|
|
|
350
388
|
// First call should generate
|
|
351
389
|
await checkD2Content({ content });
|
|
352
390
|
|
|
353
|
-
// Second call should use cache
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
391
|
+
// Second call should use cache
|
|
392
|
+
const debugLogs = [];
|
|
393
|
+
const originalWrite = process.stderr.write;
|
|
394
|
+
process.stderr.write = (chunk) => {
|
|
395
|
+
debugLogs.push(chunk.toString());
|
|
396
|
+
return true;
|
|
397
|
+
};
|
|
398
|
+
Debug.enable("doc-smith");
|
|
357
399
|
|
|
358
|
-
|
|
359
|
-
|
|
400
|
+
try {
|
|
401
|
+
const startTime = Date.now();
|
|
402
|
+
await checkD2Content({ content });
|
|
403
|
+
const endTime = Date.now();
|
|
404
|
+
|
|
405
|
+
// Cache hit should be very fast (< 100ms)
|
|
406
|
+
expect(endTime - startTime).toBeLessThan(100);
|
|
407
|
+
expect(
|
|
408
|
+
debugLogs.some((log) => log.includes("Found assets cache, skipping generation")),
|
|
409
|
+
).toBe(true);
|
|
410
|
+
} finally {
|
|
411
|
+
process.stderr.write = originalWrite;
|
|
412
|
+
Debug.disable();
|
|
413
|
+
}
|
|
360
414
|
});
|
|
361
415
|
|
|
362
416
|
test("should handle generation errors in strict mode", async () => {
|
|
@@ -391,6 +445,23 @@ E -> F
|
|
|
391
445
|
expect(error).toBeDefined();
|
|
392
446
|
}
|
|
393
447
|
});
|
|
448
|
+
|
|
449
|
+
test("should write .d2 file when debug is enabled", async () => {
|
|
450
|
+
const content = "A -> B: debug test";
|
|
451
|
+
|
|
452
|
+
Debug.enable("doc-smith");
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
await checkD2Content({ content });
|
|
456
|
+
|
|
457
|
+
const assetDir = path.join(process.cwd(), ".aigne", "doc-smith", ".tmp", "assets", "d2");
|
|
458
|
+
const files = await readdir(assetDir);
|
|
459
|
+
const d2File = files.find((file) => file.endsWith(".d2"));
|
|
460
|
+
expect(d2File).toBeDefined();
|
|
461
|
+
} finally {
|
|
462
|
+
Debug.disable();
|
|
463
|
+
}
|
|
464
|
+
});
|
|
394
465
|
});
|
|
395
466
|
|
|
396
467
|
describe("ensureTmpDir", () => {
|
package/utils/auth-utils.mjs
CHANGED
|
@@ -25,7 +25,7 @@ const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
|
25
25
|
* @param {string} appUrl - The application URL
|
|
26
26
|
* @returns {Promise<string>} - The access token
|
|
27
27
|
*/
|
|
28
|
-
export async function getAccessToken(appUrl) {
|
|
28
|
+
export async function getAccessToken(appUrl, ltToken = "") {
|
|
29
29
|
const DOC_SMITH_ENV_FILE = join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
30
30
|
const { hostname } = new URL(appUrl);
|
|
31
31
|
|
|
@@ -92,7 +92,14 @@ export async function getAccessToken(appUrl) {
|
|
|
92
92
|
closeOnSuccess: true,
|
|
93
93
|
appName: "AIGNE DocSmith",
|
|
94
94
|
appLogo: "https://docsmith.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
|
|
95
|
-
openPage: (pageUrl) =>
|
|
95
|
+
openPage: (pageUrl) => {
|
|
96
|
+
const url = new URL(pageUrl);
|
|
97
|
+
if (ltToken) {
|
|
98
|
+
url.searchParams.set("__lt", ltToken);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
open(url.toString());
|
|
102
|
+
},
|
|
96
103
|
});
|
|
97
104
|
|
|
98
105
|
accessToken = result.accessKeySecret;
|
package/utils/blocklet.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { joinURL } from "ufo";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Custom error class for invalid blocklet application URLs
|
|
3
5
|
*/
|
|
@@ -23,17 +25,14 @@ export class ComponentNotFoundError extends Error {
|
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
export async function
|
|
27
|
-
const
|
|
28
|
-
const blockletJsUrl = `${url.origin}/__blocklet__.js?type=json`;
|
|
28
|
+
export async function getComponentInfo(appUrl) {
|
|
29
|
+
const blockletJsUrl = joinURL(appUrl, "__blocklet__.js?type=json");
|
|
29
30
|
|
|
30
31
|
let blockletJs;
|
|
31
32
|
try {
|
|
32
33
|
blockletJs = await fetch(blockletJsUrl, {
|
|
33
34
|
method: "GET",
|
|
34
|
-
headers: {
|
|
35
|
-
Accept: "application/json",
|
|
36
|
-
},
|
|
35
|
+
headers: { Accept: "application/json" },
|
|
37
36
|
});
|
|
38
37
|
} catch (error) {
|
|
39
38
|
throw new InvalidBlockletError(appUrl, null, error.message);
|
|
@@ -50,6 +49,12 @@ export async function getComponentMountPoint(appUrl, did) {
|
|
|
50
49
|
throw new InvalidBlockletError(appUrl, null, "Invalid JSON response");
|
|
51
50
|
}
|
|
52
51
|
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function getComponentMountPoint(appUrl, did) {
|
|
56
|
+
const config = await getComponentInfo(appUrl);
|
|
57
|
+
|
|
53
58
|
const component = config.componentMountPoints?.find((component) => component.did === did);
|
|
54
59
|
if (!component) {
|
|
55
60
|
throw new ComponentNotFoundError(did, appUrl);
|
|
@@ -57,3 +62,17 @@ export async function getComponentMountPoint(appUrl, did) {
|
|
|
57
62
|
|
|
58
63
|
return component.mountPoint;
|
|
59
64
|
}
|
|
65
|
+
|
|
66
|
+
export async function getComponentInfoWithMountPoint(appUrl, did) {
|
|
67
|
+
const config = await getComponentInfo(appUrl);
|
|
68
|
+
|
|
69
|
+
const component = config.componentMountPoints?.find((component) => component.did === did);
|
|
70
|
+
if (!component) {
|
|
71
|
+
throw new ComponentNotFoundError(did, appUrl);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
...config,
|
|
76
|
+
mountPoint: component.mountPoint,
|
|
77
|
+
};
|
|
78
|
+
}
|
package/utils/constants.mjs
CHANGED
|
@@ -330,6 +330,8 @@ export const DEPTH_RECOMMENDATION_LOGIC = {
|
|
|
330
330
|
// Component mount point ID for Discuss Kit
|
|
331
331
|
export const DISCUSS_KIT_DID = "z8ia1WEiBZ7hxURf6LwH21Wpg99vophFwSJdu";
|
|
332
332
|
|
|
333
|
+
export const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
334
|
+
|
|
333
335
|
// Discuss Kit related URLs
|
|
334
336
|
export const DISCUSS_KIT_STORE_URL =
|
|
335
337
|
"https://store.blocklet.dev/blocklets/z8ia1WEiBZ7hxURf6LwH21Wpg99vophFwSJdu";
|
|
@@ -513,7 +515,21 @@ export const RESOLUTION_STRATEGIES = {
|
|
|
513
515
|
export const D2_CONFIG = `vars: {
|
|
514
516
|
d2-config: {
|
|
515
517
|
layout-engine: elk
|
|
516
|
-
theme-id:
|
|
518
|
+
theme-id: 105
|
|
519
|
+
theme-overrides: {
|
|
520
|
+
N1: "#18181B"
|
|
521
|
+
N2: "#421E0B"
|
|
522
|
+
N4: "#E6E8EC"
|
|
523
|
+
N5: "#E6E8EC"
|
|
524
|
+
|
|
525
|
+
B3: "#FFE9D1"
|
|
526
|
+
B4: "transparent"
|
|
527
|
+
|
|
528
|
+
AA2: "#421E0B"
|
|
529
|
+
AA4: "#FFE9D1"
|
|
530
|
+
|
|
531
|
+
AB4: "#FBF4E4"
|
|
532
|
+
}
|
|
517
533
|
}
|
|
518
534
|
}`;
|
|
519
535
|
|