Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/gittensory-mcp/lib/local-branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ function isGeneratedCodeFile(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|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test(file) && !isTestFile(file) && !isGeneratedCodeFile(file);
return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|kts|scala|java|go|sql|cs|swift|groovy|php|cpp|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test(file) && !isTestFile(file) && !isGeneratedCodeFile(file);
}

function numberValue(value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function isGeneratedCodeFile(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|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test(file) && !isTestFile(file) && !isGeneratedCodeFile(file);
return /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|kts|scala|java|go|sql|cs|swift|groovy|php|cpp|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test(file) && !isTestFile(file) && !isGeneratedCodeFile(file);
}

function lineCount(file) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,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", ".cc", ".c", ".h", ".hpp", ".m", ".vue", ".svelte", ".astro", ".dart")) and not is_generated_code_file(lower_path):
elif lower_path.endswith((".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".py", ".rb", ".rs", ".go", ".java", ".kt", ".kts", ".scala", ".sql", ".cs", ".swift", ".groovy", ".php", ".cpp", ".cc", ".c", ".h", ".hpp", ".m", ".vue", ".svelte", ".astro", ".dart")) and not is_generated_code_file(lower_path):
source += lines
else:
non_code += lines
Expand Down
4 changes: 4 additions & 0 deletions test/unit/local-branch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,10 @@ describe("local MCP git metadata collection", () => {
expect(isTestFile(file)).toBe(false);
expect(isCodeFile(file)).toBe(false);
}
// #3889 follow-up: .kts source must count as code in the MCP copy now that isSourcePath knows it.
expect(isCodeFile("app/Build.kts")).toBe(true);
expect(isTestFile("build/SettingsTests.kts")).toBe(true);
expect(isCodeFile("build/SettingsTests.kts")).toBe(false);
});

it("extracts linked issues only from standalone closing keywords, not keyword substrings", async () => {
Expand Down
20 changes: 20 additions & 0 deletions test/unit/path-matchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import {
classifyChangedFile,
isCodeFile,
isDependencyManifestFile,
isConfigFile,
isDocsFile,
Expand All @@ -26,6 +27,25 @@ describe("path-matchers.ts never imports from local-branch.ts", () => {
});
});

describe("isCodeFile extension sets", () => {
it("keeps isSourcePath core extensions and EXTENDED_SOURCE_EXTENSION disjoint", () => {
const testEvidenceSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../../src/signals/test-evidence.ts"), "utf8");
const pathMatchersSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../../src/signals/path-matchers.ts"), "utf8");
const coreMatch = testEvidenceSource.match(/SOURCE_FILE_EXTENSION = \/\\\.\(([^)]+)\)\$\/i/);
const extendedMatch = pathMatchersSource.match(/EXTENDED_SOURCE_EXTENSION = \/\\\.\(([^)]+)\)\$\/i/);
expect(coreMatch).not.toBeNull();
expect(extendedMatch).not.toBeNull();
const coreExts = new Set(coreMatch![1]!.split("|"));
const extendedExts = new Set(extendedMatch![1]!.split("|"));
expect([...coreExts].filter((ext) => extendedExts.has(ext))).toEqual([]);
});

it("classifies .kts Gradle Kotlin-script source as code via the core matcher", () => {
expect(isCodeFile("app/Build.kts")).toBe(true);
expect(isCodeFile("build/SettingsTests.kts")).toBe(false);
});
});

describe("isGeneratedFile", () => {
it("matches generated output by directory, suffix, codegen, and source maps", () => {
for (const path of [
Expand Down
22 changes: 22 additions & 0 deletions test/unit/score-preview-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,28 @@ describe("gittensor-score-preview.mjs classifier parity with the server", () =>
expect(py.nonCodeTokenScore).toBe(0);
});

it("classifies Kotlin-script source and class-suffix .kts tests in both .mjs and .py previews", () => {
const files = [
{ path: "app/Build.kts", additions: 9, deletions: 0 },
{ path: "build/SettingsTests.kts", additions: 4, deletions: 0 },
];
const mjs = runPreview(files);
expect(mjs.sourceTokenScore).toBe(9);
expect(mjs.testTokenScore).toBe(4);
expect(mjs.nonCodeTokenScore).toBe(0);

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(9);
expect(py.testTokenScore).toBe(4);
expect(py.nonCodeTokenScore).toBe(0);
});

it("does not misclassify a *.test.mjs.map source-map as a test (extension anchored to end-of-path, matching the server)", () => {
// A substring match on ".test.mjs" wrongly flagged non-tests like dist/widget.test.mjs.map; the rule must be
// end-anchored like isTestPath. It's a source-map — neither test nor code — so it counts as non-code.
Expand Down