feat: add keyboard navigation to combobox and select fields on web

This commit is contained in:
Mohamed Boudra
2026-01-30 22:52:07 +07:00
parent 025a68c3a4
commit ea33d1980f
8 changed files with 320 additions and 22 deletions
@@ -13,6 +13,7 @@
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { basename } from "path";
import { z } from "zod";
import { ensureValidJson } from "../json-utils.js";
import type { Logger } from "pino";
@@ -25,6 +26,7 @@ import {
import {
NotGitRepoError,
renameCurrentBranch,
getCheckoutStatus,
} from "../../utils/checkout-git.js";
export interface AgentSelfIdMcpOptions {
@@ -165,6 +167,21 @@ export async function createAgentSelfIdMcpServer(
);
}
const status = await getCheckoutStatus(agent.cwd, {
paseoHome: options.paseoHome,
});
if (!status.isGit || !status.currentBranch) {
throw new AgentSelfIdToolError("NOT_GIT_REPO", "Unable to determine current branch");
}
const worktreeDirName = basename(status.repoRoot);
if (status.currentBranch !== worktreeDirName) {
throw new AgentSelfIdToolError(
"NOT_ALLOWED",
"Branch has already been renamed. Use git commands for subsequent renames."
);
}
childLogger.debug({ branch: name }, "Renaming branch");
const result = await renameCurrentBranch(agent.cwd, name);
if (result.currentBranch !== name) {
@@ -224,6 +224,72 @@ describe("self-identification MCP tools", () => {
}
});
test("set_branch rejects subsequent renames after initial rename", async () => {
const repoDir = mkdtempSync(path.join(tmpdir(), "paseo-self-ident-"));
const paseoHome = path.join(repoDir, "paseo-home");
try {
initGitRepo(repoDir);
const worktree = await createWorktree({
branchName: "initial-branch",
cwd: repoDir,
baseBranch: "main",
worktreeSlug: "initial-branch",
paseoHome,
});
const storagePath = path.join(repoDir, "agents");
const storage = new AgentStorage(storagePath, logger);
const manager = new AgentManager({
clients: { codex: new TestAgentClient() },
registry: storage,
logger,
idFactory: () => "agent-subsequent-rename",
});
const agent = await manager.createAgent({
provider: "codex",
cwd: worktree.worktreePath,
});
const server = await createAgentSelfIdMcpServer({
agentManager: manager,
paseoHome,
callerAgentId: agent.id,
logger,
});
const tool = (server as any)._registeredTools["set_branch"];
// First rename should succeed
await tool.callback({ name: "first-rename" });
const branchAfterFirst = execSync("git rev-parse --abbrev-ref HEAD", {
cwd: worktree.worktreePath,
stdio: "pipe",
})
.toString()
.trim();
expect(branchAfterFirst).toBe("first-rename");
// Second rename should fail
await expect(tool.callback({ name: "second-rename" })).rejects.toMatchObject({
code: "NOT_ALLOWED",
message: expect.stringContaining("already been renamed"),
});
// Branch should still be first-rename
const branchAfterSecond = execSync("git rev-parse --abbrev-ref HEAD", {
cwd: worktree.worktreePath,
stdio: "pipe",
})
.toString()
.trim();
expect(branchAfterSecond).toBe("first-rename");
} finally {
rmSync(repoDir, { recursive: true, force: true });
}
});
test("set_branch rejects non-Paseo checkouts", async () => {
const repoDir = mkdtempSync(path.join(tmpdir(), "paseo-self-ident-"));