@aigne/doc-smith 0.8.12-beta.3 → 0.8.12-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aigne/doc-smith/config.yaml +9 -6
- package/.aigne/doc-smith/output/structure-plan.json +123 -109
- package/.aigne/doc-smith/upload-cache.yaml +48 -0
- package/.github/workflows/publish-docs.yml +4 -7
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +20 -0
- package/agents/clear/choose-contents.mjs +22 -5
- package/agents/clear/clear-auth-tokens.mjs +2 -4
- package/agents/clear/clear-deployment-config.mjs +49 -0
- package/agents/clear/clear-document-config.mjs +2 -5
- package/agents/clear/clear-document-structure.mjs +2 -6
- package/agents/clear/clear-generated-docs.mjs +0 -1
- package/agents/generate/check-need-generate-structure.mjs +15 -60
- package/agents/generate/document-structure-tools/generate-sub-structure.mjs +131 -0
- package/agents/generate/generate-structure-without-tools.yaml +65 -0
- package/agents/generate/generate-structure.yaml +7 -1
- package/agents/generate/index.yaml +0 -3
- package/agents/generate/update-document-structure.yaml +3 -0
- package/agents/generate/user-review-document-structure.mjs +7 -5
- package/agents/init/index.mjs +15 -15
- package/agents/publish/publish-docs.mjs +130 -111
- package/agents/translate/index.yaml +1 -1
- package/agents/update/batch-generate-document.yaml +1 -1
- package/agents/update/batch-update-document.yaml +1 -1
- package/agents/update/check-document.mjs +4 -19
- package/agents/update/user-review-document.mjs +4 -3
- package/agents/utils/load-sources.mjs +54 -151
- package/agents/utils/transform-detail-datasources.mjs +14 -18
- package/aigne.yaml +2 -0
- package/biome.json +1 -1
- package/docs/_sidebar.md +13 -15
- package/docs/configuration-initial-setup.ja.md +179 -0
- package/docs/configuration-initial-setup.md +179 -0
- package/docs/configuration-initial-setup.zh-TW.md +179 -0
- package/docs/configuration-initial-setup.zh.md +179 -0
- package/docs/configuration-managing-preferences.ja.md +100 -0
- package/docs/configuration-managing-preferences.md +100 -0
- package/docs/configuration-managing-preferences.zh-TW.md +100 -0
- package/docs/configuration-managing-preferences.zh.md +100 -0
- package/docs/configuration.ja.md +68 -184
- package/docs/configuration.md +62 -178
- package/docs/configuration.zh-TW.md +70 -186
- package/docs/configuration.zh.md +67 -183
- package/docs/getting-started.ja.md +46 -78
- package/docs/getting-started.md +46 -78
- package/docs/getting-started.zh-TW.md +47 -79
- package/docs/getting-started.zh.md +47 -79
- package/docs/guides-cleaning-up.ja.md +50 -0
- package/docs/guides-cleaning-up.md +50 -0
- package/docs/guides-cleaning-up.zh-TW.md +50 -0
- package/docs/guides-cleaning-up.zh.md +50 -0
- package/docs/guides-evaluating-documents.ja.md +66 -0
- package/docs/guides-evaluating-documents.md +66 -0
- package/docs/guides-evaluating-documents.zh-TW.md +66 -0
- package/docs/guides-evaluating-documents.zh.md +66 -0
- package/docs/guides-generating-documentation.ja.md +149 -0
- package/docs/guides-generating-documentation.md +149 -0
- package/docs/guides-generating-documentation.zh-TW.md +149 -0
- package/docs/guides-generating-documentation.zh.md +149 -0
- package/docs/guides-interactive-chat.ja.md +85 -0
- package/docs/guides-interactive-chat.md +85 -0
- package/docs/guides-interactive-chat.zh-TW.md +85 -0
- package/docs/guides-interactive-chat.zh.md +85 -0
- package/docs/guides-managing-history.ja.md +51 -0
- package/docs/guides-managing-history.md +51 -0
- package/docs/guides-managing-history.zh-TW.md +51 -0
- package/docs/guides-managing-history.zh.md +51 -0
- package/docs/guides-publishing-your-docs.ja.md +78 -0
- package/docs/guides-publishing-your-docs.md +78 -0
- package/docs/guides-publishing-your-docs.zh-TW.md +78 -0
- package/docs/guides-publishing-your-docs.zh.md +78 -0
- package/docs/guides-translating-documentation.ja.md +95 -0
- package/docs/guides-translating-documentation.md +95 -0
- package/docs/guides-translating-documentation.zh-TW.md +95 -0
- package/docs/guides-translating-documentation.zh.md +95 -0
- package/docs/guides-updating-documentation.ja.md +77 -0
- package/docs/guides-updating-documentation.md +77 -0
- package/docs/guides-updating-documentation.zh-TW.md +77 -0
- package/docs/guides-updating-documentation.zh.md +77 -0
- package/docs/guides.ja.md +32 -0
- package/docs/guides.md +32 -0
- package/docs/guides.zh-TW.md +32 -0
- package/docs/guides.zh.md +32 -0
- package/docs/overview.ja.md +39 -60
- package/docs/overview.md +39 -60
- package/docs/overview.zh-TW.md +39 -60
- package/docs/overview.zh.md +39 -60
- package/docs/release-notes.ja.md +255 -0
- package/docs/release-notes.md +255 -0
- package/docs/release-notes.zh-TW.md +255 -0
- package/docs/release-notes.zh.md +255 -0
- package/package.json +4 -2
- package/prompts/common/document/content-rules-core.md +1 -0
- package/prompts/common/document-structure/document-structure-rules.md +8 -9
- package/prompts/common/document-structure/output-constraints.md +1 -1
- package/prompts/structure/document-rules.md +8 -2
- package/prompts/structure/generate/system-prompt.md +27 -2
- package/prompts/structure/generate/user-prompt.md +18 -0
- package/prompts/structure/update/system-prompt.md +12 -0
- package/prompts/structure/update/user-prompt.md +3 -0
- package/tests/agents/clear/choose-contents.test.mjs +8 -4
- package/tests/agents/generate/check-need-generate-structure.test.mjs +53 -63
- package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +277 -0
- package/tests/agents/init/init.test.mjs +18 -18
- package/tests/agents/publish/publish-docs.test.mjs +79 -0
- package/tests/agents/update/check-document.test.mjs +7 -67
- package/tests/agents/utils/load-sources.test.mjs +90 -90
- package/tests/agents/utils/transform-detail-datasources.test.mjs +153 -196
- package/tests/utils/file-utils.test.mjs +309 -1
- package/utils/auth-utils.mjs +9 -3
- package/utils/constants/index.mjs +3 -1
- package/utils/file-utils.mjs +315 -0
- package/utils/utils.mjs +89 -50
- package/docs/advanced-how-it-works.ja.md +0 -101
- package/docs/advanced-how-it-works.md +0 -101
- package/docs/advanced-how-it-works.zh-TW.md +0 -101
- package/docs/advanced-how-it-works.zh.md +0 -101
- package/docs/advanced-quality-assurance.ja.md +0 -92
- package/docs/advanced-quality-assurance.md +0 -92
- package/docs/advanced-quality-assurance.zh-TW.md +0 -92
- package/docs/advanced-quality-assurance.zh.md +0 -92
- package/docs/advanced.ja.md +0 -20
- package/docs/advanced.md +0 -20
- package/docs/advanced.zh-TW.md +0 -20
- package/docs/advanced.zh.md +0 -20
- package/docs/changelog.ja.md +0 -486
- package/docs/changelog.md +0 -486
- package/docs/changelog.zh-TW.md +0 -486
- package/docs/changelog.zh.md +0 -486
- package/docs/cli-reference.ja.md +0 -311
- package/docs/cli-reference.md +0 -311
- package/docs/cli-reference.zh-TW.md +0 -311
- package/docs/cli-reference.zh.md +0 -311
- package/docs/configuration-interactive-setup.ja.md +0 -138
- package/docs/configuration-interactive-setup.md +0 -138
- package/docs/configuration-interactive-setup.zh-TW.md +0 -138
- package/docs/configuration-interactive-setup.zh.md +0 -138
- package/docs/configuration-language-support.ja.md +0 -64
- package/docs/configuration-language-support.md +0 -64
- package/docs/configuration-language-support.zh-TW.md +0 -64
- package/docs/configuration-language-support.zh.md +0 -64
- package/docs/configuration-llm-setup.ja.md +0 -56
- package/docs/configuration-llm-setup.md +0 -56
- package/docs/configuration-llm-setup.zh-TW.md +0 -56
- package/docs/configuration-llm-setup.zh.md +0 -56
- package/docs/configuration-preferences.ja.md +0 -144
- package/docs/configuration-preferences.md +0 -144
- package/docs/configuration-preferences.zh-TW.md +0 -144
- package/docs/configuration-preferences.zh.md +0 -144
- package/docs/features-generate-documentation.ja.md +0 -95
- package/docs/features-generate-documentation.md +0 -95
- package/docs/features-generate-documentation.zh-TW.md +0 -95
- package/docs/features-generate-documentation.zh.md +0 -95
- package/docs/features-publish-your-docs.ja.md +0 -130
- package/docs/features-publish-your-docs.md +0 -130
- package/docs/features-publish-your-docs.zh-TW.md +0 -130
- package/docs/features-publish-your-docs.zh.md +0 -130
- package/docs/features-translate-documentation.ja.md +0 -90
- package/docs/features-translate-documentation.md +0 -90
- package/docs/features-translate-documentation.zh-TW.md +0 -90
- package/docs/features-translate-documentation.zh.md +0 -90
- package/docs/features-update-and-refine.ja.md +0 -142
- package/docs/features-update-and-refine.md +0 -142
- package/docs/features-update-and-refine.zh-TW.md +0 -143
- package/docs/features-update-and-refine.zh.md +0 -142
- package/docs/features.ja.md +0 -62
- package/docs/features.md +0 -62
- package/docs/features.zh-TW.md +0 -62
- package/docs/features.zh.md +0 -62
|
@@ -35,10 +35,13 @@ describe("check-need-generate-structure", () => {
|
|
|
35
35
|
mockOptions = {
|
|
36
36
|
prompts: {
|
|
37
37
|
input: mock(async () => ""),
|
|
38
|
-
select: mock(async () => ""),
|
|
38
|
+
select: mock(async () => "generate"),
|
|
39
39
|
},
|
|
40
40
|
context: {
|
|
41
|
-
agents: {
|
|
41
|
+
agents: {
|
|
42
|
+
generateStructure: {},
|
|
43
|
+
generateStructureWithoutTools: {},
|
|
44
|
+
},
|
|
42
45
|
invoke: mock(async () => ({
|
|
43
46
|
documentStructure: originalDocumentStructure,
|
|
44
47
|
projectName: "Test Project",
|
|
@@ -99,69 +102,22 @@ describe("check-need-generate-structure", () => {
|
|
|
99
102
|
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
100
103
|
});
|
|
101
104
|
|
|
102
|
-
test("should
|
|
103
|
-
const providedFeedback = "Already provided feedback";
|
|
104
|
-
|
|
105
|
-
await checkNeedGenerateStructure(
|
|
106
|
-
{ originalDocumentStructure, feedback: providedFeedback, docsDir: "./docs" },
|
|
107
|
-
mockOptions,
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
expect(mockOptions.prompts.input).not.toHaveBeenCalled();
|
|
111
|
-
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("should handle empty user feedback input", async () => {
|
|
115
|
-
mockOptions.prompts.input.mockImplementation(async () => " ");
|
|
116
|
-
// Default mock behavior: no sidebar file exists
|
|
117
|
-
|
|
105
|
+
test("should return original structure when it exists and no force regenerate", async () => {
|
|
118
106
|
const result = await checkNeedGenerateStructure(
|
|
119
107
|
{ originalDocumentStructure, docsDir: "./docs" },
|
|
120
108
|
mockOptions,
|
|
121
109
|
);
|
|
122
110
|
|
|
123
|
-
expect(
|
|
111
|
+
expect(mockOptions.prompts.input).not.toHaveBeenCalled();
|
|
124
112
|
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("should regenerate when _sidebar.md exists", async () => {
|
|
128
|
-
accessSpy.mockImplementation(() => Promise.resolve());
|
|
129
|
-
|
|
130
|
-
await checkNeedGenerateStructure({ originalDocumentStructure, docsDir: "./docs" }, mockOptions);
|
|
131
|
-
|
|
132
|
-
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("should not regenerate when _sidebar.md does not exist", async () => {
|
|
136
|
-
// Default mock behavior: file access fails
|
|
137
|
-
|
|
138
|
-
const result = await checkNeedGenerateStructure(
|
|
139
|
-
{ originalDocumentStructure, docsDir: "./docs" },
|
|
140
|
-
mockOptions,
|
|
141
|
-
);
|
|
142
|
-
|
|
143
113
|
expect(result.documentStructure).toEqual(originalDocumentStructure);
|
|
144
|
-
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("should check git changes when lastGitHead is provided", async () => {
|
|
148
|
-
getCurrentGitHeadSpy.mockImplementation(() => "def456");
|
|
149
|
-
hasFileChangesBetweenCommitsSpy.mockImplementation(() => true);
|
|
150
|
-
|
|
151
|
-
await checkNeedGenerateStructure(
|
|
152
|
-
{ originalDocumentStructure, lastGitHead: "abc123", docsDir: "./docs" },
|
|
153
|
-
mockOptions,
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
expect(hasFileChangesBetweenCommitsSpy).toHaveBeenCalledWith("abc123", "def456");
|
|
157
|
-
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
158
114
|
});
|
|
159
115
|
|
|
160
|
-
test("should
|
|
161
|
-
|
|
116
|
+
test("should handle empty user feedback input", async () => {
|
|
117
|
+
mockOptions.prompts.input.mockImplementation(async () => " ");
|
|
162
118
|
|
|
163
119
|
const result = await checkNeedGenerateStructure(
|
|
164
|
-
{ originalDocumentStructure,
|
|
120
|
+
{ originalDocumentStructure, docsDir: "./docs" },
|
|
165
121
|
mockOptions,
|
|
166
122
|
);
|
|
167
123
|
|
|
@@ -178,12 +134,12 @@ describe("check-need-generate-structure", () => {
|
|
|
178
134
|
expect(mockOptions.context.invoke).toHaveBeenCalled();
|
|
179
135
|
});
|
|
180
136
|
|
|
181
|
-
test("should include user preferences", async () => {
|
|
137
|
+
test("should include user preferences when generating", async () => {
|
|
182
138
|
const mockRules = [{ rule: "Structure rule 1" }];
|
|
183
139
|
getActiveRulesForScopeSpy.mockImplementation(() => mockRules);
|
|
184
140
|
|
|
185
141
|
await checkNeedGenerateStructure(
|
|
186
|
-
{ originalDocumentStructure,
|
|
142
|
+
{ originalDocumentStructure, forceRegenerate: true, docsDir: "./docs" },
|
|
187
143
|
mockOptions,
|
|
188
144
|
);
|
|
189
145
|
|
|
@@ -199,7 +155,7 @@ describe("check-need-generate-structure", () => {
|
|
|
199
155
|
}));
|
|
200
156
|
|
|
201
157
|
const result = await checkNeedGenerateStructure(
|
|
202
|
-
{ originalDocumentStructure,
|
|
158
|
+
{ originalDocumentStructure, forceRegenerate: true, docsDir: "./docs" },
|
|
203
159
|
mockOptions,
|
|
204
160
|
);
|
|
205
161
|
|
|
@@ -220,7 +176,7 @@ describe("check-need-generate-structure", () => {
|
|
|
220
176
|
}));
|
|
221
177
|
|
|
222
178
|
const result = await checkNeedGenerateStructure(
|
|
223
|
-
{ originalDocumentStructure,
|
|
179
|
+
{ originalDocumentStructure, forceRegenerate: true, docsDir: "./docs" },
|
|
224
180
|
mockOptions,
|
|
225
181
|
);
|
|
226
182
|
|
|
@@ -243,16 +199,21 @@ describe("check-need-generate-structure", () => {
|
|
|
243
199
|
expect(result.originalDocumentStructure).toEqual(newDocumentStructure);
|
|
244
200
|
});
|
|
245
201
|
|
|
246
|
-
test("should clear feedback in result", async () => {
|
|
202
|
+
test("should clear feedback in result when regenerating", async () => {
|
|
247
203
|
const result = await checkNeedGenerateStructure(
|
|
248
|
-
{
|
|
204
|
+
{
|
|
205
|
+
originalDocumentStructure,
|
|
206
|
+
forceRegenerate: true,
|
|
207
|
+
feedback: "some feedback",
|
|
208
|
+
docsDir: "./docs",
|
|
209
|
+
},
|
|
249
210
|
mockOptions,
|
|
250
211
|
);
|
|
251
212
|
|
|
252
213
|
expect(result.feedback).toBe("");
|
|
253
214
|
});
|
|
254
215
|
|
|
255
|
-
test("should pass through additional parameters", async () => {
|
|
216
|
+
test("should pass through additional parameters when regenerating", async () => {
|
|
256
217
|
const additionalParams = {
|
|
257
218
|
customParam1: "value1",
|
|
258
219
|
customParam2: "value2",
|
|
@@ -261,7 +222,7 @@ describe("check-need-generate-structure", () => {
|
|
|
261
222
|
await checkNeedGenerateStructure(
|
|
262
223
|
{
|
|
263
224
|
originalDocumentStructure,
|
|
264
|
-
|
|
225
|
+
forceRegenerate: true,
|
|
265
226
|
docsDir: "./docs",
|
|
266
227
|
...additionalParams,
|
|
267
228
|
},
|
|
@@ -269,7 +230,7 @@ describe("check-need-generate-structure", () => {
|
|
|
269
230
|
);
|
|
270
231
|
|
|
271
232
|
expect(mockOptions.context.invoke).toHaveBeenCalledWith(
|
|
272
|
-
mockOptions.context.agents.
|
|
233
|
+
mockOptions.context.agents.generateStructureWithoutTools,
|
|
273
234
|
expect.objectContaining(additionalParams),
|
|
274
235
|
);
|
|
275
236
|
});
|
|
@@ -286,4 +247,33 @@ describe("check-need-generate-structure", () => {
|
|
|
286
247
|
expect(mockOptions.prompts.select).toHaveBeenCalled();
|
|
287
248
|
expect(mockOptions.context.invoke).not.toHaveBeenCalled();
|
|
288
249
|
});
|
|
250
|
+
|
|
251
|
+
test("should use generateStructure agent when isLargeContext is true", async () => {
|
|
252
|
+
await checkNeedGenerateStructure(
|
|
253
|
+
{ originalDocumentStructure, forceRegenerate: true, isLargeContext: true, docsDir: "./docs" },
|
|
254
|
+
mockOptions,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledWith(
|
|
258
|
+
mockOptions.context.agents.generateStructure,
|
|
259
|
+
expect.any(Object),
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("should use generateStructureWithoutTools agent when isLargeContext is false", async () => {
|
|
264
|
+
await checkNeedGenerateStructure(
|
|
265
|
+
{
|
|
266
|
+
originalDocumentStructure,
|
|
267
|
+
forceRegenerate: true,
|
|
268
|
+
isLargeContext: false,
|
|
269
|
+
docsDir: "./docs",
|
|
270
|
+
},
|
|
271
|
+
mockOptions,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(mockOptions.context.invoke).toHaveBeenCalledWith(
|
|
275
|
+
mockOptions.context.agents.generateStructureWithoutTools,
|
|
276
|
+
expect.any(Object),
|
|
277
|
+
);
|
|
278
|
+
});
|
|
289
279
|
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import generateSubStructure from "../../../../agents/generate/document-structure-tools/generate-sub-structure.mjs";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
describe("generate-sub-structure", () => {
|
|
10
|
+
let testDir;
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
testDir = join(__dirname, "test-generate-sub-structure");
|
|
14
|
+
await mkdir(testDir, { recursive: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
try {
|
|
19
|
+
await rm(testDir, { recursive: true, force: true });
|
|
20
|
+
} catch {
|
|
21
|
+
// Ignore cleanup errors
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("generateSubStructure function", () => {
|
|
26
|
+
test("should return empty subStructure when subSourcePaths is empty", async () => {
|
|
27
|
+
const result = await generateSubStructure(
|
|
28
|
+
{
|
|
29
|
+
parentDocument: {
|
|
30
|
+
title: "Test Parent",
|
|
31
|
+
description: "Test Description",
|
|
32
|
+
path: "/test",
|
|
33
|
+
parentId: "parent-1",
|
|
34
|
+
sourceIds: [],
|
|
35
|
+
},
|
|
36
|
+
subSourcePaths: [],
|
|
37
|
+
},
|
|
38
|
+
{},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect(result).toBeDefined();
|
|
42
|
+
expect(result.subStructure).toBeDefined();
|
|
43
|
+
expect(Array.isArray(result.subStructure)).toBe(true);
|
|
44
|
+
expect(result.subStructure.length).toBe(0);
|
|
45
|
+
expect(result.message).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("should process single source path with small context", async () => {
|
|
49
|
+
const testFile = join(testDir, "test.js");
|
|
50
|
+
await writeFile(testFile, "// Small test file\nconst x = 1;");
|
|
51
|
+
|
|
52
|
+
const mockContext = {
|
|
53
|
+
agents: {
|
|
54
|
+
generateStructureWithoutTools: "mock-agent-without-tools",
|
|
55
|
+
},
|
|
56
|
+
invoke: async (agent, params) => {
|
|
57
|
+
expect(agent).toBe("mock-agent-without-tools");
|
|
58
|
+
expect(params.isSubStructure).toBe(true);
|
|
59
|
+
expect(params.parentDocument).toBeDefined();
|
|
60
|
+
expect(params.datasources).toBeDefined();
|
|
61
|
+
expect(params.allFilesPaths).toBeDefined();
|
|
62
|
+
expect(params.isLargeContext).toBe(false);
|
|
63
|
+
expect(params.files).toBeDefined();
|
|
64
|
+
expect(params.totalTokens).toBeGreaterThan(0);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
documentStructure: [
|
|
68
|
+
{
|
|
69
|
+
title: "Test Document",
|
|
70
|
+
path: "/test-doc",
|
|
71
|
+
description: "Generated from test file",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = await generateSubStructure(
|
|
79
|
+
{
|
|
80
|
+
parentDocument: {
|
|
81
|
+
title: "Parent",
|
|
82
|
+
path: "/parent",
|
|
83
|
+
description: "Parent doc",
|
|
84
|
+
},
|
|
85
|
+
subSourcePaths: [{ path: testFile, reason: "Test file" }],
|
|
86
|
+
},
|
|
87
|
+
{ context: mockContext },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(result).toBeDefined();
|
|
91
|
+
expect(result.subStructure).toBeDefined();
|
|
92
|
+
expect(Array.isArray(result.subStructure)).toBe(true);
|
|
93
|
+
expect(result.subStructure.length).toBe(1);
|
|
94
|
+
expect(result.message).toContain("Generated a sub structure");
|
|
95
|
+
expect(result.message).toContain("/parent");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("should process multiple source paths", async () => {
|
|
99
|
+
const testFile1 = join(testDir, "test1.js");
|
|
100
|
+
const testFile2 = join(testDir, "test2.js");
|
|
101
|
+
await writeFile(testFile1, "// Test file 1\nconst a = 1;");
|
|
102
|
+
await writeFile(testFile2, "// Test file 2\nconst b = 2;");
|
|
103
|
+
|
|
104
|
+
const mockContext = {
|
|
105
|
+
agents: {
|
|
106
|
+
generateStructureWithoutTools: "mock-agent-without-tools",
|
|
107
|
+
},
|
|
108
|
+
invoke: async (_agent, params) => {
|
|
109
|
+
expect(params.files.length).toBe(2);
|
|
110
|
+
expect(params.allFilesPaths).toContain("test1.js");
|
|
111
|
+
expect(params.allFilesPaths).toContain("test2.js");
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
documentStructure: [
|
|
115
|
+
{ title: "Doc 1", path: "/doc1" },
|
|
116
|
+
{ title: "Doc 2", path: "/doc2" },
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const result = await generateSubStructure(
|
|
123
|
+
{
|
|
124
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
125
|
+
subSourcePaths: [
|
|
126
|
+
{ path: testFile1, reason: "First test" },
|
|
127
|
+
{ path: testFile2, reason: "Second test" },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{ context: mockContext },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(result.subStructure.length).toBe(2);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("should use generateStructure agent for large context", async () => {
|
|
137
|
+
const largeContent = `// Large file\n${"const x = 1;\n".repeat(150000)}`;
|
|
138
|
+
const testFile = join(testDir, "large.js");
|
|
139
|
+
await writeFile(testFile, largeContent);
|
|
140
|
+
|
|
141
|
+
const mockContext = {
|
|
142
|
+
agents: {
|
|
143
|
+
generateStructure: "mock-agent-with-tools",
|
|
144
|
+
generateStructureWithoutTools: "mock-agent-without-tools",
|
|
145
|
+
},
|
|
146
|
+
invoke: async (agent, params) => {
|
|
147
|
+
expect(agent).toBe("mock-agent-with-tools");
|
|
148
|
+
expect(params.isLargeContext).toBe(true);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
documentStructure: [{ title: "Large Doc", path: "/large" }],
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result = await generateSubStructure(
|
|
157
|
+
{
|
|
158
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
159
|
+
subSourcePaths: [{ path: testFile, reason: "Large file" }],
|
|
160
|
+
},
|
|
161
|
+
{ context: mockContext },
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(result.subStructure).toBeDefined();
|
|
165
|
+
expect(result.subStructure.length).toBe(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("should handle custom include and exclude patterns", async () => {
|
|
169
|
+
const srcDir = join(testDir, "src");
|
|
170
|
+
await mkdir(srcDir, { recursive: true });
|
|
171
|
+
await writeFile(join(srcDir, "index.js"), "// index");
|
|
172
|
+
await writeFile(join(srcDir, "test.spec.js"), "// test");
|
|
173
|
+
await writeFile(join(srcDir, "utils.ts"), "// utils");
|
|
174
|
+
|
|
175
|
+
const mockContext = {
|
|
176
|
+
agents: {
|
|
177
|
+
generateStructureWithoutTools: "mock-agent",
|
|
178
|
+
},
|
|
179
|
+
invoke: async (_agent, params) => {
|
|
180
|
+
const hasTestFile = params.files.some((f) => f.includes("test.spec.js"));
|
|
181
|
+
expect(hasTestFile).toBe(false);
|
|
182
|
+
|
|
183
|
+
return { documentStructure: [] };
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await generateSubStructure(
|
|
188
|
+
{
|
|
189
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
190
|
+
subSourcePaths: [{ path: testDir, reason: "Test directory" }],
|
|
191
|
+
includePatterns: ["**/*.js", "**/*.ts"],
|
|
192
|
+
excludePatterns: ["**/*.spec.js"],
|
|
193
|
+
},
|
|
194
|
+
{ context: mockContext },
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("should deduplicate files in result", async () => {
|
|
199
|
+
const testFile = join(testDir, "test.js");
|
|
200
|
+
await writeFile(testFile, "// test");
|
|
201
|
+
|
|
202
|
+
const mockContext = {
|
|
203
|
+
agents: {
|
|
204
|
+
generateStructureWithoutTools: "mock-agent",
|
|
205
|
+
},
|
|
206
|
+
invoke: async (_agent, params) => {
|
|
207
|
+
const uniqueFiles = new Set(params.files);
|
|
208
|
+
expect(uniqueFiles.size).toBe(params.files.length);
|
|
209
|
+
|
|
210
|
+
return { documentStructure: [] };
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
await generateSubStructure(
|
|
215
|
+
{
|
|
216
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
217
|
+
subSourcePaths: [
|
|
218
|
+
{ path: testFile, reason: "First" },
|
|
219
|
+
{ path: testFile, reason: "Second" },
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
{ context: mockContext },
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("should pass extra parameters to agent", async () => {
|
|
227
|
+
const testFile = join(testDir, "test.js");
|
|
228
|
+
await writeFile(testFile, "// test");
|
|
229
|
+
|
|
230
|
+
const mockContext = {
|
|
231
|
+
agents: {
|
|
232
|
+
generateStructureWithoutTools: "mock-agent",
|
|
233
|
+
},
|
|
234
|
+
invoke: async (_agent, params) => {
|
|
235
|
+
expect(params.extraParam1).toBe("value1");
|
|
236
|
+
expect(params.extraParam2).toBe("value2");
|
|
237
|
+
|
|
238
|
+
return { documentStructure: [] };
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
await generateSubStructure(
|
|
243
|
+
{
|
|
244
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
245
|
+
subSourcePaths: [{ path: testFile, reason: "Test" }],
|
|
246
|
+
extraParam1: "value1",
|
|
247
|
+
extraParam2: "value2",
|
|
248
|
+
},
|
|
249
|
+
{ context: mockContext },
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("should handle empty documentStructure from agent", async () => {
|
|
254
|
+
const testFile = join(testDir, "test.js");
|
|
255
|
+
await writeFile(testFile, "// test");
|
|
256
|
+
|
|
257
|
+
const mockContext = {
|
|
258
|
+
agents: {
|
|
259
|
+
generateStructureWithoutTools: "mock-agent",
|
|
260
|
+
},
|
|
261
|
+
invoke: async () => {
|
|
262
|
+
return {};
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const result = await generateSubStructure(
|
|
267
|
+
{
|
|
268
|
+
parentDocument: { title: "Parent", path: "/parent" },
|
|
269
|
+
subSourcePaths: [{ path: testFile, reason: "Test" }],
|
|
270
|
+
},
|
|
271
|
+
{ context: mockContext },
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
expect(result.subStructure).toEqual([]);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
@@ -986,13 +986,13 @@ describe("init", () => {
|
|
|
986
986
|
const mockResponses = {
|
|
987
987
|
checkbox_1: ["getStarted", "findAnswers"], // Document purpose
|
|
988
988
|
checkbox_2: ["developers"], // Target audience
|
|
989
|
-
|
|
990
|
-
select_4: "
|
|
991
|
-
select_5: "
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
input_8: join(tempDir, "docs"), // Documentation directory
|
|
989
|
+
select_3: "domainFamiliar", // Reader knowledge level
|
|
990
|
+
select_4: "balancedCoverage", // Documentation depth
|
|
991
|
+
select_5: "en", // Primary language
|
|
992
|
+
checkbox_6: ["zh", "ja"], // Translation languages
|
|
993
|
+
input_7: join(tempDir, "docs"), // Documentation directory
|
|
995
994
|
search: "", // Source paths (empty to finish)
|
|
995
|
+
input_9: "Custom rules for documentation", // Custom rules (last step)
|
|
996
996
|
};
|
|
997
997
|
|
|
998
998
|
const mockPrompts = createMockPrompts(mockResponses);
|
|
@@ -1044,13 +1044,13 @@ describe("init", () => {
|
|
|
1044
1044
|
checkbox_1: ["mixedPurpose"], // Document purpose - triggers follow-up
|
|
1045
1045
|
checkbox: ["completeTasks", "findAnswers"], // Top priorities after mixedPurpose
|
|
1046
1046
|
checkbox_2: ["developers", "devops"], // Target audience
|
|
1047
|
-
|
|
1048
|
-
select_4: "
|
|
1049
|
-
select_5: "
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
input_8: join(tempDir, "documentation"), // Documentation directory
|
|
1047
|
+
select_3: "experiencedUsers", // Reader knowledge level
|
|
1048
|
+
select_4: "comprehensive", // Documentation depth
|
|
1049
|
+
select_5: "zh-CN", // Primary language
|
|
1050
|
+
checkbox_6: ["en"], // Translation languages
|
|
1051
|
+
input_7: join(tempDir, "documentation"), // Documentation directory
|
|
1053
1052
|
search: "", // Source paths (empty to finish)
|
|
1053
|
+
input_9: "Custom rules for documentation", // Custom rules (last step)
|
|
1054
1054
|
};
|
|
1055
1055
|
|
|
1056
1056
|
const mockPrompts = createMockPrompts(mockResponses);
|
|
@@ -1091,13 +1091,13 @@ describe("init", () => {
|
|
|
1091
1091
|
const mockResponses = {
|
|
1092
1092
|
checkbox_1: ["getStarted"], // Document purpose
|
|
1093
1093
|
checkbox_2: ["endUsers"], // Target audience
|
|
1094
|
-
|
|
1095
|
-
select_4: "
|
|
1096
|
-
select_5: "
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
input_8: join(tempDir, "simple-docs"), // Documentation directory
|
|
1094
|
+
select_3: "completeBeginners", // Reader knowledge level
|
|
1095
|
+
select_4: "essentialOnly", // Documentation depth
|
|
1096
|
+
select_5: "en", // Primary language
|
|
1097
|
+
checkbox_6: [], // No translation languages
|
|
1098
|
+
input_7: join(tempDir, "simple-docs"), // Documentation directory
|
|
1100
1099
|
search: "", // Source paths (empty to finish)
|
|
1100
|
+
input_9: "Custom rules for documentation", // Custom rules (last step)
|
|
1101
1101
|
};
|
|
1102
1102
|
|
|
1103
1103
|
const mockPrompts = createMockPrompts(mockResponses);
|
|
@@ -22,6 +22,12 @@ const mockPublishDocs = {
|
|
|
22
22
|
publishDocs: mock(() => Promise.resolve({ success: true, boardId: "new-board-id" })),
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
const mockBrokerClient = {
|
|
26
|
+
checkCacheSession: mock(() => Promise.resolve({ sessionId: null, paymentLink: null })),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const mockBrokerClientConstructor = mock(() => mockBrokerClient);
|
|
30
|
+
|
|
25
31
|
const mockChalk = {
|
|
26
32
|
bold: mock((text) => text),
|
|
27
33
|
cyan: mock((text) => text),
|
|
@@ -47,6 +53,7 @@ describe("publish-docs", () => {
|
|
|
47
53
|
|
|
48
54
|
// Spies for internal utils
|
|
49
55
|
let getAccessTokenSpy;
|
|
56
|
+
let getOfficialAccessTokenSpy;
|
|
50
57
|
let beforePublishHookSpy;
|
|
51
58
|
let ensureTmpDirSpy;
|
|
52
59
|
let getGithubRepoUrlSpy;
|
|
@@ -57,6 +64,9 @@ describe("publish-docs", () => {
|
|
|
57
64
|
beforeAll(() => {
|
|
58
65
|
// Apply mocks for external dependencies only
|
|
59
66
|
mock.module("@aigne/publish-docs", () => mockPublishDocs);
|
|
67
|
+
mock.module("@blocklet/payment-broker-client/node", () => ({
|
|
68
|
+
BrokerClient: mockBrokerClientConstructor,
|
|
69
|
+
}));
|
|
60
70
|
mock.module("chalk", () => ({ default: mockChalk }));
|
|
61
71
|
mock.module("fs-extra", () => ({ default: mockFsExtra }));
|
|
62
72
|
mock.module("node:path", () => mockPath);
|
|
@@ -91,8 +101,19 @@ describe("publish-docs", () => {
|
|
|
91
101
|
mockChalk.cyan.mockClear();
|
|
92
102
|
mockChalk.cyan.mockImplementation((text) => text);
|
|
93
103
|
|
|
104
|
+
// Reset BrokerClient mock
|
|
105
|
+
mockBrokerClientConstructor.mockClear();
|
|
106
|
+
mockBrokerClientConstructor.mockImplementation(() => mockBrokerClient);
|
|
107
|
+
mockBrokerClient.checkCacheSession.mockClear();
|
|
108
|
+
mockBrokerClient.checkCacheSession.mockImplementation(() =>
|
|
109
|
+
Promise.resolve({ sessionId: null, paymentLink: null }),
|
|
110
|
+
);
|
|
111
|
+
|
|
94
112
|
// Set up spies for internal utils
|
|
95
113
|
getAccessTokenSpy = spyOn(authUtils, "getAccessToken").mockResolvedValue("mock-token");
|
|
114
|
+
getOfficialAccessTokenSpy = spyOn(authUtils, "getOfficialAccessToken").mockResolvedValue(
|
|
115
|
+
"official-mock-token",
|
|
116
|
+
);
|
|
96
117
|
beforePublishHookSpy = spyOn(d2Utils, "beforePublishHook").mockResolvedValue();
|
|
97
118
|
ensureTmpDirSpy = spyOn(d2Utils, "ensureTmpDir").mockResolvedValue();
|
|
98
119
|
getGithubRepoUrlSpy = spyOn(utils, "getGithubRepoUrl").mockReturnValue(
|
|
@@ -126,6 +147,7 @@ describe("publish-docs", () => {
|
|
|
126
147
|
|
|
127
148
|
// Restore all spies
|
|
128
149
|
getAccessTokenSpy?.mockRestore();
|
|
150
|
+
getOfficialAccessTokenSpy?.mockRestore();
|
|
129
151
|
beforePublishHookSpy?.mockRestore();
|
|
130
152
|
ensureTmpDirSpy?.mockRestore();
|
|
131
153
|
getGithubRepoUrlSpy?.mockRestore();
|
|
@@ -425,6 +447,55 @@ describe("publish-docs", () => {
|
|
|
425
447
|
});
|
|
426
448
|
|
|
427
449
|
// ERROR HANDLING TESTS
|
|
450
|
+
test("should handle failed official access token (null)", async () => {
|
|
451
|
+
loadConfigFromFileSpy.mockResolvedValue({});
|
|
452
|
+
getOfficialAccessTokenSpy.mockResolvedValue(null);
|
|
453
|
+
mockOptions.prompts.select.mockResolvedValue("default");
|
|
454
|
+
|
|
455
|
+
const result = await publishDocs(
|
|
456
|
+
{
|
|
457
|
+
docsDir: "./docs",
|
|
458
|
+
appUrl: "https://docsmith.aigne.io",
|
|
459
|
+
},
|
|
460
|
+
mockOptions,
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
expect(result.message).toBe("❌ Failed to publish docs: Failed to get official access token");
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test("should handle failed official access token (undefined)", async () => {
|
|
467
|
+
loadConfigFromFileSpy.mockResolvedValue({});
|
|
468
|
+
getOfficialAccessTokenSpy.mockResolvedValue(undefined);
|
|
469
|
+
mockOptions.prompts.select.mockResolvedValue("default");
|
|
470
|
+
|
|
471
|
+
const result = await publishDocs(
|
|
472
|
+
{
|
|
473
|
+
docsDir: "./docs",
|
|
474
|
+
appUrl: "https://docsmith.aigne.io",
|
|
475
|
+
},
|
|
476
|
+
mockOptions,
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
expect(result.message).toBe("❌ Failed to publish docs: Failed to get official access token");
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
test("should handle checkCacheSession failure", async () => {
|
|
483
|
+
loadConfigFromFileSpy.mockResolvedValue({});
|
|
484
|
+
getOfficialAccessTokenSpy.mockResolvedValue("valid-token");
|
|
485
|
+
mockBrokerClient.checkCacheSession.mockRejectedValue(new Error("Cache session failed"));
|
|
486
|
+
mockOptions.prompts.select.mockResolvedValue("default");
|
|
487
|
+
|
|
488
|
+
const result = await publishDocs(
|
|
489
|
+
{
|
|
490
|
+
docsDir: "./docs",
|
|
491
|
+
appUrl: "https://docsmith.aigne.io",
|
|
492
|
+
},
|
|
493
|
+
mockOptions,
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
expect(result.message).toBe("❌ Failed to publish docs: Cache session failed");
|
|
497
|
+
});
|
|
498
|
+
|
|
428
499
|
test("should handle publish failure", async () => {
|
|
429
500
|
mockPublishDocs.publishDocs.mockRejectedValue(new Error("Publish failed"));
|
|
430
501
|
|
|
@@ -605,6 +676,10 @@ describe("publish-docs", () => {
|
|
|
605
676
|
loadConfigFromFileSpy.mockResolvedValue({
|
|
606
677
|
checkoutId: "cached-checkout-123",
|
|
607
678
|
});
|
|
679
|
+
mockBrokerClient.checkCacheSession.mockResolvedValue({
|
|
680
|
+
sessionId: "cached-checkout-123",
|
|
681
|
+
paymentLink: "https://payment.example.com",
|
|
682
|
+
});
|
|
608
683
|
mockOptions.prompts.select.mockResolvedValue("default");
|
|
609
684
|
|
|
610
685
|
await publishDocs(
|
|
@@ -673,6 +748,10 @@ describe("publish-docs", () => {
|
|
|
673
748
|
checkoutId: "cached-checkout-123",
|
|
674
749
|
paymentUrl: "https://payment.example.com",
|
|
675
750
|
});
|
|
751
|
+
mockBrokerClient.checkCacheSession.mockResolvedValue({
|
|
752
|
+
sessionId: "cached-checkout-123",
|
|
753
|
+
paymentLink: "https://payment.example.com",
|
|
754
|
+
});
|
|
676
755
|
mockOptions.prompts.select.mockResolvedValue("new-instance-continue");
|
|
677
756
|
|
|
678
757
|
const consoleSpy = spyOn(console, "log").mockImplementation(() => {});
|