From 4efba2208ae1e2c46413070e493643334cde7309 Mon Sep 17 00:00:00 2001 From: jimcody1995 Date: Sun, 5 Jul 2026 02:51:53 +0200 Subject: [PATCH] fix(signals): classify Vue/Svelte/Astro source files as code Align isCodeFile with review/rag.ts CODE_EXT_RE and review/visual/paths.ts so front-end framework source is counted as genuine code in slop signals, missing-tests checks, and the MCP local score preview mirrors. Co-authored-by: Cursor --- packages/gittensory-mcp/lib/local-branch.js | 2 +- .../scripts/gittensor-score-preview.mjs | 2 +- .../scripts/gittensor-score-preview.py | 2 +- src/signals/engine.ts | 7 ++--- src/signals/local-branch.ts | 8 +++--- .../local-branch-file-classifiers.test.ts | 5 ++++ test/unit/local-branch.test.ts | 5 ++++ test/unit/score-preview-script.test.ts | 26 +++++++++++++++++++ 8 files changed, 48 insertions(+), 9 deletions(-) diff --git a/packages/gittensory-mcp/lib/local-branch.js b/packages/gittensory-mcp/lib/local-branch.js index 05b09f94f2..0057e513a0 100644 --- a/packages/gittensory-mcp/lib/local-branch.js +++ b/packages/gittensory-mcp/lib/local-branch.js @@ -610,7 +610,7 @@ export function isTestFile(file) { } export function isCodeFile(file) { - return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m)$/i.test(file) && !isTestFile(file); + return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m|vue|svelte|astro)$/i.test(file) && !isTestFile(file); } function numberValue(value) { diff --git a/packages/gittensory-mcp/scripts/gittensor-score-preview.mjs b/packages/gittensory-mcp/scripts/gittensor-score-preview.mjs index af0d257ea9..1386435b5a 100644 --- a/packages/gittensory-mcp/scripts/gittensor-score-preview.mjs +++ b/packages/gittensory-mcp/scripts/gittensor-score-preview.mjs @@ -19,7 +19,7 @@ function isTestFile(file) { } function isCodeFile(file) { - return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m)$/i.test(file) && !isTestFile(file); + return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m|vue|svelte|astro)$/i.test(file) && !isTestFile(file); } function lineCount(file) { diff --git a/packages/gittensory-mcp/scripts/gittensor-score-preview.py b/packages/gittensory-mcp/scripts/gittensor-score-preview.py index fa5f1af88d..99bac2f271 100644 --- a/packages/gittensory-mcp/scripts/gittensor-score-preview.py +++ b/packages/gittensory-mcp/scripts/gittensor-score-preview.py @@ -142,7 +142,7 @@ def metadata_fallback(metadata: dict) -> dict: lines = max(int(entry.get("additions") or 0) + int(entry.get("deletions") or 0), 0) if is_test_file(path): tests += lines - elif lower_path.endswith((".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".py", ".rb", ".rs", ".go", ".java", ".kt", ".scala", ".sql", ".cs", ".swift", ".groovy", ".php", ".cpp", ".c", ".h", ".m")): + elif lower_path.endswith((".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".py", ".rb", ".rs", ".go", ".java", ".kt", ".scala", ".sql", ".cs", ".swift", ".groovy", ".php", ".cpp", ".c", ".h", ".m", ".vue", ".svelte", ".astro")): source += lines else: non_code += lines diff --git a/src/signals/engine.ts b/src/signals/engine.ts index fb8346461f..53a0d8d591 100644 --- a/src/signals/engine.ts +++ b/src/signals/engine.ts @@ -5538,10 +5538,11 @@ function sanitizeOutcomeDimensionKey(key: string): string { function isCodeFile(file: string): boolean { // Mirrors isCodeFile in local-branch.ts — kept in sync (cs/swift/groovy/php and C/C++/Objective-C added // so native/C#/Swift/Groovy/PHP source counts as code, matching the test conventions - // isTestPath already recognizes). + // isTestPath already recognizes; vue/svelte/astro match rag.ts + visual paths). return ( - /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m)$/i.test(file) && - !isTestFile(file) + /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m|vue|svelte|astro)$/i.test( + file, + ) && !isTestFile(file) ); } diff --git a/src/signals/local-branch.ts b/src/signals/local-branch.ts index 6ebede429c..64ca173ff3 100644 --- a/src/signals/local-branch.ts +++ b/src/signals/local-branch.ts @@ -1270,10 +1270,12 @@ export function isCodeFile(file: string): boolean { // cs/swift/groovy/php plus C/C++/Objective-C round out the native/JVM/.NET/Swift/PHP set: isTestPath already // recognizes their `SomethingTest(s)`/`Spec` test files, so their source must // count as code too — otherwise a C#/Swift/Groovy/PHP/native source file is neither test - // nor code in the local scorer. + // nor code in the local scorer. vue/svelte/astro align with review/rag.ts CODE_EXT_RE and + // review/visual/paths.ts so front-end framework source is not misclassified as "other". return ( - /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m)$/i.test(file) && - !isTestFile(file) + /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|c|h|m|vue|svelte|astro)$/i.test( + file, + ) && !isTestFile(file) ); } diff --git a/test/unit/local-branch-file-classifiers.test.ts b/test/unit/local-branch-file-classifiers.test.ts index 4fd9e37d11..392a23fb47 100644 --- a/test/unit/local-branch-file-classifiers.test.ts +++ b/test/unit/local-branch-file-classifiers.test.ts @@ -131,6 +131,11 @@ describe("isCodeFile", () => { "src/native/add.cpp", "include/native/add.h", "src/objc/View.m", + // Front-end framework source — already indexed as code by rag.ts and flagged + // as visual paths, but must count as code for slop/missing-tests signals. + "src/App.vue", + "src/Widget.svelte", + "src/pages/index.astro", ]) { expect(isCodeFile(path)).toBe(true); } diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts index d28c1b9ad1..0ba51a53e5 100644 --- a/test/unit/local-branch.test.ts +++ b/test/unit/local-branch.test.ts @@ -1774,6 +1774,11 @@ describe("local MCP git metadata collection", () => { expect(isTestFile(file)).toBe(false); expect(isCodeFile(file)).toBe(true); } + // Front-end framework source mirrors review/rag.ts and visual-path classifiers. + for (const file of ["src/App.vue", "src/Widget.svelte", "src/pages/index.astro"]) { + expect(isTestFile(file)).toBe(false); + expect(isCodeFile(file)).toBe(true); + } }); it("extracts linked issues only from standalone closing keywords, not keyword substrings", async () => { diff --git a/test/unit/score-preview-script.test.ts b/test/unit/score-preview-script.test.ts index c54b10eaa8..0c8703c351 100644 --- a/test/unit/score-preview-script.test.ts +++ b/test/unit/score-preview-script.test.ts @@ -132,6 +132,32 @@ describe("gittensor-score-preview.mjs classifier parity with the server", () => expect(py.nonCodeTokenScore).toBe(3); }); + it("classifies Vue/Svelte/Astro source as code in both .mjs and .py previews", () => { + // Parity with review/rag.ts CODE_EXT_RE and review/visual/paths.ts: front-end framework + // source must count as code, not non-code, in every mirrored classifier. + const files = [ + { path: "src/App.vue", additions: 6, deletions: 0 }, + { path: "src/Widget.svelte", additions: 4, deletions: 0 }, + { path: "src/pages/index.astro", additions: 5, deletions: 0 }, + { path: "README.md", additions: 2, deletions: 0 }, // non-code control + ]; + const mjs = runPreview(files); + expect(mjs.sourceTokenScore).toBe(15); + expect(mjs.testTokenScore).toBe(0); + expect(mjs.nonCodeTokenScore).toBe(2); + + const python = findPython(); + if (!python) return; + const env = { ...process.env }; + delete env.GITTENSOR_ROOT; + const res = spawnSync(python, [scriptPy], { input: JSON.stringify({ changedFiles: files }), encoding: "utf8", env }); + expect(res.status, res.stderr).toBe(0); + const py = JSON.parse(res.stdout); + expect(py.sourceTokenScore).toBe(15); + expect(py.testTokenScore).toBe(0); + expect(py.nonCodeTokenScore).toBe(2); + }); + it("classifies PascalCase PHP test files as tests in both .mjs and .py previews", () => { // Parity with src/signals/test-evidence.ts: PHPUnit/PHPSpec class-suffix files must not be counted as // PHP source simply because they live outside a conventional tests/ directory.