Skip to content

Commit 501b719

Browse files
authored
refactor(mcp): depend on the published gittensory-engine, drop the hand-ports (#4159)
Now that @jsonbored/gittensory-engine is published (0.1.0), gittensory-mcp adds it as a real dependency and both packages/gittensory-mcp/lib/local-branch.js and scripts/gittensor-score-preview.mjs import isTestFile/isCodeFile from @jsonbored/gittensory-engine/signals/test-evidence instead of hand-porting the regex logic -- the two remaining copies from the three-way drift that motivated the gittensory-engine consolidation (#4144). package-lock.json: hand-edited a single line rather than running `npm install --package-lock-only`, which was proven (twice, with and without --workspace scoping) to destructively prune ~150 unrelated packages from this monorepo's lockfile. Workspace-to-workspace dependencies only need the manifest-mirror entry updated; the shared node_modules symlink already resolves it. ci.yml: gittensory-mcp's "Build MCP"/"MCP package check" steps and the mcpCliHarness test-skip filter only checked the mcp path filter, not engine -- a PR touching only packages/gittensory-engine/** would have silently skipped rebuilding, pack-checking, and testing its now-real dependent. Verified end-to-end outside the monorepo: packed the mcp tarball, installed it into an isolated scratch directory, confirmed gittensory-engine resolved as a real registry copy (not a workspace symlink) and the classifier functions work through the full published dependency chain.
1 parent ab57363 commit 501b719

5 files changed

Lines changed: 16 additions & 57 deletions

File tree

.github/workflows/ci.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ jobs:
101101
# their shared test/unit/support/mcp-cli-harness.ts import nothing but node:* builtins + vitest,
102102
# and mcp-discovery.test.ts spawns packages/gittensory-mcp/bin/gittensory-mcp.js as a real
103103
# subprocess via StdioClientTransport -- none of the 6 ever load root src/ in-process. This mirrors
104-
# the `mcp` filter's own established trust boundary above (a self-contained package build).
104+
# the `mcp` filter's own established trust boundary above (a self-contained package build). They are
105+
# NOT self-contained w.r.t. packages/gittensory-engine/**, though: gittensory-mcp now has a real
106+
# dependency on @jsonbored/gittensory-engine (isTestFile/isCodeFile), which the bin subprocess and
107+
# local-branch.js load at runtime -- hence that path below, alongside the mcp package's own.
105108
# test/unit/mcp-output-schemas.test.ts is DELIBERATELY NOT in this filter: unlike the other 6, it
106109
# imports src/mcp/server.ts in-process, and server.ts alone directly imports ~40 other src/ modules
107110
# (src/github/app.ts, src/signals/slop.ts, src/settings/autonomy.ts, src/orb/analytics.ts, and
@@ -121,6 +124,7 @@ jobs:
121124
- 'migrations/**'
122125
- '.github/workflows/**'
123126
- 'packages/gittensory-mcp/**'
127+
- 'packages/gittensory-engine/**'
124128
- 'test/helpers/**'
125129
- 'test/unit/mcp-cli-*.test.ts'
126130
- 'test/unit/mcp-discovery.test.ts'
@@ -396,11 +400,14 @@ jobs:
396400
- name: Worker runtime tests
397401
if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' }}
398402
run: npm run test:workers
403+
# gittensory-mcp now depends on @jsonbored/gittensory-engine for real (isTestFile/isCodeFile), so a
404+
# PR that only touches packages/gittensory-engine/** must also rebuild + pack-check the mcp package,
405+
# not just the mcp filter's own (deliberately narrower) path list.
399406
- name: Build MCP
400-
if: ${{ github.event_name == 'push' || needs.changes.outputs.mcp == 'true' }}
407+
if: ${{ github.event_name == 'push' || needs.changes.outputs.mcp == 'true' || needs.changes.outputs.engine == 'true' }}
401408
run: npm run build:mcp
402409
- name: MCP package check
403-
if: ${{ github.event_name == 'push' || needs.changes.outputs.mcp == 'true' }}
410+
if: ${{ github.event_name == 'push' || needs.changes.outputs.mcp == 'true' || needs.changes.outputs.engine == 'true' }}
404411
run: npm run test:mcp-pack
405412
- name: Build miner CLI
406413
if: ${{ github.event_name == 'push' || needs.changes.outputs.miner == 'true' }}

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/gittensory-mcp/lib/local-branch.js

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { execFileSync } from "node:child_process";
22
import { realpathSync } from "node:fs";
33
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
44
import { fileURLToPath } from "node:url";
5+
import { isCodeFile, isTestPath as isTestFile } from "@jsonbored/gittensory-engine/signals/test-evidence";
6+
7+
export { isCodeFile, isTestFile };
58

69
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
710

@@ -587,36 +590,6 @@ function firstCommitTitle(messages) {
587590
return messages.find((message) => message.trim().length > 0)?.split("\n")[0]?.trim();
588591
}
589592

590-
// Must mirror the canonical server-side matcher in src/signals/test-evidence.ts (isTestPath); the
591-
// server local-branch analysis delegates to it, so this client copy classifies the same diff and must
592-
// agree. The last two branches (Cypress/e2e `*.cy.*`/`*.e2e.*` and `__snapshots__/`) were missing here,
593-
// so a changed `Button.cy.ts` / snapshot file was misclassified as source (isCodeFile) and dropped from
594-
// testFiles, inflating source-line/token counts and under-reporting test evidence in the local packet.
595-
export function isTestFile(file) {
596-
return (
597-
/(^|\/)(test|tests|spec|__tests__)\//i.test(file) ||
598-
/(^|\/)src\/test\//i.test(file) ||
599-
/(^|\/)[^/]+_test\.(go|py|rb|dart)$/i.test(file) || // Dart/Flutter `foo_test.dart` co-located with source
600-
/(^|\/)test_[^/]*\.py$/i.test(file) || // pytest's default `test_*.py` prefix convention (the suffix rule above only catches `*_test.py`)
601-
/(^|\/)[^/]+_spec\.rb$/i.test(file) ||
602-
/\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs)$/i.test(file) ||
603-
/(^|\/)[^/]+\.(cy|e2e)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i.test(file) ||
604-
// JVM / C# / Swift / PHP `SomethingTest(s)`/`SomethingSpec` class-suffix convention (JUnit, Kotlin/ScalaTest,
605-
// Spock, xUnit/NUnit, XCTest, PHPUnit/PHPSpec). Case-sensitive on the PascalCase suffix so it can't false-positive on words
606-
// that merely end in "test"/"spec" (Latest.java, Contest.cs, manifest.scala, Latest.php).
607-
/(^|\/)\w*(Tests?|Spec)\.(java|kt|kts|scala|cs|swift|groovy|php)$/.test(file) ||
608-
/(^|\/)__snapshots__\//i.test(file)
609-
);
610-
}
611-
612-
function isGeneratedCodeFile(file) {
613-
return /\.(g|freezed|gr)\.dart$/i.test(file);
614-
}
615-
616-
export function isCodeFile(file) {
617-
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);
618-
}
619-
620593
function numberValue(value) {
621594
const number = Number(value);
622595
return Number.isFinite(number) ? number : undefined;

packages/gittensory-mcp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"build": "node --check bin/gittensory-mcp.js && node --check lib/local-branch.js && node --check scripts/gittensor-score-preview.mjs"
3939
},
4040
"dependencies": {
41+
"@jsonbored/gittensory-engine": "^0.1.0",
4142
"@modelcontextprotocol/sdk": "1.29.0",
4243
"zod": "^4.4.3"
4344
},

packages/gittensory-mcp/scripts/gittensor-score-preview.mjs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,7 @@
11
#!/usr/bin/env node
22
import { dirname, join } from "node:path";
33
import { fileURLToPath } from "node:url";
4-
5-
function isTestFile(file) {
6-
return (
7-
/(^|\/)(test|tests|spec|__tests__)\//i.test(file) ||
8-
/(^|\/)src\/test\//i.test(file) ||
9-
/(^|\/)[^/]+_test\.(go|py|rb|dart)$/i.test(file) || // Dart/Flutter `foo_test.dart` co-located with source
10-
/(^|\/)test_[^/]*\.py$/i.test(file) || // pytest's default `test_*.py` prefix (the suffix rule above only catches `*_test.py`)
11-
/(^|\/)[^/]+_spec\.rb$/i.test(file) ||
12-
/\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs)$/i.test(file) ||
13-
/(^|\/)[^/]+\.(cy|e2e)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i.test(file) ||
14-
// JVM/.NET/Swift/PHP PascalCase test-class suffix (case-sensitive, matching the
15-
// signal classifiers) so C#/Swift/Groovy/PHP tests aren't counted as source.
16-
/(^|\/)\w*(Tests?|Spec)\.(java|kt|kts|scala|cs|swift|groovy|php)$/.test(file) ||
17-
/(^|\/)__snapshots__\//i.test(file)
18-
);
19-
}
20-
21-
function isGeneratedCodeFile(file) {
22-
return /\.(g|freezed|gr)\.dart$/i.test(file);
23-
}
24-
25-
function isCodeFile(file) {
26-
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);
27-
}
4+
import { isCodeFile, isTestPath as isTestFile } from "@jsonbored/gittensory-engine/signals/test-evidence";
285

296
function lineCount(file) {
307
const additions = Number(file.additions ?? 0);

0 commit comments

Comments
 (0)