Skip to content

Commit 824c8fe

Browse files
KryptosAIclaude
andauthored
improve: usage-driven UX changes based on telemetry (#83)
Promote security findings in default output, make test the hero command, hide dead commands, reorganize menu based on 1,792 telemetry events. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e3d3c13 commit 824c8fe

File tree

3 files changed

+40
-18
lines changed

3 files changed

+40
-18
lines changed

src/cli.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,26 @@ const MENU_GROUPS: MenuGroup[] = [
4141
{
4242
heading: "",
4343
items: [
44-
{ command: ["scan"], label: "scan", outcome: "Check all your MCP servers", recommended: true },
45-
{ command: ["scan", "deep"], label: "scan deep", outcome: "^ plus invoke tools to verify they actually work" },
46-
{ command: ["suggest"], label: "suggest", outcome: "Discover MCP servers for your stack" },
47-
{ command: ["score"], label: "score", outcome: "Health score (0-100) for a specific server" },
44+
{ command: ["test"], label: "test <cmd>", outcome: "Test a specific MCP server", recommended: true },
45+
{ command: ["scan"], label: "scan", outcome: "Check all your configured MCP servers" },
46+
{ command: ["scan", "deep"], label: "scan deep", outcome: "^ plus invoke tools to verify they work" },
4847
],
4948
},
5049
{
5150
heading: "CI / Regression Testing",
5251
items: [
53-
{ command: ["watch"], label: "watch", outcome: "Run a check, diff against previous, alert on regressions" },
54-
{ command: ["record"], label: "record", outcome: "Capture a session for offline replay or CI" },
55-
{ command: ["diff"], label: "diff", outcome: "Compare two runs for regressions" },
56-
{ command: ["test"], label: "test", outcome: "Test a single server by command" },
52+
{ command: ["watch"], label: "watch", outcome: "Run check, diff against previous, alert regressions" },
53+
{ command: ["lock"], label: "lock", outcome: "Snapshot MCP server schemas into a lock file" },
54+
{ command: ["lock", "verify"], label: "lock verify", outcome: "Verify servers match the lock file" },
55+
{ command: ["diff"], label: "diff", outcome: "Compare two run artifacts for regressions" },
56+
{ command: ["history"], label: "history", outcome: "Show health score trends over time" },
57+
],
58+
},
59+
{
60+
heading: "Scoring & Badges",
61+
items: [
62+
{ command: ["score"], label: "score", outcome: "Health score (0-100) for a specific server" },
63+
{ command: ["badge"], label: "badge", outcome: "Generate an SVG health badge for README" },
5764
],
5865
},
5966
];
@@ -219,17 +226,16 @@ async function main(): Promise<void> {
219226
"",
220227
` ${c(ANSI.bold, "Quick Start")}`,
221228
"",
222-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} scan`)} Check all your MCP servers`,
223-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} scan deep`)} ^ plus invoke tools to verify they work`,
224-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} suggest`)} Discover MCP servers for your stack`,
225-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} score`)} ${c(ANSI.dim, "<cmd>")} Health score (0-100) for any server`,
229+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} test`)} ${c(ANSI.dim, "<cmd>")} Test a specific MCP server`,
230+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} scan`)} Check all your configured servers`,
231+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} scan deep`)} ^ plus invoke tools to verify they work`,
226232
"",
227233
` ${c(ANSI.bold, "CI / Regression Testing")}`,
228234
"",
229-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} watch`)} ${c(ANSI.dim, "<cmd>")} Run check, diff against previous, alert regressions`,
230-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} record`)} ${c(ANSI.dim, "<cmd>")} Capture a session for offline replay`,
231-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} diff`)} ${c(ANSI.dim, "<a> <b>")} Compare two runs for regressions`,
232-
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} badge`)} ${c(ANSI.dim, "<cmd>")} Generate a health badge for README`,
235+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} watch`)} ${c(ANSI.dim, "<cmd>")} Diff against previous run, alert regressions`,
236+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} lock`)} Snapshot server schemas (like package-lock)`,
237+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} lock verify`)} Verify no schema drift since last lock`,
238+
` ${c(ANSI.dim, "$")} ${c(ANSI.cyan, `${bin} diff`)} ${c(ANSI.dim, "<a> <b>")} Compare two runs for regressions`,
233239
"",
234240
` ${c(ANSI.dim, `Run ${bin} <command> --help for details on any command.`)}`,
235241
"",

src/commands/record-replay.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function registerRecordReplayCommands(program: Command, bin: string): voi
2525
// ── record ─────────────────────────────────────────────────────────────
2626

2727
program
28-
.command("record")
28+
.command("record", { hidden: true })
2929
.passThroughOptions()
3030
.description("Record a server session to a cassette file for replay.")
3131
.argument("[command...]", "Server command and arguments to run.")
@@ -71,7 +71,7 @@ export function registerRecordReplayCommands(program: Command, bin: string): voi
7171
// ── replay ─────────────────────────────────────────────────────────────
7272

7373
program
74-
.command("replay")
74+
.command("replay", { hidden: true })
7575
.description("Replay a cassette file offline — no live server needed.")
7676
.argument("<cassette>", "Path to a cassette JSON file.")
7777
.option("--no-color", "Disable colored output.")

src/reporters/terminal.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,22 @@ function renderRunTerminal(artifact: RunArtifact): string {
199199
lines.push(formatCheck(check));
200200
}
201201

202+
// Show security-lite findings prominently even without --security flag
203+
const secLite = artifact.checks.find(ch => ch.id === "security-lite");
204+
if (secLite && secLite.status !== "pass" && secLite.evidence.length > 0) {
205+
const diagnostics = secLite.evidence[0]?.diagnostics ?? [];
206+
if (diagnostics.length > 0) {
207+
lines.push("");
208+
lines.push(co(ANSI.red, " Security:"));
209+
for (const d of diagnostics.slice(0, 3)) {
210+
lines.push(` ${co(ANSI.dim, "→")} ${d}`);
211+
}
212+
if (diagnostics.length > 3) {
213+
lines.push(` ${co(ANSI.dim, ` ...and ${diagnostics.length - 3} more (run with --security for full scan)`)}`);
214+
}
215+
}
216+
}
217+
202218
return lines.join("\n");
203219
}
204220

0 commit comments

Comments
 (0)