asciidoclint 1.1.0 → 1.2.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/README.md +51 -7
- package/dist/cli/index.js +7 -7
- package/dist/cli/install-skill.d.ts +5 -0
- package/dist/cli/install-skill.js +131 -42
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +250 -77
- package/package.json +1 -1
- package/skills/asciidoclint/SKILL.md +8 -5
- package/skills/asciidoclint/references/feedback.md +1 -1
- package/skills/asciidoclint/references/lint-summary.md +4 -4
- package/skills/asciidoclint/references/rule-create.md +6 -6
package/README.md
CHANGED
|
@@ -9,12 +9,39 @@ CLI, AI-agent, and editor workflows.
|
|
|
9
9
|
|
|
10
10
|
## Install the npm package
|
|
11
11
|
|
|
12
|
+
`asciidoclint` requires Node.js 20 or newer. The package does not install or
|
|
13
|
+
manage Node.js; use the Node.js version chosen by your shell, package manager, or
|
|
14
|
+
higher-level tooling.
|
|
15
|
+
|
|
16
|
+
Install globally when you want `asciidoclint` available as a shell command:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g asciidoclint
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If the shell cannot find `asciidoclint` after install, npm's global bin
|
|
23
|
+
directory may not be on `PATH`. It is usually:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$(npm prefix -g)/bin
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For documentation repositories that need a pinned project-local install without
|
|
30
|
+
creating a project-root `package.json`, install under `.asciidoclint`:
|
|
31
|
+
|
|
12
32
|
```bash
|
|
13
|
-
npm
|
|
33
|
+
npm --prefix .asciidoclint install asciidoclint@<version>
|
|
14
34
|
```
|
|
15
35
|
|
|
16
36
|
## Use the CLI
|
|
17
37
|
|
|
38
|
+
The examples below use `npx asciidoclint` because that works even when npm's
|
|
39
|
+
global bin directory is not on `PATH`. If `asciidoclint` is recognized by your
|
|
40
|
+
shell, you can use `asciidoclint` instead.
|
|
41
|
+
|
|
42
|
+
If the package is installed with the dedicated project-local layout, use
|
|
43
|
+
`.asciidoclint/node_modules/.bin/asciidoclint`.
|
|
44
|
+
|
|
18
45
|
Run lint:
|
|
19
46
|
|
|
20
47
|
```bash
|
|
@@ -60,26 +87,43 @@ reporting.
|
|
|
60
87
|
|
|
61
88
|
## Install the AI skill
|
|
62
89
|
|
|
63
|
-
The
|
|
64
|
-
npm package
|
|
90
|
+
The npm package includes an `asciidoclint` skill for AI agents. Installing the
|
|
91
|
+
npm package does not automatically install the skill into agent roots; run
|
|
92
|
+
`install-skill` explicitly.
|
|
93
|
+
|
|
94
|
+
For a user-global skill install:
|
|
65
95
|
|
|
66
96
|
```bash
|
|
67
97
|
npx asciidoclint install-skill
|
|
68
98
|
```
|
|
69
99
|
|
|
70
|
-
|
|
100
|
+
This copies the bundled skill to `~/.agents/skills/asciidoclint` and creates
|
|
101
|
+
`~/.claude/skills/asciidoclint` as a symbolic link to that copy.
|
|
102
|
+
|
|
103
|
+
For a pinned project-local skill install:
|
|
71
104
|
|
|
72
105
|
```bash
|
|
73
|
-
|
|
106
|
+
npm --prefix .asciidoclint install asciidoclint@<version>
|
|
107
|
+
.asciidoclint/node_modules/.bin/asciidoclint install-skill --project
|
|
74
108
|
```
|
|
75
109
|
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
This creates symbolic links at `.agents/skills/asciidoclint` and
|
|
111
|
+
`.claude/skills/asciidoclint`, pointing to the skill source bundled with the
|
|
112
|
+
package or checkout that ran `install-skill`. Add `--force` only when replacing
|
|
113
|
+
an existing project-local skill install.
|
|
114
|
+
|
|
115
|
+
Remove installed skills explicitly:
|
|
78
116
|
|
|
79
117
|
```bash
|
|
80
118
|
npx asciidoclint uninstall-skill
|
|
119
|
+
npx asciidoclint uninstall-skill --project
|
|
81
120
|
```
|
|
82
121
|
|
|
122
|
+
Normal npm uninstall removes package files only. It does not remove
|
|
123
|
+
`~/.agents/skills/asciidoclint`, `~/.claude/skills/asciidoclint`,
|
|
124
|
+
`.agents/skills/asciidoclint`, or `.claude/skills/asciidoclint`; run
|
|
125
|
+
`uninstall-skill` for those.
|
|
126
|
+
|
|
83
127
|
The public skill exposes these user-facing workflows:
|
|
84
128
|
|
|
85
129
|
| Workflow | Purpose |
|
package/dist/cli/index.js
CHANGED
|
@@ -38,13 +38,13 @@ program
|
|
|
38
38
|
.command("install-skill")
|
|
39
39
|
.description("install the bundled AI-agent skill")
|
|
40
40
|
.option("--dest <directory>", "skills root directory")
|
|
41
|
-
.option("--agent <agent>", "
|
|
42
|
-
.option("--project", "install into
|
|
41
|
+
.option("--agent <agent>", "deprecated; fails with guidance instead of selecting a skill root")
|
|
42
|
+
.option("--project", "install into project .agents and .claude skill roots")
|
|
43
43
|
.option("--force", "replace an existing installed skill")
|
|
44
44
|
.action((options) => {
|
|
45
45
|
try {
|
|
46
46
|
const result = installSkill(options);
|
|
47
|
-
console.log(`Installed asciidoclint skill to ${result.
|
|
47
|
+
console.log(`Installed asciidoclint skill to ${result.destinations.join(", ")}`);
|
|
48
48
|
}
|
|
49
49
|
catch (error) {
|
|
50
50
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -55,14 +55,14 @@ program
|
|
|
55
55
|
.command("uninstall-skill")
|
|
56
56
|
.description("uninstall the AI-agent skill")
|
|
57
57
|
.option("--dest <directory>", "skills root directory")
|
|
58
|
-
.option("--agent <agent>", "
|
|
59
|
-
.option("--project", "uninstall from
|
|
58
|
+
.option("--agent <agent>", "deprecated; fails with guidance instead of selecting a skill root")
|
|
59
|
+
.option("--project", "uninstall from project .agents and .claude skill roots")
|
|
60
60
|
.action((options) => {
|
|
61
61
|
try {
|
|
62
62
|
const result = uninstallSkill(options);
|
|
63
63
|
console.log(result.removed
|
|
64
|
-
? `Uninstalled asciidoclint skill from ${result.
|
|
65
|
-
: `No asciidoclint skill installed at ${result.
|
|
64
|
+
? `Uninstalled asciidoclint skill from ${result.removedDestinations.join(", ")}`
|
|
65
|
+
: `No asciidoclint skill installed at ${result.destinations.join(", ")}`);
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
68
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -3,19 +3,24 @@ export interface InstallSkillOptions {
|
|
|
3
3
|
project?: boolean;
|
|
4
4
|
agent?: SupportedSkillAgent;
|
|
5
5
|
force?: boolean;
|
|
6
|
+
homeDir?: string;
|
|
6
7
|
}
|
|
7
8
|
export interface InstallSkillResult {
|
|
8
9
|
source: string;
|
|
9
10
|
destination: string;
|
|
11
|
+
destinations: string[];
|
|
10
12
|
}
|
|
11
13
|
export interface UninstallSkillOptions {
|
|
12
14
|
dest?: string;
|
|
13
15
|
project?: boolean;
|
|
14
16
|
agent?: SupportedSkillAgent;
|
|
17
|
+
homeDir?: string;
|
|
15
18
|
}
|
|
16
19
|
export interface UninstallSkillResult {
|
|
17
20
|
destination: string;
|
|
21
|
+
destinations: string[];
|
|
18
22
|
removed: boolean;
|
|
23
|
+
removedDestinations: string[];
|
|
19
24
|
}
|
|
20
25
|
export type SupportedSkillAgent = "codex" | "cursor" | "claude-code" | "openclaw";
|
|
21
26
|
export declare function installSkill(options?: InstallSkillOptions): InstallSkillResult;
|
|
@@ -3,60 +3,149 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
const skillName = "asciidoclint";
|
|
6
|
-
const
|
|
7
|
-
const skillAgentRoots = {
|
|
8
|
-
codex: {
|
|
9
|
-
project: path.join(".agents", "skills"),
|
|
10
|
-
global: path.join(os.homedir(), ".codex", "skills"),
|
|
11
|
-
},
|
|
12
|
-
cursor: {
|
|
13
|
-
project: path.join(".agents", "skills"),
|
|
14
|
-
global: path.join(os.homedir(), ".cursor", "skills"),
|
|
15
|
-
},
|
|
16
|
-
"claude-code": {
|
|
17
|
-
project: path.join(".claude", "skills"),
|
|
18
|
-
global: path.join(os.homedir(), ".claude", "skills"),
|
|
19
|
-
},
|
|
20
|
-
openclaw: {
|
|
21
|
-
project: "skills",
|
|
22
|
-
global: path.join(os.homedir(), ".openclaw", "skills"),
|
|
23
|
-
},
|
|
24
|
-
};
|
|
6
|
+
const deprecatedAgentMessage = "--agent is deprecated and no longer selects a skill root. Use the command without --agent for the default global roots, add --project for project roots, or use --dest <directory> for a custom root.";
|
|
25
7
|
export function installSkill(options = {}) {
|
|
8
|
+
rejectDeprecatedAgentOption(options.agent);
|
|
26
9
|
const source = findBundledSkill();
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
if (options.dest) {
|
|
11
|
+
const destination = path.join(path.resolve(options.dest), skillName);
|
|
12
|
+
installCopy(source, destination, options.force);
|
|
13
|
+
return { source, destination, destinations: [destination] };
|
|
14
|
+
}
|
|
15
|
+
if (options.project) {
|
|
16
|
+
const destinations = projectSkillDestinations(process.cwd());
|
|
17
|
+
prepareDestinations(destinations, options.force);
|
|
18
|
+
installPreparedDestinations(destinations, (destination) => writeSymlink(source, destination));
|
|
19
|
+
return { source, destination: destinations[0], destinations };
|
|
20
|
+
}
|
|
21
|
+
const { common, claude } = globalSkillDestinations(options.homeDir);
|
|
22
|
+
prepareDestinations([common, claude], options.force);
|
|
23
|
+
installPreparedDestinations([common, claude], (destination) => {
|
|
24
|
+
if (destination === common) {
|
|
25
|
+
writeCopy(source, common);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
writeSymlink(common, claude);
|
|
29
|
+
});
|
|
30
|
+
return { source, destination: common, destinations: [common, claude] };
|
|
31
|
+
}
|
|
32
|
+
export function uninstallSkill(options = {}) {
|
|
33
|
+
rejectDeprecatedAgentOption(options.agent);
|
|
34
|
+
const destinations = uninstallDestinations(options);
|
|
35
|
+
const removedDestinations = [];
|
|
36
|
+
for (const destination of destinations) {
|
|
37
|
+
if (pathExists(destination)) {
|
|
38
|
+
removePath(destination);
|
|
39
|
+
removedDestinations.push(destination);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
// For global uninstall, destinations are ordered for removal safety:
|
|
44
|
+
// Claude symlink first, then the common copy.
|
|
45
|
+
destination: destinations[0],
|
|
46
|
+
destinations,
|
|
47
|
+
removed: removedDestinations.length > 0,
|
|
48
|
+
removedDestinations,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function rejectDeprecatedAgentOption(agent) {
|
|
52
|
+
if (agent) {
|
|
53
|
+
throw new Error(deprecatedAgentMessage);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function uninstallDestinations(options) {
|
|
57
|
+
if (options.dest) {
|
|
58
|
+
return [path.join(path.resolve(options.dest), skillName)];
|
|
59
|
+
}
|
|
60
|
+
if (options.project) {
|
|
61
|
+
return projectSkillDestinations(process.cwd());
|
|
62
|
+
}
|
|
63
|
+
const { common, claude } = globalSkillDestinations(options.homeDir);
|
|
64
|
+
return [claude, common];
|
|
65
|
+
}
|
|
66
|
+
function projectSkillDestinations(root) {
|
|
67
|
+
return [
|
|
68
|
+
path.resolve(root, ".agents", "skills", skillName),
|
|
69
|
+
path.resolve(root, ".claude", "skills", skillName),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
function globalSkillDestinations(homeDir = os.homedir()) {
|
|
73
|
+
return {
|
|
74
|
+
common: path.join(homeDir, ".agents", "skills", skillName),
|
|
75
|
+
claude: path.join(homeDir, ".claude", "skills", skillName),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function installCopy(source, destination, force = false) {
|
|
29
79
|
if (path.resolve(source) === path.resolve(destination)) {
|
|
30
|
-
return
|
|
80
|
+
return;
|
|
31
81
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
82
|
+
prepareDestination(destination, force);
|
|
83
|
+
writeCopy(source, destination);
|
|
84
|
+
}
|
|
85
|
+
function prepareDestinations(destinations, force = false) {
|
|
86
|
+
for (const destination of destinations) {
|
|
87
|
+
prepareDestination(destination, force);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function installPreparedDestinations(destinations, write) {
|
|
91
|
+
const written = [];
|
|
92
|
+
try {
|
|
93
|
+
for (const destination of destinations) {
|
|
94
|
+
write(destination);
|
|
95
|
+
written.push(destination);
|
|
35
96
|
}
|
|
36
|
-
fs.rmSync(destination, { recursive: true, force: true });
|
|
37
97
|
}
|
|
38
|
-
|
|
98
|
+
catch (error) {
|
|
99
|
+
rollbackWrittenDestinations(written);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function rollbackWrittenDestinations(destinations) {
|
|
104
|
+
for (const destination of destinations.slice().reverse()) {
|
|
105
|
+
if (pathExists(destination)) {
|
|
106
|
+
removePath(destination);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function writeCopy(source, destination) {
|
|
111
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
39
112
|
fs.cpSync(source, destination, { recursive: true });
|
|
40
|
-
return { source, destination };
|
|
41
113
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (removed) {
|
|
46
|
-
fs.rmSync(destination, { recursive: true, force: true });
|
|
114
|
+
function writeSymlink(source, destination) {
|
|
115
|
+
if (path.resolve(source) === path.resolve(destination)) {
|
|
116
|
+
return;
|
|
47
117
|
}
|
|
48
|
-
|
|
118
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
119
|
+
fs.symlinkSync(path.relative(path.dirname(destination), source), destination, "dir");
|
|
49
120
|
}
|
|
50
|
-
function
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
121
|
+
function prepareDestination(destination, force) {
|
|
122
|
+
if (!pathExists(destination)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!force) {
|
|
126
|
+
throw new Error(`${destination} already exists; pass --force to replace it`);
|
|
127
|
+
}
|
|
128
|
+
removePath(destination);
|
|
129
|
+
}
|
|
130
|
+
function removePath(file) {
|
|
131
|
+
const stat = fs.lstatSync(file);
|
|
132
|
+
if (stat.isSymbolicLink()) {
|
|
133
|
+
fs.unlinkSync(file);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
fs.rmSync(file, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
function pathExists(file) {
|
|
139
|
+
try {
|
|
140
|
+
fs.lstatSync(file);
|
|
141
|
+
return true;
|
|
53
142
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (error.code === "ENOENT") {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
58
148
|
}
|
|
59
|
-
return options.project ? path.resolve(process.cwd(), roots.project) : roots.global;
|
|
60
149
|
}
|
|
61
150
|
function findBundledSkill() {
|
|
62
151
|
let current = path.dirname(fileURLToPath(import.meta.url));
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.2.0";
|
|
2
2
|
export declare function getVersion(): string;
|
package/dist/version.js
CHANGED
package/docs/architecture.md
CHANGED
|
@@ -612,6 +612,149 @@ workflows all consume the same metadata and examples.
|
|
|
612
612
|
Details live in [Rule architecture](rule-architecture.md) and the generated
|
|
613
613
|
[built-in rule docs](rules/).
|
|
614
614
|
|
|
615
|
+
## CLI Installation Scopes
|
|
616
|
+
|
|
617
|
+
`asciidoclint` is a Node.js npm package. The package should declare its minimum
|
|
618
|
+
supported Node.js version, but it should not install or manage Node.js itself.
|
|
619
|
+
Users, system administrators, package managers, or external runtime managers
|
|
620
|
+
decide how Node.js is provided. A manager may reuse an existing `node`/`npm`
|
|
621
|
+
pair when it satisfies the package's minimum version requirement, or install
|
|
622
|
+
and use a separately managed Node.js runtime. That runtime choice remains
|
|
623
|
+
outside the `asciidoclint` package.
|
|
624
|
+
|
|
625
|
+
The preferred user experience is a user-global `asciidoclint` executable on
|
|
626
|
+
`PATH`. Project-local installation is an explicit pinning model for special
|
|
627
|
+
scenarios such as reproducible managed document workspaces, projects that need
|
|
628
|
+
a version different from the user's global tool, or environments that cannot
|
|
629
|
+
depend on user-global state.
|
|
630
|
+
|
|
631
|
+
### User-Global Installation
|
|
632
|
+
|
|
633
|
+
User-global installation makes `asciidoclint` available on `PATH`, usually
|
|
634
|
+
through npm global bin handling, a package manager, an admin-managed wrapper, or
|
|
635
|
+
another runtime manager. `asciidoclint` should not advocate a specific user bin
|
|
636
|
+
directory such as `~/.local/bin`; it only needs to work when the executable is
|
|
637
|
+
available on `PATH`. For npm global installs, the executable is normally linked
|
|
638
|
+
under `$(npm prefix -g)/bin`; if that directory is not on `PATH`, the package can
|
|
639
|
+
be installed successfully while `asciidoclint` is still not recognized by the
|
|
640
|
+
shell.
|
|
641
|
+
|
|
642
|
+
```text
|
|
643
|
+
~/
|
|
644
|
+
.asciidoclint/
|
|
645
|
+
config.yaml # user-global asciidoclint config
|
|
646
|
+
.agents/
|
|
647
|
+
skills/
|
|
648
|
+
asciidoclint/ # common global skill install
|
|
649
|
+
.claude/
|
|
650
|
+
skills/
|
|
651
|
+
asciidoclint -> ../../.agents/skills/asciidoclint
|
|
652
|
+
# Claude-native mirror
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
The stable user-global config location is `~/.asciidoclint/config.yaml`.
|
|
656
|
+
The common global skill source is `~/.agents/skills/asciidoclint`. Global
|
|
657
|
+
`install-skill` copies the bundled skill there first, then creates
|
|
658
|
+
`~/.claude/skills/asciidoclint` as a symbolic link to that common copy. This
|
|
659
|
+
keeps one durable global skill copy while covering agents that read the common
|
|
660
|
+
`.agents/skills` root and Claude Code's native `.claude/skills` root.
|
|
661
|
+
|
|
662
|
+
Global skill directories are explicit installation targets. Normal `npm
|
|
663
|
+
install` should not copy the skill into agent roots. The `install-skill`
|
|
664
|
+
command, or an external skills installer, performs that step when requested.
|
|
665
|
+
|
|
666
|
+
### Project-Local Installation
|
|
667
|
+
|
|
668
|
+
Project-local installation may pin a package version under the project's
|
|
669
|
+
`.asciidoclint` directory. This is the only recommended per-project npm install
|
|
670
|
+
location for agentic workflows because it avoids creating or modifying a
|
|
671
|
+
project-root `package.json`, `package-lock.json`, or `node_modules` directory in
|
|
672
|
+
non-Node documentation repositories:
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
npm --prefix .asciidoclint install asciidoclint@<version>
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
```text
|
|
679
|
+
project/
|
|
680
|
+
.asciidoclint/
|
|
681
|
+
config.yaml # project asciidoclint config
|
|
682
|
+
node_modules/
|
|
683
|
+
.bin/
|
|
684
|
+
asciidoclint # npm-installed project-local executable
|
|
685
|
+
asciidoclint/ # pinned package contents
|
|
686
|
+
.agents/
|
|
687
|
+
skills/
|
|
688
|
+
asciidoclint -> <invoking-package>/skills/asciidoclint
|
|
689
|
+
# common project-local skill link
|
|
690
|
+
.claude/
|
|
691
|
+
skills/
|
|
692
|
+
asciidoclint -> <invoking-package>/skills/asciidoclint
|
|
693
|
+
# Claude-native project-local skill link
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
The project config location is `.asciidoclint/config.yaml`. The npm-created
|
|
697
|
+
project executable is `.asciidoclint/node_modules/.bin/asciidoclint`. In the
|
|
698
|
+
recommended released project-local channel, the invoking package is
|
|
699
|
+
`.asciidoclint/node_modules/asciidoclint` and owns the bundled skill source at
|
|
700
|
+
`.asciidoclint/node_modules/asciidoclint/skills/asciidoclint`.
|
|
701
|
+
|
|
702
|
+
The Skill can find project-local installs by searching for
|
|
703
|
+
`.asciidoclint/node_modules/.bin/asciidoclint`; a separate
|
|
704
|
+
`.asciidoclint/bin/asciidoclint` wrapper is not required. A manager may still
|
|
705
|
+
create that wrapper when it wants a stable project-owned executable path that
|
|
706
|
+
encodes a specific Node.js executable, package install path, `PATH` setup,
|
|
707
|
+
package mirror, cache policy, bootstrap behavior, or upgrade policy. A wrapper
|
|
708
|
+
may look like:
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
#!/usr/bin/env bash
|
|
712
|
+
exec /path/to/node /path/to/asciidoclint/dist/cli/index.js "$@"
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
The wrapper owns that runtime choice; the `asciidoclint` package itself does
|
|
716
|
+
not install or select Node.js.
|
|
717
|
+
|
|
718
|
+
`customRules` entries in config may point to any local path or shared package
|
|
719
|
+
chosen by the user. The `.asciidoclint` directory is not required to contain
|
|
720
|
+
custom rules.
|
|
721
|
+
|
|
722
|
+
Project-local `install-skill` always creates symbolic links in both
|
|
723
|
+
`.agents/skills/asciidoclint` and `.claude/skills/asciidoclint`, pointing to the
|
|
724
|
+
bundled skill source from the package or checkout that invoked `install-skill`.
|
|
725
|
+
For the recommended released project-local channel, that source is the
|
|
726
|
+
project-local npm package. This keeps the package version, CLI, library, and
|
|
727
|
+
Skill coupled to the project pin, while covering both the common `.agents/skills`
|
|
728
|
+
root and Claude Code's native `.claude/skills` root. If the project-local npm
|
|
729
|
+
package is removed, the broken links correctly reflect that the pinned project
|
|
730
|
+
install was removed.
|
|
731
|
+
|
|
732
|
+
This model is intended to be backward-compatible with user-visible CLI,
|
|
733
|
+
configuration, editor, and diagnostics behavior. Existing `npx asciidoclint`
|
|
734
|
+
commands, standard npm installs such as `npm install --save-dev asciidoclint`,
|
|
735
|
+
normal npm global installs, `PATH` installs, `~/.asciidoclint/config.yaml`,
|
|
736
|
+
`.asciidoclint/config.yaml`, existing `customRules` paths, and
|
|
737
|
+
`.asciidoclint/diagnostics.json` should continue to work. The agentic Skill does
|
|
738
|
+
not need to search standard workspace `node_modules/.bin/asciidoclint`; users
|
|
739
|
+
who want an explicit project-pinned Skill runtime should use the dedicated
|
|
740
|
+
`.asciidoclint/node_modules` install. Project-local skill links and
|
|
741
|
+
manager-created wrappers are conventions around installation state, not changes
|
|
742
|
+
to the lint API, editor settings, or diagnostics artifact contract.
|
|
743
|
+
|
|
744
|
+
### CLI Resolution Order
|
|
745
|
+
|
|
746
|
+
Agent-facing workflows should resolve a concrete `<asciidoclint>` command once,
|
|
747
|
+
then use that placeholder in all commands. Use this order:
|
|
748
|
+
|
|
749
|
+
1. Nearest project ancestor's
|
|
750
|
+
`.asciidoclint/node_modules/.bin/asciidoclint`.
|
|
751
|
+
2. `asciidoclint` on `PATH`.
|
|
752
|
+
3. `npx asciidoclint` as a fallback when no installed executable is available.
|
|
753
|
+
|
|
754
|
+
Managers may add their own wrapper paths, such as
|
|
755
|
+
`.asciidoclint/bin/asciidoclint`, before this list in managed environments, but
|
|
756
|
+
the public Skill should not require those wrappers to exist.
|
|
757
|
+
|
|
615
758
|
## AI Skill Distribution
|
|
616
759
|
|
|
617
760
|
`asciidoclint` should ship an installable AI-agent skill in the repository:
|
|
@@ -638,8 +781,8 @@ are:
|
|
|
638
781
|
findings", "apply safe fixes", "apply unsafe fixes", "use AI to repair the
|
|
639
782
|
remaining issues", "add a waiver", "create a custom rule", "review this
|
|
640
783
|
rule", and "prepare a GitHub issue".
|
|
641
|
-
- Resolve the tool
|
|
642
|
-
|
|
784
|
+
- Resolve the tool using the CLI resolution order above, then refer to the
|
|
785
|
+
resolved executable as `<asciidoclint>` in agent-facing instructions.
|
|
643
786
|
- Run `--format json` for machine-readable results.
|
|
644
787
|
- Run `--fix` for deterministic safe fixes.
|
|
645
788
|
- Run `--fix --unsafe` only when the user explicitly requests unsafe fixes.
|
|
@@ -654,53 +797,84 @@ are:
|
|
|
654
797
|
- Rerun lint after edits, waivers, and rule changes, then summarize fixed and
|
|
655
798
|
remaining findings.
|
|
656
799
|
|
|
657
|
-
The
|
|
800
|
+
The open skills ecosystem may be able to install the public repository skill
|
|
801
|
+
directly:
|
|
658
802
|
|
|
659
803
|
```bash
|
|
660
804
|
npx skills add f33lgood/asciidoclint --skill asciidoclint -a codex -g
|
|
661
805
|
```
|
|
662
806
|
|
|
807
|
+
That path is an external installer alternative, not the recommended
|
|
808
|
+
`asciidoclint` package workflow. It may use the target paths and copy/symlink
|
|
809
|
+
policy of the `skills` CLI, and it installs only the Skill source. It cannot
|
|
810
|
+
guarantee that the `asciidoclint` npm package or executable is installed, so the
|
|
811
|
+
package-owned `asciidoclint install-skill` behavior remains the supported path
|
|
812
|
+
described below.
|
|
813
|
+
|
|
663
814
|
The repository should not check in agent-specific install copies under
|
|
664
815
|
`.agents/skills`, `.claude/skills`, or other target-agent directories. The
|
|
665
816
|
single public source of truth is `skills/asciidoclint`, and developers should
|
|
666
817
|
install it into their target agent when they need repo-local skill assistance.
|
|
667
|
-
With that organization, the shorter form also installs the public skill:
|
|
668
|
-
|
|
669
|
-
```bash
|
|
670
|
-
npx skills add f33lgood/asciidoclint
|
|
671
|
-
```
|
|
672
818
|
|
|
673
819
|
The npm package should also expose a convenience installer:
|
|
674
820
|
|
|
675
821
|
```bash
|
|
676
|
-
|
|
677
|
-
|
|
822
|
+
asciidoclint install-skill
|
|
823
|
+
asciidoclint uninstall-skill
|
|
678
824
|
```
|
|
679
825
|
|
|
680
|
-
This command
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
`
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
the
|
|
826
|
+
This command installs the bundled `skills/asciidoclint` skill into both the
|
|
827
|
+
common `.agents/skills` root and Claude Code's `.claude/skills` root. It does
|
|
828
|
+
not need agent selection choices for the default path; installing both roots is
|
|
829
|
+
small, predictable, and covers the majority of current skill-aware agents.
|
|
830
|
+
The existing `--dest` option remains useful for tests and explicit custom skill
|
|
831
|
+
roots. Existing `--agent` usage no longer maps cleanly to the deterministic
|
|
832
|
+
two-root policy, so explicit `--agent` use should fail with a clear deprecation
|
|
833
|
+
message instead of silently selecting a legacy root or being ignored.
|
|
834
|
+
|
|
835
|
+
Use this policy:
|
|
836
|
+
|
|
837
|
+
- Project-local install: create symbolic links at
|
|
838
|
+
`.agents/skills/asciidoclint` and `.claude/skills/asciidoclint`, both pointing
|
|
839
|
+
to the bundled `skills/asciidoclint` source from the invoking package or
|
|
840
|
+
checkout. In the recommended released project-local channel, that is
|
|
841
|
+
`.asciidoclint/node_modules/asciidoclint/skills/asciidoclint`.
|
|
842
|
+
- User-global install: copy the bundled skill to
|
|
843
|
+
`~/.agents/skills/asciidoclint`, then create
|
|
844
|
+
`~/.claude/skills/asciidoclint` as a symbolic link to that global common
|
|
845
|
+
copy.
|
|
846
|
+
|
|
847
|
+
The reasoning is scope ownership. A project-local package install lives in
|
|
848
|
+
project state, so project-local skill links can point directly at the package's
|
|
849
|
+
bundled skill source; if the project package is removed, the broken links
|
|
850
|
+
correctly indicate that the project pin was removed. A user-global install may
|
|
851
|
+
be invoked from `npx`/npm exec cache, such as `~/.npm/_npx/<hash>/...`, which is
|
|
852
|
+
not durable user skill state. Therefore global install first copies the skill
|
|
853
|
+
to `~/.agents/skills/asciidoclint`; the Claude root is only a mirror to that
|
|
854
|
+
durable copy.
|
|
855
|
+
|
|
856
|
+
`uninstall-skill` should remove `asciidoclint` from both `.agents/skills` and
|
|
857
|
+
`.claude/skills` at the selected scope. It should be idempotent so users can
|
|
858
|
+
disable skill assistance without needing to inspect the filesystem first.
|
|
859
|
+
For project-local installs, it removes the two project skill symlinks. For
|
|
860
|
+
user-global installs, it removes the `~/.claude/skills/asciidoclint` symlink
|
|
861
|
+
and the copied `~/.agents/skills/asciidoclint` directory. It does not need to
|
|
862
|
+
match the currently running npm package version because the global skill copy
|
|
863
|
+
is installed skill state, not package-owned state.
|
|
864
|
+
|
|
865
|
+
Normal npm uninstall manages only package files. It must not be expected to
|
|
866
|
+
remove user-global or project-local skill roots. For example,
|
|
867
|
+
`npm uninstall -g asciidoclint` does not remove `~/.agents/skills/asciidoclint`
|
|
868
|
+
or `~/.claude/skills/asciidoclint`, and
|
|
869
|
+
`npm --prefix .asciidoclint uninstall asciidoclint` does not remove
|
|
870
|
+
`project/.agents/skills/asciidoclint` or
|
|
871
|
+
`project/.claude/skills/asciidoclint`. Users who want to remove skill
|
|
872
|
+
installations should run `asciidoclint uninstall-skill` explicitly.
|
|
873
|
+
|
|
874
|
+
The installer should implement this deterministic policy directly. Tests should
|
|
875
|
+
cover the global copy plus Claude symlink, project symlinks, explicit `--dest`
|
|
876
|
+
copy compatibility path, `--force` replacement, and idempotent uninstall of
|
|
877
|
+
both valid and broken symlinks.
|
|
704
878
|
|
|
705
879
|
### Skill Installation Channels
|
|
706
880
|
|
|
@@ -711,19 +885,18 @@ the repository checkout lives:
|
|
|
711
885
|
npx asciidoclint@latest install-skill --force
|
|
712
886
|
```
|
|
713
887
|
|
|
714
|
-
|
|
888
|
+
For a project-local install that should travel with the current workspace:
|
|
715
889
|
|
|
716
890
|
```bash
|
|
717
|
-
|
|
891
|
+
npm --prefix .asciidoclint install asciidoclint@<version>
|
|
892
|
+
.asciidoclint/node_modules/.bin/asciidoclint install-skill --project
|
|
718
893
|
```
|
|
719
894
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
npx asciidoclint@latest install-skill --project --agent claude-code --force
|
|
726
|
-
```
|
|
895
|
+
Do not use `npx asciidoclint install-skill --project` as the documented
|
|
896
|
+
project-local path unless the command also bootstraps the
|
|
897
|
+
`.asciidoclint/node_modules/asciidoclint` package install before creating
|
|
898
|
+
symlinks. Otherwise the project skill links could point at npm exec cache rather
|
|
899
|
+
than project-owned package state.
|
|
727
900
|
|
|
728
901
|
Developers need two switchable channels:
|
|
729
902
|
|
|
@@ -732,9 +905,10 @@ Developers need two switchable channels:
|
|
|
732
905
|
`asciidoclint` repository checkout.
|
|
733
906
|
|
|
734
907
|
The active channel is determined by the CLI used to run `install-skill` and the
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
existing `asciidoclint` skill
|
|
908
|
+
scope selected by `--project` or by the default global install. First-time
|
|
909
|
+
installation does not require `--force`. Use `--force` only as the explicit
|
|
910
|
+
switch operation because it replaces the existing `asciidoclint` skill links or
|
|
911
|
+
global copy at that scope.
|
|
738
912
|
|
|
739
913
|
Inside the `asciidoclint` repository, install the workspace-under-development
|
|
740
914
|
skill into the repository-local skill root:
|
|
@@ -743,15 +917,11 @@ skill into the repository-local skill root:
|
|
|
743
917
|
npx tsx src/cli/index.ts install-skill --project --force
|
|
744
918
|
```
|
|
745
919
|
|
|
746
|
-
Use `--agent claude-code` or `--agent cursor` to test those project install
|
|
747
|
-
layouts from the same checkout. Avoid `--agent openclaw --project` inside the
|
|
748
|
-
`asciidoclint` repository because OpenClaw's project path is `skills/`, which is
|
|
749
|
-
also this repository's canonical public skill source directory.
|
|
750
|
-
|
|
751
920
|
Switch that same repository workspace back to the released skill:
|
|
752
921
|
|
|
753
922
|
```bash
|
|
754
|
-
|
|
923
|
+
npm --prefix .asciidoclint install asciidoclint@latest
|
|
924
|
+
.asciidoclint/node_modules/.bin/asciidoclint install-skill --project --force
|
|
755
925
|
```
|
|
756
926
|
|
|
757
927
|
For global developer testing, use the same commands without `--project`.
|
|
@@ -773,15 +943,8 @@ npx tsx /path/to/asciidoclint/src/cli/index.ts install-skill --project --force
|
|
|
773
943
|
Switch that outside project back to the released skill with:
|
|
774
944
|
|
|
775
945
|
```bash
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
Use `--dest <skills-root>` when testing against an explicit Codex home or a
|
|
780
|
-
temporary skills root:
|
|
781
|
-
|
|
782
|
-
```bash
|
|
783
|
-
npx asciidoclint@latest install-skill --dest /tmp/codex-skills --force
|
|
784
|
-
node /path/to/asciidoclint/dist/cli/index.js install-skill --dest /tmp/codex-skills --force
|
|
946
|
+
npm --prefix .asciidoclint install asciidoclint@latest
|
|
947
|
+
.asciidoclint/node_modules/.bin/asciidoclint install-skill --project --force
|
|
785
948
|
```
|
|
786
949
|
|
|
787
950
|
Remove a released, project-local, or temporary skill install with the matching
|
|
@@ -790,19 +953,18 @@ scope:
|
|
|
790
953
|
```bash
|
|
791
954
|
npx asciidoclint@latest uninstall-skill
|
|
792
955
|
npx asciidoclint@latest uninstall-skill --project
|
|
793
|
-
npx asciidoclint@latest uninstall-skill --project --agent claude-code
|
|
794
|
-
npx asciidoclint@latest uninstall-skill --dest /tmp/codex-skills
|
|
795
956
|
```
|
|
796
957
|
|
|
797
958
|
Do not install separate public and developer copies under different skill names
|
|
798
959
|
by default. A single `asciidoclint` skill name keeps user prompts stable; channel
|
|
799
|
-
switching should happen by replacing the installed skill at the chosen scope
|
|
960
|
+
switching should happen by replacing the installed skill at the chosen scope in
|
|
961
|
+
both `.agents/skills` and `.claude/skills`.
|
|
800
962
|
The repository's `skills/asciidoclint` directory is the canonical public skill
|
|
801
963
|
source. Checked-in `.agents/skills`, `.claude/skills`, and other agent-specific
|
|
802
964
|
install directories are intentionally avoided; they are generated installation
|
|
803
965
|
targets, not source artifacts. Developers who want repo-local skill assistance
|
|
804
|
-
should install the development channel with `install-skill --project --
|
|
805
|
-
|
|
966
|
+
should install the development channel with `install-skill --project --force`
|
|
967
|
+
and uninstall it when done.
|
|
806
968
|
|
|
807
969
|
LLM calls should remain outside the core npm package initially. The package
|
|
808
970
|
stays deterministic and offline-friendly; the skill uses the surrounding agent
|
|
@@ -1100,6 +1262,12 @@ Expected editor behavior:
|
|
|
1100
1262
|
|
|
1101
1263
|
- Activate for `asciidoc`, `adoc`, `asc`, and `asciidoc` language/file
|
|
1102
1264
|
extensions.
|
|
1265
|
+
- Preserve existing user-facing settings, command names, and diagnostics import
|
|
1266
|
+
behavior across extension auto-updates. In particular,
|
|
1267
|
+
`.asciidoclint/config.yaml`, `.asciidoclint/diagnostics.json`,
|
|
1268
|
+
`asciidoclint.config`, `asciidoclint.customRules`,
|
|
1269
|
+
`asciidoclint.importCliDiagnostics`, and existing command palette workflows
|
|
1270
|
+
remain compatibility contracts.
|
|
1103
1271
|
- Do not lint on file open, focus, or typing. Lint on save in document context
|
|
1104
1272
|
by default, or through explicit commands.
|
|
1105
1273
|
- Read `.asciidoclint/config.yaml` from the workspace root or nearest project
|
|
@@ -1130,17 +1298,18 @@ Settings:
|
|
|
1130
1298
|
{
|
|
1131
1299
|
"asciidoclint.enable": true,
|
|
1132
1300
|
"asciidoclint.run": "onSave",
|
|
1133
|
-
"asciidoclint.defaultScope": "document",
|
|
1134
1301
|
"asciidoclint.config": ".asciidoclint/config.yaml",
|
|
1135
|
-
"asciidoclint.executablePath": "",
|
|
1136
1302
|
"asciidoclint.customRules": [],
|
|
1137
1303
|
"asciidoclint.hiddenRules": [],
|
|
1138
1304
|
"asciidoclint.unsafeFixes": false,
|
|
1139
|
-
"asciidoclint.followSymlinks": false,
|
|
1140
1305
|
"asciidoclint.importCliDiagnostics": true
|
|
1141
1306
|
}
|
|
1142
1307
|
```
|
|
1143
1308
|
|
|
1309
|
+
New editor settings such as a default lint scope, executable override, or
|
|
1310
|
+
symlink-following policy should be additive. They should not rename or remove
|
|
1311
|
+
the existing settings above without a migration path.
|
|
1312
|
+
|
|
1144
1313
|
When the official Asciidoctor VS Code extension is also installed, it can publish
|
|
1145
1314
|
its own native Asciidoctor diagnostics as files are opened. Those diagnostics are
|
|
1146
1315
|
useful for preview workflows, but they are a separate Problems source from
|
|
@@ -1161,14 +1330,18 @@ file navigation.
|
|
|
1161
1330
|
|
|
1162
1331
|
Resolution order for the lint engine:
|
|
1163
1332
|
|
|
1164
|
-
1.
|
|
1165
|
-
2.
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1333
|
+
1. User-configured executable override, if that setting is added later.
|
|
1334
|
+
2. Project-local `.asciidoclint/node_modules/.bin/asciidoclint` or package API
|
|
1335
|
+
when installed.
|
|
1336
|
+
3. Standard workspace `node_modules/.bin/asciidoclint` or package API when
|
|
1337
|
+
installed.
|
|
1338
|
+
4. Extension-bundled engine version.
|
|
1339
|
+
|
|
1340
|
+
The extension should prefer a project/workspace engine when present so project
|
|
1341
|
+
configuration and custom rule packages resolve exactly as they do in CI. It
|
|
1342
|
+
should continue to support the standard workspace `node_modules` convention for
|
|
1343
|
+
compatibility. The extension-bundled engine is a convenience fallback for users
|
|
1344
|
+
who only install the editor extension.
|
|
1172
1345
|
|
|
1173
1346
|
Custom rule scalability must match the CLI:
|
|
1174
1347
|
|
package/package.json
CHANGED
|
@@ -5,18 +5,21 @@ description: Use when linting AsciiDoc with asciidoclint, summarizing findings,
|
|
|
5
5
|
|
|
6
6
|
# asciidoclint
|
|
7
7
|
|
|
8
|
-
Use this skill for end-user `asciidoclint` workflows. Prefer the
|
|
9
|
-
`asciidoclint` install when present
|
|
10
|
-
|
|
8
|
+
Use this skill for end-user `asciidoclint` workflows. Prefer the explicit
|
|
9
|
+
project-pinned `asciidoclint` install when present; otherwise use the user's
|
|
10
|
+
global `asciidoclint` on `PATH`.
|
|
11
11
|
|
|
12
12
|
## Tool Resolution
|
|
13
13
|
|
|
14
14
|
Use this order:
|
|
15
15
|
|
|
16
|
-
1.
|
|
16
|
+
1. nearest project ancestor's `.asciidoclint/node_modules/.bin/asciidoclint`
|
|
17
17
|
2. `asciidoclint` on `PATH`
|
|
18
18
|
3. `npx asciidoclint`
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
After resolving the executable, refer to it as `<asciidoclint>` in workflow
|
|
21
|
+
commands. Do not search standard workspace `node_modules/.bin/asciidoclint`
|
|
22
|
+
from this skill.
|
|
20
23
|
|
|
21
24
|
Run from the repository or document workspace root unless the user names a
|
|
22
25
|
specific directory.
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Run lint in JSON mode:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
6
|
+
<asciidoclint> --format json <targets>
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
If the user wants deterministic safe fixes, run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
|
|
12
|
+
<asciidoclint> --fix --format json <targets>
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Then rerun without `--fix` and summarize remaining findings.
|
|
@@ -17,7 +17,7 @@ Then rerun without `--fix` and summarize remaining findings.
|
|
|
17
17
|
Run unsafe fixes only when explicitly requested:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
20
|
+
<asciidoclint> --fix --unsafe --format json <targets>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Unsafe fixes may alter rendered structure, link semantics, paths, or author
|
|
@@ -26,7 +26,7 @@ intent. Rerun lint after unsafe fixes.
|
|
|
26
26
|
For VS Code-compatible editor diagnostics import, write:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
|
|
29
|
+
<asciidoclint> --format json \
|
|
30
30
|
--output-diagnostics .asciidoclint/diagnostics.json \
|
|
31
31
|
<targets>
|
|
32
32
|
```
|
|
@@ -15,8 +15,8 @@ Default placement:
|
|
|
15
15
|
For custom rules, scaffold with:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
<asciidoclint> init-rule --tag organization --id ORG001 --alias no-todo
|
|
19
|
+
<asciidoclint> init-rule --tag organization --id ORG002 --alias section-policy --directory ./lint-rules
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
`--tag` is a grouping tag stored in rule metadata. `--pack` remains accepted as
|
|
@@ -128,10 +128,10 @@ Tests and examples:
|
|
|
128
128
|
After creating or editing a custom rule, validate:
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
<asciidoclint> --validate-rules
|
|
132
|
+
<asciidoclint> --list-rules --format json
|
|
133
|
+
<asciidoclint> --custom-rule ./lint-rules --list-rules
|
|
134
|
+
<asciidoclint> --custom-rule ./lint-rules index.adoc
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
For user-facing custom rule usage, prefer the guide in `docs/custom-rules.md`.
|