Files
kandev/apps/web/lib/pretty-tool-title.test.ts
T
José Almeida 1733014721 feat(web): prettify Kandev MCP tool titles in chat (#858)
* feat(web): prettify Kandev MCP tool titles in chat

Render `create_task_kandev` as "Kandev: Create Task" instead of the
raw underscored tool name. Frontend-only display transform via a
regex-gated helper so non-Kandev tool names (Read, Bash, other MCP
servers, Claude-supplied human titles) pass through unchanged.

* fix(web): update e2e assertions for prettified Kandev tool titles + trim pass-through

- E2E `config-management.spec.ts` asserted raw `*_kandev` names in chat;
  update to the new "Kandev: ..." display so Shard 6/10 passes.
- `prettifyToolTitle` now returns `trimmed` instead of `raw` on the
  non-match path so whitespace handling is symmetric across both
  branches (Greptile review).

* fix(web): expand collapsed tool-call groups in e2e config-management assertions

Once a turn completes, consecutive tool-call messages collapse into a
"{N} tool call(s)" group header, hiding the individual prettified titles
the test asserts against. The previous tests passed by lucky substring
match against the script directives in the user prompt; once
prettification removed `*_kandev` from the chat surface that
coincidence broke. Add an `expandToolCallGroups` helper that clicks
each group header before running the title visibility assertions.

* refactor(web): trim ACRONYMS to only entries used by current Kandev tool catalog

Per "no speculative code" guideline (Claude review): only `mcp` is
produced by the current 29-tool catalog. Remove the unused
id/url/api/ui/vs/ide/cli entries; add new ones when a catalog tool
first needs them.

* fix(web): strip MCP namespace prefix when prettifying Kandev tool titles

Codex passes the tool name as `<server>/<tool>` (e.g.
`kandev/get_task_plan_kandev`), so the bare-suffix regex didn't match
and the raw underscored name leaked through to the chat. Split on
`/` or `__` and prettify the trailing segment, covering Codex's
`server/tool` and Claude's `mcp__server__tool` naming alike.
2026-05-10 18:50:32 +01:00

65 lines
2.9 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { prettifyToolTitle } from "./pretty-tool-title";
import { EXTERNAL_MCP_TOOL_GROUPS } from "./settings/external-mcp-tools";
describe("prettifyToolTitle", () => {
it("converts a kandev tool name to Title Case with brand prefix", () => {
expect(prettifyToolTitle("create_task_kandev")).toBe("Kandev: Create Task");
expect(prettifyToolTitle("list_workflow_steps_kandev")).toBe("Kandev: List Workflow Steps");
expect(prettifyToolTitle("ask_user_question_kandev")).toBe("Kandev: Ask User Question");
expect(prettifyToolTitle("move_task_kandev")).toBe("Kandev: Move Task");
});
it("uppercases known acronyms", () => {
expect(prettifyToolTitle("update_mcp_config_kandev")).toBe("Kandev: Update MCP Config");
expect(prettifyToolTitle("get_mcp_config_kandev")).toBe("Kandev: Get MCP Config");
});
it("trims surrounding whitespace, both for matches and pass-through", () => {
expect(prettifyToolTitle(" create_task_kandev ")).toBe("Kandev: Create Task");
expect(prettifyToolTitle(" Reading foo.ts ")).toBe("Reading foo.ts");
});
it("leaves non-kandev MCP tool names unchanged", () => {
expect(prettifyToolTitle("mcp__github__list_issues")).toBe("mcp__github__list_issues");
expect(prettifyToolTitle("Read")).toBe("Read");
expect(prettifyToolTitle("Bash")).toBe("Bash");
});
it("strips namespace prefixes used by different agents", () => {
// Codex passes `<server>/<tool>`.
expect(prettifyToolTitle("kandev/get_task_plan_kandev")).toBe("Kandev: Get Task Plan");
expect(prettifyToolTitle("kandev/update_mcp_config_kandev")).toBe("Kandev: Update MCP Config");
// Claude-style `mcp__<server>__<tool>` prefix.
expect(prettifyToolTitle("mcp__kandev__create_task_kandev")).toBe("Kandev: Create Task");
});
it("leaves Claude-supplied human titles unchanged", () => {
expect(prettifyToolTitle("Reading file foo.ts")).toBe("Reading file foo.ts");
expect(prettifyToolTitle("Running `git status`")).toBe("Running `git status`");
});
it("does not match strings that merely contain _kandev mid-name", () => {
expect(prettifyToolTitle("create_kandev_task")).toBe("create_kandev_task");
expect(prettifyToolTitle("kandev_create_task")).toBe("kandev_create_task");
});
it("does not match uppercased or hyphenated variants", () => {
expect(prettifyToolTitle("CREATE_TASK_KANDEV")).toBe("CREATE_TASK_KANDEV");
expect(prettifyToolTitle("create-task-kandev")).toBe("create-task-kandev");
});
it("returns empty input unchanged", () => {
expect(prettifyToolTitle("")).toBe("");
});
it("prettifies every tool name in the external MCP catalog", () => {
const names = EXTERNAL_MCP_TOOL_GROUPS.flatMap((g) => g.tools.map((t) => t.name));
for (const name of names) {
const out = prettifyToolTitle(name);
expect(out.startsWith("Kandev: ")).toBe(true);
expect(out).not.toMatch(/_/);
}
});
});