pr-checkmate 3.2.3 → 4.0.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.
Files changed (174) hide show
  1. package/.eslintrc.js +3 -9
  2. package/LICENSE +8 -3
  3. package/README.md +54 -5
  4. package/dist/cjs/agent/hook.d.ts +37 -0
  5. package/dist/cjs/agent/hook.js +175 -0
  6. package/dist/cjs/agent/registration.d.ts +10 -0
  7. package/dist/cjs/agent/registration.js +28 -0
  8. package/dist/cjs/agent/setup.d.ts +20 -0
  9. package/dist/cjs/agent/setup.js +229 -0
  10. package/dist/cjs/cli.js +101 -0
  11. package/dist/cjs/config/constants.d.ts +2 -0
  12. package/dist/cjs/config/constants.js +5 -1
  13. package/dist/cjs/config/pr-checkmate.template.json +11 -1
  14. package/dist/cjs/core/checks/dependencies/grype-scan.js +18 -0
  15. package/dist/cjs/core/checks/findings.js +33 -7
  16. package/dist/cjs/core/checks/git/large-files.d.ts +1 -0
  17. package/dist/cjs/core/checks/git/large-files.js +3 -3
  18. package/dist/cjs/core/checks/languages/lint.js +12 -0
  19. package/dist/cjs/core/checks/languages/ruff-shared.js +6 -1
  20. package/dist/cjs/core/checks/pr/commitlint.js +4 -2
  21. package/dist/cjs/core/checks/pr/pr-size-check.d.ts +2 -0
  22. package/dist/cjs/core/checks/pr/pr-size-check.js +5 -5
  23. package/dist/cjs/core/checks/quality/package-size.d.ts +23 -1
  24. package/dist/cjs/core/checks/quality/package-size.js +20 -4
  25. package/dist/cjs/core/checks/quality/spellcheck.js +8 -5
  26. package/dist/cjs/core/checks/security/agent-instructions.d.ts +4 -0
  27. package/dist/cjs/core/checks/security/agent-instructions.js +149 -0
  28. package/dist/cjs/core/checks/security/diff-security.d.ts +7 -0
  29. package/dist/cjs/core/checks/security/diff-security.js +21 -1
  30. package/dist/cjs/core/checks/security/security.d.ts +0 -13
  31. package/dist/cjs/core/checks/security/security.js +27 -0
  32. package/dist/cjs/core/checks/security/workflow-security.js +33 -0
  33. package/dist/cjs/core/commands.d.ts +6 -0
  34. package/dist/cjs/core/commands.js +11 -1
  35. package/dist/cjs/core/finding-store.d.ts +16 -0
  36. package/dist/cjs/core/finding-store.js +73 -0
  37. package/dist/cjs/core/install/doctor.js +12 -0
  38. package/dist/cjs/core/install/grype-db.d.ts +25 -0
  39. package/dist/cjs/core/install/grype-db.js +45 -0
  40. package/dist/cjs/core/install/profiles.d.ts +1 -1
  41. package/dist/cjs/core/install/profiles.js +1 -1
  42. package/dist/cjs/core/install/reuse.js +6 -3
  43. package/dist/cjs/core/registry.js +2 -0
  44. package/dist/cjs/core/reporters.js +9 -0
  45. package/dist/cjs/core/run-checks.js +15 -5
  46. package/dist/cjs/core/sarif.d.ts +13 -0
  47. package/dist/cjs/core/sarif.js +60 -0
  48. package/dist/cjs/core/types.d.ts +7 -2
  49. package/dist/cjs/index.d.ts +2 -1
  50. package/dist/cjs/index.js +4 -1
  51. package/dist/cjs/init.d.ts +0 -8
  52. package/dist/cjs/init.js +12 -2
  53. package/dist/cjs/interfaces/index.d.ts +12 -0
  54. package/dist/cjs/mcp/config-tools.d.ts +30 -0
  55. package/dist/cjs/mcp/config-tools.js +320 -0
  56. package/dist/cjs/mcp/dependency.d.ts +33 -0
  57. package/dist/cjs/mcp/dependency.js +407 -0
  58. package/dist/cjs/mcp/protocol.d.ts +97 -0
  59. package/dist/cjs/mcp/protocol.js +181 -0
  60. package/dist/cjs/mcp/review.d.ts +63 -0
  61. package/dist/cjs/mcp/review.js +150 -0
  62. package/dist/cjs/mcp/serve.d.ts +8 -0
  63. package/dist/cjs/mcp/serve.js +49 -0
  64. package/dist/cjs/mcp/settings.d.ts +13 -0
  65. package/dist/cjs/mcp/settings.js +50 -0
  66. package/dist/cjs/mcp/tools.d.ts +23 -0
  67. package/dist/cjs/mcp/tools.js +633 -0
  68. package/dist/cjs/utils/execa.d.ts +7 -0
  69. package/dist/cjs/utils/execa.js +41 -1
  70. package/dist/cjs/utils/own-deps.js +8 -1
  71. package/dist/cjs/utils/resolve-bin.d.ts +2 -0
  72. package/dist/cjs/utils/resolve-bin.js +5 -0
  73. package/dist/cjs/utils/result-collector.d.ts +17 -1
  74. package/dist/cjs/utils/result-collector.js +7 -2
  75. package/dist/esm/agent/hook.d.ts +37 -0
  76. package/dist/esm/agent/hook.js +167 -0
  77. package/dist/esm/agent/registration.d.ts +10 -0
  78. package/dist/esm/agent/registration.js +21 -0
  79. package/dist/esm/agent/setup.d.ts +20 -0
  80. package/dist/esm/agent/setup.js +220 -0
  81. package/dist/esm/cli.js +100 -2
  82. package/dist/esm/config/constants.d.ts +2 -0
  83. package/dist/esm/config/constants.js +4 -0
  84. package/dist/esm/config/pr-checkmate.template.json +11 -1
  85. package/dist/esm/core/checks/dependencies/dependency-check.js +2 -2
  86. package/dist/esm/core/checks/dependencies/grype-scan.js +18 -0
  87. package/dist/esm/core/checks/dependencies/license-check.js +2 -2
  88. package/dist/esm/core/checks/dependencies/npm-audit.js +2 -2
  89. package/dist/esm/core/checks/findings.js +33 -7
  90. package/dist/esm/core/checks/git/diff-smell.js +1 -1
  91. package/dist/esm/core/checks/git/large-files.d.ts +1 -0
  92. package/dist/esm/core/checks/git/large-files.js +1 -1
  93. package/dist/esm/core/checks/languages/cpp-format.js +1 -1
  94. package/dist/esm/core/checks/languages/lint.js +12 -3
  95. package/dist/esm/core/checks/languages/prettier.js +3 -3
  96. package/dist/esm/core/checks/languages/python-format.js +2 -2
  97. package/dist/esm/core/checks/languages/python-lint.js +2 -2
  98. package/dist/esm/core/checks/languages/ruff-shared.js +6 -1
  99. package/dist/esm/core/checks/languages/typecheck.js +1 -1
  100. package/dist/esm/core/checks/pr/commitlint.js +4 -2
  101. package/dist/esm/core/checks/pr/pr-body.js +1 -1
  102. package/dist/esm/core/checks/pr/pr-size-check.d.ts +2 -0
  103. package/dist/esm/core/checks/pr/pr-size-check.js +3 -3
  104. package/dist/esm/core/checks/pr/pr-title-lint.js +1 -1
  105. package/dist/esm/core/checks/quality/dead-code.js +1 -1
  106. package/dist/esm/core/checks/quality/duplicate-check.js +1 -1
  107. package/dist/esm/core/checks/quality/markdown-lint.js +1 -1
  108. package/dist/esm/core/checks/quality/package-size.d.ts +23 -1
  109. package/dist/esm/core/checks/quality/package-size.js +19 -4
  110. package/dist/esm/core/checks/quality/spellcheck.js +11 -8
  111. package/dist/esm/core/checks/security/agent-instructions.d.ts +4 -0
  112. package/dist/esm/core/checks/security/agent-instructions.js +146 -0
  113. package/dist/esm/core/checks/security/diff-security.d.ts +7 -0
  114. package/dist/esm/core/checks/security/diff-security.js +21 -1
  115. package/dist/esm/core/checks/security/security.d.ts +0 -13
  116. package/dist/esm/core/checks/security/security.js +28 -1
  117. package/dist/esm/core/checks/security/workflow-security.js +33 -0
  118. package/dist/esm/core/commands.d.ts +6 -0
  119. package/dist/esm/core/commands.js +10 -0
  120. package/dist/esm/core/finding-store.d.ts +16 -0
  121. package/dist/esm/core/finding-store.js +67 -0
  122. package/dist/esm/core/install/doctor.js +12 -0
  123. package/dist/esm/core/install/grype-db.d.ts +25 -0
  124. package/dist/esm/core/install/grype-db.js +41 -0
  125. package/dist/esm/core/install/prepare.js +1 -1
  126. package/dist/esm/core/install/profiles.d.ts +1 -1
  127. package/dist/esm/core/install/profiles.js +1 -1
  128. package/dist/esm/core/install/reuse.js +6 -3
  129. package/dist/esm/core/registry.js +2 -0
  130. package/dist/esm/core/reporters.js +9 -0
  131. package/dist/esm/core/run-checks.js +17 -7
  132. package/dist/esm/core/sarif.d.ts +13 -0
  133. package/dist/esm/core/sarif.js +57 -0
  134. package/dist/esm/core/types.d.ts +7 -2
  135. package/dist/esm/index.d.ts +2 -1
  136. package/dist/esm/index.js +2 -0
  137. package/dist/esm/init.d.ts +0 -8
  138. package/dist/esm/init.js +15 -4
  139. package/dist/esm/interfaces/index.d.ts +12 -0
  140. package/dist/esm/mcp/config-tools.d.ts +30 -0
  141. package/dist/esm/mcp/config-tools.js +310 -0
  142. package/dist/esm/mcp/dependency.d.ts +33 -0
  143. package/dist/esm/mcp/dependency.js +402 -0
  144. package/dist/esm/mcp/protocol.d.ts +97 -0
  145. package/dist/esm/mcp/protocol.js +175 -0
  146. package/dist/esm/mcp/review.d.ts +63 -0
  147. package/dist/esm/mcp/review.js +139 -0
  148. package/dist/esm/mcp/serve.d.ts +8 -0
  149. package/dist/esm/mcp/serve.js +43 -0
  150. package/dist/esm/mcp/settings.d.ts +13 -0
  151. package/dist/esm/mcp/settings.js +43 -0
  152. package/dist/esm/mcp/tools.d.ts +23 -0
  153. package/dist/esm/mcp/tools.js +626 -0
  154. package/dist/esm/utils/execa.d.ts +7 -0
  155. package/dist/esm/utils/execa.js +40 -1
  156. package/dist/esm/utils/own-deps.js +8 -1
  157. package/dist/esm/utils/resolve-bin.d.ts +2 -0
  158. package/dist/esm/utils/resolve-bin.js +4 -0
  159. package/dist/esm/utils/result-collector.d.ts +17 -1
  160. package/dist/esm/utils/result-collector.js +7 -2
  161. package/eslint-default-rules.json +2 -8
  162. package/eslint.config.mjs +37 -8
  163. package/install-manifests/commitlint/package-lock.json +5 -4
  164. package/install-manifests/core/package-lock.json +5 -3
  165. package/install-manifests/js/package-lock.json +250 -571
  166. package/install-manifests/js/package.json +10 -8
  167. package/install-manifests/markdown/package-lock.json +16 -13
  168. package/install-manifests/markdown/package.json +3 -0
  169. package/install-manifests/spellcheck/package-lock.json +3 -3
  170. package/package.json +24 -16
  171. package/pr-checkmate.schema.json +35 -0
  172. package/src/config/.eslintrc.js +3 -9
  173. package/src/config/eslint-default-rules.json +2 -8
  174. package/src/config/pr-checkmate.schema.json +35 -0
@@ -0,0 +1,63 @@
1
+ import type { CheckReport } from '../core/types.js';
2
+ /**
3
+ * What a review looks at.
4
+ *
5
+ * - `worktree` — every uncommitted change, staged or not, new files included.
6
+ * The default: it is the code the agent just wrote.
7
+ * - `staged` — the index only; what `git commit` would record.
8
+ * - `branch` — commits since the branch left the default branch.
9
+ * - `full` — every tracked file.
10
+ */
11
+ export type ReviewScope = 'worktree' | 'staged' | 'branch' | 'full';
12
+ export declare const REVIEW_SCOPES: readonly ReviewScope[];
13
+ /**
14
+ * The checks cheap enough to run after every edit: nothing heavy, nothing that
15
+ * talks to a registry. Derived from the registry rather than listed, so a check
16
+ * added next month is in the loop from day one unless it declares itself heavy.
17
+ */
18
+ export declare function fastCheckNames(): string[];
19
+ /** The repository containing `dir`, or `undefined` when it is not inside one. */
20
+ export declare function resolveRepoRoot(dir: string): Promise<string | undefined>;
21
+ /**
22
+ * Turn the working tree into a git tree object without touching the real index.
23
+ *
24
+ * `git add -A` against a scratch copy of the index, then `write-tree`: the result
25
+ * is the same kind of object `precommit` diffs against, so every delta-aware check
26
+ * works unchanged. The agent's staging area stays exactly as they left it, and
27
+ * `.gitignore` is honoured because git itself does the walking.
28
+ *
29
+ * The one side effect is loose blob objects for the changed files in the object
30
+ * store; `git gc` reclaims them and they are invisible to every other command.
31
+ *
32
+ * @returns base and head to diff, or `undefined` when there is no HEAD yet.
33
+ */
34
+ export declare function snapshotWorktree(cwd: string): Promise<{
35
+ baseSha: string;
36
+ headSha: string;
37
+ } | undefined>;
38
+ export interface ReviewOptions {
39
+ /** Directory inside the repository to review. */
40
+ cwd: string;
41
+ scope?: ReviewScope;
42
+ /** Exact check names to run; when set it overrides {@link ReviewOptions.deep}. */
43
+ only?: string[];
44
+ /** Include heavy and network checks (typecheck, grype, spellcheck, audit…). */
45
+ deep?: boolean;
46
+ /**
47
+ * Let formatters and `--fix` rewrite files. Off everywhere except `apply_fixes`;
48
+ * a review that edits the code it is reviewing has stopped being a review.
49
+ */
50
+ write?: boolean;
51
+ }
52
+ export interface ReviewOutcome {
53
+ report: CheckReport;
54
+ scope: ReviewScope;
55
+ /** Files the diff touches; absent for `full`, which has no diff. */
56
+ changedFiles?: number;
57
+ /** Set when the requested scope could not be built and a fallback ran instead. */
58
+ note?: string;
59
+ }
60
+ /** Run one review, read-only and silent: never writes files, never posts a comment. */
61
+ export declare function review(opts: ReviewOptions): Promise<ReviewOutcome>;
62
+ /** Files in the working-tree diff, with a content hash, so a later call can tell what a fixer rewrote. */
63
+ export declare function fingerprintChanges(cwd: string): Promise<Map<string, string>>;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.REVIEW_SCOPES = void 0;
7
+ exports.fastCheckNames = fastCheckNames;
8
+ exports.resolveRepoRoot = resolveRepoRoot;
9
+ exports.snapshotWorktree = snapshotWorktree;
10
+ exports.review = review;
11
+ exports.fingerprintChanges = fingerprintChanges;
12
+ /**
13
+ * Running the gate on behalf of an AI agent — the part the MCP server and the
14
+ * commit hook share.
15
+ *
16
+ * Both exist for the same reason: an agent has not committed yet. The CLI's own
17
+ * scopes answer other questions — `precommit` looks at the index, which an agent
18
+ * that never ran `git add` leaves empty, and the default branch scope looks at
19
+ * commits, which is exactly what has not happened. So this module adds the scope
20
+ * an agent actually needs: *everything I have changed so far*.
21
+ */
22
+ const node_crypto_1 = require("node:crypto");
23
+ const node_fs_1 = __importDefault(require("node:fs"));
24
+ const node_os_1 = __importDefault(require("node:os"));
25
+ const node_path_1 = __importDefault(require("node:path"));
26
+ const loader_js_1 = require("../config/loader.js");
27
+ const registry_js_1 = require("../core/registry.js");
28
+ const run_checks_js_1 = require("../core/run-checks.js");
29
+ const execa_js_1 = require("../utils/execa.js");
30
+ exports.REVIEW_SCOPES = ['worktree', 'staged', 'branch', 'full'];
31
+ /**
32
+ * Checks that need the network. A tool call that waits on a registry is a tool
33
+ * call the agent stops trusting, so they sit behind `deep` with the heavy ones.
34
+ */
35
+ const NETWORK_CHECKS = new Set(['NPM Audit', 'Outdated Deps']);
36
+ /**
37
+ * The checks cheap enough to run after every edit: nothing heavy, nothing that
38
+ * talks to a registry. Derived from the registry rather than listed, so a check
39
+ * added next month is in the loop from day one unless it declares itself heavy.
40
+ */
41
+ function fastCheckNames() {
42
+ return registry_js_1.REGISTRY.filter(c => c.weight !== 'heavy' && !NETWORK_CHECKS.has(c.name)).map(c => c.name);
43
+ }
44
+ /** The repository containing `dir`, or `undefined` when it is not inside one. */
45
+ async function resolveRepoRoot(dir) {
46
+ try {
47
+ const { stdout } = await (0, execa_js_1.execa)('git', ['rev-parse', '--show-toplevel'], { cwd: dir });
48
+ return stdout.trim() || undefined;
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ }
54
+ /**
55
+ * Turn the working tree into a git tree object without touching the real index.
56
+ *
57
+ * `git add -A` against a scratch copy of the index, then `write-tree`: the result
58
+ * is the same kind of object `precommit` diffs against, so every delta-aware check
59
+ * works unchanged. The agent's staging area stays exactly as they left it, and
60
+ * `.gitignore` is honoured because git itself does the walking.
61
+ *
62
+ * The one side effect is loose blob objects for the changed files in the object
63
+ * store; `git gc` reclaims them and they are invisible to every other command.
64
+ *
65
+ * @returns base and head to diff, or `undefined` when there is no HEAD yet.
66
+ */
67
+ async function snapshotWorktree(cwd) {
68
+ let baseSha;
69
+ try {
70
+ baseSha = (await (0, execa_js_1.execa)('git', ['rev-parse', 'HEAD'], { cwd })).stdout.trim();
71
+ }
72
+ catch {
73
+ return undefined;
74
+ }
75
+ const scratch = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'pr-checkmate-index-'));
76
+ try {
77
+ const indexPath = (await (0, execa_js_1.execa)('git', ['rev-parse', '--git-path', 'index'], { cwd })).stdout;
78
+ const realIndex = node_path_1.default.resolve(cwd, indexPath.trim());
79
+ const scratchIndex = node_path_1.default.join(scratch, 'index');
80
+ if (node_fs_1.default.existsSync(realIndex))
81
+ node_fs_1.default.copyFileSync(realIndex, scratchIndex);
82
+ const env = { GIT_INDEX_FILE: scratchIndex };
83
+ await (0, execa_js_1.execa)('git', ['add', '-A'], { cwd, env });
84
+ const headSha = (await (0, execa_js_1.execa)('git', ['write-tree'], { cwd, env })).stdout.trim();
85
+ return { baseSha, headSha };
86
+ }
87
+ finally {
88
+ node_fs_1.default.rmSync(scratch, { recursive: true, force: true });
89
+ }
90
+ }
91
+ /** Run one review, read-only and silent: never writes files, never posts a comment. */
92
+ async function review(opts) {
93
+ const scope = opts.scope ?? 'worktree';
94
+ const only = opts.only && opts.only.length > 0 ? opts.only : opts.deep ? undefined : fastCheckNames();
95
+ const base = {
96
+ cwd: opts.cwd,
97
+ config: (0, loader_js_1.loadProjectConfig)(opts.cwd),
98
+ only,
99
+ reporter: 'none',
100
+ write: opts.write === true,
101
+ };
102
+ if (scope === 'full')
103
+ return { report: await (0, run_checks_js_1.runChecks)({ ...base, full: true }), scope };
104
+ if (scope === 'staged')
105
+ return { report: await (0, run_checks_js_1.runChecks)({ ...base, staged: true }), scope };
106
+ if (scope === 'branch')
107
+ return { report: await (0, run_checks_js_1.runChecks)(base), scope };
108
+ const range = await snapshotWorktree(opts.cwd);
109
+ if (!range) {
110
+ // A repository with no commit has no base to diff against. Falling back to a
111
+ // full scan keeps the file-based checks useful, and the note says so.
112
+ return {
113
+ report: await (0, run_checks_js_1.runChecks)({ ...base, full: true }),
114
+ scope: 'full',
115
+ note: 'no commits yet, so there is no diff to review; scanned every tracked file instead',
116
+ };
117
+ }
118
+ const changedFiles = (await (0, execa_js_1.execa)('git', ['diff', '--name-only', range.baseSha, range.headSha], { cwd: opts.cwd })).stdout
119
+ .split('\n')
120
+ .filter(Boolean).length;
121
+ // Nothing edited yet: say so instead of printing a green report that reads as a
122
+ // verdict on code nobody wrote.
123
+ if (changedFiles === 0) {
124
+ return {
125
+ report: { results: [], summary: { passed: 0, warned: 0, failed: 0, skipped: 0 }, ok: true },
126
+ scope,
127
+ changedFiles,
128
+ };
129
+ }
130
+ return {
131
+ report: await (0, run_checks_js_1.runChecks)({ ...base, baseSha: range.baseSha, headSha: range.headSha }),
132
+ scope,
133
+ changedFiles,
134
+ };
135
+ }
136
+ /** Files in the working-tree diff, with a content hash, so a later call can tell what a fixer rewrote. */
137
+ async function fingerprintChanges(cwd) {
138
+ const range = await snapshotWorktree(cwd);
139
+ const files = new Map();
140
+ if (!range)
141
+ return files;
142
+ const { stdout } = await (0, execa_js_1.execa)('git', ['diff', '--name-only', range.baseSha, range.headSha], {
143
+ cwd,
144
+ });
145
+ for (const name of stdout.split('\n').filter(Boolean)) {
146
+ const abs = node_path_1.default.join(cwd, name);
147
+ files.set(name, node_fs_1.default.existsSync(abs) ? (0, node_crypto_1.createHash)('sha1').update(node_fs_1.default.readFileSync(abs)).digest('hex') : '');
148
+ }
149
+ return files;
150
+ }
@@ -0,0 +1,8 @@
1
+ import { type ServerDefinition } from './protocol.js';
2
+ /**
3
+ * Run the server until the client closes stdin.
4
+ *
5
+ * Resolves once every request already received has been answered, so a client
6
+ * that sends a call and immediately closes the pipe still gets its response.
7
+ */
8
+ export declare function serve(server: ServerDefinition, input?: NodeJS.ReadableStream): Promise<void>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.serve = serve;
7
+ /**
8
+ * The stdio transport: one JSON message per line in, one per line out.
9
+ *
10
+ * stdout belongs to the protocol. A single stray byte on it — a log line, a
11
+ * progress message from a tool a check spawned — corrupts the stream and the
12
+ * client drops the server with an error that never mentions the cause. So the
13
+ * first thing `serve` does is take the real `stdout.write` for itself and point
14
+ * everything else at stderr, which MCP clients capture as the server's log.
15
+ */
16
+ const node_readline_1 = __importDefault(require("node:readline"));
17
+ const execa_js_1 = require("../utils/execa.js");
18
+ const index_js_1 = require("../utils/index.js");
19
+ const protocol_js_1 = require("./protocol.js");
20
+ /**
21
+ * Run the server until the client closes stdin.
22
+ *
23
+ * Resolves once every request already received has been answered, so a client
24
+ * that sends a call and immediately closes the pipe still gets its response.
25
+ */
26
+ async function serve(server, input = process.stdin) {
27
+ const protocolWrite = process.stdout.write.bind(process.stdout);
28
+ const toStderr = ((chunk) => process.stderr.write(chunk));
29
+ process.stdout.write = toStderr;
30
+ (0, execa_js_1.isolateInheritedStdio)();
31
+ index_js_1.logger.setSink({
32
+ out: chunk => void process.stderr.write(chunk),
33
+ err: chunk => void process.stderr.write(chunk),
34
+ });
35
+ const pending = new Set();
36
+ const lines = node_readline_1.default.createInterface({ input, crlfDelay: Infinity });
37
+ lines.on('line', line => {
38
+ if (!line.trim())
39
+ return;
40
+ const job = (0, protocol_js_1.handleLine)(server, line).then(response => {
41
+ if (response)
42
+ protocolWrite(`${JSON.stringify(response)}\n`);
43
+ });
44
+ pending.add(job);
45
+ void job.finally(() => pending.delete(job));
46
+ });
47
+ await new Promise(resolve => lines.once('close', resolve));
48
+ await Promise.allSettled([...pending]);
49
+ }
@@ -0,0 +1,13 @@
1
+ import type { ToolDefinition } from './protocol.js';
2
+ export interface McpSettings {
3
+ /** `false` leaves the server connected but with no tools, resources or prompts. */
4
+ enabled: boolean;
5
+ /** Tool names to hide from the agent. Unknown names are ignored. */
6
+ disabledTools: string[];
7
+ /** Hide every tool that writes files (`apply_fixes`, `write_config`). */
8
+ readOnly: boolean;
9
+ }
10
+ /** The `mcp` block of the project's `pr-checkmate.json`, with defaults for whatever it leaves out. */
11
+ export declare function readMcpSettings(cwd: string): McpSettings;
12
+ /** The tools the agent may see under `settings`. */
13
+ export declare function allowedTools(tools: ToolDefinition[], settings: McpSettings): ToolDefinition[];
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readMcpSettings = readMcpSettings;
7
+ exports.allowedTools = allowedTools;
8
+ /**
9
+ * What a project allows the MCP server to do, read from the `mcp` block of its
10
+ * `pr-checkmate.json`.
11
+ *
12
+ * Read again on every request instead of once at startup: the server outlives any
13
+ * single edit, and a user who turns a tool off expects the next call to obey,
14
+ * not the next restart. A missing or unreadable file means the defaults, so a
15
+ * typo in the JSON never silently switches the server off.
16
+ *
17
+ * Kept free of other pr-checkmate modules (type imports only) because the run
18
+ * summary asks the same question, and a heavier import would loop back into it.
19
+ */
20
+ const node_fs_1 = __importDefault(require("node:fs"));
21
+ const node_path_1 = __importDefault(require("node:path"));
22
+ const DEFAULTS = { enabled: true, disabledTools: [], readOnly: false };
23
+ function isRecord(value) {
24
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
25
+ }
26
+ /** The `mcp` block of the project's `pr-checkmate.json`, with defaults for whatever it leaves out. */
27
+ function readMcpSettings(cwd) {
28
+ try {
29
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(cwd, 'pr-checkmate.json'), 'utf-8'));
30
+ const block = isRecord(parsed) ? parsed.mcp : undefined;
31
+ if (!isRecord(block))
32
+ return DEFAULTS;
33
+ return {
34
+ enabled: block.enabled !== false,
35
+ disabledTools: Array.isArray(block.disabledTools)
36
+ ? block.disabledTools.filter((n) => typeof n === 'string')
37
+ : [],
38
+ readOnly: block.readOnly === true,
39
+ };
40
+ }
41
+ catch {
42
+ return DEFAULTS;
43
+ }
44
+ }
45
+ /** The tools the agent may see under `settings`. */
46
+ function allowedTools(tools, settings) {
47
+ if (!settings.enabled)
48
+ return [];
49
+ return tools.filter(t => !settings.disabledTools.includes(t.name) && !(settings.readOnly && t.writes));
50
+ }
@@ -0,0 +1,23 @@
1
+ import type { CheckReport } from '../core/types.js';
2
+ import type { ServerDefinition, ToolResult } from './protocol.js';
3
+ import { type ReviewScope } from './review.js';
4
+ /**
5
+ * Render a report for a model: findings first, nothing for what passed.
6
+ *
7
+ * A passing check is the expected case and carries no information the agent can
8
+ * act on, so only the count survives. Skips stay because one of them can mean "the
9
+ * tooling never arrived" — a hole in coverage the agent should be able to see.
10
+ */
11
+ export declare function formatReport(report: CheckReport, meta: {
12
+ scope: ReviewScope;
13
+ changedFiles?: number;
14
+ note?: string;
15
+ advice?: string;
16
+ }): ToolResult;
17
+ /**
18
+ * The server definition `serve` hands to the protocol layer.
19
+ *
20
+ * What it offers is decided per access, from the project's `mcp` settings, so a
21
+ * change to `pr-checkmate.json` takes effect on the agent's next request.
22
+ */
23
+ export declare function createServerDefinition(cwd?: string): ServerDefinition;