@kevinrabun/judges 3.119.0 → 3.122.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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/api.d.ts +2 -1
  3. package/dist/api.js +3 -1
  4. package/dist/cli-dispatch.d.ts +7 -0
  5. package/dist/cli-dispatch.js +654 -0
  6. package/dist/cli-formatters.d.ts +6 -0
  7. package/dist/cli-formatters.js +186 -0
  8. package/dist/cli.js +69 -4159
  9. package/dist/commands/baseline.js +2 -42
  10. package/dist/commands/coverage.js +3 -39
  11. package/dist/commands/diff.js +2 -38
  12. package/dist/commands/fix-pr.js +2 -23
  13. package/dist/commands/fix.js +3 -27
  14. package/dist/commands/llm-benchmark.d.ts +7 -0
  15. package/dist/commands/llm-benchmark.js +27 -1
  16. package/dist/commands/quality-gate.js +1 -12
  17. package/dist/commands/review-parallel.js +1 -19
  18. package/dist/commands/review.js +2 -33
  19. package/dist/commands/rule-test.js +1 -15
  20. package/dist/commands/tune.js +2 -29
  21. package/dist/commands/watch.js +3 -42
  22. package/dist/config.js +1 -1
  23. package/dist/evaluators/hallucination-detection.js +343 -0
  24. package/dist/evaluators/index.d.ts +2 -11
  25. package/dist/evaluators/index.js +3 -181
  26. package/dist/evaluators/security.js +226 -2
  27. package/dist/evaluators/suppressions.d.ts +49 -0
  28. package/dist/evaluators/suppressions.js +185 -0
  29. package/dist/ext-to-lang.d.ts +16 -0
  30. package/dist/ext-to-lang.js +60 -0
  31. package/dist/github-app.d.ts +1 -3
  32. package/dist/github-app.js +2 -34
  33. package/dist/parallel.js +2 -14
  34. package/dist/probabilistic/llm-response-validator.js +1 -1
  35. package/dist/reports/public-repo-report.js +9 -1
  36. package/dist/skill-loader.js +9 -6
  37. package/dist/tools/register-evaluation.js +2 -29
  38. package/package.json +1 -1
  39. package/server.json +2 -2
  40. package/src/skill-loader.ts +9 -6
@@ -9,50 +9,10 @@
9
9
  // ──────────────────────────────────────────────────────────────────────────────
10
10
  import { createHash } from "crypto";
11
11
  import { readFileSync, writeFileSync, existsSync } from "fs";
12
- import { resolve, extname, relative } from "path";
12
+ import { resolve, relative } from "path";
13
13
  import { evaluateWithTribunal } from "../evaluators/index.js";
14
14
  import { collectFiles } from "../cli.js";
15
- // ─── Language Detection ─────────────────────────────────────────────────────
16
- const EXT_TO_LANG = {
17
- ".ts": "typescript",
18
- ".tsx": "typescript",
19
- ".js": "javascript",
20
- ".jsx": "javascript",
21
- ".mjs": "javascript",
22
- ".cjs": "javascript",
23
- ".py": "python",
24
- ".rs": "rust",
25
- ".go": "go",
26
- ".java": "java",
27
- ".cs": "csharp",
28
- ".rb": "ruby",
29
- ".php": "php",
30
- ".swift": "swift",
31
- ".kt": "kotlin",
32
- ".scala": "scala",
33
- ".c": "c",
34
- ".cpp": "cpp",
35
- ".h": "c",
36
- ".hpp": "cpp",
37
- ".yaml": "yaml",
38
- ".yml": "yaml",
39
- ".json": "json",
40
- ".tf": "terraform",
41
- ".hcl": "terraform",
42
- ".sh": "bash",
43
- ".bash": "bash",
44
- ".ps1": "powershell",
45
- ".psm1": "powershell",
46
- ".dart": "dart",
47
- ".sql": "sql",
48
- ".bicep": "bicep",
49
- };
50
- function detectLanguage(filePath) {
51
- if (filePath.toLowerCase().includes("dockerfile"))
52
- return "dockerfile";
53
- const ext = extname(filePath.toLowerCase());
54
- return EXT_TO_LANG[ext];
55
- }
15
+ import { detectLanguageFromPath as detectLanguage } from "../ext-to-lang.js";
56
16
  // ─── Fingerprinting ────────────────────────────────────────────────────────
57
17
  // Hash ruleId + title + normalized surrounding source context so that findings
58
18
  // survive line-number shifts caused by unrelated edits.
@@ -6,42 +6,10 @@
6
6
  // const report = computeLanguageCoverage(fileList);
7
7
  // console.log(formatCoverageReport(report));
8
8
  // ──────────────────────────────────────────────────────────────────────────────
9
- import { extname } from "path";
10
9
  import { normalizeLanguage } from "../language-patterns.js";
11
10
  import { JUDGES } from "../judges/index.js";
12
- // ─── Extension → Language mapping (mirrors CLI) ─────────────────────────────
13
- const EXT_TO_LANG = {
14
- ".ts": "typescript",
15
- ".tsx": "typescript",
16
- ".js": "javascript",
17
- ".jsx": "javascript",
18
- ".mjs": "javascript",
19
- ".cjs": "javascript",
20
- ".py": "python",
21
- ".rs": "rust",
22
- ".go": "go",
23
- ".java": "java",
24
- ".cs": "csharp",
25
- ".rb": "ruby",
26
- ".php": "php",
27
- ".swift": "swift",
28
- ".kt": "kotlin",
29
- ".scala": "scala",
30
- ".c": "c",
31
- ".cpp": "cpp",
32
- ".h": "c",
33
- ".hpp": "cpp",
34
- ".yaml": "yaml",
35
- ".yml": "yaml",
36
- ".json": "json",
37
- ".tf": "terraform",
38
- ".hcl": "terraform",
39
- ".sh": "bash",
40
- ".bash": "bash",
41
- ".ps1": "powershell",
42
- ".psm1": "powershell",
43
- ".bicep": "bicep",
44
- };
11
+ // ─── Extension → Language mapping ────────────────────────────────────────────
12
+ import { detectLanguageFromPath } from "../ext-to-lang.js";
45
13
  /**
46
14
  * Languages for which the Judges Panel has first-class evaluator coverage.
47
15
  * A language is "covered" if normalizeLanguage returns a recognized LangFamily
@@ -70,11 +38,7 @@ const COVERED_LANGUAGES = new Set([
70
38
  * Detect language from file path extension.
71
39
  */
72
40
  export function detectFileLanguage(filePath) {
73
- const lower = filePath.toLowerCase();
74
- if (lower.endsWith("dockerfile") || lower.includes("dockerfile."))
75
- return "dockerfile";
76
- const ext = extname(lower);
77
- return EXT_TO_LANG[ext] ?? "unknown";
41
+ return detectLanguageFromPath(filePath) ?? "unknown";
78
42
  }
79
43
  /**
80
44
  * Count how many judges can evaluate a given language.
@@ -6,7 +6,7 @@
6
6
  // judges diff --file changes.patch --language typescript
7
7
  // ──────────────────────────────────────────────────────────────────────────────
8
8
  import { readFileSync, existsSync } from "fs";
9
- import { resolve, extname } from "path";
9
+ import { resolve } from "path";
10
10
  import { evaluateDiff } from "../evaluators/index.js";
11
11
  /**
12
12
  * Parse a unified diff into hunks with changed line information.
@@ -79,43 +79,7 @@ function parseUnifiedDiff(diffText) {
79
79
  return hunks;
80
80
  }
81
81
  // ─── Language Detection ─────────────────────────────────────────────────────
82
- const EXT_TO_LANG = {
83
- ".ts": "typescript",
84
- ".tsx": "typescript",
85
- ".js": "javascript",
86
- ".jsx": "javascript",
87
- ".mjs": "javascript",
88
- ".cjs": "javascript",
89
- ".py": "python",
90
- ".rs": "rust",
91
- ".go": "go",
92
- ".java": "java",
93
- ".cs": "csharp",
94
- ".rb": "ruby",
95
- ".php": "php",
96
- ".swift": "swift",
97
- ".kt": "kotlin",
98
- ".scala": "scala",
99
- ".c": "c",
100
- ".cpp": "cpp",
101
- ".h": "c",
102
- ".hpp": "cpp",
103
- ".yaml": "yaml",
104
- ".yml": "yaml",
105
- ".json": "json",
106
- ".tf": "terraform",
107
- ".hcl": "terraform",
108
- ".sh": "bash",
109
- ".bash": "bash",
110
- ".ps1": "powershell",
111
- ".psm1": "powershell",
112
- };
113
- function detectLanguage(filePath) {
114
- const ext = extname(filePath.toLowerCase());
115
- if (filePath.toLowerCase().includes("dockerfile"))
116
- return "dockerfile";
117
- return EXT_TO_LANG[ext];
118
- }
82
+ import { detectLanguageFromPath as detectLanguage } from "../ext-to-lang.js";
119
83
  // ─── Deletion Analysis ──────────────────────────────────────────────────────
120
84
  /**
121
85
  * Patterns that indicate security-relevant code. When these are removed
@@ -20,30 +20,9 @@ import { tmpdir } from "os";
20
20
  import { evaluateWithTribunal } from "../evaluators/index.js";
21
21
  import { applyPatches } from "./fix.js";
22
22
  import { parseGitHubRepo, runGit, tryRunGit } from "../tools/command-safety.js";
23
- // ─── Language Detection ─────────────────────────────────────────────────────
24
- const EXT_TO_LANG = {
25
- ".ts": "typescript",
26
- ".tsx": "typescript",
27
- ".js": "javascript",
28
- ".jsx": "javascript",
29
- ".mjs": "javascript",
30
- ".cjs": "javascript",
31
- ".py": "python",
32
- ".rs": "rust",
33
- ".go": "go",
34
- ".java": "java",
35
- ".cs": "csharp",
36
- ".cpp": "cpp",
37
- ".cc": "cpp",
38
- ".h": "c",
39
- ".hpp": "cpp",
40
- };
41
- const SUPPORTED_EXTENSIONS = new Set(Object.keys(EXT_TO_LANG));
23
+ import { detectLanguageFromPath, SUPPORTED_EXTENSIONS } from "../ext-to-lang.js";
42
24
  function detectLanguage(filePath) {
43
- const base = filePath.toLowerCase();
44
- if (base.endsWith("dockerfile") || base.includes("dockerfile."))
45
- return "dockerfile";
46
- return EXT_TO_LANG[extname(base)] || "typescript";
25
+ return detectLanguageFromPath(filePath) ?? "typescript";
47
26
  }
48
27
  // ─── File Collection ────────────────────────────────────────────────────────
49
28
  function collectFiles(dir, maxFiles = 200) {
@@ -10,36 +10,12 @@
10
10
  * judges fix src/app.ts --judge cyber # Fixes from one judge only
11
11
  */
12
12
  import { existsSync, readFileSync, writeFileSync } from "fs";
13
- import { resolve, extname } from "path";
13
+ import { resolve } from "path";
14
14
  import { evaluateWithTribunal, evaluateWithJudge } from "../evaluators/index.js";
15
15
  import { getJudge } from "../judges/index.js";
16
- // ─── Language Detection (shared with cli.ts) ────────────────────────────────
17
- const EXT_TO_LANG = {
18
- ".ts": "typescript",
19
- ".tsx": "typescript",
20
- ".js": "javascript",
21
- ".jsx": "javascript",
22
- ".mjs": "javascript",
23
- ".cjs": "javascript",
24
- ".py": "python",
25
- ".rs": "rust",
26
- ".go": "go",
27
- ".java": "java",
28
- ".cs": "csharp",
29
- ".cpp": "cpp",
30
- ".cc": "cpp",
31
- ".cxx": "cpp",
32
- ".h": "c",
33
- ".hpp": "cpp",
34
- ".ps1": "powershell",
35
- ".psm1": "powershell",
36
- };
16
+ import { detectLanguageFromPath } from "../ext-to-lang.js";
37
17
  function detectLanguage(filePath) {
38
- const base = filePath.toLowerCase();
39
- if (base.endsWith("dockerfile") || base.includes("dockerfile."))
40
- return "dockerfile";
41
- const ext = extname(base);
42
- return EXT_TO_LANG[ext] || "typescript";
18
+ return detectLanguageFromPath(filePath) ?? "typescript";
43
19
  }
44
20
  const SEVERITY_RANK = {
45
21
  critical: 5,
@@ -82,6 +82,13 @@ export interface LlmCaseResult {
82
82
  * Matches patterns like CYBER-001, SEC-003, AUTH-001, etc.
83
83
  */
84
84
  export declare function getValidRulePrefixes(): Set<string>;
85
+ /**
86
+ * Valid prefixes for tribunal mode — excludes meta-judges that are not
87
+ * included in the tribunal prompt (INTENT, COH, MFPR, FPR, OVER).
88
+ * Findings with these prefixes are hallucinated by the LLM and should
89
+ * not be scored.
90
+ */
91
+ export declare function getTribunalValidPrefixes(): Set<string>;
85
92
  export declare function parseLlmRuleIds(response: string): string[];
86
93
  /**
87
94
  * Preferred entrypoint: extract findings from raw LLM text with validation. Falls back to regex rule-id scan.
@@ -30,6 +30,15 @@ export const TRIBUNAL_JUDGES = JUDGES.filter((j) => !TRIBUNAL_EXCLUDED_PREFIXES.
30
30
  export function getValidRulePrefixes() {
31
31
  return new Set(JUDGES.map((j) => j.rulePrefix));
32
32
  }
33
+ /**
34
+ * Valid prefixes for tribunal mode — excludes meta-judges that are not
35
+ * included in the tribunal prompt (INTENT, COH, MFPR, FPR, OVER).
36
+ * Findings with these prefixes are hallucinated by the LLM and should
37
+ * not be scored.
38
+ */
39
+ export function getTribunalValidPrefixes() {
40
+ return new Set(TRIBUNAL_JUDGES.map((j) => j.rulePrefix));
41
+ }
33
42
  export function parseLlmRuleIds(response) {
34
43
  const validPrefixes = getValidRulePrefixes();
35
44
  const pattern = /\b([A-Z]{2,})-(\d{3})\b/g;
@@ -174,6 +183,14 @@ export function selectStratifiedSample(cases, targetSize) {
174
183
  * Returns a fully populated LlmCaseResult.
175
184
  */
176
185
  export function scoreLlmCase(tc, detectedRuleIds, rawResponse, tokensUsed) {
186
+ // ── Prefix-level FP deduplication ─────────────────────────────────────
187
+ // TPs are counted per-expected-rule using prefix matching: a single
188
+ // detected CYBER-xxx satisfies all expected CYBER-yyy rules.
189
+ // FPs should use symmetric counting: each erroneously-firing judge prefix
190
+ // counts as ONE false positive regardless of how many rule IDs the LLM
191
+ // generates for that prefix. This prevents verbose LLM output from
192
+ // inflating the FP metric (e.g. CYBER-001…005 on clean code = 1 FP,
193
+ // not 5).
177
194
  const detectedPrefixes = new Set(detectedRuleIds.map((r) => r.split("-")[0]));
178
195
  const matchedExpected = tc.expectedRuleIds.filter((expected) => {
179
196
  const prefix = expected.split("-")[0];
@@ -189,7 +206,7 @@ export function scoreLlmCase(tc, detectedRuleIds, rawResponse, tokensUsed) {
189
206
  // doesn't match any expected prefix (prevents silent over-reporting).
190
207
  const isCleanCase = tc.expectedRuleIds.length === 0;
191
208
  const expectedPrefixes = new Set(tc.expectedRuleIds.map((r) => r.split("-")[0]));
192
- const falsePositiveIds = isCleanCase
209
+ const falsePositiveIdsRaw = isCleanCase
193
210
  ? detectedRuleIds
194
211
  : tc.unexpectedRuleIds
195
212
  ? detectedRuleIds.filter((found) => {
@@ -200,6 +217,15 @@ export function scoreLlmCase(tc, detectedRuleIds, rawResponse, tokensUsed) {
200
217
  const prefix = found.split("-")[0];
201
218
  return !expectedPrefixes.has(prefix);
202
219
  });
220
+ // Deduplicate FPs by prefix — keep one representative rule ID per prefix
221
+ const fpPrefixSeen = new Set();
222
+ const falsePositiveIds = falsePositiveIdsRaw.filter((id) => {
223
+ const prefix = id.split("-")[0];
224
+ if (fpPrefixSeen.has(prefix))
225
+ return false;
226
+ fpPrefixSeen.add(prefix);
227
+ return true;
228
+ });
203
229
  const casePassed = isCleanCase ? falsePositiveIds.length === 0 : matchedExpected.length > 0;
204
230
  return {
205
231
  caseId: tc.id,
@@ -159,18 +159,7 @@ export function parseQualityGateConfig(obj) {
159
159
  return Object.keys(gates).length > 0 ? gates : undefined;
160
160
  }
161
161
  // ─── CLI Runner ─────────────────────────────────────────────────────────────
162
- const EXT_TO_LANG = {
163
- ".ts": "typescript",
164
- ".tsx": "typescript",
165
- ".js": "javascript",
166
- ".jsx": "javascript",
167
- ".py": "python",
168
- ".rs": "rust",
169
- ".go": "go",
170
- ".java": "java",
171
- ".cs": "csharp",
172
- ".cpp": "cpp",
173
- };
162
+ import { EXT_TO_LANG } from "../ext-to-lang.js";
174
163
  export function runQualityGate(argv) {
175
164
  if (argv.includes("--help") || argv.includes("-h")) {
176
165
  console.log(`
@@ -4,25 +4,7 @@
4
4
  import { readFileSync, existsSync, readdirSync } from "fs";
5
5
  import { join, extname } from "path";
6
6
  // ─── Language Detection ─────────────────────────────────────────────────────
7
- const EXT_TO_LANG = {
8
- ".ts": "typescript",
9
- ".tsx": "typescript",
10
- ".js": "javascript",
11
- ".jsx": "javascript",
12
- ".py": "python",
13
- ".java": "java",
14
- ".cs": "csharp",
15
- ".go": "go",
16
- ".rs": "rust",
17
- ".rb": "ruby",
18
- ".php": "php",
19
- ".cpp": "cpp",
20
- ".c": "c",
21
- ".swift": "swift",
22
- ".kt": "kotlin",
23
- ".scala": "scala",
24
- };
25
- const SOURCE_EXTS = new Set(Object.keys(EXT_TO_LANG));
7
+ import { SUPPORTED_EXTENSIONS as SOURCE_EXTS, EXT_TO_LANG } from "../ext-to-lang.js";
26
8
  // ─── File Discovery ─────────────────────────────────────────────────────────
27
9
  function discoverFiles(dir, maxFiles) {
28
10
  const files = [];
@@ -16,7 +16,7 @@
16
16
  import { execFileSync } from "child_process";
17
17
  import { readFileSync, writeFileSync, unlinkSync } from "fs";
18
18
  import { tmpdir } from "os";
19
- import { resolve, join, extname } from "path";
19
+ import { resolve, join } from "path";
20
20
  import { createHash } from "node:crypto";
21
21
  import { evaluateDiff, evaluateWithTribunal } from "../evaluators/index.js";
22
22
  import { evaluateProject } from "../evaluators/project.js";
@@ -63,38 +63,7 @@ function hashBody(body) {
63
63
  return createHash("sha1").update(body).digest("hex").slice(0, 8);
64
64
  }
65
65
  // ─── Language Detection ─────────────────────────────────────────────────────
66
- const EXT_TO_LANG = {
67
- ".ts": "typescript",
68
- ".tsx": "typescript",
69
- ".js": "javascript",
70
- ".jsx": "javascript",
71
- ".mjs": "javascript",
72
- ".cjs": "javascript",
73
- ".py": "python",
74
- ".rs": "rust",
75
- ".go": "go",
76
- ".java": "java",
77
- ".cs": "csharp",
78
- ".rb": "ruby",
79
- ".php": "php",
80
- ".swift": "swift",
81
- ".kt": "kotlin",
82
- ".tf": "terraform",
83
- ".hcl": "terraform",
84
- ".bicep": "bicep",
85
- ".sh": "bash",
86
- ".ps1": "powershell",
87
- ".c": "c",
88
- ".cpp": "cpp",
89
- ".h": "c",
90
- ".hpp": "cpp",
91
- };
92
- function detectLanguage(filePath) {
93
- const ext = extname(filePath.toLowerCase());
94
- if (filePath.toLowerCase().includes("dockerfile"))
95
- return "dockerfile";
96
- return EXT_TO_LANG[ext];
97
- }
66
+ import { detectLanguageFromPath as detectLanguage } from "../ext-to-lang.js";
98
67
  // ─── Severity Helpers ───────────────────────────────────────────────────────
99
68
  const SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
100
69
  function severityRank(s) {
@@ -34,21 +34,7 @@ function collectFiles(dirPath) {
34
34
  return results;
35
35
  }
36
36
  // ─── Language matching ──────────────────────────────────────────────────────
37
- const EXT_TO_LANG = {
38
- ".ts": "typescript",
39
- ".tsx": "typescript",
40
- ".js": "javascript",
41
- ".jsx": "javascript",
42
- ".py": "python",
43
- ".go": "go",
44
- ".rs": "rust",
45
- ".java": "java",
46
- ".cs": "csharp",
47
- ".cpp": "cpp",
48
- ".c": "c",
49
- ".rb": "ruby",
50
- ".php": "php",
51
- };
37
+ import { EXT_TO_LANG } from "../ext-to-lang.js";
52
38
  function matchesLanguage(filePath, languages) {
53
39
  if (!languages || languages.length === 0)
54
40
  return true;
@@ -14,37 +14,10 @@
14
14
  * judges tune --max-files 20 # Limit sample size
15
15
  */
16
16
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
17
- import { resolve, extname, join } from "path";
17
+ import { resolve, join } from "path";
18
18
  import { evaluateWithTribunal } from "../evaluators/index.js";
19
19
  import { PRESETS } from "../presets.js";
20
- // ─── Language Detection ─────────────────────────────────────────────────────
21
- const EXT_TO_LANG = {
22
- ".ts": "typescript",
23
- ".tsx": "typescript",
24
- ".js": "javascript",
25
- ".jsx": "javascript",
26
- ".mjs": "javascript",
27
- ".cjs": "javascript",
28
- ".py": "python",
29
- ".rs": "rust",
30
- ".go": "go",
31
- ".java": "java",
32
- ".cs": "csharp",
33
- ".rb": "ruby",
34
- ".php": "php",
35
- ".swift": "swift",
36
- ".kt": "kotlin",
37
- ".tf": "terraform",
38
- ".hcl": "terraform",
39
- ".bicep": "bicep",
40
- ".sh": "bash",
41
- };
42
- function detectLanguage(filePath) {
43
- const ext = extname(filePath.toLowerCase());
44
- if (filePath.toLowerCase().includes("dockerfile"))
45
- return "dockerfile";
46
- return EXT_TO_LANG[ext];
47
- }
20
+ import { detectLanguageFromPath as detectLanguage } from "../ext-to-lang.js";
48
21
  // ─── Framework Detection ────────────────────────────────────────────────────
49
22
  function detectFramework(dir) {
50
23
  const signals = [];
@@ -11,50 +11,11 @@ import { existsSync, readFileSync, statSync, watch as fsWatch } from "fs";
11
11
  import { resolve, extname, join, relative } from "path";
12
12
  import { evaluateWithTribunal, evaluateWithJudge } from "../evaluators/index.js";
13
13
  import { getJudge } from "../judges/index.js";
14
+ import { detectLanguageFromPath, SUPPORTED_EXTENSIONS } from "../ext-to-lang.js";
14
15
  // ─── Language Detection ─────────────────────────────────────────────────────
15
- const WATCH_EXTENSIONS = new Set([
16
- ".ts",
17
- ".tsx",
18
- ".js",
19
- ".jsx",
20
- ".mjs",
21
- ".cjs",
22
- ".py",
23
- ".rs",
24
- ".go",
25
- ".java",
26
- ".cs",
27
- ".cpp",
28
- ".cc",
29
- ".cxx",
30
- ".h",
31
- ".hpp",
32
- ".ps1",
33
- ".psm1",
34
- ]);
35
- const EXT_TO_LANG = {
36
- ".ts": "typescript",
37
- ".tsx": "typescript",
38
- ".js": "javascript",
39
- ".jsx": "javascript",
40
- ".mjs": "javascript",
41
- ".cjs": "javascript",
42
- ".py": "python",
43
- ".rs": "rust",
44
- ".go": "go",
45
- ".java": "java",
46
- ".cs": "csharp",
47
- ".cpp": "cpp",
48
- ".cc": "cpp",
49
- ".cxx": "cpp",
50
- ".h": "c",
51
- ".hpp": "cpp",
52
- ".ps1": "powershell",
53
- ".psm1": "powershell",
54
- };
16
+ const WATCH_EXTENSIONS = SUPPORTED_EXTENSIONS;
55
17
  function detectLanguage(filePath) {
56
- const ext = extname(filePath.toLowerCase());
57
- return EXT_TO_LANG[ext] || "typescript";
18
+ return detectLanguageFromPath(filePath) ?? "typescript";
58
19
  }
59
20
  export function parseWatchArgs(argv) {
60
21
  const args = {
package/dist/config.js CHANGED
@@ -12,7 +12,7 @@ import { matchGlobPath } from "./tools/command-safety.js";
12
12
  export function expandEnvPlaceholders(content) {
13
13
  if (!content)
14
14
  return content;
15
- return content.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
15
+ return content.replace(/\$\{([^}]{1,100})\}/g, (_match, varName) => {
16
16
  const envVal = process.env[varName];
17
17
  return envVal !== undefined ? envVal : "";
18
18
  });