@aigne/doc-smith 0.0.1
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/README.md +87 -0
- package/agents/batch-docs-detail-generator.yaml +14 -0
- package/agents/batch-translate.yaml +44 -0
- package/agents/check-detail-generated.mjs +128 -0
- package/agents/check-detail-result.mjs +141 -0
- package/agents/check-structure-planning-result.yaml +30 -0
- package/agents/check-structure-planning.mjs +54 -0
- package/agents/content-detail-generator.yaml +50 -0
- package/agents/detail-generator-and-translate.yaml +88 -0
- package/agents/detail-regenerator.yaml +107 -0
- package/agents/docs-generator.yaml +93 -0
- package/agents/format-structure-plan.mjs +23 -0
- package/agents/input-generator.mjs +142 -0
- package/agents/load-sources.mjs +329 -0
- package/agents/publish-docs.mjs +212 -0
- package/agents/reflective-structure-planner.yaml +10 -0
- package/agents/save-docs.mjs +153 -0
- package/agents/save-output.mjs +25 -0
- package/agents/save-single-doc.mjs +18 -0
- package/agents/schema/structure-plan-result.yaml +32 -0
- package/agents/schema/structure-plan.yaml +26 -0
- package/agents/structure-planning.yaml +49 -0
- package/agents/transform-detail-datasources.mjs +14 -0
- package/agents/translate.yaml +28 -0
- package/aigne.yaml +28 -0
- package/biome.json +51 -0
- package/docs-mcp/aigne.yaml +8 -0
- package/docs-mcp/get-docs-detail.mjs +42 -0
- package/docs-mcp/get-docs-structure.mjs +11 -0
- package/package.json +33 -0
- package/prompts/check-structure-planning-result.md +82 -0
- package/prompts/content-detail-generator.md +99 -0
- package/prompts/document/detail-example.md +441 -0
- package/prompts/document/detail-generator.md +95 -0
- package/prompts/document/structure-example.md +98 -0
- package/prompts/document/structure-getting-started.md +10 -0
- package/prompts/document/structure-planning.md +17 -0
- package/prompts/structure-planning.md +108 -0
- package/prompts/translator.md +69 -0
- package/tests/README.md +93 -0
- package/tests/check-detail-result.test.mjs +103 -0
- package/tests/load-sources.test.mjs +642 -0
- package/tests/test-save-docs.mjs +132 -0
- package/utils/utils.mjs +86 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
type: team
|
|
2
|
+
name: regenerator
|
|
3
|
+
description: 生成文档详情并翻译
|
|
4
|
+
skills:
|
|
5
|
+
- ./load-sources.mjs
|
|
6
|
+
- type: transform
|
|
7
|
+
jsonata: |
|
|
8
|
+
$merge([
|
|
9
|
+
$,
|
|
10
|
+
{
|
|
11
|
+
'structurePlan': originalStructurePlan,
|
|
12
|
+
'structurePlanResult': $map(originalStructurePlan, function($item) {
|
|
13
|
+
$merge([
|
|
14
|
+
$item,
|
|
15
|
+
{
|
|
16
|
+
'translates': [$map(translateLanguages, function($lang) { {"language": $lang} })]
|
|
17
|
+
}
|
|
18
|
+
])
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
])
|
|
22
|
+
- type: transform
|
|
23
|
+
jsonata: |
|
|
24
|
+
(
|
|
25
|
+
$array := structurePlanResult;
|
|
26
|
+
$path := currentPath;
|
|
27
|
+
$foundItem := $array[path=$path][0];
|
|
28
|
+
$merge([
|
|
29
|
+
$foundItem,
|
|
30
|
+
{ "originalStructurePlan": originalStructurePlan }
|
|
31
|
+
|
|
32
|
+
])
|
|
33
|
+
)
|
|
34
|
+
- ./format-structure-plan.mjs
|
|
35
|
+
- ./detail-generator-and-translate.yaml
|
|
36
|
+
input_schema:
|
|
37
|
+
type: object
|
|
38
|
+
properties:
|
|
39
|
+
nodeName:
|
|
40
|
+
type: string
|
|
41
|
+
description: 节点名称
|
|
42
|
+
default: 部分
|
|
43
|
+
locale:
|
|
44
|
+
type: string
|
|
45
|
+
description: 语言
|
|
46
|
+
targetAudience:
|
|
47
|
+
type: string
|
|
48
|
+
description: 目标受众
|
|
49
|
+
glossary:
|
|
50
|
+
type: string
|
|
51
|
+
description: 术语表
|
|
52
|
+
currentPath:
|
|
53
|
+
type: string
|
|
54
|
+
description: 当前路径
|
|
55
|
+
feedback:
|
|
56
|
+
type: string
|
|
57
|
+
description: 反馈
|
|
58
|
+
sources:
|
|
59
|
+
type: array
|
|
60
|
+
items:
|
|
61
|
+
type: string
|
|
62
|
+
description: 源码路径
|
|
63
|
+
sourcesPath:
|
|
64
|
+
type: array
|
|
65
|
+
items:
|
|
66
|
+
type: string
|
|
67
|
+
description: 源码路径
|
|
68
|
+
default:
|
|
69
|
+
- ./
|
|
70
|
+
includePatterns:
|
|
71
|
+
type: array
|
|
72
|
+
items:
|
|
73
|
+
type: string
|
|
74
|
+
description: 包含的文件名
|
|
75
|
+
excludePatterns:
|
|
76
|
+
type: array
|
|
77
|
+
items:
|
|
78
|
+
type: string
|
|
79
|
+
description: 排除的文件名
|
|
80
|
+
outputDir:
|
|
81
|
+
type: string
|
|
82
|
+
description: 输出目录
|
|
83
|
+
default: ./aigne-docs/output
|
|
84
|
+
docsDir:
|
|
85
|
+
type: string
|
|
86
|
+
description: 文档目录
|
|
87
|
+
default: ./aigne-docs/docs
|
|
88
|
+
additionalInformation:
|
|
89
|
+
type: string
|
|
90
|
+
description: 额外补充信息
|
|
91
|
+
labels:
|
|
92
|
+
type: array
|
|
93
|
+
items:
|
|
94
|
+
type: string
|
|
95
|
+
description: 标签
|
|
96
|
+
output_schema:
|
|
97
|
+
type: object
|
|
98
|
+
properties:
|
|
99
|
+
title:
|
|
100
|
+
type: string
|
|
101
|
+
description:
|
|
102
|
+
type: string
|
|
103
|
+
path:
|
|
104
|
+
type: string
|
|
105
|
+
content:
|
|
106
|
+
type: string
|
|
107
|
+
mode: sequential
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
type: team
|
|
2
|
+
name: generator
|
|
3
|
+
description: 文档生成
|
|
4
|
+
skills:
|
|
5
|
+
- ./load-sources.mjs
|
|
6
|
+
- ./check-structure-planning.mjs
|
|
7
|
+
- type: transform
|
|
8
|
+
jsonata: |
|
|
9
|
+
$merge([$, {
|
|
10
|
+
"saveKey": "structurePlan",
|
|
11
|
+
"savePath": outputDir,
|
|
12
|
+
"fileName": "structure-plan.json"
|
|
13
|
+
}])
|
|
14
|
+
- ./save-output.mjs
|
|
15
|
+
- type: transform
|
|
16
|
+
jsonata: |
|
|
17
|
+
$merge([
|
|
18
|
+
$,
|
|
19
|
+
{
|
|
20
|
+
'structurePlanResult': $map(structurePlan, function($item) {
|
|
21
|
+
$merge([
|
|
22
|
+
$item,
|
|
23
|
+
{
|
|
24
|
+
'translates': [$map(translateLanguages, function($lang) { {"language": $lang} })]
|
|
25
|
+
}
|
|
26
|
+
])
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
])
|
|
30
|
+
- ./format-structure-plan.mjs
|
|
31
|
+
- ./batch-docs-detail-generator.yaml
|
|
32
|
+
- ./save-docs.mjs
|
|
33
|
+
|
|
34
|
+
input_schema:
|
|
35
|
+
type: object
|
|
36
|
+
properties:
|
|
37
|
+
nodeName:
|
|
38
|
+
type: string
|
|
39
|
+
description: 节点名称
|
|
40
|
+
default: Section
|
|
41
|
+
rules:
|
|
42
|
+
type: string
|
|
43
|
+
description: 用户的结构规划的要求和规则
|
|
44
|
+
locale:
|
|
45
|
+
type: string
|
|
46
|
+
description: 用户语言,如 zh、en
|
|
47
|
+
sources:
|
|
48
|
+
type: array
|
|
49
|
+
items:
|
|
50
|
+
type: string
|
|
51
|
+
description: 用户生成文档的源码文件
|
|
52
|
+
sourcesPath:
|
|
53
|
+
type: array
|
|
54
|
+
items:
|
|
55
|
+
type: string
|
|
56
|
+
description: 源码路径
|
|
57
|
+
default:
|
|
58
|
+
- ./
|
|
59
|
+
docsDir:
|
|
60
|
+
type: string
|
|
61
|
+
description: 文档保存目录
|
|
62
|
+
default: ./aigne-docs/docs
|
|
63
|
+
outputDir:
|
|
64
|
+
type: string
|
|
65
|
+
description: 输出目录
|
|
66
|
+
default: ./aigne-docs/output
|
|
67
|
+
translateLanguages:
|
|
68
|
+
type: array
|
|
69
|
+
items:
|
|
70
|
+
type: string
|
|
71
|
+
description: 翻译语言,如 zh、en
|
|
72
|
+
glossary:
|
|
73
|
+
type: string
|
|
74
|
+
description: 术语表
|
|
75
|
+
additionalInformation:
|
|
76
|
+
type: string
|
|
77
|
+
description: 额外补充信息
|
|
78
|
+
docsType:
|
|
79
|
+
type: string
|
|
80
|
+
description: 文档类型,支持:general、getting-started、reference、faq
|
|
81
|
+
default: general
|
|
82
|
+
structurePlanFeedback:
|
|
83
|
+
type: string
|
|
84
|
+
description: 结构规划反馈
|
|
85
|
+
labels:
|
|
86
|
+
type: array
|
|
87
|
+
items:
|
|
88
|
+
type: string
|
|
89
|
+
description: 标签
|
|
90
|
+
required:
|
|
91
|
+
- rules
|
|
92
|
+
- docsDir
|
|
93
|
+
mode: sequential
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { stringify } from "yaml";
|
|
2
|
+
|
|
3
|
+
export default async function formatStructurePlan({ structurePlan }) {
|
|
4
|
+
// Extract required fields from each item in structurePlan
|
|
5
|
+
const formattedData = structurePlan.map((item) => ({
|
|
6
|
+
title: item.title,
|
|
7
|
+
path: item.path,
|
|
8
|
+
parentId: item.parentId,
|
|
9
|
+
description: item.description,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
// Convert to YAML string
|
|
13
|
+
const yamlString = stringify(formattedData, {
|
|
14
|
+
indent: 2,
|
|
15
|
+
lineWidth: 120,
|
|
16
|
+
minContentWidth: 20,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
structurePlanYaml: yamlString,
|
|
21
|
+
structurePlan,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Guide users through multi-turn dialogue to collect information and generate YAML configuration
|
|
6
|
+
* @param {Object} params
|
|
7
|
+
* @param {string} params.outputPath - Output file path
|
|
8
|
+
* @param {string} params.fileName - File name
|
|
9
|
+
* @returns {Promise<Object>}
|
|
10
|
+
*/
|
|
11
|
+
export default async function init(
|
|
12
|
+
{ outputPath = "./doc-smith", fileName = "input.yaml" },
|
|
13
|
+
options
|
|
14
|
+
) {
|
|
15
|
+
console.log("Welcome to AIGNE Doc Smith!");
|
|
16
|
+
console.log(
|
|
17
|
+
"I will help you generate a configuration file through several questions.\n"
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Collect user information
|
|
21
|
+
const input = {};
|
|
22
|
+
|
|
23
|
+
// 1. Document generation rules
|
|
24
|
+
console.log("=== Document Generation Rules ===");
|
|
25
|
+
const rulesInput = await options.prompts.input({
|
|
26
|
+
message: "Please describe the document generation rules and requirements:",
|
|
27
|
+
});
|
|
28
|
+
input.rules = rulesInput.trim();
|
|
29
|
+
|
|
30
|
+
// 2. Target audience
|
|
31
|
+
console.log("\n=== Target Audience ===");
|
|
32
|
+
const targetAudienceInput = await options.prompts.input({
|
|
33
|
+
message:
|
|
34
|
+
"What is the target audience? (e.g., developers, users, press Enter to skip):",
|
|
35
|
+
});
|
|
36
|
+
input.targetAudience = targetAudienceInput.trim() || "";
|
|
37
|
+
|
|
38
|
+
// 3. Language settings
|
|
39
|
+
console.log("\n=== Language Settings ===");
|
|
40
|
+
const localeInput = await options.prompts.input({
|
|
41
|
+
message: "Primary language (e.g., en, zh, press Enter to skip):",
|
|
42
|
+
});
|
|
43
|
+
input.locale = localeInput.trim() || "";
|
|
44
|
+
|
|
45
|
+
// 4. Translation languages
|
|
46
|
+
console.log("\n=== Translation Settings ===");
|
|
47
|
+
const translateInput = await options.prompts.input({
|
|
48
|
+
message:
|
|
49
|
+
"Translation language list (comma-separated, e.g., zh,en, press Enter to skip):",
|
|
50
|
+
});
|
|
51
|
+
input.translateLanguages = translateInput.trim()
|
|
52
|
+
? translateInput.split(",").map((lang) => lang.trim())
|
|
53
|
+
: [];
|
|
54
|
+
|
|
55
|
+
// Generate YAML content
|
|
56
|
+
const yamlContent = generateYAML(input, outputPath);
|
|
57
|
+
|
|
58
|
+
// Save file
|
|
59
|
+
try {
|
|
60
|
+
const filePath = join(outputPath, fileName);
|
|
61
|
+
const dirPath = dirname(filePath);
|
|
62
|
+
|
|
63
|
+
// Create directory if it doesn't exist
|
|
64
|
+
await mkdir(dirPath, { recursive: true });
|
|
65
|
+
|
|
66
|
+
await writeFile(filePath, yamlContent, "utf8");
|
|
67
|
+
console.log(`\n✅ Configuration file saved to: ${filePath}`);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
inputGeneratorStatus: true,
|
|
71
|
+
inputGeneratorPath: filePath,
|
|
72
|
+
inputGeneratorContent: yamlContent,
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(`❌ Failed to save configuration file: ${error.message}`);
|
|
76
|
+
return {
|
|
77
|
+
inputGeneratorStatus: false,
|
|
78
|
+
inputGeneratorError: error.message,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generate YAML configuration content
|
|
85
|
+
* @param {Object} input - Input object
|
|
86
|
+
* @param {string} outputPath - Output path for directory configuration
|
|
87
|
+
* @returns {string} YAML string
|
|
88
|
+
*/
|
|
89
|
+
function generateYAML(input, outputPath) {
|
|
90
|
+
let yaml = "";
|
|
91
|
+
|
|
92
|
+
// Add rules
|
|
93
|
+
if (input.rules) {
|
|
94
|
+
yaml += `rules: |\n`;
|
|
95
|
+
yaml += ` ${input.rules.split("\n").join("\n ")}\n\n`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add target audience
|
|
99
|
+
if (input.targetAudience && input.targetAudience.trim()) {
|
|
100
|
+
yaml += `targetAudience: ${input.targetAudience}\n`;
|
|
101
|
+
} else {
|
|
102
|
+
yaml += `# targetAudience: developers # Target audience for the documentation (e.g., developers, users)\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add language settings
|
|
106
|
+
if (input.locale && input.locale.trim()) {
|
|
107
|
+
yaml += `locale: ${input.locale}\n`;
|
|
108
|
+
} else {
|
|
109
|
+
yaml += `# locale: en # Primary language for the documentation (e.g., en, zh)\n`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Add translation languages
|
|
113
|
+
if (
|
|
114
|
+
input.translateLanguages &&
|
|
115
|
+
input.translateLanguages.length > 0 &&
|
|
116
|
+
input.translateLanguages.some((lang) => lang.trim())
|
|
117
|
+
) {
|
|
118
|
+
yaml += `translateLanguages:\n`;
|
|
119
|
+
input.translateLanguages.forEach((lang) => {
|
|
120
|
+
if (lang.trim()) {
|
|
121
|
+
yaml += ` - ${lang}\n`;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
yaml += `# translateLanguages: # List of languages to translate the documentation to\n`;
|
|
126
|
+
yaml += `# - zh # Example: Chinese translation\n`;
|
|
127
|
+
yaml += `# - en # Example: English translation\n`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Add default directory and source path configurations
|
|
131
|
+
yaml += `docsDir: ${outputPath}/docs # Directory to save generated documentation\n`;
|
|
132
|
+
yaml += `outputDir: ${outputPath}/output # Directory to save output files\n`;
|
|
133
|
+
yaml += `sourcesPath: # Source code paths to analyze\n`;
|
|
134
|
+
yaml += ` - ./ # Current directory\n`;
|
|
135
|
+
|
|
136
|
+
return yaml;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Execute the function if this file is run directly
|
|
140
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
141
|
+
inputGenerator({}).catch(console.error);
|
|
142
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
|
|
5
|
+
// Default file patterns for inclusion and exclusion
|
|
6
|
+
const DEFAULT_INCLUDE_PATTERNS = [
|
|
7
|
+
"*.py",
|
|
8
|
+
"*.js",
|
|
9
|
+
"*.jsx",
|
|
10
|
+
"*.ts",
|
|
11
|
+
"*.tsx",
|
|
12
|
+
"*.go",
|
|
13
|
+
"*.java",
|
|
14
|
+
"*.pyi",
|
|
15
|
+
"*.pyx",
|
|
16
|
+
"*.c",
|
|
17
|
+
"*.cc",
|
|
18
|
+
"*.cpp",
|
|
19
|
+
"*.h",
|
|
20
|
+
"*.md",
|
|
21
|
+
"*.rst",
|
|
22
|
+
"*.json",
|
|
23
|
+
"*Dockerfile",
|
|
24
|
+
"*Makefile",
|
|
25
|
+
"*.yaml",
|
|
26
|
+
"*.yml",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
30
|
+
"aigne-docs/**",
|
|
31
|
+
"doc-smith/**",
|
|
32
|
+
"assets/**",
|
|
33
|
+
"data/**",
|
|
34
|
+
"images/**",
|
|
35
|
+
"public/**",
|
|
36
|
+
"static/**",
|
|
37
|
+
"**/vendor/**",
|
|
38
|
+
"temp/**",
|
|
39
|
+
"**/*docs/**",
|
|
40
|
+
"**/*doc/**",
|
|
41
|
+
"**/*venv/**",
|
|
42
|
+
"*.venv/**",
|
|
43
|
+
"*test*",
|
|
44
|
+
"**/*test/**",
|
|
45
|
+
"**/*tests/**",
|
|
46
|
+
"**/*examples/**",
|
|
47
|
+
"v1/**",
|
|
48
|
+
"**/*dist/**",
|
|
49
|
+
"**/*build/**",
|
|
50
|
+
"**/*experimental/**",
|
|
51
|
+
"**/*deprecated/**",
|
|
52
|
+
"**/*misc/**",
|
|
53
|
+
"**/*legacy/**",
|
|
54
|
+
".git/**",
|
|
55
|
+
".github/**",
|
|
56
|
+
".next/**",
|
|
57
|
+
".vscode/**",
|
|
58
|
+
"**/*obj/**",
|
|
59
|
+
"**/*bin/**",
|
|
60
|
+
"**/*node_modules/**",
|
|
61
|
+
"*.log",
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Load .gitignore patterns from a directory
|
|
66
|
+
* @param {string} dir - Directory path
|
|
67
|
+
* @returns {object|null} Ignore instance or null if no .gitignore found
|
|
68
|
+
*/
|
|
69
|
+
async function loadGitignore(dir) {
|
|
70
|
+
const gitignorePath = path.join(dir, ".gitignore");
|
|
71
|
+
try {
|
|
72
|
+
await access(gitignorePath);
|
|
73
|
+
const gitignoreContent = await readFile(gitignorePath, "utf8");
|
|
74
|
+
// Create ignore patterns from .gitignore content
|
|
75
|
+
const ignorePatterns = gitignoreContent
|
|
76
|
+
.split("\n")
|
|
77
|
+
.map((line) => line.trim())
|
|
78
|
+
.filter((line) => line && !line.startsWith("#"))
|
|
79
|
+
.map((line) => line.replace(/^\//, "")); // Remove leading slash
|
|
80
|
+
|
|
81
|
+
return ignorePatterns.length > 0 ? ignorePatterns : null;
|
|
82
|
+
} catch {
|
|
83
|
+
// .gitignore file doesn't exist
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get files using glob patterns
|
|
90
|
+
* @param {string} dir - Directory to search
|
|
91
|
+
* @param {string[]} includePatterns - Include patterns
|
|
92
|
+
* @param {string[]} excludePatterns - Exclude patterns
|
|
93
|
+
* @param {string[]} gitignorePatterns - .gitignore patterns
|
|
94
|
+
* @returns {Promise<string[]>} Array of file paths
|
|
95
|
+
*/
|
|
96
|
+
async function getFilesWithGlob(
|
|
97
|
+
dir,
|
|
98
|
+
includePatterns,
|
|
99
|
+
excludePatterns,
|
|
100
|
+
gitignorePatterns
|
|
101
|
+
) {
|
|
102
|
+
// Prepare all ignore patterns
|
|
103
|
+
const allIgnorePatterns = [];
|
|
104
|
+
|
|
105
|
+
if (excludePatterns) {
|
|
106
|
+
allIgnorePatterns.push(...excludePatterns);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (gitignorePatterns) {
|
|
110
|
+
allIgnorePatterns.push(...gitignorePatterns);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Add default exclusions if not already present
|
|
114
|
+
const defaultExclusions = ["node_modules/**", "test/**", "temp/**"];
|
|
115
|
+
for (const exclusion of defaultExclusions) {
|
|
116
|
+
if (!allIgnorePatterns.includes(exclusion)) {
|
|
117
|
+
allIgnorePatterns.push(exclusion);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Convert patterns to be relative to the directory
|
|
122
|
+
const patterns = includePatterns.map((pattern) => {
|
|
123
|
+
// If pattern doesn't start with / or **, make it relative to dir
|
|
124
|
+
if (!pattern.startsWith("/") && !pattern.startsWith("**")) {
|
|
125
|
+
return `**/${pattern}`; // Use ** to search recursively
|
|
126
|
+
}
|
|
127
|
+
return pattern;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const files = await glob(patterns, {
|
|
132
|
+
cwd: dir,
|
|
133
|
+
ignore: allIgnorePatterns.length > 0 ? allIgnorePatterns : undefined,
|
|
134
|
+
absolute: true,
|
|
135
|
+
nodir: true, // Only return files, not directories
|
|
136
|
+
dot: false, // Don't include dot files by default
|
|
137
|
+
gitignore: true, // Enable .gitignore support
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return files;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.warn(
|
|
143
|
+
`Warning: Error during glob search in ${dir}: ${error.message}`
|
|
144
|
+
);
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default async function loadSources({
|
|
150
|
+
sources = [],
|
|
151
|
+
sourcesPath = [],
|
|
152
|
+
includePatterns,
|
|
153
|
+
excludePatterns,
|
|
154
|
+
outputDir,
|
|
155
|
+
docsDir,
|
|
156
|
+
currentPath,
|
|
157
|
+
useDefaultPatterns = true,
|
|
158
|
+
} = {}) {
|
|
159
|
+
let files = Array.isArray(sources) ? [...sources] : [];
|
|
160
|
+
|
|
161
|
+
if (sourcesPath) {
|
|
162
|
+
const paths = Array.isArray(sourcesPath) ? sourcesPath : [sourcesPath];
|
|
163
|
+
let allFiles = [];
|
|
164
|
+
|
|
165
|
+
for (const dir of paths) {
|
|
166
|
+
try {
|
|
167
|
+
// Load .gitignore for this directory
|
|
168
|
+
const gitignorePatterns = await loadGitignore(dir);
|
|
169
|
+
|
|
170
|
+
// Prepare patterns
|
|
171
|
+
let finalIncludePatterns = null;
|
|
172
|
+
let finalExcludePatterns = null;
|
|
173
|
+
|
|
174
|
+
if (useDefaultPatterns) {
|
|
175
|
+
// Merge with default patterns
|
|
176
|
+
const userInclude = includePatterns
|
|
177
|
+
? Array.isArray(includePatterns)
|
|
178
|
+
? includePatterns
|
|
179
|
+
: [includePatterns]
|
|
180
|
+
: [];
|
|
181
|
+
const userExclude = excludePatterns
|
|
182
|
+
? Array.isArray(excludePatterns)
|
|
183
|
+
? excludePatterns
|
|
184
|
+
: [excludePatterns]
|
|
185
|
+
: [];
|
|
186
|
+
|
|
187
|
+
finalIncludePatterns = [...DEFAULT_INCLUDE_PATTERNS, ...userInclude];
|
|
188
|
+
finalExcludePatterns = [...DEFAULT_EXCLUDE_PATTERNS, ...userExclude];
|
|
189
|
+
} else {
|
|
190
|
+
// Use only user patterns
|
|
191
|
+
if (includePatterns) {
|
|
192
|
+
finalIncludePatterns = Array.isArray(includePatterns)
|
|
193
|
+
? includePatterns
|
|
194
|
+
: [includePatterns];
|
|
195
|
+
}
|
|
196
|
+
if (excludePatterns) {
|
|
197
|
+
finalExcludePatterns = Array.isArray(excludePatterns)
|
|
198
|
+
? excludePatterns
|
|
199
|
+
: [excludePatterns];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Get files using glob
|
|
204
|
+
const filesInDir = await getFilesWithGlob(
|
|
205
|
+
dir,
|
|
206
|
+
finalIncludePatterns,
|
|
207
|
+
finalExcludePatterns,
|
|
208
|
+
gitignorePatterns
|
|
209
|
+
);
|
|
210
|
+
allFiles = allFiles.concat(filesInDir);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (err.code !== "ENOENT") throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
files = files.concat(allFiles);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
files = [...new Set(files)];
|
|
220
|
+
let allSources = "";
|
|
221
|
+
const sourceFiles = await Promise.all(
|
|
222
|
+
files.map(async (file) => {
|
|
223
|
+
const content = await readFile(file, "utf8");
|
|
224
|
+
allSources += `// sourceId: ${file}\n${content}\n`;
|
|
225
|
+
return {
|
|
226
|
+
sourceId: file,
|
|
227
|
+
content,
|
|
228
|
+
};
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Get the last structure plan result
|
|
233
|
+
let originalStructurePlan;
|
|
234
|
+
const structurePlanPath = path.join(outputDir, "structure-plan.json");
|
|
235
|
+
try {
|
|
236
|
+
await access(structurePlanPath);
|
|
237
|
+
const structurePlanResult = await readFile(structurePlanPath, "utf8");
|
|
238
|
+
if (structurePlanResult) {
|
|
239
|
+
try {
|
|
240
|
+
originalStructurePlan = JSON.parse(structurePlanResult);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.error(`Failed to parse structure-plan.json: ${err.message}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
// The file does not exist, originalStructurePlan remains undefined
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get the last output result of the specified path
|
|
250
|
+
let content;
|
|
251
|
+
if (currentPath) {
|
|
252
|
+
const flatName = currentPath.replace(/^\//, "").replace(/\//g, "-");
|
|
253
|
+
const fileFullName = `${flatName}.md`;
|
|
254
|
+
const filePath = path.join(docsDir, fileFullName);
|
|
255
|
+
try {
|
|
256
|
+
await access(filePath);
|
|
257
|
+
content = await readFile(filePath, "utf8");
|
|
258
|
+
} catch {
|
|
259
|
+
// The file does not exist, content remains undefined
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
datasourcesList: sourceFiles,
|
|
265
|
+
datasources: allSources,
|
|
266
|
+
content,
|
|
267
|
+
originalStructurePlan,
|
|
268
|
+
files,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
loadSources.input_schema = {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
sources: {
|
|
276
|
+
type: "array",
|
|
277
|
+
items: { type: "string" },
|
|
278
|
+
description: "Array of paths to the sources files",
|
|
279
|
+
},
|
|
280
|
+
sourcesPath: {
|
|
281
|
+
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
282
|
+
description: "Directory or directories to recursively read files from",
|
|
283
|
+
},
|
|
284
|
+
includePatterns: {
|
|
285
|
+
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
286
|
+
description:
|
|
287
|
+
"Glob patterns to filter files by path or filename. If not set, include all.",
|
|
288
|
+
},
|
|
289
|
+
excludePatterns: {
|
|
290
|
+
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
291
|
+
description:
|
|
292
|
+
"Glob patterns to exclude files by path or filename. If not set, exclude none.",
|
|
293
|
+
},
|
|
294
|
+
useDefaultPatterns: {
|
|
295
|
+
type: "boolean",
|
|
296
|
+
description:
|
|
297
|
+
"Whether to use default include/exclude patterns. Defaults to true.",
|
|
298
|
+
},
|
|
299
|
+
currentPath: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "The current path of the document",
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
required: [],
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
loadSources.output_schema = {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
datasources: {
|
|
311
|
+
type: "string",
|
|
312
|
+
},
|
|
313
|
+
datasourcesList: {
|
|
314
|
+
type: "array",
|
|
315
|
+
items: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
sourceId: { type: "string" },
|
|
319
|
+
content: { type: "string" },
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
files: {
|
|
324
|
+
type: "array",
|
|
325
|
+
items: { type: "string" },
|
|
326
|
+
description: "Array of file paths that were loaded",
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
};
|