@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.
Files changed (44) hide show
  1. package/README.md +87 -0
  2. package/agents/batch-docs-detail-generator.yaml +14 -0
  3. package/agents/batch-translate.yaml +44 -0
  4. package/agents/check-detail-generated.mjs +128 -0
  5. package/agents/check-detail-result.mjs +141 -0
  6. package/agents/check-structure-planning-result.yaml +30 -0
  7. package/agents/check-structure-planning.mjs +54 -0
  8. package/agents/content-detail-generator.yaml +50 -0
  9. package/agents/detail-generator-and-translate.yaml +88 -0
  10. package/agents/detail-regenerator.yaml +107 -0
  11. package/agents/docs-generator.yaml +93 -0
  12. package/agents/format-structure-plan.mjs +23 -0
  13. package/agents/input-generator.mjs +142 -0
  14. package/agents/load-sources.mjs +329 -0
  15. package/agents/publish-docs.mjs +212 -0
  16. package/agents/reflective-structure-planner.yaml +10 -0
  17. package/agents/save-docs.mjs +153 -0
  18. package/agents/save-output.mjs +25 -0
  19. package/agents/save-single-doc.mjs +18 -0
  20. package/agents/schema/structure-plan-result.yaml +32 -0
  21. package/agents/schema/structure-plan.yaml +26 -0
  22. package/agents/structure-planning.yaml +49 -0
  23. package/agents/transform-detail-datasources.mjs +14 -0
  24. package/agents/translate.yaml +28 -0
  25. package/aigne.yaml +28 -0
  26. package/biome.json +51 -0
  27. package/docs-mcp/aigne.yaml +8 -0
  28. package/docs-mcp/get-docs-detail.mjs +42 -0
  29. package/docs-mcp/get-docs-structure.mjs +11 -0
  30. package/package.json +33 -0
  31. package/prompts/check-structure-planning-result.md +82 -0
  32. package/prompts/content-detail-generator.md +99 -0
  33. package/prompts/document/detail-example.md +441 -0
  34. package/prompts/document/detail-generator.md +95 -0
  35. package/prompts/document/structure-example.md +98 -0
  36. package/prompts/document/structure-getting-started.md +10 -0
  37. package/prompts/document/structure-planning.md +17 -0
  38. package/prompts/structure-planning.md +108 -0
  39. package/prompts/translator.md +69 -0
  40. package/tests/README.md +93 -0
  41. package/tests/check-detail-result.test.mjs +103 -0
  42. package/tests/load-sources.test.mjs +642 -0
  43. package/tests/test-save-docs.mjs +132 -0
  44. package/utils/utils.mjs +86 -0
@@ -0,0 +1,212 @@
1
+ import { join } from "node:path";
2
+ import { joinURL } from "ufo";
3
+ import open from "open";
4
+ import { publishDocs as publishDocsFn } from "@aigne/publish-docs";
5
+ import { createConnect } from "@aigne/cli/utils/load-aigne.js";
6
+ import { existsSync, mkdirSync } from "node:fs";
7
+ import { readFile, writeFile } from "node:fs/promises";
8
+ import { homedir } from "node:os";
9
+ import { parse, stringify } from "yaml";
10
+ import { execSync } from "node:child_process";
11
+ import { basename } from "node:path";
12
+
13
+ const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
14
+
15
+ /**
16
+ * Get project name from git repository or current directory
17
+ * @returns {string} - The project name
18
+ */
19
+ function getProjectName() {
20
+ // Check if we're in a git repository
21
+ try {
22
+ const gitRemote = execSync("git remote get-url origin", {
23
+ encoding: "utf8",
24
+ stdio: ["pipe", "pipe", "ignore"],
25
+ }).trim();
26
+
27
+ // Extract repository name from git remote URL
28
+ const repoName = gitRemote.split("/").pop().replace(".git", "");
29
+ return repoName;
30
+ } catch (error) {
31
+ // Not in git repository or no origin remote, use current directory name
32
+ return basename(process.cwd());
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Get access token from environment, config file, or prompt user for authorization
38
+ * @param {string} appUrl - The application URL
39
+ * @returns {Promise<string>} - The access token
40
+ */
41
+ async function getAccessToken(appUrl) {
42
+ const DOC_SMITH_ENV_FILE = join(
43
+ homedir(),
44
+ ".aigne",
45
+ "doc-smith-connected.yaml"
46
+ );
47
+ const { hostname } = new URL(appUrl);
48
+
49
+ let accessToken = process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN;
50
+
51
+ // Check if access token exists in environment or config file
52
+ if (!accessToken) {
53
+ try {
54
+ if (existsSync(DOC_SMITH_ENV_FILE)) {
55
+ const data = await readFile(DOC_SMITH_ENV_FILE, "utf8");
56
+ if (data.includes("DOC_DISCUSS_KIT_ACCESS_TOKEN")) {
57
+ const envs = parse(data);
58
+ if (envs[hostname] && envs[hostname].DOC_DISCUSS_KIT_ACCESS_TOKEN) {
59
+ accessToken = envs[hostname].DOC_DISCUSS_KIT_ACCESS_TOKEN;
60
+ }
61
+ }
62
+ }
63
+ } catch (error) {
64
+ console.warn("Failed to read config file:", error.message);
65
+ }
66
+ }
67
+
68
+ // If still no access token, prompt user to authorize
69
+ if (!accessToken) {
70
+ const DISCUSS_KIT_URL = appUrl;
71
+ const connectUrl = joinURL(
72
+ new URL(DISCUSS_KIT_URL).origin,
73
+ WELLKNOWN_SERVICE_PATH_PREFIX
74
+ );
75
+
76
+ try {
77
+ const result = await createConnect({
78
+ connectUrl: connectUrl,
79
+ connectAction: "gen-simple-access-key",
80
+ source: `@aigne/cli doc-smith connect to Discuss Kit`,
81
+ closeOnSuccess: true,
82
+ openPage: (pageUrl) => open(pageUrl),
83
+ });
84
+
85
+ accessToken = result.accessKeySecret;
86
+ process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = accessToken;
87
+
88
+ // Save the access token to config file
89
+ const aigneDir = join(homedir(), ".aigne");
90
+ if (!existsSync(aigneDir)) {
91
+ mkdirSync(aigneDir, { recursive: true });
92
+ }
93
+
94
+ const existingConfig = existsSync(DOC_SMITH_ENV_FILE)
95
+ ? parse(await readFile(DOC_SMITH_ENV_FILE, "utf8"))
96
+ : {};
97
+
98
+ await writeFile(
99
+ DOC_SMITH_ENV_FILE,
100
+ stringify({
101
+ ...existingConfig,
102
+ [hostname]: {
103
+ DOC_DISCUSS_KIT_ACCESS_TOKEN: accessToken,
104
+ DOC_DISCUSS_KIT_URL: DISCUSS_KIT_URL,
105
+ },
106
+ })
107
+ );
108
+ } catch (error) {
109
+ console.error("Failed to get access token:", error);
110
+ throw new Error(
111
+ "Failed to obtain access token. Please check your network connection and try again later."
112
+ );
113
+ }
114
+ }
115
+
116
+ return accessToken;
117
+ }
118
+
119
+ /**
120
+ * Save boardId to input.yaml file if it was auto-created
121
+ * @param {string} boardId - The original boardId (may be empty)
122
+ * @param {string} newBoardId - The boardId returned from publishDocsFn
123
+ */
124
+ async function saveBoardIdToInput(boardId, newBoardId) {
125
+ // Only save if boardId was auto-created
126
+ if (!boardId && newBoardId) {
127
+ try {
128
+ const docSmithDir = join(process.cwd(), "doc-smith");
129
+ if (!existsSync(docSmithDir)) {
130
+ mkdirSync(docSmithDir, { recursive: true });
131
+ }
132
+
133
+ const inputFilePath = join(docSmithDir, "input.yaml");
134
+ let fileContent = "";
135
+
136
+ // Read existing file content if it exists
137
+ if (existsSync(inputFilePath)) {
138
+ fileContent = await readFile(inputFilePath, "utf8");
139
+ }
140
+
141
+ // Check if boardId already exists in the file
142
+ const boardIdRegex = /^boardId:\s*.*$/m;
143
+ const newBoardIdLine = `boardId: ${newBoardId}`;
144
+
145
+ if (boardIdRegex.test(fileContent)) {
146
+ // Replace existing boardId line
147
+ fileContent = fileContent.replace(boardIdRegex, newBoardIdLine);
148
+ } else {
149
+ // Add boardId to the end of file
150
+ if (fileContent && !fileContent.endsWith("\n")) {
151
+ fileContent += "\n";
152
+ }
153
+ fileContent += newBoardIdLine + "\n";
154
+ }
155
+
156
+ await writeFile(inputFilePath, fileContent);
157
+ console.log(`Board ID saved to: ${inputFilePath}`);
158
+ } catch (error) {
159
+ console.warn("Failed to save board ID to input.yaml:", error.message);
160
+ }
161
+ }
162
+ }
163
+
164
+ export default async function publish({ docsDir, appUrl, boardId }) {
165
+ const accessToken = await getAccessToken(appUrl);
166
+
167
+ process.env.DOC_ROOT_DIR = docsDir;
168
+
169
+ const sidebarPath = join(docsDir, "_sidebar.md");
170
+
171
+ const boardName = boardId ? "" : getProjectName();
172
+
173
+ const { success, boardId: newBoardId } = await publishDocsFn({
174
+ sidebarPath,
175
+ accessToken,
176
+ appUrl,
177
+ boardId,
178
+ // If boardId is empty, use project name as boardName and auto create board
179
+ boardName,
180
+ autoCreateBoard: !boardId,
181
+ });
182
+
183
+ // Save boardId to input.yaml if it was auto-created
184
+ await saveBoardIdToInput(boardId, newBoardId);
185
+
186
+ return {
187
+ publishResult: {
188
+ success,
189
+ },
190
+ };
191
+ }
192
+
193
+ publish.input_schema = {
194
+ type: "object",
195
+ properties: {
196
+ docsDir: {
197
+ type: "string",
198
+ description: "The directory of the docs",
199
+ },
200
+ appUrl: {
201
+ type: "string",
202
+ description: "The url of the app",
203
+ default:
204
+ // "https://bbqawfllzdt3pahkdsrsone6p3wpxcwp62vlabtawfu.did.abtnet.io",
205
+ "https://www.staging.arcblock.io",
206
+ },
207
+ boardId: {
208
+ type: "string",
209
+ description: "The id of the board",
210
+ },
211
+ },
212
+ };
@@ -0,0 +1,10 @@
1
+ type: team
2
+ name: reflective-structure-planner
3
+ description: A team of agents that plan the structure of the documentation.
4
+ skills:
5
+ - structure-planning.yaml
6
+ reflection:
7
+ reviewer: check-structure-planning-result.yaml
8
+ is_approved: isValid
9
+ max_iterations: 3
10
+ return_last_on_max_iterations: true
@@ -0,0 +1,153 @@
1
+ import { writeFile, readdir, unlink } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * @param {Object} params
6
+ * @param {Array<{path: string, content: string, title: string}>} params.structurePlan
7
+ * @param {string} params.docsDir
8
+ * @param {Array<string>} [params.translateLanguages] - Translation languages
9
+ * @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
10
+ */
11
+ export default async function saveDocs({
12
+ structurePlanResult: structurePlan,
13
+ docsDir,
14
+ translateLanguages = [],
15
+ }) {
16
+ const results = [];
17
+
18
+ // Generate _sidebar.md
19
+ try {
20
+ const sidebar = generateSidebar(structurePlan);
21
+ const sidebarPath = join(docsDir, "_sidebar.md");
22
+ await writeFile(sidebarPath, sidebar, "utf8");
23
+ results.push({ path: sidebarPath, success: true });
24
+ } catch (err) {
25
+ results.push({ path: "_sidebar.md", success: false, error: err.message });
26
+ }
27
+
28
+ // Clean up invalid .md files that are no longer in the structure plan
29
+ try {
30
+ const cleanupResults = await cleanupInvalidFiles(
31
+ structurePlan,
32
+ docsDir,
33
+ translateLanguages
34
+ );
35
+ results.push(...cleanupResults);
36
+ } catch (err) {
37
+ results.push({ path: "cleanup", success: false, error: err.message });
38
+ }
39
+
40
+ return { saveDocsResult: results };
41
+ }
42
+
43
+ /**
44
+ * Clean up .md files that are no longer in the structure plan
45
+ * @param {Array<{path: string, title: string}>} structurePlan
46
+ * @param {string} docsDir
47
+ * @param {Array<string>} translateLanguages
48
+ * @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
49
+ */
50
+ async function cleanupInvalidFiles(structurePlan, docsDir, translateLanguages) {
51
+ const results = [];
52
+
53
+ try {
54
+ // Get all .md files in docsDir
55
+ const files = await readdir(docsDir);
56
+ const mdFiles = files.filter((file) => file.endsWith(".md"));
57
+
58
+ // Generate expected file names from structure plan
59
+ const expectedFiles = new Set();
60
+
61
+ // Add main document files
62
+ for (const { path } of structurePlan) {
63
+ const flatName = path.replace(/^\//, "").replace(/\//g, "-");
64
+ const fileName = `${flatName}.md`;
65
+ expectedFiles.add(fileName);
66
+
67
+ // Add translation files for each language
68
+ for (const lang of translateLanguages) {
69
+ const translateFileName = `${flatName}.${lang}.md`;
70
+ expectedFiles.add(translateFileName);
71
+ }
72
+ }
73
+
74
+ // Find files to delete (files that are not in expectedFiles and not _sidebar.md)
75
+ const filesToDelete = mdFiles.filter(
76
+ (file) => !expectedFiles.has(file) && file !== "_sidebar.md"
77
+ );
78
+
79
+ // Delete invalid files
80
+ for (const file of filesToDelete) {
81
+ try {
82
+ const filePath = join(docsDir, file);
83
+ await unlink(filePath);
84
+ results.push({
85
+ path: filePath,
86
+ success: true,
87
+ message: `Deleted invalid file: ${file}`,
88
+ });
89
+ } catch (err) {
90
+ results.push({
91
+ path: file,
92
+ success: false,
93
+ error: `Failed to delete ${file}: ${err.message}`,
94
+ });
95
+ }
96
+ }
97
+
98
+ if (filesToDelete.length > 0) {
99
+ console.log(
100
+ `Cleaned up ${filesToDelete.length} invalid .md files from ${docsDir}`
101
+ );
102
+ }
103
+ } catch (err) {
104
+ // If docsDir doesn't exist or can't be read, that's okay
105
+ if (err.code !== "ENOENT") {
106
+ throw err;
107
+ }
108
+ }
109
+
110
+ return results;
111
+ }
112
+
113
+ // Generate sidebar content, support nested structure, and the order is consistent with structurePlan
114
+ function generateSidebar(structurePlan) {
115
+ // Build tree structure
116
+ const root = {};
117
+ for (const { path, title, parentId } of structurePlan) {
118
+ const relPath = path.replace(/^\//, "");
119
+ const segments = relPath.split("/");
120
+ let node = root;
121
+ for (let i = 0; i < segments.length; i++) {
122
+ const seg = segments[i];
123
+ if (!node[seg])
124
+ node[seg] = {
125
+ __children: {},
126
+ __title: null,
127
+ __fullPath: segments.slice(0, i + 1).join("/"),
128
+ __parentId: parentId,
129
+ };
130
+ if (i === segments.length - 1) node[seg].__title = title;
131
+ node = node[seg].__children;
132
+ }
133
+ }
134
+ // Recursively generate sidebar text, the link path is the flattened file name
135
+ function walk(node, parentSegments = [], indent = "") {
136
+ let out = "";
137
+ for (const key of Object.keys(node)) {
138
+ const item = node[key];
139
+ const fullSegments = [...parentSegments, key];
140
+ const flatFile = fullSegments.join("-") + ".md";
141
+ if (item.__title) {
142
+ const realIndent = item.__parentId === null ? "" : indent;
143
+ out += `${realIndent}* [${item.__title}](/${flatFile})\n`;
144
+ }
145
+ const children = item.__children;
146
+ if (Object.keys(children).length > 0) {
147
+ out += walk(children, fullSegments, `${indent} `);
148
+ }
149
+ }
150
+ return out;
151
+ }
152
+ return walk(root).replace(/\n+$/, "");
153
+ }
@@ -0,0 +1,25 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ export default async function saveOutput({ savePath, fileName, saveKey, ...rest }) {
5
+ console.log("saveOutput", savePath, fileName, saveKey, rest);
6
+ if (!(saveKey in rest)) {
7
+ console.warn(`saveKey "${saveKey}" not found in input, skip saving.`);
8
+ return {
9
+ saveOutputStatus: false,
10
+ saveOutputPath: null,
11
+ };
12
+ }
13
+
14
+ const value = rest[saveKey];
15
+ const content =
16
+ typeof value === "object" && value !== null ? JSON.stringify(value, null, 2) : String(value);
17
+ await fs.mkdir(savePath, { recursive: true });
18
+ const filePath = join(savePath, fileName);
19
+ await fs.writeFile(filePath, content, "utf8");
20
+
21
+ return {
22
+ saveOutputStatus: true,
23
+ saveOutputPath: filePath,
24
+ };
25
+ }
@@ -0,0 +1,18 @@
1
+ import { saveDocWithTranslations } from "../utils/utils.mjs";
2
+
3
+ export default async function saveSingleDoc({
4
+ path,
5
+ content,
6
+ docsDir,
7
+ translates,
8
+ labels,
9
+ }) {
10
+ const results = await saveDocWithTranslations({
11
+ path,
12
+ content,
13
+ docsDir,
14
+ translates,
15
+ labels,
16
+ });
17
+ return { saveSingleDocResult: results };
18
+ }
@@ -0,0 +1,32 @@
1
+ type: array
2
+ items:
3
+ type: object
4
+ properties:
5
+ title:
6
+ type: string
7
+ description:
8
+ type: string
9
+ path:
10
+ type: string
11
+ description: 路径,以 RUL 的格式返回,不能为空, 必须以 / 开头,不需要包含语言层级,比如 /zh/about 直接返回 /about
12
+ parentId:
13
+ type:
14
+ - string
15
+ - "null"
16
+ translates:
17
+ type: array
18
+ items:
19
+ type: object
20
+ properties:
21
+ language:
22
+ type: string
23
+ description: 语言代码,比如 zh, en, ja 等
24
+ sourceIds:
25
+ type: array
26
+ items:
27
+ type: string
28
+ description: 关联的 dataSources 中的 sourceId,用于后续的翻译和内容生成,必须来自 datasources 中的 sourceId,不能有虚假的 id, 不能为空
29
+ required:
30
+ - title
31
+ - description
32
+ - path
@@ -0,0 +1,26 @@
1
+ type: array
2
+ items:
3
+ type: object
4
+ properties:
5
+ title:
6
+ type: string
7
+ description:
8
+ type: string
9
+ path:
10
+ type: string
11
+ description: 路径,以 RUL 的格式返回,不能为空, 必须以 / 开头,不需要包含语言层级,比如 /zh/about 直接返回 /about
12
+ parentId:
13
+ type:
14
+ - string
15
+ - "null"
16
+ description: 父节点 path,如果为 null 则表示是顶级节点
17
+ sourceIds:
18
+ type: array
19
+ description: 关联的 dataSources 中的 sourceId,用于后续的翻译和内容生成,必须来自 datasources 中的 sourceId,不能有虚假的 id, **不能为空**
20
+ items:
21
+ type: string
22
+ required:
23
+ - title
24
+ - description
25
+ - path
26
+ - sourceIds
@@ -0,0 +1,49 @@
1
+ name: structure-planning
2
+ description: 通用结构规划生成器,支持网站、文档、书籍、演示文稿等多种场景
3
+ instructions:
4
+ url: ../prompts/structure-planning.md
5
+ input_schema:
6
+ type: object
7
+ properties:
8
+ rules:
9
+ type: string
10
+ description: 用户的结构规划的要求
11
+ locale:
12
+ type: string
13
+ description: 用户语言,如 zh、en
14
+ datasources:
15
+ type: string
16
+ description: 结构规划的上下文,用于辅助结构规划
17
+ targetAudience:
18
+ type: string
19
+ description: 结构规划的目标受众
20
+ nodeName:
21
+ type: string
22
+ description: 结构规划的节点名称
23
+ glossary:
24
+ type: string
25
+ description: 术语表
26
+ structurePlanFeedback:
27
+ type: string
28
+ description: 结构规划的反馈
29
+ docsType:
30
+ type: string
31
+ description: 文档类型,支持:general、getting-started、reference、faq
32
+ default: general
33
+ required:
34
+ - rules
35
+ - datasources
36
+ output_schema:
37
+ type: object
38
+ properties:
39
+ structurePlan: ./schema/structure-plan.yaml
40
+ structurePlanTree:
41
+ type: string
42
+ description: |
43
+ 结构规划的树形结构,用于展示结构规划的层级关系,每一级有不同的缩进,方便用户直观查看结构规划的层级关系,参考格式:
44
+ ```
45
+ - 首页
46
+ - 产品
47
+ - 产品详情
48
+ - 产品介绍
49
+ ```
@@ -0,0 +1,14 @@
1
+ export default function transformDetailDatasources({
2
+ sourceIds,
3
+ datasourcesList,
4
+ }) {
5
+ // Build a map for fast lookup
6
+ const dsMap = Object.fromEntries(
7
+ (datasourcesList || []).map((ds) => [ds.sourceId, ds.content])
8
+ );
9
+ // Collect formatted contents in order
10
+ const contents = (sourceIds || [])
11
+ .filter((id) => dsMap[id])
12
+ .map((id) => `// sourceId: ${id}\n${dsMap[id]}\n`);
13
+ return { detailDataSources: contents.join("") };
14
+ }
@@ -0,0 +1,28 @@
1
+ name: translate
2
+ description: Translate the wiki article to another language
3
+ instructions:
4
+ url: ../prompts/translator.md
5
+ input_schema:
6
+ type: object
7
+ properties:
8
+ language:
9
+ type: string
10
+ description: Language to translate the article into (e.g., 'zh_CN' for Chinese)
11
+ content:
12
+ type: string
13
+ description: Content to translate
14
+ glossary:
15
+ type: string
16
+ description: 术语表
17
+ required:
18
+ - language
19
+ - content
20
+ output_schema:
21
+ type: object
22
+ properties:
23
+ translation:
24
+ type: string
25
+ description: Translation of the content
26
+ language:
27
+ type: string
28
+ description: Language of the translation
package/aigne.yaml ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env aigne
2
+
3
+ chat_model:
4
+ provider: gemini
5
+ name: gemini-2.5-pro-preview-05-06
6
+ # name: gemini-2.5-flash-preview-05-20
7
+ temperature: 0.8
8
+ agents:
9
+ - ./agents/docs-generator.yaml
10
+ - ./agents/structure-planning.yaml
11
+ - ./agents/batch-docs-detail-generator.yaml
12
+ - ./agents/load-sources.mjs
13
+ - ./agents/save-docs.mjs
14
+ - ./agents/translate.yaml
15
+ - ./agents/detail-generator-and-translate.yaml
16
+ - ./agents/check-detail-generated.mjs
17
+ - ./agents/transform-detail-datasources.mjs
18
+ - ./agents/batch-translate.yaml
19
+ - ./agents/save-single-doc.mjs
20
+ - ./agents/save-output.mjs
21
+ - ./agents/check-structure-planning.mjs
22
+ - ./agents/content-detail-generator.yaml
23
+ - ./agents/reflective-structure-planner.yaml
24
+ - ./agents/check-structure-planning-result.yaml
25
+ - ./agents/input-generator.mjs
26
+ - ./agents/detail-regenerator.yaml
27
+ - ./agents/publish-docs.mjs
28
+ - ./agents/format-structure-plan.mjs
package/biome.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.1.2/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": true
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": true,
10
+ "experimentalScannerIgnores": ["node_modules"]
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "space",
15
+ "lineWidth": 100
16
+ },
17
+ "linter": {
18
+ "enabled": true,
19
+ "rules": {
20
+ "recommended": true,
21
+ "correctness": {
22
+ "useImportExtensions": "error",
23
+ "noUnusedVariables": "error",
24
+ "noUnusedImports": "error",
25
+ "noUnusedFunctionParameters": "error",
26
+ "noUnusedPrivateClassMembers": "error"
27
+ },
28
+ "suspicious": {
29
+ "noConfusingVoidType": "off",
30
+ "noExplicitAny": "off"
31
+ },
32
+ "complexity": {
33
+ "noForEach": "off",
34
+ "useLiteralKeys": "off"
35
+ }
36
+ }
37
+ },
38
+ "javascript": {
39
+ "formatter": {
40
+ "quoteStyle": "double"
41
+ }
42
+ },
43
+ "assist": {
44
+ "enabled": true,
45
+ "actions": {
46
+ "source": {
47
+ "organizeImports": "on"
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,8 @@
1
+ chat_model:
2
+ provider: gemini
3
+ name: gemini-2.5-pro-preview-05-06
4
+ # name: gemini-2.5-flash-preview-05-20
5
+ temperature: 0.8
6
+ agents:
7
+ - get-docs-structure.mjs
8
+ - get-docs-detail.mjs