diff --git a/packages/loopover-mcp/bin/loopover-mcp.js b/packages/loopover-mcp/bin/loopover-mcp.js index a5bcdc648..18997c03b 100644 --- a/packages/loopover-mcp/bin/loopover-mcp.js +++ b/packages/loopover-mcp/bin/loopover-mcp.js @@ -3865,12 +3865,27 @@ function printDecisionPackHelp() { ); } +function printContributorProfileHelp() { + process.stdout.write( + [ + "Usage: loopover-mcp contributor-profile --login [--json]", + "", + "Fetch the contributor profile for a GitHub login.", + "Mirrors the loopover_get_contributor_profile MCP tool and GET /v1/contributors/{login}/profile. No source upload.", + "", + "Login resolves from --login, the active session, LOOPOVER_LOGIN, then GITHUB_LOGIN.", + "Pass --json for machine-readable output.", + ].join("\n") + "\n", + ); +} + // #6737: CLI mirror of the loopover_get_contributor_profile MCP tool and GET /v1/contributors/{login}/profile // (requireContributorAccess-gated -- the same gate decision-pack/repo-decision already satisfy). Login resolves // from --login / the active session / LOOPOVER_LOGIN / GITHUB_LOGIN, exactly like the sibling contributor // commands, so an already-logged-in contributor never retypes their own login. Named `contributor-profile` // because the top-level `profile` command already manages MCP client profiles. async function contributorProfileCli(options) { + if (options.help === true) return printContributorProfileHelp(); const login = options.login ?? activeProfile.session?.login ?? process.env.LOOPOVER_LOGIN ?? process.env.GITHUB_LOGIN; if (!login) throw new Error("Pass --login , log in with `loopover-mcp login`, or set LOOPOVER_LOGIN."); const payload = await apiGet(`/v1/contributors/${encodeURIComponent(login)}/profile`); diff --git a/test/unit/mcp-cli-contributor-profile.test.ts b/test/unit/mcp-cli-contributor-profile.test.ts index 7208086e0..cb5d5ad6a 100644 --- a/test/unit/mcp-cli-contributor-profile.test.ts +++ b/test/unit/mcp-cli-contributor-profile.test.ts @@ -2,7 +2,7 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { closeFixtureServer, runAsync, runExpectingFailure, startFixtureServer } from "./support/mcp-cli-harness"; +import { closeFixtureServer, run, runAsync, runExpectingFailure, startFixtureServer } from "./support/mcp-cli-harness"; describe("loopover-mcp CLI — contributor-profile (#6737)", () => { let tempDir: string | null = null; @@ -49,4 +49,13 @@ describe("loopover-mcp CLI — contributor-profile (#6737)", () => { expect(`${failure.stdout}${failure.stderr}`).toMatch(/Pass --login/); expect(requests.filter((url) => url.includes("/profile"))).toHaveLength(0); }); + + it("prints usage for --help without resolving a login or hitting the network (#6992)", () => { + // --help must short-circuit BEFORE login resolution, so this succeeds with no --login and no server -- + // previously it fell through and threw the "Pass --login" error instead of printing usage. + const help = run(["contributor-profile", "--help"], { LOOPOVER_LOGIN: "", GITHUB_LOGIN: "" }); + expect(help).toContain("Usage: loopover-mcp contributor-profile --login [--json]"); + expect(help).toContain("Mirrors the loopover_get_contributor_profile MCP tool"); + expect(help).not.toMatch(/Pass --login/); + }); });