pr-checkmate 1.21.15 → 1.21.16

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
@@ -4,6 +4,13 @@
4
4
  ![npm](https://img.shields.io/npm/v/pr-checkmate?color=blue&label=npm)
5
5
  ![License](https://img.shields.io/badge/license-Proprietary-lightgrey)
6
6
 
7
+ **Bundled tools:**
8
+ ![ESLint](https://img.shields.io/badge/ESLint-9-4B32C3?logo=eslint&logoColor=white)
9
+ ![Prettier](https://img.shields.io/badge/Prettier-3-F7B93E?logo=prettier&logoColor=white)
10
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)
11
+ ![cspell](https://img.shields.io/badge/cspell-10-4285F4)
12
+ ![jscpd](https://img.shields.io/badge/jscpd-3.5-blue)
13
+
7
14
  > **PR CheckMate** is a universal PR quality checker that runs as a single `npm install` and works across **TypeScript, JavaScript, Python, C++, Swift, and Android/Kotlin** repositories — no per-language toolchain setup required.
8
15
 
9
16
  ---
@@ -280,7 +287,7 @@ In GitHub Actions PR context, language checks automatically switch to **delta mo
280
287
 
281
288
  After `npx pr-checkmate all`, a summary table is posted to the PR. On repeated pushes the comment is updated in place — no duplicate comments.
282
289
 
283
- ```
290
+ ```text
284
291
  ## PR CheckMate Summary
285
292
 
286
293
  11 passed · 3 warnings · 0 failed · 0 skipped
package/cspell.json CHANGED
@@ -189,6 +189,16 @@
189
189
  "sultansadikov",
190
190
  "telaviv",
191
191
  "travis",
192
+ "kotlin",
193
+ "Werror",
194
+ "positiving",
195
+ "pyflakes",
196
+ "pycodestyle",
197
+ "swiftlint",
198
+ "pathspec",
199
+ "Pipfile",
200
+ "conanfile",
201
+ "ktlint",
192
202
  "Términos",
193
203
  "Teudat",
194
204
  "Tiempo",
@@ -543,7 +553,145 @@
543
553
  "Makefiles",
544
554
  "dpdm",
545
555
  "commitlintrc",
546
- "shortstat"
556
+ "shortstat",
557
+ "Cooldown",
558
+ "SCRIPTERROR",
559
+ "attendace",
560
+ "attendance",
561
+ "kupat",
562
+ "gemel",
563
+ "hishtalmut",
564
+ "keren",
565
+ "tofes",
566
+ "Bdikat",
567
+ "Hishuv",
568
+ "formcontrolname",
569
+ "zrufot",
570
+ "Klali",
571
+ "contenteditable",
572
+ "האניקומב",
573
+ "ishur",
574
+ "Yeshut",
575
+ "Nihul",
576
+ "pratim",
577
+ "nispahim",
578
+ "networkidle",
579
+ "domcontentloaded",
580
+ "secapp",
581
+ "taxinfo",
582
+ "gmishurim",
583
+ "ezor",
584
+ "ishi",
585
+ "ishurim",
586
+ "nikui",
587
+ "mekabel",
588
+ "shdochshana",
589
+ "gershayim",
590
+ "Rashum",
591
+ "Shem",
592
+ "Mishpaha",
593
+ "Rshum",
594
+ "Prati",
595
+ "Ktovet",
596
+ "Doar",
597
+ "Megurim",
598
+ "ddlkidomet",
599
+ "Aher",
600
+ "Esek",
601
+ "Isuk",
602
+ "Mazav",
603
+ "Mish",
604
+ "Beyachad",
605
+ "Shlichat",
606
+ "Neezarti",
607
+ "Doch",
608
+ "Klalim",
609
+ "Hehzer",
610
+ "Ozer",
611
+ "נייע",
612
+ "recognised",
613
+ "קולומייץ",
614
+ "אירינה",
615
+ "סרגיי",
616
+ "פולישוק",
617
+ "ולדימיר",
618
+ "moov",
619
+ "virtualenv",
620
+ "pytesseract",
621
+ "qrdet",
622
+ "Jsons",
623
+ "easyocr",
624
+ "EXIF",
625
+ "fliplr",
626
+ "noautorotate",
627
+ "timestep",
628
+ "VAQH",
629
+ "DSBL",
630
+ "MAXU",
631
+ "XCUI",
632
+ "Apks",
633
+ "bootstatus",
634
+ "BEGINSWITH",
635
+ "anypassword",
636
+ "wrongpassword",
637
+ "xmark",
638
+ "didnt",
639
+ "Anthropic",
640
+ "bartowski",
641
+ "specialisation",
642
+ "cursorrules",
643
+ "deepseek",
644
+ "GGUF",
645
+ "hf",
646
+ "llm",
647
+ "machdep",
648
+ "migrat",
649
+ "modelcontextprotocol",
650
+ "mxbai",
651
+ "noheader",
652
+ "codedb",
653
+ "nomic",
654
+ "nounits",
655
+ "nvidia",
656
+ "ollama",
657
+ "qwen",
658
+ "shellcheck",
659
+ "standards",
660
+ "vram",
661
+ "ATATT",
662
+ "ATSTT",
663
+ "AUTOINCREMENT",
664
+ "BEAGENT",
665
+ "CEREBRAS",
666
+ "cpucore",
667
+ "Fingerprinter",
668
+ "FREELLM",
669
+ "ircular",
670
+ "integrat",
671
+ "LOOKBACK",
672
+ "markitdown",
673
+ "memswap",
674
+ "nknown",
675
+ "processlist",
676
+ "unixepoch",
677
+ "unreviewed",
678
+ "xapp",
679
+ "bpoa",
680
+ "installroot",
681
+ "dedups",
682
+ "behaviourally",
683
+ "normalised",
684
+ "раннера",
685
+ "раннере",
686
+ "пайплайн",
687
+ "dind",
688
+ "dlib",
689
+ "pugixml",
690
+ "opencvcommit",
691
+ "litert",
692
+ "SHTPB",
693
+ "FUTX",
694
+ "subdirs"
547
695
  ],
548
696
  "ignorePaths": [
549
697
  "**/node_modules/**",
@@ -163,6 +163,10 @@ function getDefaultConfig() {
163
163
  'test',
164
164
  'build',
165
165
  ],
166
+ // Skip release PRs and squash-merge titles that don't follow conventional
167
+ // commits (e.g. "Release 15 (#114)"). commitlint already ignores literal
168
+ // "Merge"/"Revert" messages, but not GitHub squash-merge PR titles.
169
+ ignores: ['^Release\\b', '^chore\\(release\\):'],
166
170
  },
167
171
  licenseCheck: {
168
172
  allowPackages: [...constants_1.DEFAULT_ALLOW_PACKAGES],
@@ -36,14 +36,21 @@ const DEFAULT_TYPES = [
36
36
  'test',
37
37
  'build',
38
38
  ];
39
- function buildTempConfig(types) {
40
- const config = {
41
- // Resolve via Node module resolution — robust to npm hoisting in the client.
42
- extends: [require.resolve('@commitlint/config-conventional')],
43
- rules: { 'type-enum': [2, 'always', types] },
44
- };
45
- const tmpFile = node_path_1.default.join(node_os_1.default.tmpdir(), `.commitlint-${process.pid}.json`);
46
- node_fs_1.default.writeFileSync(tmpFile, JSON.stringify(config, null, 2));
39
+ const DEFAULT_IGNORES = ['^Release\\b', '^chore\\(release\\):'];
40
+ function buildTempConfig(types, ignores) {
41
+ // Emit a CJS module so `ignores` can hold functions — commitlint requires
42
+ // predicate functions there, which a JSON config cannot express.
43
+ // Resolve config-conventional via Node module resolution — robust to hoisting.
44
+ const configConventional = require.resolve('@commitlint/config-conventional');
45
+ const content = `const patterns = ${JSON.stringify(ignores)}.map((p) => new RegExp(p, 'i'));
46
+ module.exports = {
47
+ extends: [${JSON.stringify(configConventional)}],
48
+ rules: { 'type-enum': [2, 'always', ${JSON.stringify(types)}] },
49
+ ignores: [(commit) => patterns.some((re) => re.test(commit))],
50
+ };
51
+ `;
52
+ const tmpFile = node_path_1.default.join(node_os_1.default.tmpdir(), `.commitlint-${process.pid}.cjs`);
53
+ node_fs_1.default.writeFileSync(tmpFile, content);
47
54
  return tmpFile;
48
55
  }
49
56
  exports.commitlintCheck = {
@@ -65,7 +72,8 @@ exports.commitlintCheck = {
65
72
  }
66
73
  else {
67
74
  const types = ctx.config.commitlint?.types ?? DEFAULT_TYPES;
68
- tempConfigFile = buildTempConfig(types);
75
+ const ignores = ctx.config.commitlint?.ignores ?? DEFAULT_IGNORES;
76
+ tempConfigFile = buildTempConfig(types, ignores);
69
77
  args.push('--config', tempConfigFile);
70
78
  }
71
79
  const result = await (0, execa_1.execa)(process.execPath, [commitlintBin, ...args], {
@@ -99,6 +99,46 @@ const SECURITY_PATTERNS = [
99
99
  severity: 'error',
100
100
  },
101
101
  ];
102
+ /** Bucket added-line matches by severity, keyed by pattern label. */
103
+ function scanDiff(stdout) {
104
+ const errors = {};
105
+ const warnings = {};
106
+ for (const line of stdout.split('\n')) {
107
+ for (const { pattern, label, severity } of SECURITY_PATTERNS) {
108
+ if (pattern.test(line)) {
109
+ const bucket = severity === 'error' ? errors : warnings;
110
+ (bucket[label] ?? (bucket[label] = [])).push(line.slice(1).trim().substring(0, 120));
111
+ }
112
+ }
113
+ }
114
+ return { errors, warnings };
115
+ }
116
+ /** Log up to 3 sample lines per label at the given severity. */
117
+ function logFindings(bucket, isError) {
118
+ const emit = isError ? utils_1.logger.error.bind(utils_1.logger) : utils_1.logger.warn.bind(utils_1.logger);
119
+ const icon = isError ? '❌' : '⚠️';
120
+ for (const [label, occurrences] of Object.entries(bucket)) {
121
+ emit(`[Diff Security]: ${icon} ${label}: ${occurrences.length} occurrence(s)`);
122
+ occurrences.slice(0, 3).forEach(l => emit(` ${l}`));
123
+ if (occurrences.length > 3)
124
+ emit(` ... and ${occurrences.length - 3} more`);
125
+ }
126
+ }
127
+ /** Report findings and produce the check outcome. */
128
+ function reportFindings(errors, warnings) {
129
+ const totalErrors = Object.values(errors).reduce((s, a) => s + a.length, 0);
130
+ const totalWarnings = Object.values(warnings).reduce((s, a) => s + a.length, 0);
131
+ if (totalErrors === 0 && totalWarnings === 0)
132
+ return (0, types_1.pass)();
133
+ logFindings(errors, true);
134
+ logFindings(warnings, false);
135
+ const parts = [];
136
+ if (totalErrors > 0)
137
+ parts.push(`${totalErrors} issue(s)`);
138
+ if (totalWarnings > 0)
139
+ parts.push(`${totalWarnings} warning(s)`);
140
+ return (0, types_1.warn)(parts.join(', '));
141
+ }
102
142
  exports.diffSecurityCheck = {
103
143
  name: 'Diff Security',
104
144
  phase: 'informational',
@@ -121,38 +161,8 @@ exports.diffSecurityCheck = {
121
161
  '*.mjs',
122
162
  '*.cjs',
123
163
  ], { cwd: ctx.cwd });
124
- const errors = {};
125
- const warnings = {};
126
- for (const line of stdout.split('\n')) {
127
- for (const { pattern, label, severity } of SECURITY_PATTERNS) {
128
- if (pattern.test(line)) {
129
- const bucket = severity === 'error' ? errors : warnings;
130
- (bucket[label] ?? (bucket[label] = [])).push(line.slice(1).trim().substring(0, 120));
131
- }
132
- }
133
- }
134
- const totalErrors = Object.values(errors).reduce((s, a) => s + a.length, 0);
135
- const totalWarnings = Object.values(warnings).reduce((s, a) => s + a.length, 0);
136
- if (totalErrors === 0 && totalWarnings === 0)
137
- return (0, types_1.pass)();
138
- for (const [label, occurrences] of Object.entries(errors)) {
139
- utils_1.logger.error(`[Diff Security]: ❌ ${label}: ${occurrences.length} occurrence(s)`);
140
- occurrences.slice(0, 3).forEach(l => utils_1.logger.error(` ${l}`));
141
- if (occurrences.length > 3)
142
- utils_1.logger.error(` ... and ${occurrences.length - 3} more`);
143
- }
144
- for (const [label, occurrences] of Object.entries(warnings)) {
145
- utils_1.logger.warn(`[Diff Security]: ⚠️ ${label}: ${occurrences.length} occurrence(s)`);
146
- occurrences.slice(0, 3).forEach(l => utils_1.logger.warn(` ${l}`));
147
- if (occurrences.length > 3)
148
- utils_1.logger.warn(` ... and ${occurrences.length - 3} more`);
149
- }
150
- const parts = [];
151
- if (totalErrors > 0)
152
- parts.push(`${totalErrors} issue(s)`);
153
- if (totalWarnings > 0)
154
- parts.push(`${totalWarnings} warning(s)`);
155
- return (0, types_1.warn)(parts.join(', '));
164
+ const { errors, warnings } = scanDiff(stdout);
165
+ return reportFindings(errors, warnings);
156
166
  }
157
167
  catch (err) {
158
168
  utils_1.logger.warn(`[Diff Security]: ⚠️ Could not analyze diff\n${err}`);
@@ -26,7 +26,7 @@ exports.duplicateCheck = {
26
26
  return (0, types_1.skip)('no relevant files changed');
27
27
  const srcPath = node_path_1.default.join(ctx.cwd, 'src');
28
28
  const targetPaths = changedFiles ?? (node_fs_1.default.existsSync(srcPath) ? [srcPath] : [ctx.cwd]);
29
- const jscpdBin = (0, utils_1.resolvePackageBin)('jscpd', 'bin/jscpd');
29
+ const jscpdBin = (0, utils_1.resolvePackageBin)('jscpd', 'run-jscpd.js');
30
30
  try {
31
31
  const result = await (0, execa_1.execa)(process.execPath, [
32
32
  jscpdBin,
@@ -38,7 +38,6 @@ exports.duplicateCheck = {
38
38
  'console',
39
39
  '--ignore',
40
40
  'node_modules/**,dist/**,.git/**',
41
- '--quiet',
42
41
  ...targetPaths,
43
42
  ], { cwd: ctx.cwd, reject: false });
44
43
  const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
@@ -24,6 +24,11 @@ exports.licenseCheck = {
24
24
  async run(ctx) {
25
25
  const allowPackages = new Set(ctx.config.licenseCheck?.allowPackages ?? []);
26
26
  const allowLicenses = ctx.config.licenseCheck?.allowLicenses ?? [];
27
+ // pr-checkmate installs into the consumer's node_modules, so its own runtime
28
+ // deps (eslint plugins, cspell dictionaries, …) leak into the client's
29
+ // production tree. Those are our dependencies, not the client's — never fail
30
+ // a client's PR because of a license we ourselves pulled in.
31
+ const ownDeps = (0, utils_1.getOwnDependencyClosure)();
27
32
  // Resolve the bundled license-checker binary and run it via the current node — never
28
33
  // shell out to `npx`, which fails (exit 127) when license-checker isn't on the path.
29
34
  let licenseCheckerBin;
@@ -44,7 +49,8 @@ exports.licenseCheck = {
44
49
  }
45
50
  const violations = [];
46
51
  for (const [pkg, info] of Object.entries(licenses)) {
47
- if (allowPackages.has(bareName(pkg)))
52
+ const name = bareName(pkg);
53
+ if (allowPackages.has(name) || ownDeps.has(name))
48
54
  continue;
49
55
  const raw = info.licenses ?? '';
50
56
  const license = Array.isArray(raw) ? raw.join(' OR ') : raw;
@@ -87,19 +87,17 @@ exports.markdownLintCheck = {
87
87
  '.claude/**',
88
88
  ...(userConfig.ignores ?? []),
89
89
  ];
90
- const gitignorePath = node_path_1.default.join(ctx.cwd, '.gitignore');
91
- const tmpIgnoreFile = node_path_1.default.join(node_os_1.default.tmpdir(), `.markdownlintignore-${process.pid}`);
92
- const gitignoreContent = node_fs_1.default.existsSync(gitignorePath)
93
- ? node_fs_1.default.readFileSync(gitignorePath, 'utf-8') + '\n'
94
- : '';
95
- node_fs_1.default.writeFileSync(tmpIgnoreFile, gitignoreContent + defaultIgnorePatterns.join('\n') + '\n');
96
- const args = [
97
- ...(ctx.write ? ['--fix'] : []),
98
- ...targets,
99
- ...configArg,
100
- '--ignore-path',
101
- tmpIgnoreFile,
102
- ];
90
+ // markdownlint-cli2 has no --ignore-path flag; exclusions are expressed as
91
+ // negated globs (`#pattern`). A bare directory name also needs `#name/**`
92
+ // to exclude its contents.
93
+ const ignoreGlobs = defaultIgnorePatterns.flatMap(pattern => {
94
+ const clean = pattern.replace(/\/+$/, '');
95
+ const globs = [`#${clean}`];
96
+ if (!clean.includes('*'))
97
+ globs.push(`#${clean}/**`);
98
+ return globs;
99
+ });
100
+ const args = [...(ctx.write ? ['--fix'] : []), ...targets, ...ignoreGlobs, ...configArg];
103
101
  try {
104
102
  const result = await (0, execa_1.execa)(process.execPath, [markdownlintBin, ...args], {
105
103
  cwd: ctx.cwd,
@@ -111,8 +109,6 @@ exports.markdownLintCheck = {
111
109
  finally {
112
110
  if (tempConfigFile && node_fs_1.default.existsSync(tempConfigFile))
113
111
  node_fs_1.default.unlinkSync(tempConfigFile);
114
- if (node_fs_1.default.existsSync(tmpIgnoreFile))
115
- node_fs_1.default.unlinkSync(tmpIgnoreFile);
116
112
  }
117
113
  },
118
114
  };
@@ -53,6 +53,45 @@ const SENSITIVE_CATEGORIES = [
53
53
  severity: 'warn',
54
54
  },
55
55
  ];
56
+ /** Bucket changed files by category severity, keyed by category label. */
57
+ function categorizeFiles(changedFiles) {
58
+ var _a;
59
+ const errors = {};
60
+ const warnings = {};
61
+ for (const filePath of changedFiles) {
62
+ for (const category of SENSITIVE_CATEGORIES) {
63
+ if (category.patterns.some(p => p.test(filePath))) {
64
+ const bucket = category.severity === 'error' ? errors : warnings;
65
+ (bucket[_a = category.label] ?? (bucket[_a] = [])).push(filePath);
66
+ }
67
+ }
68
+ }
69
+ return { errors, warnings };
70
+ }
71
+ /** Log every file per label at the given severity. */
72
+ function logFiles(bucket, isError) {
73
+ const emit = isError ? utils_1.logger.error.bind(utils_1.logger) : utils_1.logger.warn.bind(utils_1.logger);
74
+ const icon = isError ? '❌' : '⚠️';
75
+ for (const [label, files] of Object.entries(bucket)) {
76
+ emit(`[Sensitive Files]: ${icon} ${label}:`);
77
+ files.forEach(f => emit(` ${f}`));
78
+ }
79
+ }
80
+ /** Report categorized files and produce the check outcome. */
81
+ function reportFiles(errors, warnings) {
82
+ const totalErrors = Object.values(errors).reduce((s, a) => s + a.length, 0);
83
+ const totalWarnings = Object.values(warnings).reduce((s, a) => s + a.length, 0);
84
+ if (totalErrors === 0 && totalWarnings === 0)
85
+ return (0, types_1.pass)();
86
+ logFiles(errors, true);
87
+ logFiles(warnings, false);
88
+ const parts = [];
89
+ if (totalErrors > 0)
90
+ parts.push(`${totalErrors} sensitive file(s) changed`);
91
+ if (totalWarnings > 0)
92
+ parts.push(`${totalWarnings} notable file(s) changed`);
93
+ return (0, types_1.warn)(parts.join(', '));
94
+ }
56
95
  exports.sensitiveFileGuardCheck = {
57
96
  name: 'Sensitive Files',
58
97
  phase: 'informational',
@@ -60,40 +99,13 @@ exports.sensitiveFileGuardCheck = {
60
99
  return ctx.config.sensitiveFileGuard?.enabled !== false && Boolean(ctx.baseSha);
61
100
  },
62
101
  async run(ctx) {
63
- var _a;
64
102
  try {
65
103
  const { stdout } = await (0, execa_1.execa)('git', ['diff', '--name-only', '--diff-filter=ACMRD', ctx.baseSha, 'HEAD'], { cwd: ctx.cwd });
66
104
  const changedFiles = stdout.split('\n').filter(Boolean);
67
105
  if (changedFiles.length === 0)
68
106
  return (0, types_1.pass)();
69
- const errors = {};
70
- const warnings = {};
71
- for (const filePath of changedFiles) {
72
- for (const category of SENSITIVE_CATEGORIES) {
73
- if (category.patterns.some(p => p.test(filePath))) {
74
- const bucket = category.severity === 'error' ? errors : warnings;
75
- (bucket[_a = category.label] ?? (bucket[_a] = [])).push(filePath);
76
- }
77
- }
78
- }
79
- const totalErrors = Object.values(errors).reduce((s, a) => s + a.length, 0);
80
- const totalWarnings = Object.values(warnings).reduce((s, a) => s + a.length, 0);
81
- if (totalErrors === 0 && totalWarnings === 0)
82
- return (0, types_1.pass)();
83
- for (const [label, files] of Object.entries(errors)) {
84
- utils_1.logger.error(`[Sensitive Files]: ❌ ${label}:`);
85
- files.forEach(f => utils_1.logger.error(` ${f}`));
86
- }
87
- for (const [label, files] of Object.entries(warnings)) {
88
- utils_1.logger.warn(`[Sensitive Files]: ⚠️ ${label}:`);
89
- files.forEach(f => utils_1.logger.warn(` ${f}`));
90
- }
91
- const parts = [];
92
- if (totalErrors > 0)
93
- parts.push(`${totalErrors} sensitive file(s) changed`);
94
- if (totalWarnings > 0)
95
- parts.push(`${totalWarnings} notable file(s) changed`);
96
- return (0, types_1.warn)(parts.join(', '));
107
+ const { errors, warnings } = categorizeFiles(changedFiles);
108
+ return reportFiles(errors, warnings);
97
109
  }
98
110
  catch (err) {
99
111
  utils_1.logger.warn(`[Sensitive Files]: ⚠️ Could not check file changes\n${err}`);
@@ -7,27 +7,27 @@ const commitlint_1 = require("./checks/commitlint");
7
7
  const cpp_format_1 = require("./checks/cpp-format");
8
8
  const dead_code_1 = require("./checks/dead-code");
9
9
  const dependency_check_1 = require("./checks/dependency-check");
10
+ const diff_security_1 = require("./checks/diff-security");
10
11
  const diff_smell_1 = require("./checks/diff-smell");
12
+ const duplicate_check_1 = require("./checks/duplicate-check");
13
+ const kotlin_lint_1 = require("./checks/kotlin-lint");
11
14
  const license_check_1 = require("./checks/license-check");
12
15
  const lint_1 = require("./checks/lint");
16
+ const markdown_lint_1 = require("./checks/markdown-lint");
13
17
  const npm_audit_1 = require("./checks/npm-audit");
14
18
  // informational — warn/skip only, never fail the run
15
- const diff_security_1 = require("./checks/diff-security");
16
19
  const outdated_deps_1 = require("./checks/outdated-deps");
17
20
  const pr_body_1 = require("./checks/pr-body");
18
21
  const pr_size_check_1 = require("./checks/pr-size-check");
19
22
  const pr_title_lint_1 = require("./checks/pr-title-lint");
23
+ const prettier_1 = require("./checks/prettier");
24
+ const python_format_1 = require("./checks/python-format");
25
+ const python_lint_1 = require("./checks/python-lint");
26
+ const security_1 = require("./checks/security");
20
27
  const sensitive_file_guard_1 = require("./checks/sensitive-file-guard");
21
- const duplicate_check_1 = require("./checks/duplicate-check");
22
- const markdown_lint_1 = require("./checks/markdown-lint");
23
28
  const spellcheck_1 = require("./checks/spellcheck");
24
- const python_lint_1 = require("./checks/python-lint");
25
29
  const swift_lint_1 = require("./checks/swift-lint");
26
- const kotlin_lint_1 = require("./checks/kotlin-lint");
27
30
  // format — runs last, may write files
28
- const prettier_1 = require("./checks/prettier");
29
- const python_format_1 = require("./checks/python-format");
30
- const security_1 = require("./checks/security");
31
31
  const typecheck_1 = require("./checks/typecheck");
32
32
  /**
33
33
  * Every check, in declaration order. The orchestrator groups them by `phase`
@@ -14,6 +14,8 @@ const STATUS_ICON = {
14
14
  };
15
15
  // Unique marker so we can find and update the comment on re-runs.
16
16
  const COMMENT_MARKER = '<!-- pr-checkmate-summary -->';
17
+ const SUMMARY_BORDER = '═══════════════════════════════════════════════════════════════';
18
+ const SUMMARY_RULE = '───────────────────────────────────────────────────────────────';
17
19
  function getPrContext() {
18
20
  const token = process.env.GITHUB_TOKEN;
19
21
  const repository = process.env.GITHUB_REPOSITORY;
@@ -57,16 +59,16 @@ function buildMarkdownReport(report) {
57
59
  function printStdout(report) {
58
60
  const { passed, warned, failed, skipped } = report.summary;
59
61
  utils_1.logger.info('');
60
- utils_1.logger.info('═══════════════════════════════════════════════════════════════');
62
+ utils_1.logger.info(SUMMARY_BORDER);
61
63
  utils_1.logger.info(' PR CheckMate Summary');
62
- utils_1.logger.info('═══════════════════════════════════════════════════════════════');
64
+ utils_1.logger.info(SUMMARY_BORDER);
63
65
  for (const r of report.results) {
64
66
  const details = r.details ? `: ${r.details}` : '';
65
67
  utils_1.logger.info(` ${STATUS_ICON[r.status]} ${r.name}${details}`);
66
68
  }
67
- utils_1.logger.info('───────────────────────────────────────────────────────────────');
69
+ utils_1.logger.info(SUMMARY_RULE);
68
70
  utils_1.logger.info(` ✅ ${passed} passed ⚠️ ${warned} warnings ❌ ${failed} failed ⏭️ ${skipped} skipped`);
69
- utils_1.logger.info('═══════════════════════════════════════════════════════════════');
71
+ utils_1.logger.info(SUMMARY_BORDER);
70
72
  }
71
73
  async function upsertGithubComment(body) {
72
74
  const ctx = getPrContext();
package/dist/init.js CHANGED
@@ -59,105 +59,124 @@ const DEFAULT_CONFIG = {
59
59
  },
60
60
  documentation: config_1.documentation,
61
61
  };
62
- /**
63
- *
64
- */
65
- async function init() {
66
- const root = process.cwd();
67
- if (!fs_1.default.existsSync(exports.CONFIG_FILE)) {
68
- const answers = await inquirer_1.default.prompt([
69
- {
70
- type: 'list',
71
- name: 'sourcePath',
72
- message: 'Where is your project source code?',
73
- choices: ['src', 'app', 'Enter manually'],
74
- },
75
- {
76
- type: 'input',
77
- name: 'manualPath',
78
- message: 'Enter path:',
79
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
- when: (a) => a.sourcePath === 'Enter manually',
81
- validate: (input) => input.trim().length > 0 || 'Cannot be empty',
82
- },
83
- ]);
84
- const sourcePath = answers.sourcePath === 'Enter manually' ? answers.manualPath : answers.sourcePath;
62
+ const MANUAL_ENTRY = 'Enter manually';
63
+ /** Prompt the user for the project source path, allowing a manual entry. */
64
+ async function promptSourcePath() {
65
+ const answers = await inquirer_1.default.prompt([
66
+ {
67
+ type: 'list',
68
+ name: 'sourcePath',
69
+ message: 'Where is your project source code?',
70
+ choices: ['src', 'app', MANUAL_ENTRY],
71
+ },
72
+ {
73
+ type: 'input',
74
+ name: 'manualPath',
75
+ message: 'Enter path:',
76
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
+ when: (a) => a.sourcePath === MANUAL_ENTRY,
78
+ validate: (input) => input.trim().length > 0 || 'Cannot be empty',
79
+ },
80
+ ]);
81
+ return answers.sourcePath === MANUAL_ENTRY ? answers.manualPath : answers.sourcePath;
82
+ }
83
+ /** Copy the bundled GitHub Actions workflow into the consumer repo. */
84
+ function setupWorkflow(root) {
85
+ const workflowSource = path_1.default.join(__dirname, 'config', 'pr-checkmate-workflow.yml');
86
+ const workflowDir = path_1.default.join(root, '.github', 'workflows');
87
+ const workflowDest = path_1.default.join(workflowDir, 'pr-checkmate.yml');
88
+ if (!fs_1.default.existsSync(workflowSource)) {
89
+ utils_1.logger.warn('[init]: ⚠️ Workflow template not found, skipping workflow setup');
90
+ return;
91
+ }
92
+ if (!fs_1.default.existsSync(workflowDir)) {
93
+ fs_1.default.mkdirSync(workflowDir, { recursive: true });
94
+ }
95
+ fs_1.default.copyFileSync(workflowSource, workflowDest);
96
+ utils_1.logger.info('[init]: ✅ Created .github/workflows/pr-checkmate.yml');
97
+ utils_1.logger.info('[init]: GitHub Actions workflow is ready for your PRs!');
98
+ }
99
+ /** Create a tsconfig.json scaffold if the consumer repo lacks one. */
100
+ function setupTsConfig(root, sourcePath) {
101
+ const tsconfigPath = path_1.default.join(root, 'tsconfig.json');
102
+ if (fs_1.default.existsSync(tsconfigPath))
103
+ return;
104
+ const newTsConfig = {
105
+ compilerOptions: {
106
+ target: 'ES2020',
107
+ module: 'NodeNext',
108
+ outDir: 'dist',
109
+ strict: true,
110
+ rootDir: sourcePath,
111
+ moduleResolution: 'NodeNext',
112
+ esModuleInterop: true,
113
+ resolveJsonModule: true,
114
+ forceConsistentCasingInFileNames: true,
115
+ skipLibCheck: true,
116
+ types: ['node'],
117
+ },
118
+ include: [`${sourcePath}/**/*.ts`],
119
+ exclude: ['node_modules', 'dist'],
120
+ };
121
+ fs_1.default.writeFileSync(tsconfigPath, JSON.stringify(newTsConfig, null, 2));
122
+ utils_1.logger.info('[init]: ✅ Created tsconfig.json using your sourcePath');
123
+ }
124
+ /** First-run setup: prompt, write config, and scaffold workflow + tsconfig. */
125
+ async function createConfig(root) {
126
+ const sourcePath = await promptSourcePath();
127
+ const languages = await (0, utils_1.detectLanguages)(root);
128
+ utils_1.logger.info(`[init]: 🔍 Detected languages: ${languages.length ? languages.join(', ') : 'none'}`);
129
+ const config = {
130
+ ...DEFAULT_CONFIG,
131
+ sourcePath,
132
+ languages,
133
+ };
134
+ fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2));
135
+ utils_1.logger.info(`[init]: ✅ Created pr-checkmate.json with sourcePath: ${sourcePath}`);
136
+ utils_1.logger.info(`[init]: 💡 You can now customize ESLint rules, Prettier settings,
137
+ and cspell words in pr-checkmate.json`);
138
+ setupWorkflow(root);
139
+ setupTsConfig(root, sourcePath);
140
+ }
141
+ /** Re-run setup: merge the existing config with current defaults. */
142
+ async function updateConfig(root) {
143
+ utils_1.logger.info('[init]: pr-checkmate.json already exists — updating it.');
144
+ try {
145
+ const raw = fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
146
+ const json = JSON.parse(raw);
85
147
  const languages = await (0, utils_1.detectLanguages)(root);
86
148
  utils_1.logger.info(`[init]: 🔍 Detected languages: ${languages.length ? languages.join(', ') : 'none'}`);
87
- const config = {
149
+ // Merge with defaults to add new fields
150
+ const updated = {
88
151
  ...DEFAULT_CONFIG,
89
- sourcePath,
152
+ ...json,
153
+ // Refresh detected languages on every init so new source dirs are picked up
90
154
  languages,
155
+ eslint: { ...DEFAULT_CONFIG.eslint, ...(json.eslint || {}) },
156
+ prettier: { ...DEFAULT_CONFIG.prettier, ...(json.prettier || {}) },
157
+ cspell: { ...DEFAULT_CONFIG.cspell, ...(json.cspell || {}) },
158
+ typecheck: { ...DEFAULT_CONFIG.typecheck, ...(json.typecheck || {}) },
159
+ diffSmell: { ...DEFAULT_CONFIG.diffSmell, ...(json.diffSmell || {}) },
160
+ prBody: { ...DEFAULT_CONFIG.prBody, ...(json.prBody || {}) },
161
+ duplicate: { ...DEFAULT_CONFIG.duplicate, ...(json.duplicate || {}) },
91
162
  };
92
- fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2));
93
- utils_1.logger.info(`[init]: ✅ Created pr-checkmate.json with sourcePath: ${sourcePath}`);
94
- utils_1.logger.info(`[init]: 💡 You can now customize ESLint rules, Prettier settings,
95
- and cspell words in pr-checkmate.json`);
96
- // Copy workflow file to .github/workflows/
97
- const workflowSource = path_1.default.join(__dirname, 'config', 'pr-checkmate-workflow.yml');
98
- const workflowDir = path_1.default.join(root, '.github', 'workflows');
99
- const workflowDest = path_1.default.join(workflowDir, 'pr-checkmate.yml');
100
- if (fs_1.default.existsSync(workflowSource)) {
101
- if (!fs_1.default.existsSync(workflowDir)) {
102
- fs_1.default.mkdirSync(workflowDir, { recursive: true });
103
- }
104
- fs_1.default.copyFileSync(workflowSource, workflowDest);
105
- utils_1.logger.info('[init]: ✅ Created .github/workflows/pr-checkmate.yml');
106
- utils_1.logger.info('[init]: GitHub Actions workflow is ready for your PRs!');
107
- }
108
- else {
109
- utils_1.logger.warn('[init]: ⚠️ Workflow template not found, skipping workflow setup');
110
- }
111
- const tsconfigPath = path_1.default.join(root, 'tsconfig.json');
112
- if (!fs_1.default.existsSync(tsconfigPath)) {
113
- const newTsConfig = {
114
- compilerOptions: {
115
- target: 'ES2020',
116
- module: 'NodeNext',
117
- outDir: 'dist',
118
- strict: true,
119
- rootDir: sourcePath,
120
- moduleResolution: 'NodeNext',
121
- esModuleInterop: true,
122
- resolveJsonModule: true,
123
- forceConsistentCasingInFileNames: true,
124
- skipLibCheck: true,
125
- types: ['node'],
126
- },
127
- include: [`${sourcePath}/**/*.ts`],
128
- exclude: ['node_modules', 'dist'],
129
- };
130
- fs_1.default.writeFileSync(tsconfigPath, JSON.stringify(newTsConfig, null, 2));
131
- utils_1.logger.info('[init]: ✅ Created tsconfig.json using your sourcePath');
132
- }
163
+ fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(updated, null, 2));
164
+ utils_1.logger.info('[init]: 🔧 Updated pr-checkmate.json with customization options.');
165
+ utils_1.logger.info('[init]: 💡 You can now add custom ESLint rules, cspell words, and more!');
166
+ }
167
+ catch (e) {
168
+ utils_1.logger.error(`[init]: ❌ Failed to update pr-checkmate.json: ${e}`);
169
+ }
170
+ }
171
+ /**
172
+ * Initialize (or refresh) pr-checkmate configuration in the current project.
173
+ */
174
+ async function init() {
175
+ const root = process.cwd();
176
+ if (fs_1.default.existsSync(exports.CONFIG_FILE)) {
177
+ await updateConfig(root);
133
178
  }
134
179
  else {
135
- utils_1.logger.info('[init]: pr-checkmate.json already exists — updating it.');
136
- try {
137
- const raw = fs_1.default.readFileSync(exports.CONFIG_FILE, 'utf-8');
138
- const json = JSON.parse(raw);
139
- const languages = await (0, utils_1.detectLanguages)(root);
140
- utils_1.logger.info(`[init]: 🔍 Detected languages: ${languages.length ? languages.join(', ') : 'none'}`);
141
- // Merge with defaults to add new fields
142
- const updated = {
143
- ...DEFAULT_CONFIG,
144
- ...json,
145
- // Refresh detected languages on every init so new source dirs are picked up
146
- languages,
147
- eslint: { ...DEFAULT_CONFIG.eslint, ...(json.eslint || {}) },
148
- prettier: { ...DEFAULT_CONFIG.prettier, ...(json.prettier || {}) },
149
- cspell: { ...DEFAULT_CONFIG.cspell, ...(json.cspell || {}) },
150
- typecheck: { ...DEFAULT_CONFIG.typecheck, ...(json.typecheck || {}) },
151
- diffSmell: { ...DEFAULT_CONFIG.diffSmell, ...(json.diffSmell || {}) },
152
- prBody: { ...DEFAULT_CONFIG.prBody, ...(json.prBody || {}) },
153
- duplicate: { ...DEFAULT_CONFIG.duplicate, ...(json.duplicate || {}) },
154
- };
155
- fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(updated, null, 2));
156
- utils_1.logger.info('[init]: 🔧 Updated pr-checkmate.json with customization options.');
157
- utils_1.logger.info('[init]: 💡 You can now add custom ESLint rules, cspell words, and more!');
158
- }
159
- catch (e) {
160
- utils_1.logger.error(`[init]: ❌ Failed to update pr-checkmate.json: ${e}`);
161
- }
180
+ await createConfig(root);
162
181
  }
163
182
  }
@@ -24,6 +24,7 @@ __exportStar(require("./logger"), exports);
24
24
  __exportStar(require("./delta"), exports);
25
25
  __exportStar(require("./result-collector"), exports);
26
26
  __exportStar(require("./resolve-bin"), exports);
27
+ __exportStar(require("./own-deps"), exports);
27
28
  __exportStar(require("./detect-language"), exports);
28
29
  /**
29
30
  *
@@ -0,0 +1,113 @@
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.getOwnDependencyClosure = getOwnDependencyClosure;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const OWN_PACKAGE_NAME = 'pr-checkmate';
10
+ const PACKAGE_JSON = 'package.json';
11
+ let cachedClosure;
12
+ /** Read a package.json's `dependencies` keys, tolerating missing/broken files. */
13
+ function readDependencyNames(pkgDir) {
14
+ try {
15
+ const pkg = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(pkgDir, PACKAGE_JSON), 'utf-8'));
16
+ return Object.keys(pkg.dependencies ?? {});
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ }
22
+ /** Walk up from this module until we find pr-checkmate's own package root. */
23
+ function findOwnPackageRoot() {
24
+ let dir = __dirname;
25
+ while (dir !== node_path_1.default.dirname(dir)) {
26
+ const pkgJsonPath = node_path_1.default.join(dir, PACKAGE_JSON);
27
+ if (node_fs_1.default.existsSync(pkgJsonPath)) {
28
+ try {
29
+ const pkg = JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
30
+ if (pkg.name === OWN_PACKAGE_NAME)
31
+ return dir;
32
+ }
33
+ catch { }
34
+ }
35
+ dir = node_path_1.default.dirname(dir);
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Resolve a dependency's install directory by name, starting the search from
41
+ * `fromDir`. Relies on Node's upward node_modules walk so hoisted packages are
42
+ * found regardless of physical layout.
43
+ */
44
+ function resolvePackageDir(name, fromDir) {
45
+ // Prefer the explicit package.json subpath.
46
+ try {
47
+ return node_path_1.default.dirname(require.resolve(`${name}/package.json`, { paths: [fromDir] }));
48
+ }
49
+ catch { }
50
+ // Fallback for packages whose exports map hides ./package.json: resolve the
51
+ // entry point and walk up to the package's own root.
52
+ try {
53
+ let dir = node_path_1.default.dirname(require.resolve(name, { paths: [fromDir] }));
54
+ while (dir !== node_path_1.default.dirname(dir)) {
55
+ const pkgJsonPath = node_path_1.default.join(dir, PACKAGE_JSON);
56
+ if (node_fs_1.default.existsSync(pkgJsonPath)) {
57
+ try {
58
+ const pkg = JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
59
+ if (pkg.name === name)
60
+ return dir;
61
+ }
62
+ catch { }
63
+ }
64
+ dir = node_path_1.default.dirname(dir);
65
+ }
66
+ }
67
+ catch { }
68
+ return null;
69
+ }
70
+ /**
71
+ * Compute the full set of package names in pr-checkmate's own dependency
72
+ * closure (direct + transitive runtime dependencies), plus pr-checkmate itself.
73
+ *
74
+ * pr-checkmate installs into the consumer's node_modules, so its own runtime
75
+ * deps (eslint plugins, cspell dictionaries, …) become part of the consumer's
76
+ * production dependency tree. Some carry copyleft licenses that would otherwise
77
+ * trip pr-checkmate's own license check against an innocent client. These are
78
+ * pr-checkmate's dependencies, not the client's, so they must be exempt.
79
+ *
80
+ * Result is cached for the process. Returns a set containing only pr-checkmate
81
+ * itself if the package root cannot be located (defensive; never throws).
82
+ */
83
+ function getOwnDependencyClosure() {
84
+ if (cachedClosure)
85
+ return cachedClosure;
86
+ const closure = new Set([OWN_PACKAGE_NAME]);
87
+ const root = findOwnPackageRoot();
88
+ if (!root) {
89
+ cachedClosure = closure;
90
+ return closure;
91
+ }
92
+ // BFS over the dependency graph, resolving each package relative to the parent
93
+ // that declared it so nested (non-hoisted) installs resolve correctly.
94
+ const queue = readDependencyNames(root).map(name => ({
95
+ name,
96
+ fromDir: root,
97
+ }));
98
+ while (queue.length > 0) {
99
+ const { name, fromDir } = queue.shift();
100
+ if (closure.has(name))
101
+ continue;
102
+ closure.add(name);
103
+ const depDir = resolvePackageDir(name, fromDir);
104
+ if (!depDir)
105
+ continue;
106
+ for (const child of readDependencyNames(depDir)) {
107
+ if (!closure.has(child))
108
+ queue.push({ name: child, fromDir: depDir });
109
+ }
110
+ }
111
+ cachedClosure = closure;
112
+ return closure;
113
+ }
@@ -14,6 +14,9 @@ const node_path_1 = __importDefault(require("node:path"));
14
14
  * 2. Via package.json — for packages that export ./package.json but not the bin subpath (e.g. eslint)
15
15
  * 3. Walk up from main entry — for packages that export neither (e.g. cspell)
16
16
  *
17
+ * @param pkgName - The package whose binary to resolve (e.g. `eslint`).
18
+ * @param binRelPath - The bin path relative to the package root (e.g. `bin/eslint.js`).
19
+ * @param options - Resolution options.
17
20
  * @param options.paths - Directories to resolve from (passed to `require.resolve`).
18
21
  * Use `[process.cwd()]` to prefer the consumer project's copy of the package.
19
22
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pr-checkmate",
3
- "version": "1.21.15",
3
+ "version": "1.21.16",
4
4
  "description": "Automated PR quality checks: linting, formatting, dependency analysis, and spellcheck",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -71,7 +71,7 @@
71
71
  "@typescript-eslint/eslint-plugin": "^8.47.0",
72
72
  "@typescript-eslint/parser": "^8.47.0",
73
73
  "@wasm-fmt/clang-format": "^22.1.8",
74
- "cspell": "^9.3.2",
74
+ "cspell": "^10.0.1",
75
75
  "depcheck": "^1.4.7",
76
76
  "eslint": "^9.26.0",
77
77
  "eslint-config-prettier": "^10.1.8",
@@ -81,8 +81,8 @@
81
81
  "eslint-plugin-sonarjs": "^4.1.0",
82
82
  "execa": "^9.6.0",
83
83
  "gitleaks-secret-scanner-fixed": "^2.1.3",
84
- "inquirer": "^13.0.1",
85
- "jscpd": "^3.5.10",
84
+ "inquirer": "^14.0.2",
85
+ "jscpd": "^5.0.11",
86
86
  "license-checker": "^25.0.1",
87
87
  "markdownlint-cli2": "^0.23.0",
88
88
  "prettier": ">=3.0.0",
@@ -189,6 +189,16 @@
189
189
  "sultansadikov",
190
190
  "telaviv",
191
191
  "travis",
192
+ "kotlin",
193
+ "Werror",
194
+ "positiving",
195
+ "pyflakes",
196
+ "pycodestyle",
197
+ "swiftlint",
198
+ "pathspec",
199
+ "Pipfile",
200
+ "conanfile",
201
+ "ktlint",
192
202
  "Términos",
193
203
  "Teudat",
194
204
  "Tiempo",
@@ -543,7 +553,145 @@
543
553
  "Makefiles",
544
554
  "dpdm",
545
555
  "commitlintrc",
546
- "shortstat"
556
+ "shortstat",
557
+ "Cooldown",
558
+ "SCRIPTERROR",
559
+ "attendace",
560
+ "attendance",
561
+ "kupat",
562
+ "gemel",
563
+ "hishtalmut",
564
+ "keren",
565
+ "tofes",
566
+ "Bdikat",
567
+ "Hishuv",
568
+ "formcontrolname",
569
+ "zrufot",
570
+ "Klali",
571
+ "contenteditable",
572
+ "האניקומב",
573
+ "ishur",
574
+ "Yeshut",
575
+ "Nihul",
576
+ "pratim",
577
+ "nispahim",
578
+ "networkidle",
579
+ "domcontentloaded",
580
+ "secapp",
581
+ "taxinfo",
582
+ "gmishurim",
583
+ "ezor",
584
+ "ishi",
585
+ "ishurim",
586
+ "nikui",
587
+ "mekabel",
588
+ "shdochshana",
589
+ "gershayim",
590
+ "Rashum",
591
+ "Shem",
592
+ "Mishpaha",
593
+ "Rshum",
594
+ "Prati",
595
+ "Ktovet",
596
+ "Doar",
597
+ "Megurim",
598
+ "ddlkidomet",
599
+ "Aher",
600
+ "Esek",
601
+ "Isuk",
602
+ "Mazav",
603
+ "Mish",
604
+ "Beyachad",
605
+ "Shlichat",
606
+ "Neezarti",
607
+ "Doch",
608
+ "Klalim",
609
+ "Hehzer",
610
+ "Ozer",
611
+ "נייע",
612
+ "recognised",
613
+ "קולומייץ",
614
+ "אירינה",
615
+ "סרגיי",
616
+ "פולישוק",
617
+ "ולדימיר",
618
+ "moov",
619
+ "virtualenv",
620
+ "pytesseract",
621
+ "qrdet",
622
+ "Jsons",
623
+ "easyocr",
624
+ "EXIF",
625
+ "fliplr",
626
+ "noautorotate",
627
+ "timestep",
628
+ "VAQH",
629
+ "DSBL",
630
+ "MAXU",
631
+ "XCUI",
632
+ "Apks",
633
+ "bootstatus",
634
+ "BEGINSWITH",
635
+ "anypassword",
636
+ "wrongpassword",
637
+ "xmark",
638
+ "didnt",
639
+ "Anthropic",
640
+ "bartowski",
641
+ "specialisation",
642
+ "cursorrules",
643
+ "deepseek",
644
+ "GGUF",
645
+ "hf",
646
+ "llm",
647
+ "machdep",
648
+ "migrat",
649
+ "modelcontextprotocol",
650
+ "mxbai",
651
+ "noheader",
652
+ "codedb",
653
+ "nomic",
654
+ "nounits",
655
+ "nvidia",
656
+ "ollama",
657
+ "qwen",
658
+ "shellcheck",
659
+ "standards",
660
+ "vram",
661
+ "ATATT",
662
+ "ATSTT",
663
+ "AUTOINCREMENT",
664
+ "BEAGENT",
665
+ "CEREBRAS",
666
+ "cpucore",
667
+ "Fingerprinter",
668
+ "FREELLM",
669
+ "ircular",
670
+ "integrat",
671
+ "LOOKBACK",
672
+ "markitdown",
673
+ "memswap",
674
+ "nknown",
675
+ "processlist",
676
+ "unixepoch",
677
+ "unreviewed",
678
+ "xapp",
679
+ "bpoa",
680
+ "installroot",
681
+ "dedups",
682
+ "behaviourally",
683
+ "normalised",
684
+ "раннера",
685
+ "раннере",
686
+ "пайплайн",
687
+ "dind",
688
+ "dlib",
689
+ "pugixml",
690
+ "opencvcommit",
691
+ "litert",
692
+ "SHTPB",
693
+ "FUTX",
694
+ "subdirs"
547
695
  ],
548
696
  "ignorePaths": [
549
697
  "**/node_modules/**",