@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.
- package/.aigne/doc-smith/config.yaml +1 -1
- package/.aigne/doc-smith/history.yaml +37 -0
- package/.aigne/doc-smith/media-description.yaml +91 -0
- package/.aigne/doc-smith/preferences.yml +12 -0
- package/.aigne/doc-smith/upload-cache.yaml +36 -69
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +24 -0
- package/agents/clear/choose-contents.mjs +14 -1
- package/agents/clear/clear-media-description.mjs +129 -0
- package/agents/clear/index.yaml +3 -1
- package/agents/evaluate/code-snippet.mjs +28 -24
- package/agents/evaluate/document-structure.yaml +0 -4
- package/agents/evaluate/document.yaml +1 -5
- package/agents/generate/index.yaml +1 -0
- package/agents/generate/update-document-structure.yaml +9 -3
- package/agents/history/view.mjs +5 -2
- package/agents/init/index.mjs +10 -0
- package/agents/media/batch-generate-media-description.yaml +44 -0
- package/agents/media/generate-media-description.yaml +47 -0
- package/agents/media/load-media-description.mjs +238 -0
- package/agents/update/generate-document.yaml +10 -4
- package/agents/update/index.yaml +1 -0
- package/agents/update/update-document-detail.yaml +9 -3
- package/agents/update/user-review-document.mjs +2 -1
- package/agents/utils/load-sources.mjs +103 -53
- package/aigne.yaml +6 -0
- package/assets/report-template/report.html +34 -34
- package/docs/configuration-initial-setup.md +74 -55
- package/docs/configuration.ja.md +59 -86
- package/docs/configuration.md +59 -86
- package/docs/configuration.zh-TW.md +59 -86
- package/docs/configuration.zh.md +59 -86
- package/docs/getting-started.ja.md +43 -24
- package/docs/getting-started.md +29 -10
- package/docs/getting-started.zh-TW.md +42 -23
- package/docs/getting-started.zh.md +39 -20
- package/docs/guides-cleaning-up.ja.md +16 -15
- package/docs/guides-cleaning-up.md +19 -17
- package/docs/guides-cleaning-up.zh-TW.md +16 -15
- package/docs/guides-cleaning-up.zh.md +12 -11
- package/docs/guides-evaluating-documents.md +70 -29
- package/docs/guides-generating-documentation.ja.md +34 -32
- package/docs/guides-generating-documentation.md +59 -119
- package/docs/guides-generating-documentation.zh-TW.md +34 -32
- package/docs/guides-generating-documentation.zh.md +30 -28
- package/docs/guides-interactive-chat.md +34 -26
- package/docs/guides-managing-history.ja.md +17 -20
- package/docs/guides-managing-history.md +19 -17
- package/docs/guides-managing-history.zh-TW.md +18 -21
- package/docs/guides-managing-history.zh.md +13 -16
- package/docs/guides-publishing-your-docs.md +40 -35
- package/docs/guides-translating-documentation.ja.md +17 -17
- package/docs/guides-translating-documentation.md +39 -34
- package/docs/guides-translating-documentation.zh-TW.md +21 -21
- package/docs/guides-translating-documentation.zh.md +18 -18
- package/docs/guides-updating-documentation.ja.md +35 -35
- package/docs/guides-updating-documentation.md +11 -9
- package/docs/guides-updating-documentation.zh-TW.md +27 -27
- package/docs/guides-updating-documentation.zh.md +26 -26
- package/docs/overview.ja.md +13 -13
- package/docs/overview.md +2 -2
- package/docs/overview.zh-TW.md +19 -19
- package/docs/overview.zh.md +16 -16
- package/docs/release-notes.md +60 -27
- package/package.json +2 -1
- package/prompts/common/afs/afs-tools-usage.md +5 -0
- package/prompts/common/afs/use-afs-instruction.md +1 -0
- package/prompts/detail/generate/system-prompt.md +0 -13
- package/prompts/detail/generate/user-prompt.md +7 -0
- package/prompts/detail/update/system-prompt.md +1 -2
- package/prompts/detail/update/user-prompt.md +7 -0
- package/prompts/evaluate/document-structure.md +6 -7
- package/prompts/evaluate/document.md +16 -25
- package/prompts/media/media-description/system-prompt.md +35 -0
- package/prompts/media/media-description/user-prompt.md +8 -0
- package/prompts/structure/generate/system-prompt.md +0 -19
- package/prompts/structure/generate/user-prompt.md +22 -1
- package/prompts/structure/update/system-prompt.md +0 -17
- package/prompts/structure/update/user-prompt.md +24 -0
- package/tests/agents/history/view.test.mjs +97 -0
- package/tests/utils/history-utils.test.mjs +125 -97
- package/utils/constants/index.mjs +0 -107
- package/utils/file-utils.mjs +42 -1
- package/utils/history-utils.mjs +3 -3
- package/agents/update/fs-tools/glob.mjs +0 -184
- package/agents/update/fs-tools/grep.mjs +0 -317
- package/agents/update/fs-tools/read-file.mjs +0 -309
- package/media.md +0 -19
- package/tests/agents/update/fs-tools/glob.test.mjs +0 -438
- package/tests/agents/update/fs-tools/grep.test.mjs +0 -279
- 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 {
|
|
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
|
|
11
|
-
|
|
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
|
-
|
|
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("
|
|
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: "
|
|
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("
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
expect(
|
|
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("
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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: "
|
|
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].
|
|
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("
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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: "
|
|
121
|
-
documentPath: "/
|
|
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(
|
|
126
|
-
expect(history.entries[0].feedback).toBe("
|
|
127
|
-
expect(history.entries[
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
expect(
|
|
131
|
-
expect(
|
|
132
|
-
|
|
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("
|
|
136
|
-
|
|
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
|
-
|
|
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:
|
|
184
|
+
writeFileSync(historyPath, "invalid: yaml: content: 「「「", "utf8");
|
|
146
185
|
|
|
147
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
expect(
|
|
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
|
-
};
|
package/utils/file-utils.mjs
CHANGED
|
@@ -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
|
+
}
|
package/utils/history-utils.mjs
CHANGED
|
@@ -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 {
|