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 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 install --save-dev asciidoclint
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 repository ships an `asciidoclint` skill for AI agents. Install it from 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
- Or install it directly from GitHub with the open skills CLI:
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
- npx skills add f33lgood/asciidoclint --skill asciidoclint -a codex -g
106
+ npm --prefix .asciidoclint install asciidoclint@<version>
107
+ .asciidoclint/node_modules/.bin/asciidoclint install-skill --project
74
108
  ```
75
109
 
76
- Remove the installed skill when you want to use `asciidoclint` without AI skill
77
- assistance:
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>", "target agent: codex, cursor, claude-code, or openclaw", "codex")
42
- .option("--project", "install into the target agent's project skills directory")
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.destination}`);
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>", "target agent: codex, cursor, claude-code, or openclaw", "codex")
59
- .option("--project", "uninstall from the target agent's project skills directory")
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.destination}`
65
- : `No asciidoclint skill installed at ${result.destination}`);
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 defaultAgent = "codex";
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
- const root = resolveSkillsRoot(options);
28
- const destination = path.join(root, skillName);
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 { source, destination };
80
+ return;
31
81
  }
32
- if (fs.existsSync(destination)) {
33
- if (!options.force) {
34
- throw new Error(`${destination} already exists; pass --force to replace it`);
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
- fs.mkdirSync(root, { recursive: true });
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
- export function uninstallSkill(options = {}) {
43
- const destination = path.join(resolveSkillsRoot(options), skillName);
44
- const removed = fs.existsSync(destination);
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
- return { destination, removed };
118
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
119
+ fs.symlinkSync(path.relative(path.dirname(destination), source), destination, "dir");
49
120
  }
50
- function resolveSkillsRoot(options) {
51
- if (options.dest) {
52
- return path.resolve(options.dest);
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
- const agent = options.agent ?? defaultAgent;
55
- const roots = skillAgentRoots[agent];
56
- if (!roots) {
57
- throw new Error(`Unsupported skill agent: ${agent}`);
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.0";
1
+ export declare const VERSION = "1.2.0";
2
2
  export declare function getVersion(): string;
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- export const VERSION = "1.1.0";
1
+ export const VERSION = "1.2.0";
2
2
  export function getVersion() {
3
3
  return VERSION;
4
4
  }
@@ -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 in this order: workspace `node_modules/.bin/asciidoclint`,
642
- `npx asciidoclint`, then `npx -y asciidoclint@latest`.
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 primary skill install path should follow the open skills ecosystem:
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
- npx asciidoclint install-skill
677
- npx asciidoclint uninstall-skill
822
+ asciidoclint install-skill
823
+ asciidoclint uninstall-skill
678
824
  ```
679
825
 
680
- This command copies the bundled `skills/asciidoclint` directory into
681
- the selected agent's skill root. It should support `--agent` for common coding
682
- agents, `--project` for project-local installs, `--dest` for explicit test or
683
- custom install roots, and `--force` to replace an existing skill. This keeps
684
- npm-package installs version-aligned with the skill that was published in the
685
- same package.
686
-
687
- `uninstall-skill` should remove `asciidoclint` from the same selected skills
688
- root. It should be idempotent so users can disable skill assistance without
689
- needing to inspect the filesystem first.
690
-
691
- The installer intentionally implements only the small target matrix needed by
692
- `asciidoclint`; the open `skills` CLI remains the full interactive installer.
693
- Validated target paths from `npx skills`:
694
-
695
- | Agent | `--agent` | Project root | Global root |
696
- |---|---|---|---|
697
- | Codex | `codex` | `.agents/skills/` | `~/.codex/skills/` |
698
- | Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
699
- | Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
700
- | OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
701
-
702
- `--dest <skills-root>` overrides the target matrix and installs directly under
703
- the supplied skills root.
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
- or, for a project-local install that should travel with the current workspace:
888
+ For a project-local install that should travel with the current workspace:
715
889
 
716
890
  ```bash
717
- npx asciidoclint@latest install-skill --project --force
891
+ npm --prefix .asciidoclint install asciidoclint@<version>
892
+ .asciidoclint/node_modules/.bin/asciidoclint install-skill --project
718
893
  ```
719
894
 
720
- Install a released skill for a specific project-local agent target:
721
-
722
- ```bash
723
- npx asciidoclint@latest install-skill --project --agent codex --force
724
- npx asciidoclint@latest install-skill --project --agent cursor --force
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
- destination selected by `--project`, `--dest`, or the default global skills
736
- root. `--force` is the explicit switch operation because it replaces the
737
- existing `asciidoclint` skill at that destination.
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
- npx asciidoclint@latest install-skill --project --force
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
- npx asciidoclint@latest install-skill --project --force
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 --agent
805
- <agent> --force` and uninstall it when done.
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. Workspace `node_modules/.bin/asciidoclint` or package API when installed.
1165
- 2. Extension-bundled engine version.
1166
- 3. User-configured `asciidoclint.executablePath`.
1167
-
1168
- The extension should prefer the workspace engine when present so project
1169
- configuration and custom rule packages resolve exactly as they do in CI. The
1170
- extension-bundled engine is a convenience fallback for users who only install
1171
- the editor extension.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asciidoclint",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "AsciiDoc syntax, structure, and document-policy linter",
5
5
  "author": "Shawn Hu",
6
6
  "license": "MIT",
@@ -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 workspace
9
- `asciidoclint` install when present so project configuration, custom rules, and
10
- CI behavior match the user's project.
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. `./node_modules/.bin/asciidoclint`
16
+ 1. nearest project ancestor's `.asciidoclint/node_modules/.bin/asciidoclint`
17
17
  2. `asciidoclint` on `PATH`
18
18
  3. `npx asciidoclint`
19
- 4. `npx -y asciidoclint@latest`
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.
@@ -11,7 +11,7 @@ https://github.com/f33lgood/asciidoclint/issues
11
11
  Gather what is available without exposing private source content:
12
12
 
13
13
  ```bash
14
- npx asciidoclint --version
14
+ <asciidoclint> --version
15
15
  node --version
16
16
  npm --version
17
17
  ```
@@ -3,13 +3,13 @@
3
3
  Run lint in JSON mode:
4
4
 
5
5
  ```bash
6
- npx asciidoclint --format json <targets>
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
- npx asciidoclint --fix --format json <targets>
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
- npx asciidoclint --fix --unsafe --format json <targets>
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
- npx asciidoclint --format json \
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
- npx asciidoclint init-rule --tag organization --id ORG001 --alias no-todo
19
- npx asciidoclint init-rule --tag organization --id ORG002 --alias section-policy --directory ./lint-rules
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
- npx asciidoclint --validate-rules
132
- npx asciidoclint --list-rules --format json
133
- npx asciidoclint --custom-rule ./lint-rules --list-rules
134
- npx asciidoclint --custom-rule ./lint-rules index.adoc
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`.