@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.
Files changed (58) hide show
  1. package/.aigne/doc-smith/config.yaml +3 -3
  2. package/.aigne/doc-smith/preferences.yml +58 -12
  3. package/.aigne/doc-smith/upload-cache.yaml +600 -207
  4. package/CHANGELOG.md +22 -0
  5. package/README.md +77 -5
  6. package/agents/input-generator.mjs +12 -6
  7. package/agents/publish-docs.mjs +53 -4
  8. package/docs/_sidebar.md +1 -1
  9. package/docs/advanced-how-it-works.md +55 -60
  10. package/docs/advanced-how-it-works.zh.md +60 -65
  11. package/docs/advanced-quality-assurance.md +73 -38
  12. package/docs/advanced-quality-assurance.zh.md +73 -38
  13. package/docs/advanced.md +2 -14
  14. package/docs/advanced.zh.md +5 -17
  15. package/docs/changelog.md +41 -4
  16. package/docs/changelog.zh.md +77 -40
  17. package/docs/cli-reference.md +79 -13
  18. package/docs/cli-reference.zh.md +92 -26
  19. package/docs/configuration-interactive-setup.md +102 -49
  20. package/docs/configuration-interactive-setup.zh.md +102 -49
  21. package/docs/configuration-language-support.md +69 -39
  22. package/docs/configuration-language-support.zh.md +68 -38
  23. package/docs/configuration-llm-setup.md +25 -62
  24. package/docs/configuration-llm-setup.zh.md +25 -62
  25. package/docs/configuration-preferences.md +79 -67
  26. package/docs/configuration-preferences.zh.md +78 -67
  27. package/docs/configuration.md +122 -109
  28. package/docs/configuration.zh.md +130 -117
  29. package/docs/features-generate-documentation.md +44 -24
  30. package/docs/features-generate-documentation.zh.md +52 -32
  31. package/docs/features-publish-your-docs.md +41 -40
  32. package/docs/features-publish-your-docs.zh.md +50 -49
  33. package/docs/features-translate-documentation.md +73 -17
  34. package/docs/features-translate-documentation.zh.md +76 -20
  35. package/docs/features-update-and-refine.md +72 -21
  36. package/docs/features-update-and-refine.zh.md +80 -29
  37. package/docs/features.md +24 -28
  38. package/docs/features.zh.md +25 -29
  39. package/docs/getting-started.md +87 -38
  40. package/docs/getting-started.zh.md +88 -39
  41. package/docs/overview.md +17 -35
  42. package/docs/overview.zh.md +18 -36
  43. package/package.json +9 -8
  44. package/prompts/content-detail-generator.md +1 -0
  45. package/prompts/document/custom-code-block.md +101 -0
  46. package/prompts/document/d2-chart/rules.md +941 -1031
  47. package/prompts/document/detail-generator.md +7 -53
  48. package/tests/input-generator.test.mjs +2 -2
  49. package/tests/kroki-utils.test.mjs +88 -17
  50. package/utils/auth-utils.mjs +9 -2
  51. package/utils/blocklet.mjs +25 -6
  52. package/utils/constants.mjs +17 -1
  53. package/utils/deploy.mjs +404 -0
  54. package/utils/kroki-utils.mjs +22 -14
  55. package/utils/markdown-checker.mjs +1 -1
  56. package/utils/utils.mjs +3 -2
  57. package/prompts/document/d2-chart/diy-examples.md +0 -44
  58. 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
- const assetsDir = path.join(docsDir, "../assets/d2");
242
- await mkdir(assetsDir, { recursive: true });
243
-
244
+ await mkdir(docsDir, { recursive: true });
244
245
  const markdown = `\`\`\`d2\nA -> B\n\`\`\``;
245
246
 
246
- // Pre-create a cached SVG file
247
- const testSvgContent = "<svg>test</svg>";
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
- // This would normally generate the same filename for the same content
251
- const result = await saveD2Assets({ markdown, docsDir });
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
- expect(typeof result).toBe("string");
254
- expect(result).toContain("![](../assets/d2/");
259
+ try {
260
+ const result = await saveD2Assets({ markdown, docsDir });
261
+
262
+ expect(typeof result).toBe("string");
263
+ expect(result).toContain(`![](${path.posix.join("..", TMP_ASSETS_DIR, "d2")}`);
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 (should be faster)
354
- const startTime = Date.now();
355
- await checkD2Content({ content });
356
- const endTime = Date.now();
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
- // Cache hit should be very fast (< 100ms)
359
- expect(endTime - startTime).toBeLessThan(100);
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", () => {
@@ -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) => open(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;
@@ -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 getComponentMountPoint(appUrl, did) {
27
- const url = new URL(appUrl);
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
+ }
@@ -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: 8
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