Add a keyboard-driven project setup flow (#2097)

* feat(projects): add keyboard-driven project setup

* test(app): update add project loading assertion

* fix(projects): harden GitHub project setup
This commit is contained in:
Mohamed Boudra
2026-07-15 21:16:10 +02:00
committed by GitHub
parent 90e0a0e353
commit dfe3330ef8
52 changed files with 3851 additions and 1023 deletions
+102 -2
View File
@@ -1789,14 +1789,39 @@ export const OpenProjectRequestSchema = z.object({
requestId: z.string(),
});
// Smallest shorthand repo path is "a/b": owner, slash, repository.
const MIN_REPOSITORY_PATH_LENGTH = 3;
export const ProjectAddRequestSchema = z.object({
type: z.literal("project.add.request"),
cwd: z.string(),
requestId: z.string(),
});
// Smallest shorthand repo path is "a/b": owner, slash, repository.
const MIN_REPOSITORY_PATH_LENGTH = 3;
export const ProjectCreateDirectoryRequestSchema = z.object({
type: z.literal("project.create_directory.request"),
parentPath: z.string(),
name: z.string(),
requestId: z.string(),
});
export const GithubRepositorySchema = z.object({
id: z.string().min(1),
name: z.string().min(1),
nameWithOwner: z.string().min(MIN_REPOSITORY_PATH_LENGTH),
description: z.string().nullable(),
visibility: z.enum(["public", "private", "internal"]),
updatedAt: z.string(),
cloneUrl: z.string().min(MIN_REPOSITORY_PATH_LENGTH),
});
export const WorkspaceGithubSearchRepositoriesRequestSchema = z.object({
type: z.literal("workspace.github.search_repositories.request"),
query: z.string(),
limit: z.number().int().min(1).max(50).optional(),
requestId: z.string(),
});
export const WorkspaceGithubCloneProtocolSchema = z.enum(["https", "ssh"]);
export const WorkspaceGithubCloneRequestSchema = z.object({
@@ -2191,6 +2216,8 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
LegacyOpenInEditorRequestSchema,
OpenProjectRequestSchema,
ProjectAddRequestSchema,
ProjectCreateDirectoryRequestSchema,
WorkspaceGithubSearchRepositoriesRequestSchema,
WorkspaceGithubCloneRequestSchema,
ArchiveWorkspaceRequestSchema,
WorkspaceCreateRequestSchema,
@@ -2438,6 +2465,10 @@ export const ServerInfoStatusPayloadSchema = z
workspacePinning: z.boolean().optional(),
// COMPAT(workspaceGithubClone): added in v0.1.108, remove gate after 2027-01-13.
workspaceGithubClone: z.boolean().optional(),
// COMPAT(workspaceGithubRepositorySearch): added in v0.1.108, remove gate after 2027-01-15.
workspaceGithubRepositorySearch: z.boolean().optional(),
// COMPAT(projectCreateDirectory): added in v0.1.108, remove gate after 2027-01-15.
projectCreateDirectory: z.boolean().optional(),
})
.optional(),
})
@@ -2936,6 +2967,63 @@ export const ProjectAddResponseSchema = z.object({
}),
});
export const ProjectCreateDirectoryErrorCodeSchema = z.enum([
"invalid_name",
"parent_directory_not_found",
"directory_exists",
"permission_denied",
"registration_failed",
"filesystem_error",
]);
export const ProjectCreateDirectoryResponseSchema = z.object({
type: z.literal("project.create_directory.response"),
payload: z.object({
requestId: z.string(),
directoryPath: z.string().nullable(),
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
error: z.string().nullable(),
// Error codes are open-ended on the wire so older clients can still parse
// responses after a newer daemon learns another failure reason.
errorCode: z.string().nullable(),
}),
});
export const WorkspaceGithubSearchRepositoriesResponseSchema = z.object({
type: z.literal("workspace.github.search_repositories.response"),
payload: z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(true),
error: z.null(),
}),
z.object({
status: z.literal("unavailable"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
reason: z.literal("gh_missing"),
available: z.literal(false),
error: z.string(),
}),
z.object({
status: z.literal("unauthenticated"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(false),
error: z.string(),
}),
z.object({
status: z.literal("error"),
requestId: z.string(),
repositories: z.array(GithubRepositorySchema),
available: z.literal(true),
error: z.string(),
}),
]),
});
export const WorkspaceGithubCloneResponseSchema = z.object({
type: z.literal("workspace.github.clone.response"),
payload: z.object({
@@ -4368,7 +4456,9 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
FetchRecentProviderSessionsResponseMessageSchema,
FetchWorkspacesResponseMessageSchema,
ProjectAddResponseSchema,
ProjectCreateDirectoryResponseSchema,
OpenProjectResponseMessageSchema,
WorkspaceGithubSearchRepositoriesResponseSchema,
WorkspaceGithubCloneResponseSchema,
StartWorkspaceScriptResponseMessageSchema,
LegacyListAvailableEditorsResponseMessageSchema,
@@ -4527,8 +4617,13 @@ export type FetchRecentProviderSessionsResponseMessage = z.infer<
>;
export type FetchWorkspacesResponseMessage = z.infer<typeof FetchWorkspacesResponseMessageSchema>;
export type ProjectAddResponse = z.infer<typeof ProjectAddResponseSchema>;
export type ProjectCreateDirectoryResponse = z.infer<typeof ProjectCreateDirectoryResponseSchema>;
export type ScriptStatusUpdateMessage = z.infer<typeof ScriptStatusUpdateMessageSchema>;
export type OpenProjectResponseMessage = z.infer<typeof OpenProjectResponseMessageSchema>;
export type WorkspaceGithubSearchRepositoriesResponse = z.infer<
typeof WorkspaceGithubSearchRepositoriesResponseSchema
>;
export type GithubRepository = z.infer<typeof GithubRepositorySchema>;
export type WorkspaceGithubCloneResponse = z.infer<typeof WorkspaceGithubCloneResponseSchema>;
export type StartWorkspaceScriptResponseMessage = z.infer<
typeof StartWorkspaceScriptResponseMessageSchema
@@ -4778,6 +4873,11 @@ export type LegacyListAvailableEditorsRequest = z.infer<
export type LegacyOpenInEditorRequest = z.infer<typeof LegacyOpenInEditorRequestSchema>;
export type OpenProjectRequest = z.infer<typeof OpenProjectRequestSchema>;
export type ProjectAddRequest = z.infer<typeof ProjectAddRequestSchema>;
export type ProjectCreateDirectoryRequest = z.infer<typeof ProjectCreateDirectoryRequestSchema>;
export type ProjectCreateDirectoryErrorCode = z.infer<typeof ProjectCreateDirectoryErrorCodeSchema>;
export type WorkspaceGithubSearchRepositoriesRequest = z.infer<
typeof WorkspaceGithubSearchRepositoriesRequestSchema
>;
export type WorkspaceGithubCloneRequest = z.infer<typeof WorkspaceGithubCloneRequestSchema>;
export type WorkspaceGithubCloneProtocol = z.infer<typeof WorkspaceGithubCloneProtocolSchema>;
export type ArchiveWorkspaceRequest = z.infer<typeof ArchiveWorkspaceRequestSchema>;