@aigne/doc-smith 0.8.12-beta.6 → 0.8.12-beta.8

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 (91) hide show
  1. package/.aigne/doc-smith/config.yaml +1 -1
  2. package/.aigne/doc-smith/history.yaml +37 -0
  3. package/.aigne/doc-smith/media-description.yaml +91 -0
  4. package/.aigne/doc-smith/preferences.yml +12 -0
  5. package/.aigne/doc-smith/upload-cache.yaml +36 -69
  6. package/.release-please-manifest.json +1 -1
  7. package/CHANGELOG.md +24 -0
  8. package/agents/clear/choose-contents.mjs +14 -1
  9. package/agents/clear/clear-media-description.mjs +129 -0
  10. package/agents/clear/index.yaml +3 -1
  11. package/agents/evaluate/code-snippet.mjs +28 -24
  12. package/agents/evaluate/document-structure.yaml +0 -4
  13. package/agents/evaluate/document.yaml +1 -5
  14. package/agents/generate/index.yaml +1 -0
  15. package/agents/generate/update-document-structure.yaml +9 -3
  16. package/agents/history/view.mjs +5 -2
  17. package/agents/init/index.mjs +10 -0
  18. package/agents/media/batch-generate-media-description.yaml +44 -0
  19. package/agents/media/generate-media-description.yaml +47 -0
  20. package/agents/media/load-media-description.mjs +238 -0
  21. package/agents/update/generate-document.yaml +10 -4
  22. package/agents/update/index.yaml +1 -0
  23. package/agents/update/update-document-detail.yaml +9 -3
  24. package/agents/update/user-review-document.mjs +2 -1
  25. package/agents/utils/load-sources.mjs +103 -53
  26. package/aigne.yaml +6 -0
  27. package/assets/report-template/report.html +34 -34
  28. package/docs/configuration-initial-setup.md +74 -55
  29. package/docs/configuration.ja.md +59 -86
  30. package/docs/configuration.md +59 -86
  31. package/docs/configuration.zh-TW.md +59 -86
  32. package/docs/configuration.zh.md +59 -86
  33. package/docs/getting-started.ja.md +43 -24
  34. package/docs/getting-started.md +29 -10
  35. package/docs/getting-started.zh-TW.md +42 -23
  36. package/docs/getting-started.zh.md +39 -20
  37. package/docs/guides-cleaning-up.ja.md +16 -15
  38. package/docs/guides-cleaning-up.md +19 -17
  39. package/docs/guides-cleaning-up.zh-TW.md +16 -15
  40. package/docs/guides-cleaning-up.zh.md +12 -11
  41. package/docs/guides-evaluating-documents.md +70 -29
  42. package/docs/guides-generating-documentation.ja.md +34 -32
  43. package/docs/guides-generating-documentation.md +59 -119
  44. package/docs/guides-generating-documentation.zh-TW.md +34 -32
  45. package/docs/guides-generating-documentation.zh.md +30 -28
  46. package/docs/guides-interactive-chat.md +34 -26
  47. package/docs/guides-managing-history.ja.md +17 -20
  48. package/docs/guides-managing-history.md +19 -17
  49. package/docs/guides-managing-history.zh-TW.md +18 -21
  50. package/docs/guides-managing-history.zh.md +13 -16
  51. package/docs/guides-publishing-your-docs.md +40 -35
  52. package/docs/guides-translating-documentation.ja.md +17 -17
  53. package/docs/guides-translating-documentation.md +39 -34
  54. package/docs/guides-translating-documentation.zh-TW.md +21 -21
  55. package/docs/guides-translating-documentation.zh.md +18 -18
  56. package/docs/guides-updating-documentation.ja.md +35 -35
  57. package/docs/guides-updating-documentation.md +11 -9
  58. package/docs/guides-updating-documentation.zh-TW.md +27 -27
  59. package/docs/guides-updating-documentation.zh.md +26 -26
  60. package/docs/overview.ja.md +13 -13
  61. package/docs/overview.md +2 -2
  62. package/docs/overview.zh-TW.md +19 -19
  63. package/docs/overview.zh.md +16 -16
  64. package/docs/release-notes.md +60 -27
  65. package/package.json +2 -1
  66. package/prompts/common/afs/afs-tools-usage.md +5 -0
  67. package/prompts/common/afs/use-afs-instruction.md +1 -0
  68. package/prompts/detail/generate/system-prompt.md +0 -13
  69. package/prompts/detail/generate/user-prompt.md +7 -0
  70. package/prompts/detail/update/system-prompt.md +1 -2
  71. package/prompts/detail/update/user-prompt.md +7 -0
  72. package/prompts/evaluate/document-structure.md +6 -7
  73. package/prompts/evaluate/document.md +16 -25
  74. package/prompts/media/media-description/system-prompt.md +35 -0
  75. package/prompts/media/media-description/user-prompt.md +8 -0
  76. package/prompts/structure/generate/system-prompt.md +0 -19
  77. package/prompts/structure/generate/user-prompt.md +22 -1
  78. package/prompts/structure/update/system-prompt.md +0 -17
  79. package/prompts/structure/update/user-prompt.md +24 -0
  80. package/tests/agents/history/view.test.mjs +97 -0
  81. package/tests/utils/history-utils.test.mjs +125 -97
  82. package/utils/constants/index.mjs +0 -107
  83. package/utils/file-utils.mjs +42 -1
  84. package/utils/history-utils.mjs +3 -3
  85. package/agents/update/fs-tools/glob.mjs +0 -184
  86. package/agents/update/fs-tools/grep.mjs +0 -317
  87. package/agents/update/fs-tools/read-file.mjs +0 -309
  88. package/media.md +0 -19
  89. package/tests/agents/update/fs-tools/glob.test.mjs +0 -438
  90. package/tests/agents/update/fs-tools/grep.test.mjs +0 -279
  91. package/tests/agents/update/fs-tools/read-file.test.mjs +0 -549
@@ -20,3 +20,27 @@ Initial Documentation Structure:
20
20
  <user_feedback>
21
21
  {{ feedback }}
22
22
  </user_feedback>
23
+
24
+ {% include "../../common/afs/afs-tools-usage.md" %}
25
+
26
+ <instructions>
27
+
28
+ Objectives:
29
+ - Create a clear and logical structural plan that comprehensively presents information from the user-provided context while providing users with intuitive navigation paths.
30
+ - Each {{nodeName}} should include: a {{nodeName}} title, a one-sentence introduction describing its main content, with presentation and organization methods tailored to the target audience.
31
+
32
+ Processing workflow:
33
+
34
+ - If user feedback is not in English, translate it to English first to better understand user intent
35
+ - Analyze user feedback to understand the specific intent (add, delete, update, or move sections)
36
+ - Determine which tools to use based on the user's requirements
37
+ - Execute the appropriate operations using available tools
38
+ - Ensure all modifications maintain documentation structure integrity
39
+ - Return 'success' when the latest version of websiteStructure meets user feedback
40
+
41
+ Rules:
42
+ ** All changes must be made using Tools. **
43
+ ** Carefully check if the latest version of documentStructure data meets user requirements, must avoid duplicate Tool calls. **
44
+
45
+ {% include "../../common/afs/use-afs-instruction.md" %}
46
+ </instructions>
@@ -0,0 +1,97 @@
1
+ import { afterEach, beforeEach, describe, expect, test, mock, spyOn } from "bun:test";
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { DOC_SMITH_DIR } from "../../../utils/constants/index.mjs";
5
+ import viewHistory from "../../../agents/history/view.mjs";
6
+
7
+ const TEST_DIR = join(process.cwd(), `${DOC_SMITH_DIR}-test`);
8
+ const ORIGINAL_CWD = process.cwd();
9
+
10
+ describe("History View", () => {
11
+ let consoleLogMock;
12
+
13
+ beforeEach(async () => {
14
+ // Clean up test directory
15
+ if (existsSync(TEST_DIR)) {
16
+ rmSync(TEST_DIR, { recursive: true });
17
+ }
18
+ mkdirSync(TEST_DIR, { recursive: true });
19
+
20
+ // Change to test directory
21
+ process.chdir(TEST_DIR);
22
+
23
+ // Spy on console.log
24
+ consoleLogMock = spyOn(console, "log");
25
+ });
26
+
27
+ afterEach(() => {
28
+ // Restore original directory
29
+ process.chdir(ORIGINAL_CWD);
30
+
31
+ // Clean up
32
+ if (existsSync(TEST_DIR)) {
33
+ rmSync(TEST_DIR, { recursive: true });
34
+ }
35
+
36
+ mock.restore();
37
+ });
38
+
39
+ test("should display message when no history exists", () => {
40
+ // No history file exists
41
+ const result = viewHistory();
42
+
43
+ expect(result).toEqual({});
44
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("No update history found"));
45
+ });
46
+
47
+ test("should display formatted history entries", async () => {
48
+ // Create history file with sample data
49
+ const historyPath = join(process.cwd(), DOC_SMITH_DIR, "history.yaml");
50
+ mkdirSync(join(process.cwd(), DOC_SMITH_DIR), { recursive: true });
51
+
52
+ const historyData = {
53
+ entries: [
54
+ {
55
+ timestamp: "2023-01-01T00:00:00.000Z",
56
+ operation: "document_update",
57
+ feedback: "Updated documentation",
58
+ documentPath: "/readme.md",
59
+ },
60
+ {
61
+ timestamp: "2023-01-02T00:00:00.000Z",
62
+ operation: "structure_update",
63
+ feedback: "Reorganized sections",
64
+ },
65
+ ],
66
+ };
67
+
68
+ // Use YAML format instead of JSON
69
+ const { stringify } = await import("yaml");
70
+ writeFileSync(historyPath, stringify(historyData), "utf8");
71
+
72
+ const result = viewHistory();
73
+
74
+ expect(result).toEqual({});
75
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("📜 Update History"));
76
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("document_update"));
77
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("structure_update"));
78
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("Updated documentation"));
79
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("Reorganized sections"));
80
+ });
81
+
82
+ test("should handle empty history entries", async () => {
83
+ // Create history file with empty entries
84
+ const historyPath = join(process.cwd(), DOC_SMITH_DIR, "history.yaml");
85
+ mkdirSync(join(process.cwd(), DOC_SMITH_DIR), { recursive: true });
86
+
87
+ const historyData = { entries: [] };
88
+ // Use YAML format instead of JSON
89
+ const { stringify } = await import("yaml");
90
+ writeFileSync(historyPath, stringify(historyData), "utf8");
91
+
92
+ const result = viewHistory();
93
+
94
+ expect(result).toEqual({});
95
+ expect(consoleLogMock).toHaveBeenCalledWith(expect.stringContaining("No update history found"));
96
+ });
97
+ });
@@ -1,14 +1,17 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, test, mock, spyOn } from "bun:test";
2
2
  import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { DOC_SMITH_DIR } from "../../utils/constants/index.mjs";
5
- import { getHistory, isGitAvailable, recordUpdate } from "../../utils/history-utils.mjs";
5
+ import { recordUpdate, getHistory } from "../../utils/history-utils.mjs";
6
6
 
7
+ // Mock the child_process module
7
8
  const TEST_DIR = join(process.cwd(), `${DOC_SMITH_DIR}-test`);
8
9
  const ORIGINAL_CWD = process.cwd();
9
10
 
10
- describe("History Utils - Unified", () => {
11
- beforeEach(() => {
11
+ describe("History Utils", () => {
12
+ let execSyncMock;
13
+
14
+ beforeEach(async () => {
12
15
  // Clean up test directory
13
16
  if (existsSync(TEST_DIR)) {
14
17
  rmSync(TEST_DIR, { recursive: true });
@@ -17,6 +20,9 @@ describe("History Utils - Unified", () => {
17
20
 
18
21
  // Change to test directory
19
22
  process.chdir(TEST_DIR);
23
+
24
+ // Get the mocked execSync
25
+ execSyncMock = spyOn(await import("node:child_process"), "execSync");
20
26
  });
21
27
 
22
28
  afterEach(() => {
@@ -27,11 +33,8 @@ describe("History Utils - Unified", () => {
27
33
  if (existsSync(TEST_DIR)) {
28
34
  rmSync(TEST_DIR, { recursive: true });
29
35
  }
30
- });
31
36
 
32
- test("detects git availability", () => {
33
- const hasGit = isGitAvailable();
34
- expect(typeof hasGit).toBe("boolean");
37
+ mock.restore();
35
38
  });
36
39
 
37
40
  test("skips recording on empty feedback", () => {
@@ -46,133 +49,158 @@ describe("History Utils - Unified", () => {
46
49
  expect(history.entries.length).toBe(0);
47
50
  });
48
51
 
49
- test("records update in YAML", () => {
52
+ test("git not available - write to YAML only", () => {
53
+ // Mock git as not available
54
+ execSyncMock.mockImplementation((command) => {
55
+ if (command === "git --version") {
56
+ throw new Error("git: command not found");
57
+ }
58
+ throw new Error("Unknown command");
59
+ });
60
+
61
+ // Record update
50
62
  recordUpdate({
51
- operation: "structure_update",
63
+ operation: "document_update",
52
64
  feedback: "Test feedback",
65
+ documentPath: "/test",
53
66
  });
54
67
 
68
+ // Verify YAML record was created
55
69
  const history = getHistory();
56
70
  expect(history.entries.length).toBe(1);
57
71
  expect(history.entries[0].feedback).toBe("Test feedback");
58
- expect(history.entries[0].operation).toBe("structure_update");
72
+ expect(history.entries[0].operation).toBe("document_update");
73
+ expect(history.entries[0].documentPath).toBe("/test");
59
74
  expect(history.entries[0].timestamp).toBeDefined();
60
- });
61
-
62
- test("records document path when provided as string", () => {
63
- recordUpdate({
64
- operation: "document_update",
65
- feedback: "Update document",
66
- documentPath: "/getting-started",
67
- });
68
75
 
69
- const history = getHistory();
70
- expect(history.entries.length).toBe(1);
71
- expect(history.entries[0].documentPath).toBe("/getting-started");
76
+ // Verify no git repository was created
77
+ const gitDir = join(process.cwd(), DOC_SMITH_DIR, ".git");
78
+ expect(existsSync(gitDir)).toBe(false);
72
79
  });
73
80
 
74
- test("records single document path for each update", () => {
75
- recordUpdate({
76
- operation: "document_update",
77
- feedback: "Update single document",
78
- documentPath: "/getting-started",
81
+ test("current directory is git repo - skip git integration", () => {
82
+ // Mock git as available and in repo
83
+ execSyncMock.mockImplementation((command) => {
84
+ if (command === "git --version") {
85
+ return "git version 2.30.0";
86
+ }
87
+ if (command === "git rev-parse --is-inside-work-tree") {
88
+ return "true";
89
+ }
90
+ throw new Error("Unknown command");
79
91
  });
80
92
 
81
- const history = getHistory();
82
- expect(history.entries.length).toBe(1);
83
- expect(history.entries[0].feedback).toBe("Update single document");
84
- expect(history.entries[0].documentPath).toBe("/getting-started");
85
- });
86
-
87
- test("does not include documentPath field when documentPath is null", () => {
93
+ // Record update
88
94
  recordUpdate({
89
95
  operation: "structure_update",
90
- feedback: "Update structure",
91
- documentPath: null,
96
+ feedback: "Structure update",
92
97
  });
93
98
 
99
+ // Verify YAML record was created
94
100
  const history = getHistory();
95
101
  expect(history.entries.length).toBe(1);
96
- expect(history.entries[0].documentPath).toBeUndefined();
102
+ expect(history.entries[0].feedback).toBe("Structure update");
103
+
104
+ // Verify no new git repository was created (since we're already in a git repo)
105
+ const gitDir = join(process.cwd(), DOC_SMITH_DIR, ".git");
106
+ expect(existsSync(gitDir)).toBe(false);
97
107
  });
98
108
 
99
- test("maintains chronological order (newest first)", () => {
100
- recordUpdate({ operation: "structure_update", feedback: "First" });
101
- // Small delay to ensure different timestamps
102
- const now = Date.now();
103
- while (Date.now() === now) {
104
- // Wait for next millisecond
105
- }
106
- recordUpdate({ operation: "document_update", feedback: "Second" });
109
+ test("write YAML records and commit to git", () => {
110
+ // Ensure the DOC_SMITH_DIR exists
111
+ const docSmithDir = join(process.cwd(), DOC_SMITH_DIR);
112
+ mkdirSync(docSmithDir, { recursive: true });
113
+
114
+ // Mock git as available but not in repo
115
+ execSyncMock.mockImplementation((command) => {
116
+ if (command === "git --version") {
117
+ return "git version 2.30.0";
118
+ }
119
+ if (command === "git rev-parse --is-inside-work-tree") {
120
+ throw new Error("not a git repository");
121
+ }
122
+ if (command === "git init") {
123
+ return "Initialized empty Git repository";
124
+ }
125
+ if (command === 'git add .gitignore && git commit -m "Initialize doc-smith history"') {
126
+ return "Initial commit";
127
+ }
128
+ if (command === "git add docs/ config.yaml preferences.yml history.yaml") {
129
+ return "Files added";
130
+ }
131
+ if (command === "git diff --cached --quiet") {
132
+ throw new Error("Has changes");
133
+ }
134
+ if (command === 'git commit -m "First update"') {
135
+ return "Commit successful";
136
+ }
137
+ if (command === 'git commit -m "Second update"') {
138
+ return "Commit successful";
139
+ }
140
+ throw new Error("Unknown command: ", command);
141
+ });
107
142
 
108
- const history = getHistory();
109
- expect(history.entries.length).toBe(2);
110
- expect(history.entries[0].feedback).toBe("Second");
111
- expect(history.entries[1].feedback).toBe("First");
112
- });
143
+ // Record multiple updates - this should trigger git repo creation and commits
144
+ recordUpdate({
145
+ operation: "document_update",
146
+ feedback: "First update",
147
+ documentPath: "/first",
148
+ });
113
149
 
114
- test("handles multiple updates", () => {
115
- recordUpdate({ operation: "structure_update", feedback: "Update 1" });
116
- recordUpdate({ operation: "document_update", feedback: "Update 2", documentPath: "/home" });
117
- recordUpdate({ operation: "document_update", feedback: "Update 3", documentPath: "/about" });
118
150
  recordUpdate({
119
151
  operation: "translation_update",
120
- feedback: "Update 4",
121
- documentPath: "/api",
152
+ feedback: "Second update",
153
+ documentPath: "/second",
122
154
  });
123
155
 
156
+ // Verify YAML records
124
157
  const history = getHistory();
125
- expect(history.entries.length).toBe(4);
126
- expect(history.entries[0].feedback).toBe("Update 4");
127
- expect(history.entries[0].documentPath).toBe("/api");
128
- expect(history.entries[1].feedback).toBe("Update 3");
129
- expect(history.entries[1].documentPath).toBe("/about");
130
- expect(history.entries[2].feedback).toBe("Update 2");
131
- expect(history.entries[2].documentPath).toBe("/home");
132
- expect(history.entries[3].feedback).toBe("Update 1");
158
+ expect(history.entries.length).toBe(2);
159
+ expect(history.entries[0].feedback).toBe("Second update"); // Newest first
160
+ expect(history.entries[1].feedback).toBe("First update");
161
+
162
+ // Verify git commands were called
163
+ expect(execSyncMock).toHaveBeenCalledWith("git --version", { stdio: "ignore" });
164
+ expect(execSyncMock).toHaveBeenCalledWith("git rev-parse --is-inside-work-tree", {
165
+ cwd: process.cwd(),
166
+ stdio: "pipe",
167
+ encoding: "utf8",
168
+ });
169
+ expect(execSyncMock).toHaveBeenCalledWith("git init", {
170
+ cwd: join(process.cwd(), DOC_SMITH_DIR),
171
+ stdio: "ignore",
172
+ });
133
173
  });
134
174
 
135
- test("returns empty history when file does not exist", () => {
136
- const history = getHistory();
175
+ test("query history.yaml - handle various scenarios", () => {
176
+ // Test empty history
177
+ let history = getHistory();
137
178
  expect(history.entries).toBeDefined();
138
179
  expect(history.entries.length).toBe(0);
139
- });
140
180
 
141
- test("handles corrupted history file gracefully", () => {
142
- // Create corrupted YAML file
181
+ // Test corrupted YAML file
143
182
  const historyPath = join(process.cwd(), DOC_SMITH_DIR, "history.yaml");
144
183
  mkdirSync(join(process.cwd(), DOC_SMITH_DIR), { recursive: true });
145
- writeFileSync(historyPath, "invalid: yaml: content: [[[", "utf8");
184
+ writeFileSync(historyPath, "invalid: yaml: content: 「「「", "utf8");
146
185
 
147
- const history = getHistory();
186
+ history = getHistory();
148
187
  expect(history.entries).toBeDefined();
149
188
  expect(history.entries.length).toBe(0);
150
- });
151
-
152
- test(`creates ${DOC_SMITH_DIR} directory if not exists`, () => {
153
- recordUpdate({
154
- operation: "document_update",
155
- feedback: "Test",
156
- });
157
189
 
158
- const docSmithDir = join(process.cwd(), DOC_SMITH_DIR);
159
- expect(existsSync(docSmithDir)).toBe(true);
160
- });
161
-
162
- test("timestamp is in ISO 8601 format", () => {
163
- recordUpdate({
164
- operation: "structure_update",
165
- feedback: "Test timestamp",
166
- });
167
-
168
- const history = getHistory();
169
- const timestamp = history.entries[0].timestamp;
170
-
171
- // Validate ISO 8601 format
172
- expect(timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
173
-
174
- // Validate it's a valid date
175
- const date = new Date(timestamp);
176
- expect(date.toISOString()).toBe(timestamp);
190
+ // Test valid history
191
+ const validHistory = {
192
+ entries: [
193
+ {
194
+ timestamp: "2023-01-01T00:00:00.000Z",
195
+ operation: "document_update",
196
+ feedback: "Valid entry",
197
+ },
198
+ ],
199
+ };
200
+ writeFileSync(historyPath, JSON.stringify(validHistory), "utf8");
201
+
202
+ history = getHistory();
203
+ expect(history.entries.length).toBe(1);
204
+ expect(history.entries[0].feedback).toBe("Valid entry");
177
205
  });
178
206
  });
@@ -548,110 +548,3 @@ export const DOC_SMITH_DIR = ".aigne/doc-smith";
548
548
  export const TMP_DIR = ".tmp";
549
549
  export const TMP_DOCS_DIR = "docs";
550
550
  export const TMP_ASSETS_DIR = "assets";
551
-
552
- // Default evaluation scoring weights
553
- export const DEFAULT_EVALUATION_WEIGHTS = {
554
- evaluationConfig: {
555
- accuracy: {
556
- weight: 0.35,
557
- description: "Technical correctness of the documentation.",
558
- subDimensions: {
559
- signatureConsistency: {
560
- weight: 0.5,
561
- description:
562
- "Function signatures match the source code (e.g., OpenAPI spec, TypeScript types).",
563
- },
564
- linkValidity: {
565
- weight: 0.25,
566
- description: "No broken internal links or invalid cross-references.",
567
- },
568
- codeExampleIntegrity: {
569
- weight: 0.25,
570
- description:
571
- "All code blocks are syntactically valid and labeled with the correct language.",
572
- },
573
- },
574
- },
575
- coverage: {
576
- weight: 0.35,
577
- description: "Completeness of documentation for all public components.",
578
- subDimensions: {
579
- apiCoverage: {
580
- weight: 0.4,
581
- description: "Percentage of exported functions/classes documented.",
582
- },
583
- paramReturnCoverage: {
584
- weight: 0.3,
585
- description: "Percentage of function parameters and return values described.",
586
- },
587
- changeTracking: {
588
- weight: 0.3,
589
- description:
590
- "All new or modified code since the last commit is reflected in the documentation.",
591
- },
592
- },
593
- },
594
- readability: {
595
- weight: 0.15,
596
- description: "Ease of understanding for human readers.",
597
- subDimensions: {
598
- clarityScore: {
599
- weight: 0.28,
600
- description:
601
- "Overall clarity including reading coherence, logical flow and translation quality.",
602
- },
603
- consistency: {
604
- weight: 0.12,
605
- description: "Terminology, style and formatting are unified and professional.",
606
- },
607
- contentQuality: {
608
- weight: 0.24,
609
- description:
610
- "Accurate and complete content that adds value with sufficient detail and examples.",
611
- },
612
- purposeAlignment: {
613
- weight: 0.14,
614
- description:
615
- "Content matches the intended purpose such as quick start, API reference or troubleshooting.",
616
- },
617
- audienceAlignment: {
618
- weight: 0.12,
619
- description:
620
- "Language and examples match the target audience (e.g., developers, non-technical users).",
621
- },
622
- knowledgeLevelAlignment: {
623
- weight: 0.1,
624
- description: "Depth and difficulty fit the readers' knowledge level.",
625
- },
626
- },
627
- },
628
- structure: {
629
- weight: 0.15,
630
- description: "Organization and navigability of the documentation.",
631
- subDimensions: {
632
- navigability: {
633
- weight: 0.2,
634
- description: "Table of contents entries correctly link to headings.",
635
- },
636
- informationScent: {
637
- weight: 0.2,
638
- description:
639
- "Key pages such as Quick Start can be reached within three clicks from the homepage.",
640
- },
641
- purposeCoverage: {
642
- weight: 0.25,
643
- description:
644
- "Structure covers all selected documentation goals without forcing them into a single page.",
645
- },
646
- audienceCoverage: {
647
- weight: 0.2,
648
- description: "Structure covers the main intended audience groups.",
649
- },
650
- coverageDepthAlignment: {
651
- weight: 0.15,
652
- description: "Overall balance of conciseness, depth and breadth meets user expectations.",
653
- },
654
- },
655
- },
656
- },
657
- };
@@ -12,7 +12,7 @@ import { INTELLIGENT_SUGGESTION_TOKEN_THRESHOLD } from "./constants/index.mjs";
12
12
  * @param {string} dir - Directory path to check
13
13
  * @returns {boolean} True if inside a git repository
14
14
  */
15
- function isInGitRepository(dir) {
15
+ export function isInGitRepository(dir) {
16
16
  try {
17
17
  execSync("git rev-parse --is-inside-work-tree", {
18
18
  cwd: dir,
@@ -558,3 +558,44 @@ export function getStructurePlanPath(workDir) {
558
558
  const cwd = workDir || process.cwd();
559
559
  return path.join(cwd, ".aigne", "doc-smith", "output", "structure-plan.json");
560
560
  }
561
+
562
+ /**
563
+ * Get MIME type from file path based on extension
564
+ * @param {string} filePath - File path
565
+ * @returns {string} MIME type
566
+ */
567
+ export function getMimeType(filePath) {
568
+ const ext = path.extname(filePath).toLowerCase();
569
+ const mimeTypes = {
570
+ ".jpg": "image/jpeg",
571
+ ".jpeg": "image/jpeg",
572
+ ".png": "image/png",
573
+ ".gif": "image/gif",
574
+ ".bmp": "image/bmp",
575
+ ".webp": "image/webp",
576
+ ".svg": "image/svg+xml",
577
+ ".heic": "image/heic",
578
+ ".heif": "image/heif",
579
+ ".mp4": "video/mp4",
580
+ ".mpeg": "video/mpeg",
581
+ ".mpg": "video/mpg",
582
+ ".mov": "video/mov",
583
+ ".avi": "video/avi",
584
+ ".flv": "video/x-flv",
585
+ ".mkv": "video/x-matroska",
586
+ ".webm": "video/webm",
587
+ ".wmv": "video/wmv",
588
+ ".m4v": "video/x-m4v",
589
+ ".3gpp": "video/3gpp",
590
+ };
591
+ return mimeTypes[ext] || "application/octet-stream";
592
+ }
593
+
594
+ /**
595
+ * Get media description cache file path
596
+ * @returns {string} Absolute path to media-description.yaml
597
+ */
598
+ export function getMediaDescriptionCachePath() {
599
+ const cwd = process.cwd();
600
+ return path.join(cwd, ".aigne", "doc-smith", "media-description.yaml");
601
+ }
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { parse, stringify } from "yaml";
5
5
  import { DOC_SMITH_DIR } from "./constants/index.mjs";
6
+ import { isInGitRepository } from "./file-utils.mjs";
6
7
 
7
8
  const HISTORY_FILE = "history.yaml";
8
9
 
@@ -161,9 +162,8 @@ export function recordUpdate({ operation, feedback, documentPath = null }) {
161
162
  // Always record in YAML
162
163
  recordUpdateYaml({ operation, feedback, documentPath });
163
164
 
164
- // Also record in git if available
165
- if (isGitAvailable()) {
166
- // Initialize git repo on first update if not exists
165
+ // Also record in git if git is available and not in a git repository
166
+ if (isGitAvailable() && !isInGitRepository(process.cwd())) {
167
167
  ensureGitRepo();
168
168
  recordUpdateGit({ feedback });
169
169
  } else {