@aigne/doc-smith 0.8.5 → 0.8.7
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/output/structure-plan.json +1 -5
- package/CHANGELOG.md +25 -0
- package/README.md +3 -3
- package/agents/{chat.yaml → chat/index.yaml} +7 -7
- package/agents/generate/check-document-structure.yaml +30 -0
- package/agents/{check-structure-plan.mjs → generate/check-need-generate-structure.mjs} +21 -21
- package/agents/generate/generate-structure.yaml +58 -0
- package/agents/{docs-generator.yaml → generate/index.yaml} +15 -16
- package/agents/generate/refine-document-structure.yaml +12 -0
- package/agents/{input-generator.mjs → init/index.mjs} +34 -27
- package/agents/{manage-prefs.mjs → prefs/index.mjs} +16 -16
- package/agents/publish/index.yaml +17 -0
- package/agents/{publish-docs.mjs → publish/publish-docs.mjs} +15 -16
- package/agents/schema/{structure-plan-result.yaml → document-execution-structure.yaml} +3 -3
- package/agents/schema/document-structure.yaml +26 -0
- package/agents/{language-selector.mjs → translate/choose-language.mjs} +5 -5
- package/agents/{retranslate.yaml → translate/index.yaml} +17 -18
- package/agents/translate/translate-document.yaml +32 -0
- package/agents/{batch-translate.yaml → translate/translate-multilingual.yaml} +5 -5
- package/agents/update/batch-generate-document.yaml +19 -0
- package/agents/{check-detail.mjs → update/check-document.mjs} +16 -16
- package/agents/{detail-generator-and-translate.yaml → update/generate-and-translate-document.yaml} +23 -23
- package/agents/update/generate-document.yaml +50 -0
- package/agents/{detail-regenerator.yaml → update/index.yaml} +16 -17
- package/agents/{action-success.mjs → utils/action-success.mjs} +2 -2
- package/agents/{check-detail-result.mjs → utils/check-detail-result.mjs} +3 -3
- package/agents/{check-feedback-refiner.mjs → utils/check-feedback-refiner.mjs} +6 -6
- package/agents/{find-items-by-paths.mjs → utils/choose-docs.mjs} +25 -10
- package/agents/{docs-fs.yaml → utils/docs-fs-actor.yaml} +3 -1
- package/agents/utils/feedback-refiner.yaml +50 -0
- package/agents/{find-item-by-path.mjs → utils/find-item-by-path.mjs} +17 -7
- package/agents/{find-user-preferences-by-path.mjs → utils/find-user-preferences-by-path.mjs} +1 -1
- package/agents/utils/format-document-structure.mjs +25 -0
- package/agents/{load-sources.mjs → utils/load-sources.mjs} +41 -28
- package/agents/{save-docs.mjs → utils/save-docs.mjs} +16 -16
- package/agents/{save-single-doc.mjs → utils/save-single-doc.mjs} +2 -2
- package/agents/{transform-detail-datasources.mjs → utils/transform-detail-datasources.mjs} +1 -1
- package/aigne.yaml +35 -35
- package/docs/cli-reference.md +1 -1
- package/docs/features-generate-documentation.md +1 -1
- package/docs/features-update-and-refine.md +2 -2
- package/docs-mcp/analyze-docs-relevance.yaml +10 -10
- package/docs-mcp/docs-search.yaml +5 -3
- package/package.json +10 -8
- package/prompts/{document → detail/custom}/custom-code-block.md +6 -6
- package/prompts/detail/custom/custom-components.md +172 -0
- package/prompts/{document → detail}/d2-chart/rules.md +95 -1
- package/prompts/{document → detail}/detail-example.md +80 -61
- package/prompts/{document/detail-generator.md → detail/document-rules.md} +4 -8
- package/prompts/{content-detail-generator.md → detail/generate-document.md} +48 -25
- package/prompts/{check-structure-planning-result.md → structure/check-document-structure.md} +23 -17
- package/prompts/{document/structure-planning.md → structure/document-rules.md} +0 -2
- package/prompts/{structure-planning.md → structure/generate-structure.md} +51 -30
- package/prompts/{document → structure}/structure-example.md +2 -2
- package/prompts/{document → structure}/structure-getting-started.md +2 -2
- package/prompts/translate/glossary.md +6 -0
- package/prompts/{translator.md → translate/translate-document.md} +29 -10
- package/prompts/{feedback-refiner.md → utils/feedback-refiner.md} +8 -8
- package/tests/agents/chat/chat.test.mjs +46 -0
- package/tests/agents/generate/check-document-structure.test.mjs +51 -0
- package/tests/agents/generate/check-need-generate-structure.test.mjs +292 -0
- package/tests/agents/generate/generate-structure.test.mjs +51 -0
- package/tests/{input-generator.test.mjs → agents/init/init.test.mjs} +19 -17
- package/tests/agents/prefs/prefs.test.mjs +431 -0
- package/tests/agents/publish/publish-docs.test.mjs +642 -0
- package/tests/agents/translate/choose-language.test.mjs +311 -0
- package/tests/agents/translate/translate-document.test.mjs +51 -0
- package/tests/agents/update/check-document.test.mjs +523 -0
- package/tests/agents/update/generate-document.test.mjs +51 -0
- package/tests/agents/utils/action-success.test.mjs +54 -0
- package/tests/{check-detail-result.test.mjs → agents/utils/check-detail-result.test.mjs} +98 -98
- package/tests/agents/utils/check-feedback-refiner.test.mjs +478 -0
- package/tests/agents/utils/choose-docs.test.mjs +417 -0
- package/tests/agents/utils/exit.test.mjs +70 -0
- package/tests/agents/utils/feedback-refiner.test.mjs +51 -0
- package/tests/agents/utils/find-item-by-path.test.mjs +526 -0
- package/tests/agents/utils/find-user-preferences-by-path.test.mjs +382 -0
- package/tests/agents/utils/format-document-structure.test.mjs +264 -0
- package/tests/agents/utils/fs.test.mjs +267 -0
- package/tests/{load-sources.test.mjs → agents/utils/load-sources.test.mjs} +153 -25
- package/tests/{save-docs.test.mjs → agents/utils/save-docs.test.mjs} +11 -5
- package/tests/agents/utils/save-output.test.mjs +315 -0
- package/tests/agents/utils/save-single-doc.test.mjs +364 -0
- package/tests/agents/utils/transform-detail-datasources.test.mjs +363 -0
- package/tests/utils/auth-utils.test.mjs +358 -0
- package/tests/utils/blocklet.test.mjs +334 -0
- package/tests/{conflict-resolution.test.mjs → utils/conflict-detector.test.mjs} +3 -3
- package/tests/utils/constants.test.mjs +295 -0
- package/tests/utils/d2-utils.test.mjs +423 -0
- package/tests/utils/deploy.test.mjs +365 -0
- package/tests/utils/docs-finder-utils.test.mjs +625 -0
- package/tests/utils/file-utils.test.mjs +213 -0
- package/tests/{kroki-utils.test.mjs → utils/kroki-utils.test.mjs} +2 -2
- package/tests/utils/load-config.test.mjs +141 -0
- package/tests/{mermaid-validation.test.mjs → utils/mermaid-validator.test.mjs} +2 -2
- package/tests/utils/mock-chat-model.mjs +12 -0
- package/tests/{preferences-utils.test.mjs → utils/preferences-utils.test.mjs} +1 -1
- package/tests/{save-value-to-config.test.mjs → utils/save-value-to-config.test.mjs} +61 -4
- package/tests/utils/utils.test.mjs +939 -0
- package/utils/auth-utils.mjs +1 -1
- package/utils/conflict-detector.mjs +1 -1
- package/utils/constants.mjs +5 -3
- package/utils/d2-utils.mjs +194 -0
- package/utils/deploy.mjs +3 -3
- package/utils/docs-finder-utils.mjs +26 -26
- package/utils/icon-map.mjs +26 -0
- package/{agents → utils}/load-config.mjs +2 -18
- package/utils/markdown-checker.mjs +5 -5
- package/agents/batch-docs-detail-generator.yaml +0 -19
- package/agents/check-structure-planning-result.yaml +0 -30
- package/agents/content-detail-generator.yaml +0 -50
- package/agents/feedback-refiner.yaml +0 -52
- package/agents/format-structure-plan.mjs +0 -25
- package/agents/reflective-structure-planner.yaml +0 -12
- package/agents/schema/structure-plan.yaml +0 -26
- package/agents/structure-planning.yaml +0 -58
- package/agents/team-publish-docs.yaml +0 -18
- package/agents/translate.yaml +0 -31
- package/prompts/document/custom-components.md +0 -104
- package/tests/README.md +0 -93
- package/tests/utils.test.mjs +0 -2067
- /package/agents/{exit.mjs → utils/exit.mjs} +0 -0
- /package/agents/{fs.mjs → utils/fs.mjs} +0 -0
- /package/agents/{save-output.mjs → utils/save-output.mjs} +0 -0
- /package/prompts/{document → detail}/d2-chart/official-examples.md +0 -0
- /package/prompts/{document → detail}/jsx/rules.md +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
import * as fsPromises from "node:fs/promises";
|
|
3
|
+
import checkNeedGenerateStructure from "../../../agents/generate/check-need-generate-structure.mjs";
|
|
4
|
+
|
|
5
|
+
import * as preferencesUtils from "../../../utils/preferences-utils.mjs";
|
|
6
|
+
import * as utils from "../../../utils/utils.mjs";
|
|
7
|
+
|
|
8
|
+
describe("check-need-generate-structure", () => {
|
|
9
|
+
let mockOptions;
|
|
10
|
+
let originalDocumentStructure;
|
|
11
|
+
|
|
12
|
+
// Spies for external dependencies
|
|
13
|
+
let accessSpy;
|
|
14
|
+
|
|
15
|
+
// Spies for internal utils
|
|
16
|
+
let getActiveRulesForScopeSpy;
|
|
17
|
+
let getCurrentGitHeadSpy;
|
|
18
|
+
let hasFileChangesBetweenCommitsSpy;
|
|
19
|
+
let getProjectInfoSpy;
|
|
20
|
+
let loadConfigFromFileSpy;
|
|
21
|
+
let saveValueToConfigSpy;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Reset all mocks
|
|
25
|
+
mock.restore();
|
|
26
|
+
|
|
27
|
+
originalDocumentStructure = [
|
|
28
|
+
{ path: "/getting-started", title: "Getting Started" },
|
|
29
|
+
{ path: "/api", title: "API Reference" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
mockOptions = {
|
|
33
|
+
prompts: {
|
|
34
|
+
input: mock(async () => ""),
|
|
35
|
+
},
|
|
36
|
+
context: {
|
|
37
|
+
agents: { refineDocumentStructure: {} },
|
|
38
|
+
invoke: mock(async () => ({
|
|
39
|
+
documentStructure: originalDocumentStructure,
|
|
40
|
+
projectName: "Test Project",
|
|
41
|
+
projectDesc: "Test Description",
|
|
42
|
+
})),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Set up spies for internal utils
|
|
47
|
+
getActiveRulesForScopeSpy = spyOn(preferencesUtils, "getActiveRulesForScope").mockReturnValue(
|
|
48
|
+
[],
|
|
49
|
+
);
|
|
50
|
+
getCurrentGitHeadSpy = spyOn(utils, "getCurrentGitHead").mockReturnValue(null);
|
|
51
|
+
hasFileChangesBetweenCommitsSpy = spyOn(utils, "hasFileChangesBetweenCommits").mockReturnValue(
|
|
52
|
+
false,
|
|
53
|
+
);
|
|
54
|
+
getProjectInfoSpy = spyOn(utils, "getProjectInfo").mockResolvedValue({
|
|
55
|
+
name: "Test Project",
|
|
56
|
+
description: "Test Description",
|
|
57
|
+
fromGitHub: false,
|
|
58
|
+
});
|
|
59
|
+
loadConfigFromFileSpy = spyOn(utils, "loadConfigFromFile").mockResolvedValue({});
|
|
60
|
+
saveValueToConfigSpy = spyOn(utils, "saveValueToConfig").mockResolvedValue();
|
|
61
|
+
|
|
62
|
+
// Set up spy for external dependencies
|
|
63
|
+
accessSpy = spyOn(fsPromises, "access").mockImplementation(() =>
|
|
64
|
+
Promise.reject(new Error("File not found")),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Clear prompts mock call history
|
|
68
|
+
mockOptions.prompts.input.mockClear();
|
|
69
|
+
mockOptions.context.invoke.mockClear();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
// Restore all spies
|
|
74
|
+
accessSpy?.mockRestore();
|
|
75
|
+
getActiveRulesForScopeSpy?.mockRestore();
|
|
76
|
+
getCurrentGitHeadSpy?.mockRestore();
|
|
77
|
+
hasFileChangesBetweenCommitsSpy?.mockRestore();
|
|
78
|
+
getProjectInfoSpy?.mockRestore();
|
|
79
|
+
loadConfigFromFileSpy?.mockRestore();
|
|
80
|
+
saveValueToConfigSpy?.mockRestore();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("should return original document structure when no regeneration needed", async () => {
|
|
84
|
+
// Test when no feedback and no sidebar file exists (default mock behavior)
|
|
85
|
+
const result = await checkNeedGenerateStructure(
|
|
86
|
+
{ originalDocumentStructure, docsDir: "./docs" },
|
|
87
|
+
mockOptions,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(result).toBeDefined();
|
|
91
|
+
expect(result.documentStructure).toEqual(originalDocumentStructure);
|
|
92
|
+
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("should prompt for user feedback when originalDocumentStructure exists", async () => {
|
|
96
|
+
const userFeedback = "Need more API documentation";
|
|
97
|
+
mockOptions.prompts.input.mockImplementation(async () => userFeedback);
|
|
98
|
+
|
|
99
|
+
await checkNeedGenerateStructure({ originalDocumentStructure, docsDir: "./docs" }, mockOptions);
|
|
100
|
+
|
|
101
|
+
expect(mockOptions.prompts.input).toHaveBeenCalledWith({
|
|
102
|
+
message: "How can we improve the documentation structure? (press Enter to skip):",
|
|
103
|
+
});
|
|
104
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("should skip prompting if feedback is already provided", async () => {
|
|
108
|
+
const providedFeedback = "Already provided feedback";
|
|
109
|
+
|
|
110
|
+
await checkNeedGenerateStructure(
|
|
111
|
+
{ originalDocumentStructure, feedback: providedFeedback, docsDir: "./docs" },
|
|
112
|
+
mockOptions,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(mockOptions.prompts.input).not.toHaveBeenCalled();
|
|
116
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("should handle empty user feedback input", async () => {
|
|
120
|
+
mockOptions.prompts.input.mockImplementation(async () => " ");
|
|
121
|
+
// Default mock behavior: no sidebar file exists
|
|
122
|
+
|
|
123
|
+
const result = await checkNeedGenerateStructure(
|
|
124
|
+
{ originalDocumentStructure, docsDir: "./docs" },
|
|
125
|
+
mockOptions,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(result.documentStructure).toEqual(originalDocumentStructure);
|
|
129
|
+
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("should regenerate when _sidebar.md exists", async () => {
|
|
133
|
+
accessSpy.mockImplementation(() => Promise.resolve());
|
|
134
|
+
|
|
135
|
+
await checkNeedGenerateStructure({ originalDocumentStructure, docsDir: "./docs" }, mockOptions);
|
|
136
|
+
|
|
137
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("should not regenerate when _sidebar.md does not exist", async () => {
|
|
141
|
+
// Default mock behavior: file access fails
|
|
142
|
+
|
|
143
|
+
const result = await checkNeedGenerateStructure(
|
|
144
|
+
{ originalDocumentStructure, docsDir: "./docs" },
|
|
145
|
+
mockOptions,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result.documentStructure).toEqual(originalDocumentStructure);
|
|
149
|
+
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("should check git changes when lastGitHead is provided", async () => {
|
|
153
|
+
getCurrentGitHeadSpy.mockImplementation(() => "def456");
|
|
154
|
+
hasFileChangesBetweenCommitsSpy.mockImplementation(() => true);
|
|
155
|
+
|
|
156
|
+
await checkNeedGenerateStructure(
|
|
157
|
+
{ originalDocumentStructure, lastGitHead: "abc123", docsDir: "./docs" },
|
|
158
|
+
mockOptions,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
expect(hasFileChangesBetweenCommitsSpy).toHaveBeenCalledWith("abc123", "def456");
|
|
162
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("should not regenerate when git head is same", async () => {
|
|
166
|
+
getCurrentGitHeadSpy.mockImplementation(() => "abc123");
|
|
167
|
+
|
|
168
|
+
const result = await checkNeedGenerateStructure(
|
|
169
|
+
{ originalDocumentStructure, lastGitHead: "abc123", docsDir: "./docs" },
|
|
170
|
+
mockOptions,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
expect(result.documentStructure).toEqual(originalDocumentStructure);
|
|
174
|
+
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("should force regenerate when forceRegenerate is true", async () => {
|
|
178
|
+
await checkNeedGenerateStructure(
|
|
179
|
+
{ originalDocumentStructure, forceRegenerate: true, docsDir: "./docs" },
|
|
180
|
+
mockOptions,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("should include user preferences", async () => {
|
|
187
|
+
const mockRules = [{ rule: "Structure rule 1" }];
|
|
188
|
+
getActiveRulesForScopeSpy.mockImplementation(() => mockRules);
|
|
189
|
+
|
|
190
|
+
await checkNeedGenerateStructure(
|
|
191
|
+
{ originalDocumentStructure, feedback: "test", docsDir: "./docs" },
|
|
192
|
+
mockOptions,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(getActiveRulesForScopeSpy).toHaveBeenCalledWith("structure", []);
|
|
196
|
+
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("should save project info when appropriate", async () => {
|
|
200
|
+
mockOptions.context.invoke.mockImplementation(async () => ({
|
|
201
|
+
documentStructure: originalDocumentStructure,
|
|
202
|
+
projectName: "New Project Name",
|
|
203
|
+
projectDesc: "New Description",
|
|
204
|
+
}));
|
|
205
|
+
|
|
206
|
+
const result = await checkNeedGenerateStructure(
|
|
207
|
+
{ originalDocumentStructure, feedback: "test", docsDir: "./docs" },
|
|
208
|
+
mockOptions,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(saveValueToConfigSpy).toHaveBeenCalledWith("projectName", "New Project Name");
|
|
212
|
+
expect(result.projectInfoMessage).toContain("New Project Name");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("should handle project info save errors", async () => {
|
|
216
|
+
loadConfigFromFileSpy.mockImplementation(async () => {
|
|
217
|
+
throw new Error("Config load failed");
|
|
218
|
+
});
|
|
219
|
+
const consoleSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
220
|
+
|
|
221
|
+
mockOptions.context.invoke.mockImplementation(async () => ({
|
|
222
|
+
documentStructure: originalDocumentStructure,
|
|
223
|
+
projectName: "New Project",
|
|
224
|
+
projectDesc: "New Description",
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
const result = await checkNeedGenerateStructure(
|
|
228
|
+
{ originalDocumentStructure, feedback: "test", docsDir: "./docs" },
|
|
229
|
+
mockOptions,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
233
|
+
|
|
234
|
+
expect(result).toBeDefined();
|
|
235
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("should return correct structure when no originalDocumentStructure provided", async () => {
|
|
239
|
+
const newDocumentStructure = [{ path: "/new", title: "New Section" }];
|
|
240
|
+
|
|
241
|
+
mockOptions.context.invoke.mockImplementation(async () => ({
|
|
242
|
+
documentStructure: newDocumentStructure,
|
|
243
|
+
}));
|
|
244
|
+
|
|
245
|
+
const result = await checkNeedGenerateStructure({ docsDir: "./docs" }, mockOptions);
|
|
246
|
+
|
|
247
|
+
expect(result.documentStructure).toEqual(newDocumentStructure);
|
|
248
|
+
expect(result.originalDocumentStructure).toEqual(newDocumentStructure);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("should clear feedback in result", async () => {
|
|
252
|
+
const result = await checkNeedGenerateStructure(
|
|
253
|
+
{ originalDocumentStructure, feedback: "some feedback", docsDir: "./docs" },
|
|
254
|
+
mockOptions,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(result.feedback).toBe("");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("should preserve documentStructureFeedback", async () => {
|
|
261
|
+
const feedback = "user submitted feedback";
|
|
262
|
+
|
|
263
|
+
const result = await checkNeedGenerateStructure(
|
|
264
|
+
{ originalDocumentStructure, feedback, docsDir: "./docs" },
|
|
265
|
+
mockOptions,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(result.documentStructureFeedback).toBe(feedback);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("should pass through additional parameters", async () => {
|
|
272
|
+
const additionalParams = {
|
|
273
|
+
customParam1: "value1",
|
|
274
|
+
customParam2: "value2",
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
await checkNeedGenerateStructure(
|
|
278
|
+
{
|
|
279
|
+
originalDocumentStructure,
|
|
280
|
+
feedback: "test",
|
|
281
|
+
docsDir: "./docs",
|
|
282
|
+
...additionalParams,
|
|
283
|
+
},
|
|
284
|
+
mockOptions,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledWith(
|
|
288
|
+
mockOptions.context.agents.refineDocumentStructure,
|
|
289
|
+
expect.objectContaining(additionalParams),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { AIAgent } from "@aigne/core";
|
|
4
|
+
import { loadAgent } from "@aigne/core/loader/index.js";
|
|
5
|
+
import { loadModel } from "../../utils/mock-chat-model.mjs";
|
|
6
|
+
|
|
7
|
+
describe("generateStructure Agent", () => {
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
process.env.AIGNE_OBSERVABILITY_DISABLED = "true";
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterAll(() => {
|
|
13
|
+
delete process.env.AIGNE_OBSERVABILITY_DISABLED;
|
|
14
|
+
});
|
|
15
|
+
test("should load agent correctly with proper configuration", async () => {
|
|
16
|
+
const agent = await loadAgent(
|
|
17
|
+
join(import.meta.dirname, "../../../agents/generate/generate-structure.yaml"),
|
|
18
|
+
{
|
|
19
|
+
model: loadModel,
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
expect(agent).toBeDefined();
|
|
24
|
+
|
|
25
|
+
// Verify agent exists and is correct type
|
|
26
|
+
expect(agent).toBeDefined();
|
|
27
|
+
expect(agent).toBeInstanceOf(AIAgent);
|
|
28
|
+
expect(agent.name).toBe("generateStructure");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("should have instructions loaded from file", async () => {
|
|
32
|
+
const agent = await loadAgent(
|
|
33
|
+
join(import.meta.dirname, "../../../agents/generate/generate-structure.yaml"),
|
|
34
|
+
{
|
|
35
|
+
model: loadModel,
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(agent).toBeDefined();
|
|
40
|
+
|
|
41
|
+
// Verify instructions are loaded
|
|
42
|
+
expect(agent.instructions).toBeDefined();
|
|
43
|
+
const instructions = await agent.instructions.build({});
|
|
44
|
+
expect(instructions.messages).toBeDefined();
|
|
45
|
+
expect(instructions.messages.length).toBeGreaterThan(0);
|
|
46
|
+
|
|
47
|
+
// The instructions should contain content from the prompt file
|
|
48
|
+
const systemMessage = instructions.messages.find((m) => m.role === "system");
|
|
49
|
+
expect(systemMessage).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -3,7 +3,7 @@ import { promises as fs } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { parse as parseYAML } from "yaml";
|
|
6
|
-
import init, { generateYAML } from "
|
|
6
|
+
import init, { generateYAML } from "../../../agents/init/index.mjs";
|
|
7
7
|
|
|
8
8
|
describe("generateYAML", () => {
|
|
9
9
|
// Helper function to parse YAML and verify it's valid
|
|
@@ -1013,7 +1013,7 @@ describe("init", () => {
|
|
|
1013
1013
|
);
|
|
1014
1014
|
|
|
1015
1015
|
// Check that function completed successfully
|
|
1016
|
-
expect(result).
|
|
1016
|
+
expect(result).toBeDefined();
|
|
1017
1017
|
|
|
1018
1018
|
// Check that config file was created
|
|
1019
1019
|
const configPath = join(tempDir, "config.yaml");
|
|
@@ -1068,7 +1068,7 @@ describe("init", () => {
|
|
|
1068
1068
|
options,
|
|
1069
1069
|
);
|
|
1070
1070
|
|
|
1071
|
-
expect(result).
|
|
1071
|
+
expect(result).toBeDefined();
|
|
1072
1072
|
|
|
1073
1073
|
// Verify the generated config
|
|
1074
1074
|
const configPath = join(tempDir, "test-config.yaml");
|
|
@@ -1113,7 +1113,7 @@ describe("init", () => {
|
|
|
1113
1113
|
options,
|
|
1114
1114
|
);
|
|
1115
1115
|
|
|
1116
|
-
expect(result).
|
|
1116
|
+
expect(result).toBeDefined();
|
|
1117
1117
|
|
|
1118
1118
|
const configPath = join(tempDir, "simple-config.yaml");
|
|
1119
1119
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1169,7 +1169,7 @@ describe("init", () => {
|
|
|
1169
1169
|
options,
|
|
1170
1170
|
);
|
|
1171
1171
|
|
|
1172
|
-
expect(result).
|
|
1172
|
+
expect(result).toBeDefined();
|
|
1173
1173
|
|
|
1174
1174
|
const configPath = join(tempDir, "config.yaml");
|
|
1175
1175
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1214,7 +1214,7 @@ describe("init", () => {
|
|
|
1214
1214
|
options,
|
|
1215
1215
|
);
|
|
1216
1216
|
|
|
1217
|
-
expect(result).
|
|
1217
|
+
expect(result).toBeDefined();
|
|
1218
1218
|
|
|
1219
1219
|
const configPath = join(tempDir, "config.yaml");
|
|
1220
1220
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1251,7 +1251,7 @@ describe("init", () => {
|
|
|
1251
1251
|
options,
|
|
1252
1252
|
);
|
|
1253
1253
|
|
|
1254
|
-
expect(result).
|
|
1254
|
+
expect(result).toBeDefined();
|
|
1255
1255
|
|
|
1256
1256
|
// Config should remain unchanged
|
|
1257
1257
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1283,7 +1283,7 @@ describe("init", () => {
|
|
|
1283
1283
|
options,
|
|
1284
1284
|
);
|
|
1285
1285
|
|
|
1286
|
-
expect(result).
|
|
1286
|
+
expect(result).toBeDefined();
|
|
1287
1287
|
|
|
1288
1288
|
// Config should be generated since original was empty
|
|
1289
1289
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1307,7 +1307,9 @@ describe("init", () => {
|
|
|
1307
1307
|
if (options.message.includes("[1/8]") && options.validate) {
|
|
1308
1308
|
// Test the validation function directly
|
|
1309
1309
|
const validationResult = options.validate([]);
|
|
1310
|
-
expect(validationResult).toBe(
|
|
1310
|
+
expect(validationResult).toBe(
|
|
1311
|
+
"Please choose at least one goal for your documentation.",
|
|
1312
|
+
);
|
|
1311
1313
|
validateCalled = true;
|
|
1312
1314
|
// Return valid result after testing validation
|
|
1313
1315
|
return Promise.resolve(["getStarted"]);
|
|
@@ -1326,7 +1328,7 @@ describe("init", () => {
|
|
|
1326
1328
|
options,
|
|
1327
1329
|
);
|
|
1328
1330
|
|
|
1329
|
-
expect(result).
|
|
1331
|
+
expect(result).toBeDefined();
|
|
1330
1332
|
expect(validateCalled).toBe(true);
|
|
1331
1333
|
} finally {
|
|
1332
1334
|
await cleanupTempDir(tempDir);
|
|
@@ -1346,7 +1348,7 @@ describe("init", () => {
|
|
|
1346
1348
|
if (options.message.includes("[2/8]") && options.validate) {
|
|
1347
1349
|
// Test the validation function for target audience
|
|
1348
1350
|
const validationResult = options.validate([]);
|
|
1349
|
-
expect(validationResult).toBe("Please
|
|
1351
|
+
expect(validationResult).toBe("Please choose at least one audience.");
|
|
1350
1352
|
audienceValidateCalled = true;
|
|
1351
1353
|
return Promise.resolve(["developers"]); // Valid result after testing
|
|
1352
1354
|
}
|
|
@@ -1364,7 +1366,7 @@ describe("init", () => {
|
|
|
1364
1366
|
options,
|
|
1365
1367
|
);
|
|
1366
1368
|
|
|
1367
|
-
expect(result).
|
|
1369
|
+
expect(result).toBeDefined();
|
|
1368
1370
|
expect(audienceValidateCalled).toBe(true);
|
|
1369
1371
|
} finally {
|
|
1370
1372
|
await cleanupTempDir(tempDir);
|
|
@@ -1385,11 +1387,11 @@ describe("init", () => {
|
|
|
1385
1387
|
if (options.message.includes("Which is most important?") && options.validate) {
|
|
1386
1388
|
// Test validation for empty selection
|
|
1387
1389
|
let validationResult = options.validate([]);
|
|
1388
|
-
expect(validationResult).toBe("Please
|
|
1390
|
+
expect(validationResult).toBe("Please choose at least one priority.");
|
|
1389
1391
|
|
|
1390
1392
|
// Test validation for too many selections
|
|
1391
1393
|
validationResult = options.validate(["getStarted", "completeTasks", "findAnswers"]);
|
|
1392
|
-
expect(validationResult).toBe("Please
|
|
1394
|
+
expect(validationResult).toBe("Please choose maximum 2 priorities.");
|
|
1393
1395
|
|
|
1394
1396
|
// Test validation for valid selection
|
|
1395
1397
|
validationResult = options.validate(["getStarted", "completeTasks"]);
|
|
@@ -1412,7 +1414,7 @@ describe("init", () => {
|
|
|
1412
1414
|
options,
|
|
1413
1415
|
);
|
|
1414
1416
|
|
|
1415
|
-
expect(result).
|
|
1417
|
+
expect(result).toBeDefined();
|
|
1416
1418
|
expect(priorityValidateCalled).toBe(true);
|
|
1417
1419
|
} finally {
|
|
1418
1420
|
await cleanupTempDir(tempDir);
|
|
@@ -1479,7 +1481,7 @@ describe("init", () => {
|
|
|
1479
1481
|
options,
|
|
1480
1482
|
);
|
|
1481
1483
|
|
|
1482
|
-
expect(result).
|
|
1484
|
+
expect(result).toBeDefined();
|
|
1483
1485
|
|
|
1484
1486
|
const configPath = join(tempDir, "config.yaml");
|
|
1485
1487
|
const configContent = await fs.readFile(configPath, "utf8");
|
|
@@ -1524,7 +1526,7 @@ describe("init", () => {
|
|
|
1524
1526
|
options,
|
|
1525
1527
|
);
|
|
1526
1528
|
|
|
1527
|
-
expect(result).
|
|
1529
|
+
expect(result).toBeDefined();
|
|
1528
1530
|
} finally {
|
|
1529
1531
|
await cleanupTempDir(tempDir);
|
|
1530
1532
|
}
|