Skip to content
Merged
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
30 changes: 20 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@
},
"vite": {
"esbuild": "^0.28.1"
}
},
"ws": "^8.21.0"
},
"main": "index.js",
"directories": {
Expand Down
48 changes: 48 additions & 0 deletions packages/gittensory-mcp/bin/gittensory-mcp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env node

Check warning on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #547.

Check warning on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 8 meaningful terms.

Check notice on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #547.

Check notice on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 5 meaningful terms.

Check notice on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in packages/gittensory-mcp/bin/gittensory-mcp.js

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
Expand Down Expand Up @@ -410,6 +410,54 @@
async (input) => toolResult("Gittensory private local PR scoring preview.", await previewLocalScore(await withClientWorkspaceRoots(input))),
);

server.registerTool(
"gittensory_explain_score_breakdown",
{
description: "Explain a private score preview multiplier-by-multiplier with plain-English levers and the highest-impact improvement.",
inputSchema: localScoreShape,
},
async (input) => {
const workspaceInput = await withClientWorkspaceRoots(input);
const contributorLogin = workspaceInput.contributorLogin ?? activeProfile.session?.login;
if (!contributorLogin) throw new Error("contributorLogin is required for score breakdown.");
const workspace = resolveWorkspaceCwd(workspaceInput);
const diff = collectLocalDiff(workspace.cwd, workspaceInput.baseRef, workspaceInput.workspaceRoots);
const branchPayload = buildBranchAnalysisPayload({
...workspaceInput,
login: contributorLogin,
cwd: workspace.cwd,
repoFullName: workspaceInput.repoFullName,
baseRef: workspaceInput.baseRef,
});
const upstreamPreview = branchPayload.localScorerStatus;
const estimatedSourceLines = workspaceInput.sourceLines ?? Math.max(1, diff.changedLineCount - diff.testFiles.length);
const body = {
repoFullName: workspaceInput.repoFullName,
targetType: "local_diff",
targetKey: workspaceInput.targetKey ?? localDiffTargetKey(branchPayload, workspaceInput.baseRef),
contributorLogin,
labels: workspaceInput.labels,
linkedIssueMode: workspaceInput.linkedIssueMode,
sourceTokenScore: workspaceInput.sourceTokenScore ?? estimatedSourceLines,
sourceLines: estimatedSourceLines,
totalTokenScore: workspaceInput.totalTokenScore ?? diff.changedLineCount,
testTokenScore: diff.testFiles.length,
openPrCount: workspaceInput.openPrCount,
credibility: workspaceInput.credibility,
changesRequestedCount: workspaceInput.changesRequestedCount,
pendingMergedPrCount: workspaceInput.pendingMergedPrCount,
pendingClosedPrCount: workspaceInput.pendingClosedPrCount,
approvedPrCount: workspaceInput.approvedPrCount,
expectedOpenPrCountAfterMerge: workspaceInput.expectedOpenPrCountAfterMerge,
projectedCredibility: workspaceInput.projectedCredibility,
scenarioNotes: workspaceInput.scenarioNotes,
branchEligibility: workspaceInput.branchEligibility,
metadataOnly: !upstreamPreview.ok,
};
return toolResult("Gittensory private score breakdown.", await apiPost("/v1/scoring/explain-breakdown", body));
},
);

server.registerTool(
"gittensory_get_decision_pack",
{
Expand Down
17 changes: 17 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hono, type Context } from "hono";

Check warning on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #547.

Check warning on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 8 meaningful terms.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #547.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 5 meaningful terms.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.
import { z } from "zod";
import { analyzePRQueue, type AuthorRole, type ChecksStatus } from "../queue-intelligence";
import { completeGitHubWebOAuth, createSessionFromGitHubToken, pollGitHubDeviceFlow, startGitHubDeviceFlow, startGitHubWebOAuth } from "../auth/github-oauth";
Expand Down Expand Up @@ -131,6 +131,7 @@
preflightBranchWithAgent,
startAgentRun,
} from "../services/agent-orchestrator";
import { explainScoreBreakdown } from "../services/score-breakdown";
import { buildMcpClientTelemetry } from "../services/client-telemetry";
import {
buildAndPersistContributorDecisionPack,
Expand Down Expand Up @@ -1458,6 +1459,22 @@
return c.json(record);
});

app.post("/v1/scoring/explain-breakdown", async (c) => {
const body = await c.req.json().catch(() => null);
const parsed = scorePreviewSchema.safeParse(body);
if (!parsed.success) return c.json({ error: "invalid_scoring_preview_request", issues: parsed.error.issues }, 400);
if (!parsed.data.contributorLogin) return c.json({ error: "contributor_login_required" }, 400);
const unauthorized = await requireContributorAccess(c, parsed.data.contributorLogin);
if (unauthorized) return unauthorized;
const [repo, snapshot, evidence] = await Promise.all([
getRepository(c.env, parsed.data.repoFullName),
getOrCreateScoringModelSnapshot(c.env),
getContributorEvidence(c.env, parsed.data.contributorLogin),
]);
const preview = buildScorePreview({ input: parsed.data, repo, snapshot, contributorEvidence: evidence });
return c.json(explainScoreBreakdown(preview));
});

app.get("/v1/sync/status", async (c) => {
const [snapshot, scoringSnapshot, repositories, segments, totals, detailStates, installations, rateLimits, signalSnapshots, bounties, upstreamDrift] = await Promise.all([
getLatestRegistrySnapshot(c.env),
Expand Down
38 changes: 38 additions & 0 deletions src/mcp/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMcpHandler } from "agents/mcp";

Check warning on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Items reference the same linked issue #547.

Check warning on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 8 meaningful terms.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Open PR work references issue #547.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 5 meaningful terms.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Open PR queue is busy

This repo has a busy open PR queue in the local Gittensory cache.
import type { Context } from "hono";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
Expand Down Expand Up @@ -52,6 +52,7 @@
} from "../services/agent-orchestrator";
import { loadContributorDecisionPackForServing, repoDecisionFromPack } from "../services/decision-pack";
import { buildPublicPrBodyDraft } from "../services/pr-body-draft";
import { explainScoreBreakdown } from "../services/score-breakdown";
import { loadOrComputeIssueQualityResponse } from "../services/issue-quality";
import { loadOrComputeBurdenForecastResponse } from "../services/burden-forecast";
import { buildMcpClientTelemetry } from "../services/client-telemetry";
Expand Down Expand Up @@ -532,6 +533,15 @@
report: z.unknown().optional(),
};

const scoreBreakdownOutputSchema = {
repoFullName: z.string().optional(),
scoreabilityStatus: z.string().optional(),
effectiveEstimatedScore: z.number().optional(),
components: z.unknown().optional(),
gateHighlights: z.unknown().optional(),
highestLeverageLever: z.unknown().optional(),
};

const lintPrTextOutputSchema = {
verdict: z.string().optional(),
score: z.number().optional(),
Expand Down Expand Up @@ -977,6 +987,17 @@
async (input) => this.toolResult(await this.previewScore(input)),
);

server.registerTool(
"gittensory_explain_score_breakdown",
{
description:
"Explain a private score preview multiplier-by-multiplier with plain-English levers and the single highest-impact improvement. Login and repo scoped; no new computation beyond the preview projection.",
inputSchema: scorePreviewShape,
outputSchema: scoreBreakdownOutputSchema,
},
async (input) => this.toolResult(await this.explainScoreBreakdown(input)),
);

server.registerTool(
"gittensory_explain_review_risk",
{
Expand Down Expand Up @@ -1689,6 +1710,23 @@
};
}

private async explainScoreBreakdown(input: z.infer<z.ZodObject<typeof scorePreviewShape>>): Promise<ToolPayload> {
if (!input.contributorLogin) throw new Error("contributorLogin is required for score breakdown.");
this.requireContributorAccess(input.contributorLogin);
await this.requireRepoAccess(input.repoFullName);
const [repo, snapshot, evidence] = await Promise.all([
getRepository(this.env, input.repoFullName),
getOrCreateScoringModelSnapshot(this.env),
getContributorEvidence(this.env, input.contributorLogin),
]);
const preview = buildScorePreview({ input, repo, snapshot, contributorEvidence: evidence });
const breakdown = explainScoreBreakdown(preview);
return {
summary: `Private Gittensory score breakdown for ${input.contributorLogin} in ${input.repoFullName}. Highest leverage: ${breakdown.highestLeverageLever.component}.`,
data: breakdown as unknown as Record<string, unknown>,
};
}

private async explainReviewRisk(input: z.infer<z.ZodObject<typeof preflightShape>>): Promise<ToolPayload> {
if (input.contributorLogin) this.requireContributorAccess(input.contributorLogin);
await this.requireRepoAccess(input.repoFullName);
Expand Down
Loading
Loading