@jazpiper/rules-doctor 0.1.0 → 0.3.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/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## 0.3.0 - 2026-02-25
6
+
7
+ - Added `copilot` adapter support for `.github/copilot-instructions.md`.
8
+ - Switched Copilot output to marker-managed mode to preserve user-authored content.
9
+ - Added target presets for `init` (`all`, `core`, `copilot`).
10
+ - Added `preset apply` command for applying presets to existing `.agentrules/rules.yaml`.
11
+ - Added CI workflow template for drift detection via `rules-doctor check`.
12
+ - Expanded tests for shared `AGENTS.md`, Copilot marker re-sync, and `--import` + preset behavior.
13
+ - Improved README with troubleshooting and CI examples.
14
+
15
+ ## 0.2.0 - 2026-02-25
16
+
17
+ - Added multi-target adapters for `claude`, `codex`, `cursor`, `gemini`, and `opencode`.
18
+ - Added `init --import`, dry-run default for `sync`, drift `check`, and structural `doctor`.
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @jazpiper/rules-doctor
2
2
 
3
- `rules-doctor` is a Node.js CLI (TypeScript) that keeps agent instruction files in sync from one source of truth: `.agentrules/rules.yaml`.
3
+ `rules-doctor` keeps coding-rule files in sync across multiple agent CLIs from one source of truth: `.agentrules/rules.yaml`.
4
+
5
+ It is optimized for real project adoption:
6
+ - import existing docs (`init --import`)
7
+ - target presets (`init --preset all|core|copilot`)
8
+ - safe previews by default (`sync` is dry-run unless `--write`)
9
+ - drift detection for CI (`check`)
10
+
11
+ All paths are resolved from project root (`.git` ancestor), not current subdirectory.
4
12
 
5
13
  ## Install
6
14
 
@@ -8,75 +16,169 @@
8
16
  npm install -D @jazpiper/rules-doctor
9
17
  ```
10
18
 
11
- Or run directly:
19
+ Run with:
12
20
 
13
21
  ```bash
14
- npx @jazpiper/rules-doctor init
22
+ npx rules-doctor --help
15
23
  ```
16
24
 
17
- ## Usage
25
+ ## Quick Start
18
26
 
19
- ### 1) Initialize rules
27
+ ### 1) Initialize (recommended with import)
20
28
 
21
29
  ```bash
22
- rules-doctor init
30
+ npx rules-doctor init --import
23
31
  ```
24
32
 
25
- Creates `.agentrules/rules.yaml` if it does not exist.
33
+ - Creates `.agentrules/rules.yaml`
34
+ - Reads existing docs when found (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/*.mdc`, `.github/copilot-instructions.md`)
35
+ - Writes `.agentrules/import-report.md`
36
+
37
+ Preset example (Copilot only):
38
+
39
+ ```bash
40
+ npx rules-doctor init --preset copilot
41
+ ```
26
42
 
27
- - If `package.json` has scripts for `lint`, `test`, or `build`, they are inferred as:
28
- - `npm run lint`
29
- - `npm run test`
30
- - `npm run build`
31
- - Missing scripts are created as TODO placeholders.
43
+ Preset meanings:
44
+ - `all`: all built-in targets enabled
45
+ - `core`: `claude`, `codex`, `opencode`, `cursor`, `gemini`
46
+ - `copilot`: only `copilot` enabled
32
47
 
33
- ### 2) Sync generated docs
48
+ Apply preset to an existing `.agentrules/rules.yaml`:
34
49
 
35
50
  ```bash
36
- rules-doctor sync
37
- rules-doctor sync --target claude
38
- rules-doctor sync --target codex
51
+ npx rules-doctor preset apply copilot --write
39
52
  ```
40
53
 
41
- Generates/updates:
42
- - `CLAUDE.md` (fully managed)
43
- - `AGENTS.md` (only content inside markers is managed)
54
+ ### 2) Preview changes safely
44
55
 
45
- Managed marker block in `AGENTS.md`:
56
+ ```bash
57
+ npx rules-doctor sync --diff
58
+ ```
46
59
 
47
- ```md
48
- <!-- RULES_DOCTOR:BEGIN -->
49
- ... managed content ...
50
- <!-- RULES_DOCTOR:END -->
60
+ `sync` is dry-run by default. Nothing is written yet.
61
+
62
+ ### 3) Apply changes
63
+
64
+ ```bash
65
+ npx rules-doctor sync --write
51
66
  ```
52
67
 
53
- If markers are missing, a new managed block is appended to the end of `AGENTS.md`.
68
+ Optional backups:
54
69
 
55
- ### 3) Analyze docs
70
+ ```bash
71
+ npx rules-doctor sync --write --backup
72
+ ```
73
+
74
+ ### 4) Verify drift in CI/local
56
75
 
57
76
  ```bash
58
- rules-doctor analyze
77
+ npx rules-doctor check
59
78
  ```
60
79
 
61
- Reads `CLAUDE.md` and `AGENTS.md`, then prints a concise report about:
62
- - missing markers
63
- - missing verify commands (`lint`/`test`/`build`)
64
- - obvious contradictions (simple heuristics)
80
+ Returns non-zero when generated targets are out of sync.
65
81
 
66
- ## Development
82
+ ## Supported Targets
67
83
 
68
84
  ```bash
69
- npm ci
70
- npm run build
85
+ npx rules-doctor targets list
71
86
  ```
72
87
 
73
- ## License
88
+ Built-in adapters:
89
+ - `claude` -> `CLAUDE.md` (full-managed)
90
+ - `codex` -> `AGENTS.md` (marker-managed)
91
+ - `copilot` -> `.github/copilot-instructions.md` (marker-managed, preserves existing text outside managed block)
92
+ - `opencode` -> `AGENTS.md` (marker-managed)
93
+ - `cursor` -> `.cursor/rules/rules-doctor.mdc` (full-managed)
94
+ - `gemini` -> `GEMINI.md` (full-managed)
74
95
 
75
- MIT
96
+ ## Command Reference
97
+
98
+ ### `init`
76
99
 
100
+ ```bash
101
+ npx rules-doctor init [--import]
102
+ npx rules-doctor init [--import] [--preset all|core|copilot]
103
+ ```
77
104
 
78
- ## CI
105
+ ### `preset apply`
79
106
 
80
- This repo includes a GitHub Actions workflow template at `docs/workflows/ci.yml`.
107
+ ```bash
108
+ npx rules-doctor preset apply <all|core|copilot> [--diff] [--write]
109
+ ```
81
110
 
82
- If you want CI, copy it to `.github/workflows/ci.yml` and push the change.
111
+ ### `sync`
112
+
113
+ ```bash
114
+ npx rules-doctor sync [--target all|claude,codex,...] [--diff] [--write] [--backup]
115
+ ```
116
+
117
+ ### `check`
118
+
119
+ ```bash
120
+ npx rules-doctor check [--target all|claude,codex,...] [--diff]
121
+ ```
122
+
123
+ ### `analyze`
124
+
125
+ ```bash
126
+ npx rules-doctor analyze [--strict]
127
+ ```
128
+
129
+ ### `doctor`
130
+
131
+ ```bash
132
+ npx rules-doctor doctor [--strict]
133
+ ```
134
+
135
+ ## CI Template
136
+
137
+ Copy [docs/workflows/rules-doctor-check.yml](docs/workflows/rules-doctor-check.yml) to your repository as `.github/workflows/rules-doctor-check.yml`.
138
+ It runs `npx rules-doctor check` on push and pull requests.
139
+
140
+ Inline workflow example:
141
+
142
+ ```yaml
143
+ name: Rules Doctor Check
144
+
145
+ on:
146
+ push:
147
+ pull_request:
148
+
149
+ jobs:
150
+ rules-doctor:
151
+ runs-on: ubuntu-latest
152
+ steps:
153
+ - uses: actions/checkout@v4
154
+ - uses: actions/setup-node@v4
155
+ with:
156
+ node-version: "20"
157
+ cache: npm
158
+ - run: npm ci
159
+ - run: npx rules-doctor check
160
+ ```
161
+
162
+ ## Troubleshooting
163
+
164
+ - `rules-doctor: command not found` after `npm install -D @jazpiper/rules-doctor`:
165
+ - Use `npx rules-doctor ...` (recommended for local dev dependency).
166
+ - Or add an npm script in your project: `"rules:check": "rules-doctor check"`, then run `npm run rules:check`.
167
+ - Global install (`npm i -g @jazpiper/rules-doctor`) works, but local + `npx` is safer for version consistency.
168
+ - `init` says `rules.yaml already exists`:
169
+ - Use `npx rules-doctor preset apply <preset> --write` to change target defaults on an existing project.
170
+
171
+ ## Rules Schema (v2 Draft)
172
+
173
+ See [docs/rules-v2-draft.yaml](docs/rules-v2-draft.yaml).
174
+
175
+ ## Development
176
+
177
+ ```bash
178
+ npm ci
179
+ npm test
180
+ ```
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,31 @@
1
+ const { formatCommands, formatList } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "claude",
5
+ name: "Claude Code",
6
+ description: "Generate CLAUDE.md from rules.yaml.",
7
+ defaultPath: "CLAUDE.md",
8
+ management: "full",
9
+ render(rules) {
10
+ return [
11
+ "# CLAUDE.md",
12
+ "",
13
+ "## Mission",
14
+ rules.mission,
15
+ "",
16
+ "## Workflow",
17
+ formatList(rules.workflow),
18
+ "",
19
+ "## Commands",
20
+ formatCommands(rules.commands),
21
+ "",
22
+ "## Done",
23
+ formatList(rules.done),
24
+ "",
25
+ "## Approvals",
26
+ `- Mode: \`${rules.approvals.mode}\``,
27
+ ...rules.approvals.notes.map((note) => `- ${note}`),
28
+ "",
29
+ ].join("\n");
30
+ },
31
+ };
@@ -0,0 +1,14 @@
1
+ const { renderManagedRulesBody } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "codex",
5
+ name: "Codex CLI",
6
+ description: "Manage AGENTS.md via marker-managed section.",
7
+ defaultPath: "AGENTS.md",
8
+ management: "marker",
9
+ markerBegin: "<!-- RULES_DOCTOR:BEGIN -->",
10
+ markerEnd: "<!-- RULES_DOCTOR:END -->",
11
+ render(rules) {
12
+ return renderManagedRulesBody(rules);
13
+ },
14
+ };
@@ -0,0 +1,54 @@
1
+ function formatList(items) {
2
+ if (!Array.isArray(items) || items.length === 0) {
3
+ return "- (none)";
4
+ }
5
+ return items.map((item) => `- ${item}`).join("\n");
6
+ }
7
+
8
+ function formatCommands(commands) {
9
+ if (!commands || typeof commands !== "object") {
10
+ return "- (none)";
11
+ }
12
+
13
+ const preferredOrder = ["lint", "test", "build"];
14
+ const names = [
15
+ ...preferredOrder.filter((name) => Object.prototype.hasOwnProperty.call(commands, name)),
16
+ ...Object.keys(commands).filter((name) => !preferredOrder.includes(name)),
17
+ ];
18
+
19
+ if (names.length === 0) {
20
+ return "- (none)";
21
+ }
22
+
23
+ return names.map((name) => `- ${name}: \`${commands[name]}\``).join("\n");
24
+ }
25
+
26
+ function renderManagedRulesBody(rules) {
27
+ return [
28
+ "## rules-doctor Managed Rules",
29
+ "Generated from `.agentrules/rules.yaml`. Edit that file, then run `rules-doctor sync`.",
30
+ "",
31
+ "### Mission",
32
+ rules.mission,
33
+ "",
34
+ "### Workflow",
35
+ formatList(rules.workflow),
36
+ "",
37
+ "### Commands",
38
+ formatCommands(rules.commands),
39
+ "",
40
+ "### Done",
41
+ formatList(rules.done),
42
+ "",
43
+ "### Approvals",
44
+ `- Policy: \`${rules.approvals.mode}\``,
45
+ ...rules.approvals.notes.map((note) => `- ${note}`),
46
+ "",
47
+ ].join("\n");
48
+ }
49
+
50
+ module.exports = {
51
+ formatCommands,
52
+ formatList,
53
+ renderManagedRulesBody,
54
+ };
@@ -0,0 +1,15 @@
1
+ const { renderManagedRulesBody } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "copilot",
5
+ name: "GitHub Copilot",
6
+ description:
7
+ "Manage .github/copilot-instructions.md via marker-managed section to preserve user content.",
8
+ defaultPath: ".github/copilot-instructions.md",
9
+ management: "marker",
10
+ markerBegin: "<!-- RULES_DOCTOR:COPILOT:BEGIN -->",
11
+ markerEnd: "<!-- RULES_DOCTOR:COPILOT:END -->",
12
+ render(rules) {
13
+ return ["# Copilot Instructions", "", renderManagedRulesBody(rules)].join("\n");
14
+ },
15
+ };
@@ -0,0 +1,19 @@
1
+ const { renderManagedRulesBody } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "cursor",
5
+ name: "Cursor",
6
+ description: "Manage .cursor/rules/rules-doctor.mdc as an always-applied project rule.",
7
+ defaultPath: ".cursor/rules/rules-doctor.mdc",
8
+ management: "full",
9
+ render(rules) {
10
+ return [
11
+ "---",
12
+ "description: rules-doctor managed coding rules",
13
+ "alwaysApply: true",
14
+ "---",
15
+ "",
16
+ renderManagedRulesBody(rules),
17
+ ].join("\n");
18
+ },
19
+ };
@@ -0,0 +1,12 @@
1
+ const { renderManagedRulesBody } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "gemini",
5
+ name: "Gemini CLI",
6
+ description: "Generate GEMINI.md managed instruction file.",
7
+ defaultPath: "GEMINI.md",
8
+ management: "full",
9
+ render(rules) {
10
+ return ["# GEMINI.md", "", renderManagedRulesBody(rules)].join("\n");
11
+ },
12
+ };
@@ -0,0 +1,14 @@
1
+ const claude = require("./claude");
2
+ const codex = require("./codex");
3
+ const copilot = require("./copilot");
4
+ const cursor = require("./cursor");
5
+ const gemini = require("./gemini");
6
+ const opencode = require("./opencode");
7
+
8
+ const ADAPTERS = [claude, codex, copilot, cursor, gemini, opencode];
9
+ const ADAPTERS_BY_ID = Object.fromEntries(ADAPTERS.map((adapter) => [adapter.id, adapter]));
10
+
11
+ module.exports = {
12
+ ADAPTERS,
13
+ ADAPTERS_BY_ID,
14
+ };
@@ -0,0 +1,14 @@
1
+ const { renderManagedRulesBody } = require("./common");
2
+
3
+ module.exports = {
4
+ id: "opencode",
5
+ name: "OpenCode CLI",
6
+ description: "Manage AGENTS.md via marker-managed section (OpenCode rules).",
7
+ defaultPath: "AGENTS.md",
8
+ management: "marker",
9
+ markerBegin: "<!-- RULES_DOCTOR:BEGIN -->",
10
+ markerEnd: "<!-- RULES_DOCTOR:END -->",
11
+ render(rules) {
12
+ return renderManagedRulesBody(rules);
13
+ },
14
+ };
@@ -0,0 +1 @@
1
+ export {};