@aigne/doc-smith 0.2.9 → 0.2.10
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/CHANGELOG.md +7 -0
- package/agents/check-detail.mjs +0 -6
- package/agents/check-structure-plan.mjs +24 -3
- package/agents/load-config.mjs +10 -1
- package/agents/load-sources.mjs +2 -2
- package/agents/publish-docs.mjs +1 -86
- package/agents/team-publish-docs.yaml +6 -7
- package/package.json +1 -1
- package/utils/auth-utils.mjs +105 -0
- package/utils/blocklet.mjs +25 -0
- package/utils/constants.mjs +78 -11
- package/utils/file-utils.mjs +1 -1
- package/utils/markdown-checker.mjs +15 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.10](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.9...v0.2.10) (2025-08-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* fix bug bush feedback ([#41](https://github.com/AIGNE-io/aigne-doc-smith/issues/41)) ([2740d1a](https://github.com/AIGNE-io/aigne-doc-smith/commit/2740d1abef70ea36780b030917a6d54f74df4327))
|
|
9
|
+
|
|
3
10
|
## [0.2.9](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.8...v0.2.9) (2025-08-13)
|
|
4
11
|
|
|
5
12
|
|
package/agents/check-detail.mjs
CHANGED
|
@@ -16,7 +16,6 @@ export default async function checkDetail(
|
|
|
16
16
|
originalStructurePlan,
|
|
17
17
|
structurePlan,
|
|
18
18
|
modifiedFiles,
|
|
19
|
-
lastGitHead,
|
|
20
19
|
forceRegenerate,
|
|
21
20
|
...rest
|
|
22
21
|
},
|
|
@@ -80,11 +79,6 @@ export default async function checkDetail(
|
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
// If lastGitHead is not set, regenerate
|
|
84
|
-
if (!lastGitHead) {
|
|
85
|
-
sourceFilesChanged = true;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
82
|
// If file exists, check content validation
|
|
89
83
|
let contentValidationFailed = false;
|
|
90
84
|
if (detailGenerated && fileContent && structurePlan) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import {
|
|
2
4
|
getCurrentGitHead,
|
|
3
5
|
getProjectInfo,
|
|
@@ -7,7 +9,7 @@ import {
|
|
|
7
9
|
} from "../utils/utils.mjs";
|
|
8
10
|
|
|
9
11
|
export default async function checkStructurePlan(
|
|
10
|
-
{ originalStructurePlan, feedback, lastGitHead, ...rest },
|
|
12
|
+
{ originalStructurePlan, feedback, lastGitHead, docsDir, forceRegenerate, ...rest },
|
|
11
13
|
options,
|
|
12
14
|
) {
|
|
13
15
|
// Check if we need to regenerate structure plan
|
|
@@ -16,9 +18,18 @@ export default async function checkStructurePlan(
|
|
|
16
18
|
|
|
17
19
|
// If no feedback and originalStructurePlan exists, check for git changes
|
|
18
20
|
if (originalStructurePlan) {
|
|
19
|
-
// If no lastGitHead,
|
|
21
|
+
// If no lastGitHead, check if _sidebar.md exists to determine if we should regenerate
|
|
20
22
|
if (!lastGitHead) {
|
|
21
|
-
|
|
23
|
+
try {
|
|
24
|
+
// Check if _sidebar.md exists in docsDir
|
|
25
|
+
const sidebarPath = join(docsDir, "_sidebar.md");
|
|
26
|
+
await access(sidebarPath);
|
|
27
|
+
// If _sidebar.md exists, it means last execution was completed, need to regenerate
|
|
28
|
+
shouldRegenerate = true;
|
|
29
|
+
} catch {
|
|
30
|
+
// If _sidebar.md doesn't exist, it means last execution was interrupted, no need to regenerate
|
|
31
|
+
shouldRegenerate = false;
|
|
32
|
+
}
|
|
22
33
|
} else {
|
|
23
34
|
// Check if there are relevant file changes since last generation
|
|
24
35
|
const currentGitHead = getCurrentGitHead();
|
|
@@ -43,6 +54,16 @@ export default async function checkStructurePlan(
|
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
57
|
+
// user requested regeneration
|
|
58
|
+
if (forceRegenerate) {
|
|
59
|
+
shouldRegenerate = true;
|
|
60
|
+
finalFeedback = `
|
|
61
|
+
${finalFeedback || ""}
|
|
62
|
+
|
|
63
|
+
用户请求强制重新生成结构规划,请根据最新的 Data Sources 和用户要求重生生成,**允许任何修改**。
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
|
|
46
67
|
// If no regeneration needed, return original structure plan
|
|
47
68
|
if (originalStructurePlan && !feedback && !shouldRegenerate) {
|
|
48
69
|
return {
|
package/agents/load-config.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parse } from "yaml";
|
|
4
4
|
|
|
5
|
-
export default async function loadConfig({ config }) {
|
|
5
|
+
export default async function loadConfig({ config, appUrl }) {
|
|
6
6
|
const configPath = path.join(process.cwd(), config);
|
|
7
7
|
|
|
8
8
|
try {
|
|
@@ -18,6 +18,11 @@ export default async function loadConfig({ config }) {
|
|
|
18
18
|
// Read and parse YAML file
|
|
19
19
|
const configContent = await fs.readFile(configPath, "utf-8");
|
|
20
20
|
const parsedConfig = parse(configContent);
|
|
21
|
+
|
|
22
|
+
if (appUrl) {
|
|
23
|
+
parsedConfig.appUrl = appUrl;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
return {
|
|
22
27
|
nodeName: "Section",
|
|
23
28
|
locale: "en",
|
|
@@ -40,5 +45,9 @@ loadConfig.input_schema = {
|
|
|
40
45
|
type: "string",
|
|
41
46
|
default: "./.aigne/doc-smith/config.yaml",
|
|
42
47
|
},
|
|
48
|
+
appUrl: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Application URL to override config",
|
|
51
|
+
},
|
|
43
52
|
},
|
|
44
53
|
};
|
package/agents/load-sources.mjs
CHANGED
|
@@ -174,8 +174,8 @@ export default async function loadSources({
|
|
|
174
174
|
const words = source.match(/[a-zA-Z]+/g) || [];
|
|
175
175
|
totalWords += words.length;
|
|
176
176
|
|
|
177
|
-
// Count lines
|
|
178
|
-
totalLines += source.split("\n").length;
|
|
177
|
+
// Count lines (excluding empty lines)
|
|
178
|
+
totalLines += source.split("\n").filter((line) => line.trim() !== "").length;
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
package/agents/publish-docs.mjs
CHANGED
|
@@ -1,95 +1,10 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
1
|
import { basename, join } from "node:path";
|
|
5
|
-
import { createConnect } from "@aigne/aigne-hub";
|
|
6
2
|
import { publishDocs as publishDocsFn } from "@aigne/publish-docs";
|
|
7
|
-
import
|
|
8
|
-
import { joinURL } from "ufo";
|
|
9
|
-
import { parse, stringify } from "yaml";
|
|
3
|
+
import { getAccessToken } from "../utils/auth-utils.mjs";
|
|
10
4
|
import { loadConfigFromFile, saveValueToConfig } from "../utils/utils.mjs";
|
|
11
5
|
|
|
12
|
-
const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
13
6
|
const DEFAULT_APP_URL = "https://docsmith.aigne.io";
|
|
14
7
|
|
|
15
|
-
/**
|
|
16
|
-
* Get access token from environment, config file, or prompt user for authorization
|
|
17
|
-
* @param {string} appUrl - The application URL
|
|
18
|
-
* @returns {Promise<string>} - The access token
|
|
19
|
-
*/
|
|
20
|
-
async function getAccessToken(appUrl) {
|
|
21
|
-
const DOC_SMITH_ENV_FILE = join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
22
|
-
const { hostname } = new URL(appUrl);
|
|
23
|
-
|
|
24
|
-
let accessToken = process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
25
|
-
|
|
26
|
-
// Check if access token exists in environment or config file
|
|
27
|
-
if (!accessToken) {
|
|
28
|
-
try {
|
|
29
|
-
if (existsSync(DOC_SMITH_ENV_FILE)) {
|
|
30
|
-
const data = await readFile(DOC_SMITH_ENV_FILE, "utf8");
|
|
31
|
-
if (data.includes("DOC_DISCUSS_KIT_ACCESS_TOKEN")) {
|
|
32
|
-
const envs = parse(data);
|
|
33
|
-
if (envs[hostname]?.DOC_DISCUSS_KIT_ACCESS_TOKEN) {
|
|
34
|
-
accessToken = envs[hostname].DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.warn("Failed to read config file:", error.message);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// If still no access token, prompt user to authorize
|
|
44
|
-
if (!accessToken) {
|
|
45
|
-
const DISCUSS_KIT_URL = appUrl;
|
|
46
|
-
const connectUrl = joinURL(new URL(DISCUSS_KIT_URL).origin, WELLKNOWN_SERVICE_PATH_PREFIX);
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const result = await createConnect({
|
|
50
|
-
connectUrl: connectUrl,
|
|
51
|
-
connectAction: "gen-simple-access-key",
|
|
52
|
-
source: `AIGNE DocSmith connect to Discuss Kit`,
|
|
53
|
-
closeOnSuccess: true,
|
|
54
|
-
appName: "AIGNE DocSmith",
|
|
55
|
-
appLogo: "https://www.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
|
|
56
|
-
openPage: (pageUrl) => open(pageUrl),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
accessToken = result.accessKeySecret;
|
|
60
|
-
process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = accessToken;
|
|
61
|
-
|
|
62
|
-
// Save the access token to config file
|
|
63
|
-
const aigneDir = join(homedir(), ".aigne");
|
|
64
|
-
if (!existsSync(aigneDir)) {
|
|
65
|
-
mkdirSync(aigneDir, { recursive: true });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const existingConfig = existsSync(DOC_SMITH_ENV_FILE)
|
|
69
|
-
? parse(await readFile(DOC_SMITH_ENV_FILE, "utf8"))
|
|
70
|
-
: {};
|
|
71
|
-
|
|
72
|
-
await writeFile(
|
|
73
|
-
DOC_SMITH_ENV_FILE,
|
|
74
|
-
stringify({
|
|
75
|
-
...existingConfig,
|
|
76
|
-
[hostname]: {
|
|
77
|
-
DOC_DISCUSS_KIT_ACCESS_TOKEN: accessToken,
|
|
78
|
-
DOC_DISCUSS_KIT_URL: DISCUSS_KIT_URL,
|
|
79
|
-
},
|
|
80
|
-
}),
|
|
81
|
-
);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error("Failed to get access token:", error);
|
|
84
|
-
throw new Error(
|
|
85
|
-
"Failed to obtain access token. Please check your network connection and try again later.",
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return accessToken;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
8
|
export default async function publishDocs(
|
|
94
9
|
{ docsDir, appUrl, boardId, projectName, projectDesc, projectLogo },
|
|
95
10
|
options,
|
|
@@ -10,10 +10,9 @@ skills:
|
|
|
10
10
|
skipIfExists: true
|
|
11
11
|
- load-config.mjs
|
|
12
12
|
- publish-docs.mjs
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# default: ./.aigne/doc-smith/config.yaml
|
|
13
|
+
input_schema:
|
|
14
|
+
type: object
|
|
15
|
+
properties:
|
|
16
|
+
appUrl:
|
|
17
|
+
type: string
|
|
18
|
+
description: target website URL where the documentation will be published
|
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createConnect } from "@aigne/aigne-hub";
|
|
6
|
+
import open from "open";
|
|
7
|
+
import { joinURL } from "ufo";
|
|
8
|
+
import { parse, stringify } from "yaml";
|
|
9
|
+
import { getComponentMountPoint } from "./blocklet.mjs";
|
|
10
|
+
import { DISCUSS_KIT_DID } from "./constants.mjs";
|
|
11
|
+
|
|
12
|
+
const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get access token from environment, config file, or prompt user for authorization
|
|
16
|
+
* @param {string} appUrl - The application URL
|
|
17
|
+
* @returns {Promise<string>} - The access token
|
|
18
|
+
*/
|
|
19
|
+
export async function getAccessToken(appUrl) {
|
|
20
|
+
const DOC_SMITH_ENV_FILE = join(homedir(), ".aigne", "doc-smith-connected.yaml");
|
|
21
|
+
const { hostname } = new URL(appUrl);
|
|
22
|
+
|
|
23
|
+
let accessToken = process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
24
|
+
|
|
25
|
+
// Check if access token exists in environment or config file
|
|
26
|
+
if (!accessToken) {
|
|
27
|
+
try {
|
|
28
|
+
if (existsSync(DOC_SMITH_ENV_FILE)) {
|
|
29
|
+
const data = await readFile(DOC_SMITH_ENV_FILE, "utf8");
|
|
30
|
+
if (data.includes("DOC_DISCUSS_KIT_ACCESS_TOKEN")) {
|
|
31
|
+
const envs = parse(data);
|
|
32
|
+
if (envs[hostname]?.DOC_DISCUSS_KIT_ACCESS_TOKEN) {
|
|
33
|
+
accessToken = envs[hostname].DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.warn("Failed to read config file:", error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// If still no access token, prompt user to authorize
|
|
43
|
+
if (accessToken) {
|
|
44
|
+
return accessToken;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check if Discuss Kit is running at the provided URL
|
|
48
|
+
try {
|
|
49
|
+
await getComponentMountPoint(appUrl, DISCUSS_KIT_DID);
|
|
50
|
+
} catch {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Unable to find Discuss Kit running at the provided URL: ${appUrl}\n\n` +
|
|
53
|
+
"Please ensure that:\n" +
|
|
54
|
+
"• The URL is correct and accessible\n" +
|
|
55
|
+
"• Discuss Kit is properly installed and running\n" +
|
|
56
|
+
"If you continue to experience issues, please verify your Discuss Kit installation.",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const DISCUSS_KIT_URL = appUrl;
|
|
61
|
+
const connectUrl = joinURL(new URL(DISCUSS_KIT_URL).origin, WELLKNOWN_SERVICE_PATH_PREFIX);
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const result = await createConnect({
|
|
65
|
+
connectUrl: connectUrl,
|
|
66
|
+
connectAction: "gen-simple-access-key",
|
|
67
|
+
source: `AIGNE DocSmith connect to Discuss Kit`,
|
|
68
|
+
closeOnSuccess: true,
|
|
69
|
+
appName: "AIGNE DocSmith",
|
|
70
|
+
appLogo: "https://www.aigne.io/image-bin/uploads/a7910a71364ee15a27e86f869ad59009.svg",
|
|
71
|
+
openPage: (pageUrl) => open(pageUrl),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
accessToken = result.accessKeySecret;
|
|
75
|
+
process.env.DOC_DISCUSS_KIT_ACCESS_TOKEN = accessToken;
|
|
76
|
+
|
|
77
|
+
// Save the access token to config file
|
|
78
|
+
const aigneDir = join(homedir(), ".aigne");
|
|
79
|
+
if (!existsSync(aigneDir)) {
|
|
80
|
+
mkdirSync(aigneDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const existingConfig = existsSync(DOC_SMITH_ENV_FILE)
|
|
84
|
+
? parse(await readFile(DOC_SMITH_ENV_FILE, "utf8"))
|
|
85
|
+
: {};
|
|
86
|
+
|
|
87
|
+
await writeFile(
|
|
88
|
+
DOC_SMITH_ENV_FILE,
|
|
89
|
+
stringify({
|
|
90
|
+
...existingConfig,
|
|
91
|
+
[hostname]: {
|
|
92
|
+
DOC_DISCUSS_KIT_ACCESS_TOKEN: accessToken,
|
|
93
|
+
DOC_DISCUSS_KIT_URL: DISCUSS_KIT_URL,
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.debug(error);
|
|
99
|
+
throw new Error(
|
|
100
|
+
"Failed to obtain access token. Please check your network connection and try again later.",
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return accessToken;
|
|
105
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export async function getComponentMountPoint(appUrl, did) {
|
|
2
|
+
const url = new URL(appUrl);
|
|
3
|
+
const blockletJsUrl = `${url.origin}/__blocklet__.js?type=json`;
|
|
4
|
+
|
|
5
|
+
const blockletJs = await fetch(blockletJsUrl, {
|
|
6
|
+
method: "GET",
|
|
7
|
+
headers: {
|
|
8
|
+
Accept: "application/json",
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!blockletJs.ok) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Failed to fetch blocklet json: ${blockletJs.status} ${blockletJs.statusText}, ${blockletJsUrl}`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const config = await blockletJs.json();
|
|
19
|
+
const component = config.componentMountPoints.find((component) => component.did === did);
|
|
20
|
+
if (!component) {
|
|
21
|
+
throw new Error(`Component ${did} not found in blocklet: ${appUrl}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return component.mountPoint;
|
|
25
|
+
}
|
package/utils/constants.mjs
CHANGED
|
@@ -1,25 +1,89 @@
|
|
|
1
1
|
// Default file patterns for inclusion and exclusion
|
|
2
2
|
export const DEFAULT_INCLUDE_PATTERNS = [
|
|
3
|
+
// Python
|
|
3
4
|
"*.py",
|
|
5
|
+
"*.pyi",
|
|
6
|
+
"*.pyx",
|
|
7
|
+
// JavaScript/TypeScript
|
|
4
8
|
"*.js",
|
|
5
9
|
"*.jsx",
|
|
6
10
|
"*.ts",
|
|
7
11
|
"*.tsx",
|
|
8
|
-
|
|
9
|
-
"*.java",
|
|
10
|
-
"*.pyi",
|
|
11
|
-
"*.pyx",
|
|
12
|
+
// C/C++
|
|
12
13
|
"*.c",
|
|
13
14
|
"*.cc",
|
|
14
15
|
"*.cpp",
|
|
16
|
+
"*.cxx",
|
|
17
|
+
"*.c++",
|
|
15
18
|
"*.h",
|
|
19
|
+
"*.hpp",
|
|
20
|
+
"*.hxx",
|
|
21
|
+
"*.h++",
|
|
22
|
+
// JVM Languages
|
|
23
|
+
"*.java",
|
|
24
|
+
"*.kt",
|
|
25
|
+
"*.scala",
|
|
26
|
+
"*.groovy",
|
|
27
|
+
"*.gvy",
|
|
28
|
+
"*.gy",
|
|
29
|
+
"*.gsh",
|
|
30
|
+
"*.clj",
|
|
31
|
+
"*.cljs",
|
|
32
|
+
"*.cljx",
|
|
33
|
+
// .NET Languages
|
|
34
|
+
"*.cs",
|
|
35
|
+
"*.vb",
|
|
36
|
+
"*.fs",
|
|
37
|
+
// Functional Languages
|
|
38
|
+
"*.f",
|
|
39
|
+
"*.ml",
|
|
40
|
+
"*.sml",
|
|
41
|
+
"*.lisp",
|
|
42
|
+
"*.lsp",
|
|
43
|
+
"*.cl",
|
|
44
|
+
// Systems Programming
|
|
45
|
+
"*.rs",
|
|
46
|
+
"*.go",
|
|
47
|
+
"*.nim",
|
|
48
|
+
"*.asm",
|
|
49
|
+
"*.s",
|
|
50
|
+
// Web Technologies
|
|
51
|
+
"*.html",
|
|
52
|
+
"*.htm",
|
|
53
|
+
"*.css",
|
|
54
|
+
"*.php",
|
|
55
|
+
// Scripting Languages
|
|
56
|
+
"*.rb",
|
|
57
|
+
"*.pl",
|
|
58
|
+
"*.ps1",
|
|
59
|
+
"*.lua",
|
|
60
|
+
"*.tcl",
|
|
61
|
+
// Mobile/Modern Languages
|
|
62
|
+
"*.swift",
|
|
63
|
+
"*.dart",
|
|
64
|
+
"*.ex",
|
|
65
|
+
"*.exs",
|
|
66
|
+
"*.erl",
|
|
67
|
+
"*.jl",
|
|
68
|
+
// Data Science
|
|
69
|
+
"*.r",
|
|
70
|
+
"*.R",
|
|
71
|
+
"*.m",
|
|
72
|
+
// Other Languages
|
|
73
|
+
"*.pas",
|
|
74
|
+
"*.cob",
|
|
75
|
+
"*.cbl",
|
|
76
|
+
"*.pro",
|
|
77
|
+
"*.prolog",
|
|
78
|
+
"*.sql",
|
|
79
|
+
// Documentation & Config
|
|
16
80
|
"*.md",
|
|
17
81
|
"*.rst",
|
|
18
82
|
"*.json",
|
|
19
|
-
"*Dockerfile",
|
|
20
|
-
"*Makefile",
|
|
21
83
|
"*.yaml",
|
|
22
84
|
"*.yml",
|
|
85
|
+
"*Dockerfile",
|
|
86
|
+
"*Makefile",
|
|
23
87
|
];
|
|
24
88
|
|
|
25
89
|
export const DEFAULT_EXCLUDE_PATTERNS = [
|
|
@@ -78,14 +142,14 @@ export const SUPPORTED_LANGUAGES = [
|
|
|
78
142
|
|
|
79
143
|
// Predefined document generation styles
|
|
80
144
|
export const DOCUMENT_STYLES = {
|
|
81
|
-
developerDocs: {
|
|
82
|
-
name: "Developer Docs",
|
|
83
|
-
rules: "Steps-first; copy-paste examples; minimal context; active 'you'.",
|
|
84
|
-
},
|
|
85
145
|
userGuide: {
|
|
86
146
|
name: "User Guide",
|
|
87
147
|
rules: "Scenario-based; step-by-step; plain language; outcomes & cautions.",
|
|
88
148
|
},
|
|
149
|
+
developerDocs: {
|
|
150
|
+
name: "Developer Docs",
|
|
151
|
+
rules: "Steps-first; copy-paste examples; minimal context; active 'you'.",
|
|
152
|
+
},
|
|
89
153
|
apiReference: {
|
|
90
154
|
name: "API Reference",
|
|
91
155
|
rules: "Exact & skimmable; schema-first; clear params/errors/examples.",
|
|
@@ -98,8 +162,11 @@ export const DOCUMENT_STYLES = {
|
|
|
98
162
|
|
|
99
163
|
// Predefined target audiences
|
|
100
164
|
export const TARGET_AUDIENCES = {
|
|
165
|
+
generalUsers: "General Users",
|
|
101
166
|
actionFirst: "Developers, Implementation Engineers, DevOps",
|
|
102
167
|
conceptFirst: "Architects, Technical Leads, Developers interested in principles",
|
|
103
|
-
generalUsers: "General Users",
|
|
104
168
|
custom: "Enter your own target audience",
|
|
105
169
|
};
|
|
170
|
+
|
|
171
|
+
// Component mount point ID for Discuss Kit
|
|
172
|
+
export const DISCUSS_KIT_DID = "z8ia1WEiBZ7hxURf6LwH21Wpg99vophFwSJdu";
|
package/utils/file-utils.mjs
CHANGED
|
@@ -91,45 +91,34 @@ function checkDeadLinks(markdown, source, allowedLinks, errorMessages) {
|
|
|
91
91
|
/**
|
|
92
92
|
* Check code block content for indentation consistency issues
|
|
93
93
|
* @param {Array} codeBlockContent - Array of {line, lineNumber} objects from the code block
|
|
94
|
+
* @param {number} codeBlockIndent - The indentation of the code block start marker (```)
|
|
94
95
|
* @param {string} source - Source description for error reporting
|
|
95
96
|
* @param {Array} errorMessages - Array to push error messages to
|
|
96
97
|
*/
|
|
97
|
-
function checkCodeBlockIndentation(codeBlockContent, source, errorMessages) {
|
|
98
|
+
function checkCodeBlockIndentation(codeBlockContent, codeBlockIndent, source, errorMessages) {
|
|
98
99
|
if (codeBlockContent.length === 0) return;
|
|
99
100
|
|
|
100
|
-
// Filter out empty lines for
|
|
101
|
+
// Filter out empty lines for analysis
|
|
101
102
|
const nonEmptyLines = codeBlockContent.filter((item) => item.line.trim().length > 0);
|
|
102
103
|
if (nonEmptyLines.length === 0) return;
|
|
103
104
|
|
|
104
|
-
//
|
|
105
|
-
|
|
105
|
+
// The expected base indentation for code block content should match the code block marker
|
|
106
|
+
const expectedBaseIndent = codeBlockIndent;
|
|
106
107
|
const problematicLines = [];
|
|
107
108
|
|
|
108
109
|
for (const item of nonEmptyLines) {
|
|
109
110
|
const { line, lineNumber } = item;
|
|
110
111
|
const match = line.match(/^(\s*)/);
|
|
111
112
|
const currentIndent = match ? match[1].length : 0;
|
|
112
|
-
const trimmedLine = line.trim();
|
|
113
113
|
|
|
114
|
-
//
|
|
115
|
-
if (trimmedLine.startsWith("#") && !trimmedLine.includes("=") && !trimmedLine.includes("{")) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Establish base indentation from the first meaningful line
|
|
120
|
-
if (baseCodeIndent === null) {
|
|
121
|
-
baseCodeIndent = currentIndent;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Check if current line has less indentation than the base
|
|
114
|
+
// Check if current line has less indentation than expected
|
|
126
115
|
// This indicates inconsistent indentation that may cause rendering issues
|
|
127
|
-
if (currentIndent <
|
|
116
|
+
if (currentIndent < expectedBaseIndent && expectedBaseIndent > 0) {
|
|
128
117
|
problematicLines.push({
|
|
129
118
|
lineNumber,
|
|
130
119
|
line: line.trimEnd(),
|
|
131
120
|
currentIndent,
|
|
132
|
-
|
|
121
|
+
expectedIndent: expectedBaseIndent,
|
|
133
122
|
});
|
|
134
123
|
}
|
|
135
124
|
}
|
|
@@ -162,7 +151,7 @@ function checkCodeBlockIndentation(codeBlockContent, source, errorMessages) {
|
|
|
162
151
|
? `lines ${group[0].lineNumber}-${group[group.length - 1].lineNumber}`
|
|
163
152
|
: `line ${firstIssue.lineNumber}`;
|
|
164
153
|
|
|
165
|
-
const issue = `
|
|
154
|
+
const issue = `insufficient indentation: ${firstIssue.currentIndent} spaces (expected: ${firstIssue.expectedIndent} spaces)`;
|
|
166
155
|
errorMessages.push(
|
|
167
156
|
`Found code block with inconsistent indentation in ${source} at ${lineNumbers}: ${issue}. This may cause rendering issues`,
|
|
168
157
|
);
|
|
@@ -185,6 +174,7 @@ function checkContentStructure(markdown, source, errorMessages) {
|
|
|
185
174
|
let inAnyCodeBlock = false;
|
|
186
175
|
let anyCodeBlockStartLine = 0;
|
|
187
176
|
let codeBlockContent = [];
|
|
177
|
+
let codeBlockIndent = 0;
|
|
188
178
|
|
|
189
179
|
for (let i = 0; i < lines.length; i++) {
|
|
190
180
|
const line = lines[i];
|
|
@@ -198,13 +188,17 @@ function checkContentStructure(markdown, source, errorMessages) {
|
|
|
198
188
|
anyCodeBlockStartLine = lineNumber;
|
|
199
189
|
inCodeBlock = true;
|
|
200
190
|
codeBlockContent = [];
|
|
191
|
+
|
|
192
|
+
// Capture the indentation of the code block start marker
|
|
193
|
+
const match = line.match(/^(\s*)/);
|
|
194
|
+
codeBlockIndent = match ? match[1].length : 0;
|
|
201
195
|
} else {
|
|
202
196
|
// Ending the code block
|
|
203
197
|
inAnyCodeBlock = false;
|
|
204
198
|
|
|
205
199
|
if (inCodeBlock) {
|
|
206
200
|
// Check code block content for indentation issues
|
|
207
|
-
checkCodeBlockIndentation(codeBlockContent, source, errorMessages);
|
|
201
|
+
checkCodeBlockIndentation(codeBlockContent, codeBlockIndent, source, errorMessages);
|
|
208
202
|
inCodeBlock = false;
|
|
209
203
|
}
|
|
210
204
|
}
|