@aigne/doc-smith 0.5.1 → 0.7.0
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/.github/workflows/ci.yml +46 -0
- package/.github/workflows/reviewer.yml +2 -1
- package/CHANGELOG.md +17 -0
- package/agents/chat.yaml +30 -0
- package/agents/check-detail-result.mjs +2 -1
- package/agents/check-detail.mjs +1 -0
- package/agents/check-structure-plan.mjs +1 -1
- package/agents/docs-fs.yaml +25 -0
- package/agents/exit.mjs +6 -0
- package/agents/feedback-refiner.yaml +5 -1
- package/agents/find-items-by-paths.mjs +10 -4
- package/agents/fs.mjs +60 -0
- package/agents/input-generator.mjs +159 -90
- package/agents/load-config.mjs +0 -5
- package/agents/load-sources.mjs +119 -12
- package/agents/publish-docs.mjs +28 -11
- package/agents/retranslate.yaml +1 -1
- package/agents/team-publish-docs.yaml +2 -2
- package/aigne.yaml +1 -0
- package/package.json +13 -10
- package/prompts/content-detail-generator.md +12 -4
- package/prompts/document/custom-components.md +80 -0
- package/prompts/document/d2-chart/diy-examples.md +44 -0
- package/prompts/document/d2-chart/official-examples.md +708 -0
- package/prompts/document/d2-chart/rules.md +48 -0
- package/prompts/document/detail-generator.md +13 -15
- package/prompts/document/structure-planning.md +1 -3
- package/prompts/feedback-refiner.md +81 -60
- package/prompts/structure-planning.md +20 -3
- package/tests/check-detail-result.test.mjs +50 -2
- package/tests/conflict-resolution.test.mjs +237 -0
- package/tests/input-generator.test.mjs +940 -0
- package/tests/load-sources.test.mjs +627 -3
- package/tests/preferences-utils.test.mjs +94 -0
- package/tests/save-value-to-config.test.mjs +182 -5
- package/tests/utils.test.mjs +49 -0
- package/utils/auth-utils.mjs +1 -1
- package/utils/conflict-detector.mjs +72 -1
- package/utils/constants.mjs +139 -126
- package/utils/kroki-utils.mjs +162 -0
- package/utils/markdown-checker.mjs +175 -67
- package/utils/utils.mjs +97 -29
|
@@ -187,6 +187,100 @@ describe("preferences-utils", () => {
|
|
|
187
187
|
expect(preferences.rules[0]).toEqual(rule2); // Most recent first
|
|
188
188
|
expect(preferences.rules[1]).toEqual(rule1);
|
|
189
189
|
});
|
|
190
|
+
|
|
191
|
+
test("should handle rules with special characters and colons", () => {
|
|
192
|
+
const ruleData = {
|
|
193
|
+
rule: 'Always use proper punctuation: semicolons, colons, and quotes"like this"',
|
|
194
|
+
scope: "document",
|
|
195
|
+
limitToInputPaths: false,
|
|
196
|
+
};
|
|
197
|
+
const feedback = 'Please ensure proper punctuation: don\'t forget quotes"and colons"';
|
|
198
|
+
|
|
199
|
+
const newRule = addPreferenceRule(ruleData, feedback);
|
|
200
|
+
|
|
201
|
+
expect(newRule.rule).toBe(
|
|
202
|
+
'Always use proper punctuation: semicolons, colons, and quotes"like this"',
|
|
203
|
+
);
|
|
204
|
+
expect(newRule.feedback).toBe(
|
|
205
|
+
'Please ensure proper punctuation: don\'t forget quotes"and colons"',
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Verify it was saved and can be read back
|
|
209
|
+
const preferences = readPreferences();
|
|
210
|
+
expect(preferences.rules).toHaveLength(1);
|
|
211
|
+
expect(preferences.rules[0].rule).toEqual(newRule.rule);
|
|
212
|
+
expect(preferences.rules[0].feedback).toEqual(newRule.feedback);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("should handle rules with multiline content and special YAML characters", () => {
|
|
216
|
+
const ruleData = {
|
|
217
|
+
rule: "Multi-line rule:\\nLine 1: Use proper formatting\\nLine 2: Handle | pipe characters\\nLine 3: And > greater than symbols",
|
|
218
|
+
scope: "structure",
|
|
219
|
+
limitToInputPaths: false,
|
|
220
|
+
};
|
|
221
|
+
const feedback =
|
|
222
|
+
"Multi-line feedback:\\n- Check formatting\\n- Validate | pipes\\n- Handle > symbols\\n- Process 'single quotes' and \"double quotes\"";
|
|
223
|
+
|
|
224
|
+
const newRule = addPreferenceRule(ruleData, feedback);
|
|
225
|
+
|
|
226
|
+
expect(newRule.rule).toContain("Multi-line rule:\\nLine 1");
|
|
227
|
+
expect(newRule.feedback).toContain("Multi-line feedback:\\n- Check");
|
|
228
|
+
|
|
229
|
+
// Verify it was saved and can be read back correctly
|
|
230
|
+
const preferences = readPreferences();
|
|
231
|
+
expect(preferences.rules).toHaveLength(1);
|
|
232
|
+
expect(preferences.rules[0].rule).toEqual(ruleData.rule);
|
|
233
|
+
expect(preferences.rules[0].feedback).toEqual(feedback);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("should handle rules with Chinese and Unicode characters", () => {
|
|
237
|
+
const ruleData = {
|
|
238
|
+
rule: "使用正确的中文标点符号:逗号,句号。还有emoji 🔥 和特殊符号 @#$%^&*()",
|
|
239
|
+
scope: "document",
|
|
240
|
+
limitToInputPaths: false,
|
|
241
|
+
};
|
|
242
|
+
const feedback = '请确保正确使用标点:引号"这样"和冒号:还有各种符号!@#$%';
|
|
243
|
+
|
|
244
|
+
const newRule = addPreferenceRule(ruleData, feedback);
|
|
245
|
+
|
|
246
|
+
expect(newRule.rule).toBe(
|
|
247
|
+
"使用正确的中文标点符号:逗号,句号。还有emoji 🔥 和特殊符号 @#$%^&*()",
|
|
248
|
+
);
|
|
249
|
+
expect(newRule.feedback).toBe('请确保正确使用标点:引号"这样"和冒号:还有各种符号!@#$%');
|
|
250
|
+
|
|
251
|
+
// Verify it was saved and can be read back
|
|
252
|
+
const preferences = readPreferences();
|
|
253
|
+
expect(preferences.rules).toHaveLength(1);
|
|
254
|
+
expect(preferences.rules[0].rule).toEqual(newRule.rule);
|
|
255
|
+
expect(preferences.rules[0].feedback).toEqual(newRule.feedback);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("should handle paths with special characters", () => {
|
|
259
|
+
const ruleData = {
|
|
260
|
+
rule: "Path-specific rule for special directories",
|
|
261
|
+
scope: "structure",
|
|
262
|
+
limitToInputPaths: true,
|
|
263
|
+
};
|
|
264
|
+
const feedback = "Apply to special paths only";
|
|
265
|
+
const paths = [
|
|
266
|
+
"/docs/api: advanced",
|
|
267
|
+
"/docs/guide-中文",
|
|
268
|
+
"/path with spaces",
|
|
269
|
+
"/symbols@#$%/file",
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
const newRule = addPreferenceRule(ruleData, feedback, paths);
|
|
273
|
+
|
|
274
|
+
expect(newRule.paths).toEqual(paths);
|
|
275
|
+
|
|
276
|
+
// Verify it was saved with all special character paths
|
|
277
|
+
const preferences = readPreferences();
|
|
278
|
+
expect(preferences.rules[0].paths).toEqual(paths);
|
|
279
|
+
expect(preferences.rules[0].paths).toContain("/docs/api: advanced");
|
|
280
|
+
expect(preferences.rules[0].paths).toContain("/docs/guide-中文");
|
|
281
|
+
expect(preferences.rules[0].paths).toContain("/path with spaces");
|
|
282
|
+
expect(preferences.rules[0].paths).toContain("/symbols@#$%/file");
|
|
283
|
+
});
|
|
190
284
|
});
|
|
191
285
|
|
|
192
286
|
describe("getActiveRulesForScope", () => {
|
|
@@ -59,7 +59,7 @@ describe("saveValueToConfig", () => {
|
|
|
59
59
|
await saveValueToConfig("projectName", "test-project");
|
|
60
60
|
const content = await readConfigFile();
|
|
61
61
|
|
|
62
|
-
expect(content).toContain(
|
|
62
|
+
expect(content).toContain("projectName: test-project");
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test("Save string value with comment to empty file", async () => {
|
|
@@ -67,7 +67,7 @@ describe("saveValueToConfig", () => {
|
|
|
67
67
|
const content = await readConfigFile();
|
|
68
68
|
|
|
69
69
|
expect(content).toContain("# Project description");
|
|
70
|
-
expect(content).toContain(
|
|
70
|
+
expect(content).toContain("projectDesc: A test project");
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
test("Save array value to empty file", async () => {
|
|
@@ -103,7 +103,7 @@ describe("saveValueToConfig", () => {
|
|
|
103
103
|
await saveValueToConfig("projectName", "new-project");
|
|
104
104
|
const content = await readConfigFile();
|
|
105
105
|
|
|
106
|
-
expect(content).toContain(
|
|
106
|
+
expect(content).toContain("projectName: new-project");
|
|
107
107
|
expect(content).toContain('version: "1.0.0"');
|
|
108
108
|
expect(content).not.toContain("old-project");
|
|
109
109
|
});
|
|
@@ -160,7 +160,7 @@ version: "1.0.0"`);
|
|
|
160
160
|
const content = await readConfigFile();
|
|
161
161
|
|
|
162
162
|
expect(content).toContain("# Updated project name");
|
|
163
|
-
expect(content).toContain(
|
|
163
|
+
expect(content).toContain("projectName: updated-project");
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
test("Update value that already has comment", async () => {
|
|
@@ -172,7 +172,7 @@ version: "1.0.0"`);
|
|
|
172
172
|
const content = await readConfigFile();
|
|
173
173
|
|
|
174
174
|
expect(content).toContain("# Project information");
|
|
175
|
-
expect(content).toContain(
|
|
175
|
+
expect(content).toContain("projectName: new-project");
|
|
176
176
|
expect(content).not.toContain("# Updated project info");
|
|
177
177
|
});
|
|
178
178
|
|
|
@@ -244,4 +244,181 @@ version: "1.0.0"`);
|
|
|
244
244
|
|
|
245
245
|
expect(content).toBe(initialContent);
|
|
246
246
|
});
|
|
247
|
+
|
|
248
|
+
// Edge cases and special character tests
|
|
249
|
+
describe("Special characters and edge cases", () => {
|
|
250
|
+
test("Save string with double quotes", async () => {
|
|
251
|
+
await saveValueToConfig("projectDesc", 'My "awesome" project with quotes');
|
|
252
|
+
const content = await readConfigFile();
|
|
253
|
+
|
|
254
|
+
// Should be properly escaped
|
|
255
|
+
expect(content).toContain('My "awesome" project with quotes');
|
|
256
|
+
// Verify YAML is valid by checking structure
|
|
257
|
+
expect(content).toContain("projectDesc:");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("Save string with single quotes", async () => {
|
|
261
|
+
await saveValueToConfig("projectName", "It's a 'great' project");
|
|
262
|
+
const content = await readConfigFile();
|
|
263
|
+
|
|
264
|
+
expect(content).toContain("It's a 'great' project");
|
|
265
|
+
expect(content).toContain("projectName:");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("Save string with colons and special YAML characters", async () => {
|
|
269
|
+
await saveValueToConfig(
|
|
270
|
+
"rule",
|
|
271
|
+
"Use proper formatting: semicolons; colons: and pipes | symbols",
|
|
272
|
+
);
|
|
273
|
+
const content = await readConfigFile();
|
|
274
|
+
|
|
275
|
+
expect(content).toContain("Use proper formatting: semicolons; colons: and pipes | symbols");
|
|
276
|
+
expect(content).toContain("rule:");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("Save multiline string with newlines", async () => {
|
|
280
|
+
const multilineValue =
|
|
281
|
+
"Line 1: First line\nLine 2: Second line\nLine 3: Third line with: special characters";
|
|
282
|
+
await saveValueToConfig("multilineRule", multilineValue);
|
|
283
|
+
const content = await readConfigFile();
|
|
284
|
+
|
|
285
|
+
expect(content).toContain("Line 1: First line");
|
|
286
|
+
expect(content).toContain("Line 2: Second line");
|
|
287
|
+
expect(content).toContain("Line 3: Third line with: special characters");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("Save string with Chinese characters and symbols", async () => {
|
|
291
|
+
await saveValueToConfig(
|
|
292
|
+
"chineseProject",
|
|
293
|
+
'中文项目:包含特殊符号!@#¥%…()—— quotes"and" colons:',
|
|
294
|
+
);
|
|
295
|
+
const content = await readConfigFile();
|
|
296
|
+
|
|
297
|
+
expect(content).toContain('中文项目:包含特殊符号!@#¥%…()—— quotes"and" colons:');
|
|
298
|
+
expect(content).toContain("chineseProject:");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("Save string with emoji and Unicode", async () => {
|
|
302
|
+
await saveValueToConfig(
|
|
303
|
+
"emojiProject",
|
|
304
|
+
"🚀 Project with emoji: 🔥 hot features 💯 and symbols ⭐ ✨ 🎉",
|
|
305
|
+
);
|
|
306
|
+
const content = await readConfigFile();
|
|
307
|
+
|
|
308
|
+
expect(content).toContain("🚀 Project with emoji: 🔥 hot features 💯 and symbols ⭐ ✨ 🎉");
|
|
309
|
+
expect(content).toContain("emojiProject:");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("Save array with special character items", async () => {
|
|
313
|
+
const specialPaths = [
|
|
314
|
+
"./src: main source",
|
|
315
|
+
"./lib/utils: helper functions",
|
|
316
|
+
"./docs-中文/guide",
|
|
317
|
+
"./path with spaces/file",
|
|
318
|
+
"./symbols@#$%/directory",
|
|
319
|
+
'./quotes"and"colons:/path',
|
|
320
|
+
];
|
|
321
|
+
await saveValueToConfig("specialPaths", specialPaths);
|
|
322
|
+
const content = await readConfigFile();
|
|
323
|
+
|
|
324
|
+
expect(content).toContain("specialPaths:");
|
|
325
|
+
specialPaths.forEach((path) => {
|
|
326
|
+
expect(content).toContain(path);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("Save array with multiline items", async () => {
|
|
331
|
+
const multilineArray = [
|
|
332
|
+
"Item 1:\nWith newline content",
|
|
333
|
+
"Item 2: Simple item",
|
|
334
|
+
"Item 3:\nMultiple\nLines\nWith: colons and | pipes",
|
|
335
|
+
];
|
|
336
|
+
await saveValueToConfig("multilineArray", multilineArray);
|
|
337
|
+
const content = await readConfigFile();
|
|
338
|
+
|
|
339
|
+
expect(content).toContain("multilineArray:");
|
|
340
|
+
expect(content).toContain("Item 1:");
|
|
341
|
+
expect(content).toContain("With newline content");
|
|
342
|
+
expect(content).toContain("Item 3:");
|
|
343
|
+
expect(content).toContain("With: colons and | pipes");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("Update existing value containing special characters", async () => {
|
|
347
|
+
await createInitialConfig('projectName: "old: project with | pipes"\nversion: "1.0.0"');
|
|
348
|
+
|
|
349
|
+
await saveValueToConfig("projectName", 'new: project with "quotes" and | pipes: updated');
|
|
350
|
+
const content = await readConfigFile();
|
|
351
|
+
|
|
352
|
+
expect(content).toContain('new: project with "quotes" and | pipes: updated');
|
|
353
|
+
expect(content).toContain('version: "1.0.0"');
|
|
354
|
+
expect(content).not.toContain("old: project");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("Save very long string with special characters", async () => {
|
|
358
|
+
const longString =
|
|
359
|
+
"A very long project description that contains many special characters: colons, semicolons; quotes \"like this\", single quotes 'like this', pipes | symbols, Chinese characters 中文内容:包含各种符号!@#¥%…()——, emojis 🚀🔥💯⭐✨🎉, and newlines\nSecond line with more content\nThird line: even more special content with all sorts of symbols @#$%^&*()_+-=[]{}|\\;':./?,<>~`";
|
|
360
|
+
await saveValueToConfig("veryLongDesc", longString);
|
|
361
|
+
const content = await readConfigFile();
|
|
362
|
+
|
|
363
|
+
expect(content).toContain("veryLongDesc:");
|
|
364
|
+
expect(content).toContain("A very long project description");
|
|
365
|
+
expect(content).toContain("中文内容:包含各种符号");
|
|
366
|
+
expect(content).toContain("🚀🔥💯⭐✨🎉");
|
|
367
|
+
expect(content).toContain("Second line with more content");
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("Save empty string", async () => {
|
|
371
|
+
await saveValueToConfig("emptyString", "");
|
|
372
|
+
const content = await readConfigFile();
|
|
373
|
+
|
|
374
|
+
expect(content).toContain('emptyString: ""');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("Save string that looks like YAML syntax", async () => {
|
|
378
|
+
await saveValueToConfig("yamlLikeString", "key: value\nother: - item1\n- item2: subvalue");
|
|
379
|
+
const content = await readConfigFile();
|
|
380
|
+
|
|
381
|
+
expect(content).toContain("yamlLikeString:");
|
|
382
|
+
expect(content).toContain("key: value");
|
|
383
|
+
expect(content).toContain("other: - item1");
|
|
384
|
+
expect(content).toContain("- item2: subvalue");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("Complex mixed scenario with multiple special character values", async () => {
|
|
388
|
+
// Create initial config with some special content
|
|
389
|
+
await createInitialConfig(`# Project configuration
|
|
390
|
+
# with special comments: and symbols
|
|
391
|
+
projectName: "original: project"
|
|
392
|
+
locale: zh`);
|
|
393
|
+
|
|
394
|
+
// Update with various special character content
|
|
395
|
+
await saveValueToConfig("projectDesc", 'Updated description: with "quotes" and | pipes');
|
|
396
|
+
await saveValueToConfig("specialArray", [
|
|
397
|
+
"path: with colons",
|
|
398
|
+
"中文路径/文件",
|
|
399
|
+
"emoji 🔥 path",
|
|
400
|
+
]);
|
|
401
|
+
await saveValueToConfig(
|
|
402
|
+
"multilineContent",
|
|
403
|
+
"Line 1: content\nLine 2: more content\nLine 3: final content",
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const content = await readConfigFile();
|
|
407
|
+
|
|
408
|
+
// Check all content is preserved
|
|
409
|
+
expect(content).toContain("# Project configuration");
|
|
410
|
+
expect(content).toContain("# with special comments: and symbols");
|
|
411
|
+
expect(content).toContain('projectName: "original: project"');
|
|
412
|
+
expect(content).toContain("locale: zh");
|
|
413
|
+
expect(content).toContain('Updated description: with "quotes" and | pipes');
|
|
414
|
+
expect(content).toContain("specialArray:");
|
|
415
|
+
expect(content).toContain("path: with colons");
|
|
416
|
+
expect(content).toContain("中文路径/文件");
|
|
417
|
+
expect(content).toContain("emoji 🔥 path");
|
|
418
|
+
expect(content).toContain("multilineContent:");
|
|
419
|
+
expect(content).toContain("Line 1: content");
|
|
420
|
+
expect(content).toContain("Line 2: more content");
|
|
421
|
+
expect(content).toContain("Line 3: final content");
|
|
422
|
+
});
|
|
423
|
+
});
|
|
247
424
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { isGlobPattern } from "../utils/utils.mjs";
|
|
3
|
+
|
|
4
|
+
describe("utils", () => {
|
|
5
|
+
describe("isGlobPattern", () => {
|
|
6
|
+
test("should return true for patterns with asterisk", () => {
|
|
7
|
+
expect(isGlobPattern("*.js")).toBe(true);
|
|
8
|
+
expect(isGlobPattern("src/*.js")).toBe(true);
|
|
9
|
+
expect(isGlobPattern("**/*.js")).toBe(true);
|
|
10
|
+
expect(isGlobPattern("src/**/*.js")).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("should return true for patterns with question mark", () => {
|
|
14
|
+
expect(isGlobPattern("file?.js")).toBe(true);
|
|
15
|
+
expect(isGlobPattern("src/?odal.js")).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("should return true for patterns with character classes", () => {
|
|
19
|
+
expect(isGlobPattern("file[abc].js")).toBe(true);
|
|
20
|
+
expect(isGlobPattern("src/[Bb]utton.js")).toBe(true);
|
|
21
|
+
expect(isGlobPattern("file[0-9].js")).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("should return true for patterns with double asterisk", () => {
|
|
25
|
+
expect(isGlobPattern("**/file.js")).toBe(true);
|
|
26
|
+
expect(isGlobPattern("src/**/file.js")).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("should return false for regular paths", () => {
|
|
30
|
+
expect(isGlobPattern("src/file.js")).toBe(false);
|
|
31
|
+
expect(isGlobPattern("./src")).toBe(false);
|
|
32
|
+
expect(isGlobPattern("/absolute/path")).toBe(false);
|
|
33
|
+
expect(isGlobPattern("regular-file.js")).toBe(false);
|
|
34
|
+
expect(isGlobPattern("package.json")).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("should return false for empty or undefined input", () => {
|
|
38
|
+
expect(isGlobPattern("")).toBe(false);
|
|
39
|
+
expect(isGlobPattern(undefined)).toBe(false);
|
|
40
|
+
expect(isGlobPattern(null)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("should handle complex patterns", () => {
|
|
44
|
+
expect(isGlobPattern("src/**/*.{js,ts,jsx,tsx}")).toBe(true);
|
|
45
|
+
expect(isGlobPattern("components/**/[A-Z]*.js")).toBe(true);
|
|
46
|
+
expect(isGlobPattern("test/**/*test.js")).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
package/utils/auth-utils.mjs
CHANGED
|
@@ -91,7 +91,7 @@ export async function getAccessToken(appUrl) {
|
|
|
91
91
|
source: `AIGNE DocSmith connect to Discuss Kit`,
|
|
92
92
|
closeOnSuccess: true,
|
|
93
93
|
appName: "AIGNE DocSmith",
|
|
94
|
-
appLogo: "https://
|
|
94
|
+
appLogo: "https://docsmith.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
|
|
95
95
|
openPage: (pageUrl) => open(pageUrl),
|
|
96
96
|
});
|
|
97
97
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONFLICT_RULES } from "./constants.mjs";
|
|
1
|
+
import { CONFLICT_RESOLUTION_RULES, CONFLICT_RULES, RESOLUTION_STRATEGIES } from "./constants.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Detect internal conflicts within the same question (multi-select conflicts)
|
|
@@ -129,3 +129,74 @@ export function validateSelection(questionType, selectedValues) {
|
|
|
129
129
|
// For moderate conflicts, allow but warn
|
|
130
130
|
return true;
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Detect conflicts in user configuration selections that can be resolved through structure planning
|
|
135
|
+
* @param {Object} config - User configuration
|
|
136
|
+
* @returns {Array} Array of detected conflicts with resolution strategies
|
|
137
|
+
*/
|
|
138
|
+
export function detectResolvableConflicts(config) {
|
|
139
|
+
const conflicts = [];
|
|
140
|
+
|
|
141
|
+
// Check each question type for conflicts
|
|
142
|
+
Object.entries(CONFLICT_RESOLUTION_RULES).forEach(([questionType, rules]) => {
|
|
143
|
+
const selectedValues = config[questionType];
|
|
144
|
+
|
|
145
|
+
if (!selectedValues || !Array.isArray(selectedValues) || selectedValues.length < 2) {
|
|
146
|
+
return; // Skip if not multi-select or less than 2 items
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Extract values from the selected items (handle both string arrays and object arrays)
|
|
150
|
+
const selectedValueStrings = selectedValues.map((item) =>
|
|
151
|
+
typeof item === "object" && item.value ? item.value : item,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Check each conflict rule
|
|
155
|
+
rules.forEach((rule) => {
|
|
156
|
+
// Check if all conflict items are selected
|
|
157
|
+
const hasConflict = rule.conflictItems.every((item) => selectedValueStrings.includes(item));
|
|
158
|
+
|
|
159
|
+
if (hasConflict) {
|
|
160
|
+
conflicts.push({
|
|
161
|
+
type: questionType,
|
|
162
|
+
items: rule.conflictItems,
|
|
163
|
+
strategy: rule.strategy,
|
|
164
|
+
description: rule.description,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return conflicts;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate conflict resolution rules based on detected conflicts
|
|
175
|
+
* @param {Array} conflicts - Array of detected conflicts
|
|
176
|
+
* @returns {string} Conflict resolution instructions
|
|
177
|
+
*/
|
|
178
|
+
export function generateConflictResolutionRules(conflicts) {
|
|
179
|
+
if (conflicts.length === 0) return "";
|
|
180
|
+
|
|
181
|
+
const rules = ["=== Conflict Resolution Guidelines ==="];
|
|
182
|
+
|
|
183
|
+
conflicts.forEach((conflict) => {
|
|
184
|
+
const strategy = RESOLUTION_STRATEGIES[conflict.strategy];
|
|
185
|
+
if (strategy) {
|
|
186
|
+
rules.push(strategy(conflict.items));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
rules.push("");
|
|
191
|
+
rules.push("Conflict Resolution Principles:");
|
|
192
|
+
rules.push(
|
|
193
|
+
"1. Meet diverse needs through intelligent structural design, not simple concatenation",
|
|
194
|
+
);
|
|
195
|
+
rules.push("2. Create clear navigation paths for different purposes and audiences");
|
|
196
|
+
rules.push(
|
|
197
|
+
"3. Ensure content hierarchy is reasonable, avoid information duplication or contradiction",
|
|
198
|
+
);
|
|
199
|
+
rules.push("4. Prioritize user experience, enable users to quickly find needed information");
|
|
200
|
+
|
|
201
|
+
return rules.join("\n");
|
|
202
|
+
}
|