Merge branch 'main' into dev
This commit is contained in:
@@ -28,6 +28,10 @@ import type {
|
||||
CheckoutPushResponse,
|
||||
CheckoutPrCreateResponse,
|
||||
CheckoutPrStatusResponse,
|
||||
CheckoutSwitchBranchResponse,
|
||||
StashSaveResponse,
|
||||
StashPopResponse,
|
||||
StashListResponse,
|
||||
ValidateBranchResponse,
|
||||
BranchSuggestionsResponse,
|
||||
GitHubSearchResponse,
|
||||
@@ -224,6 +228,10 @@ type CheckoutMergeFromBasePayload = CheckoutMergeFromBaseResponse["payload"];
|
||||
type CheckoutPushPayload = CheckoutPushResponse["payload"];
|
||||
type CheckoutPrCreatePayload = CheckoutPrCreateResponse["payload"];
|
||||
type CheckoutPrStatusPayload = CheckoutPrStatusResponse["payload"];
|
||||
type CheckoutSwitchBranchPayload = CheckoutSwitchBranchResponse["payload"];
|
||||
type StashSavePayload = StashSaveResponse["payload"];
|
||||
type StashPopPayload = StashPopResponse["payload"];
|
||||
type StashListPayload = StashListResponse["payload"];
|
||||
type ValidateBranchPayload = ValidateBranchResponse["payload"];
|
||||
type BranchSuggestionsPayload = BranchSuggestionsResponse["payload"];
|
||||
type GitHubSearchPayload = GitHubSearchResponse["payload"];
|
||||
@@ -2420,6 +2428,74 @@ export class DaemonClient {
|
||||
});
|
||||
}
|
||||
|
||||
async checkoutSwitchBranch(
|
||||
cwd: string,
|
||||
branch: string,
|
||||
requestId?: string,
|
||||
): Promise<CheckoutSwitchBranchPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: {
|
||||
type: "checkout_switch_branch_request",
|
||||
cwd,
|
||||
branch,
|
||||
},
|
||||
responseType: "checkout_switch_branch_response",
|
||||
timeout: 30000,
|
||||
});
|
||||
}
|
||||
|
||||
async stashSave(
|
||||
cwd: string,
|
||||
options?: { branch?: string },
|
||||
requestId?: string,
|
||||
): Promise<StashSavePayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: {
|
||||
type: "stash_save_request",
|
||||
cwd,
|
||||
branch: options?.branch,
|
||||
},
|
||||
responseType: "stash_save_response",
|
||||
timeout: 30000,
|
||||
});
|
||||
}
|
||||
|
||||
async stashPop(
|
||||
cwd: string,
|
||||
stashIndex: number,
|
||||
requestId?: string,
|
||||
): Promise<StashPopPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: {
|
||||
type: "stash_pop_request",
|
||||
cwd,
|
||||
stashIndex,
|
||||
},
|
||||
responseType: "stash_pop_response",
|
||||
timeout: 30000,
|
||||
});
|
||||
}
|
||||
|
||||
async stashList(
|
||||
cwd: string,
|
||||
options?: { paseoOnly?: boolean },
|
||||
requestId?: string,
|
||||
): Promise<StashListPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: {
|
||||
type: "stash_list_request",
|
||||
cwd,
|
||||
paseoOnly: options?.paseoOnly,
|
||||
},
|
||||
responseType: "stash_list_response",
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
async getPaseoWorktreeList(
|
||||
input: { cwd?: string; repoRoot?: string },
|
||||
requestId?: string,
|
||||
|
||||
@@ -302,7 +302,7 @@ export async function createPaseoDaemon(
|
||||
|
||||
app.use((req, res, next) => {
|
||||
const origin = req.headers.origin;
|
||||
if (origin && allowedOrigins.has(origin)) {
|
||||
if (origin && (allowedOrigins.has("*") || allowedOrigins.has(origin))) {
|
||||
res.setHeader("Access-Control-Allow-Origin", origin);
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||
|
||||
@@ -1664,6 +1664,22 @@ export class Session {
|
||||
this.handleUnsubscribeCheckoutDiffRequest(msg);
|
||||
break;
|
||||
|
||||
case "checkout_switch_branch_request":
|
||||
await this.handleCheckoutSwitchBranchRequest(msg);
|
||||
break;
|
||||
|
||||
case "stash_save_request":
|
||||
await this.handleStashSaveRequest(msg);
|
||||
break;
|
||||
|
||||
case "stash_pop_request":
|
||||
await this.handleStashPopRequest(msg);
|
||||
break;
|
||||
|
||||
case "stash_list_request":
|
||||
await this.handleStashListRequest(msg);
|
||||
break;
|
||||
|
||||
case "checkout_commit_request":
|
||||
await this.handleCheckoutCommitRequest(msg);
|
||||
break;
|
||||
@@ -4404,6 +4420,139 @@ export class Session {
|
||||
this.checkoutDiffSubscriptions.delete(msg.subscriptionId);
|
||||
}
|
||||
|
||||
private async handleCheckoutSwitchBranchRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "checkout_switch_branch_request" }>,
|
||||
): Promise<void> {
|
||||
const { cwd, branch, requestId } = msg;
|
||||
|
||||
try {
|
||||
await this.checkoutExistingBranch(cwd, branch);
|
||||
this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
|
||||
|
||||
// Push a workspace_update immediately so the sidebar/header reflect
|
||||
// the new branch name without waiting for the background git watcher.
|
||||
await this.emitWorkspaceUpdateForCwd(cwd);
|
||||
|
||||
this.emit({
|
||||
type: "checkout_switch_branch_response",
|
||||
payload: {
|
||||
cwd,
|
||||
success: true,
|
||||
branch,
|
||||
error: null,
|
||||
requestId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "checkout_switch_branch_response",
|
||||
payload: {
|
||||
cwd,
|
||||
success: false,
|
||||
branch,
|
||||
error: toCheckoutError(error),
|
||||
requestId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stash handlers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private static readonly PASEO_STASH_PREFIX = "paseo-auto-stash:";
|
||||
|
||||
private async handleStashSaveRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "stash_save_request" }>,
|
||||
): Promise<void> {
|
||||
const { cwd, requestId } = msg;
|
||||
try {
|
||||
const branchLabel = msg.branch?.trim() ?? "";
|
||||
const message = branchLabel
|
||||
? `${Session.PASEO_STASH_PREFIX} ${branchLabel}`
|
||||
: `${Session.PASEO_STASH_PREFIX} unnamed`;
|
||||
await execFileAsync("git", ["stash", "push", "--include-untracked", "-m", message], { cwd });
|
||||
this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
|
||||
this.emit({
|
||||
type: "stash_save_response",
|
||||
payload: { cwd, success: true, error: null, requestId },
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "stash_save_response",
|
||||
payload: { cwd, success: false, error: toCheckoutError(error), requestId },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleStashPopRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "stash_pop_request" }>,
|
||||
): Promise<void> {
|
||||
const { cwd, stashIndex, requestId } = msg;
|
||||
try {
|
||||
await execFileAsync("git", ["stash", "pop", `stash@{${stashIndex}}`], { cwd });
|
||||
this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
|
||||
this.emit({
|
||||
type: "stash_pop_response",
|
||||
payload: { cwd, success: true, error: null, requestId },
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "stash_pop_response",
|
||||
payload: { cwd, success: false, error: toCheckoutError(error), requestId },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleStashListRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "stash_list_request" }>,
|
||||
): Promise<void> {
|
||||
const { cwd, requestId } = msg;
|
||||
const paseoOnly = msg.paseoOnly !== false;
|
||||
try {
|
||||
const { stdout } = await execAsync("git stash list --format=%gd%x00%s", {
|
||||
cwd,
|
||||
env: READ_ONLY_GIT_ENV,
|
||||
});
|
||||
const lines = stdout.trim().split("\n").filter(Boolean);
|
||||
const entries: Array<{
|
||||
index: number;
|
||||
message: string;
|
||||
branch: string | null;
|
||||
isPaseo: boolean;
|
||||
}> = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const sepIdx = line.indexOf("\0");
|
||||
if (sepIdx < 0) continue;
|
||||
const refPart = line.slice(0, sepIdx);
|
||||
const subject = line.slice(sepIdx + 1);
|
||||
const indexMatch = refPart.match(/\{(\d+)\}/);
|
||||
if (!indexMatch) continue;
|
||||
const index = Number(indexMatch[1]);
|
||||
const prefixIdx = subject.indexOf(Session.PASEO_STASH_PREFIX);
|
||||
const isPaseo = prefixIdx >= 0;
|
||||
const branch = isPaseo
|
||||
? subject.slice(prefixIdx + Session.PASEO_STASH_PREFIX.length).trim() || null
|
||||
: null;
|
||||
|
||||
if (paseoOnly && !isPaseo) continue;
|
||||
entries.push({ index, message: subject, branch, isPaseo });
|
||||
}
|
||||
|
||||
this.emit({
|
||||
type: "stash_list_response",
|
||||
payload: { cwd, entries, error: null, requestId },
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "stash_list_response",
|
||||
payload: { cwd, entries: [], error: toCheckoutError(error), requestId },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleCheckoutCommitRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "checkout_commit_request" }>,
|
||||
): Promise<void> {
|
||||
@@ -5311,7 +5460,7 @@ export class Session {
|
||||
if (agent.status === "error" || agent.attentionReason === "error") {
|
||||
return "failed";
|
||||
}
|
||||
if (agent.status === "running" || agent.status === "initializing") {
|
||||
if (agent.status === "running") {
|
||||
return "running";
|
||||
}
|
||||
if (agent.requiresAttention) {
|
||||
|
||||
@@ -425,7 +425,7 @@ export class VoiceAssistantWebSocketServer {
|
||||
!!requestHost &&
|
||||
(origin === `http://${requestHost}` || origin === `https://${requestHost}`);
|
||||
|
||||
if (!origin || allowedOrigins.has(origin) || sameOrigin) {
|
||||
if (!origin || allowedOrigins.has("*") || allowedOrigins.has(origin) || sameOrigin) {
|
||||
callback(true);
|
||||
} else {
|
||||
this.incrementRuntimeCounter("originRejected");
|
||||
|
||||
@@ -1066,6 +1066,37 @@ export const ValidateBranchRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const CheckoutSwitchBranchRequestSchema = z.object({
|
||||
type: z.literal("checkout_switch_branch_request"),
|
||||
cwd: z.string(),
|
||||
branch: z.string(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const StashSaveRequestSchema = z.object({
|
||||
type: z.literal("stash_save_request"),
|
||||
cwd: z.string(),
|
||||
/** Branch name to tag the stash with for later identification. */
|
||||
branch: z.string().optional(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const StashPopRequestSchema = z.object({
|
||||
type: z.literal("stash_pop_request"),
|
||||
cwd: z.string(),
|
||||
/** Zero-based index from stash_list_response. */
|
||||
stashIndex: z.number().int().min(0),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const StashListRequestSchema = z.object({
|
||||
type: z.literal("stash_list_request"),
|
||||
cwd: z.string(),
|
||||
/** If true, only return paseo-created stashes. Default true. */
|
||||
paseoOnly: z.boolean().optional(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const BranchSuggestionsRequestSchema = z.object({
|
||||
type: z.literal("branch_suggestions_request"),
|
||||
cwd: z.string(),
|
||||
@@ -1436,6 +1467,10 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
CheckoutPushRequestSchema,
|
||||
CheckoutPrCreateRequestSchema,
|
||||
CheckoutPrStatusRequestSchema,
|
||||
CheckoutSwitchBranchRequestSchema,
|
||||
StashSaveRequestSchema,
|
||||
StashPopRequestSchema,
|
||||
StashListRequestSchema,
|
||||
ValidateBranchRequestSchema,
|
||||
BranchSuggestionsRequestSchema,
|
||||
GitHubSearchRequestSchema,
|
||||
@@ -2343,6 +2378,54 @@ export const CheckoutPrStatusResponseSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const CheckoutSwitchBranchResponseSchema = z.object({
|
||||
type: z.literal("checkout_switch_branch_response"),
|
||||
payload: z.object({
|
||||
cwd: z.string(),
|
||||
success: z.boolean(),
|
||||
branch: z.string(),
|
||||
error: CheckoutErrorSchema.nullable(),
|
||||
requestId: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
const StashEntrySchema = z.object({
|
||||
index: z.number().int().min(0),
|
||||
message: z.string(),
|
||||
branch: z.string().nullable(),
|
||||
isPaseo: z.boolean(),
|
||||
});
|
||||
|
||||
export const StashSaveResponseSchema = z.object({
|
||||
type: z.literal("stash_save_response"),
|
||||
payload: z.object({
|
||||
cwd: z.string(),
|
||||
success: z.boolean(),
|
||||
error: CheckoutErrorSchema.nullable(),
|
||||
requestId: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const StashPopResponseSchema = z.object({
|
||||
type: z.literal("stash_pop_response"),
|
||||
payload: z.object({
|
||||
cwd: z.string(),
|
||||
success: z.boolean(),
|
||||
error: CheckoutErrorSchema.nullable(),
|
||||
requestId: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const StashListResponseSchema = z.object({
|
||||
type: z.literal("stash_list_response"),
|
||||
payload: z.object({
|
||||
cwd: z.string(),
|
||||
entries: z.array(StashEntrySchema),
|
||||
error: CheckoutErrorSchema.nullable(),
|
||||
requestId: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ValidateBranchResponseSchema = z.object({
|
||||
type: z.literal("validate_branch_response"),
|
||||
payload: z.object({
|
||||
@@ -2745,6 +2828,10 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
CheckoutPushResponseSchema,
|
||||
CheckoutPrCreateResponseSchema,
|
||||
CheckoutPrStatusResponseSchema,
|
||||
CheckoutSwitchBranchResponseSchema,
|
||||
StashSaveResponseSchema,
|
||||
StashPopResponseSchema,
|
||||
StashListResponseSchema,
|
||||
ValidateBranchResponseSchema,
|
||||
BranchSuggestionsResponseSchema,
|
||||
GitHubSearchResponseSchema,
|
||||
@@ -2973,6 +3060,15 @@ export type CheckoutPrCreateRequest = z.infer<typeof CheckoutPrCreateRequestSche
|
||||
export type CheckoutPrCreateResponse = z.infer<typeof CheckoutPrCreateResponseSchema>;
|
||||
export type CheckoutPrStatusRequest = z.infer<typeof CheckoutPrStatusRequestSchema>;
|
||||
export type CheckoutPrStatusResponse = z.infer<typeof CheckoutPrStatusResponseSchema>;
|
||||
export type CheckoutSwitchBranchRequest = z.infer<typeof CheckoutSwitchBranchRequestSchema>;
|
||||
export type CheckoutSwitchBranchResponse = z.infer<typeof CheckoutSwitchBranchResponseSchema>;
|
||||
export type StashSaveRequest = z.infer<typeof StashSaveRequestSchema>;
|
||||
export type StashSaveResponse = z.infer<typeof StashSaveResponseSchema>;
|
||||
export type StashPopRequest = z.infer<typeof StashPopRequestSchema>;
|
||||
export type StashPopResponse = z.infer<typeof StashPopResponseSchema>;
|
||||
export type StashListRequest = z.infer<typeof StashListRequestSchema>;
|
||||
export type StashListResponse = z.infer<typeof StashListResponseSchema>;
|
||||
export type StashEntry = z.infer<typeof StashEntrySchema>;
|
||||
export type ValidateBranchRequest = z.infer<typeof ValidateBranchRequestSchema>;
|
||||
export type ValidateBranchResponse = z.infer<typeof ValidateBranchResponseSchema>;
|
||||
export type BranchSuggestionsRequest = z.infer<typeof BranchSuggestionsRequestSchema>;
|
||||
|
||||
@@ -191,7 +191,6 @@ type CheckoutFileChange = {
|
||||
isUntracked?: boolean;
|
||||
};
|
||||
|
||||
type BranchSuggestionRefOrigin = "local" | "remote";
|
||||
|
||||
function normalizeBranchSuggestionName(raw: string): string | null {
|
||||
const trimmed = raw.trim();
|
||||
@@ -210,47 +209,57 @@ function normalizeBranchSuggestionName(raw: string): string | null {
|
||||
normalized = normalized.slice("origin/".length);
|
||||
}
|
||||
|
||||
if (!normalized || normalized === "HEAD") {
|
||||
if (!normalized || normalized === "HEAD" || normalized === "origin") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
async function listGitRefs(cwd: string, refPrefix: string): Promise<string[]> {
|
||||
const { stdout } = await execGit(`git for-each-ref --format="%(refname:short)" ${refPrefix}`, {
|
||||
cwd,
|
||||
env: READ_ONLY_GIT_ENV,
|
||||
});
|
||||
interface GitRef {
|
||||
name: string;
|
||||
committerDate: number;
|
||||
}
|
||||
|
||||
async function listGitRefs(cwd: string, refPrefix: string): Promise<GitRef[]> {
|
||||
const { stdout } = await execGit(
|
||||
`git for-each-ref --sort=-committerdate --format="%(refname)%09%(committerdate:unix)" ${refPrefix}`,
|
||||
{ cwd, env: READ_ONLY_GIT_ENV },
|
||||
);
|
||||
return stdout
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line.length > 0);
|
||||
.map((line) => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return null;
|
||||
const [name, dateStr] = trimmed.split("\t");
|
||||
if (!name) return null;
|
||||
return { name, committerDate: Number(dateStr) || 0 };
|
||||
})
|
||||
.filter((ref): ref is GitRef => ref !== null);
|
||||
}
|
||||
|
||||
function sortBranchSuggestions(
|
||||
branchNames: string[],
|
||||
localBranchNames: Set<string>,
|
||||
branchMeta: Map<string, { isLocal: boolean; committerDate: number }>,
|
||||
query: string,
|
||||
): string[] {
|
||||
const normalizedQuery = query.trim().toLowerCase();
|
||||
const hasQuery = normalizedQuery.length > 0;
|
||||
return branchNames.sort((a, b) => {
|
||||
const aLower = a.toLowerCase();
|
||||
const bLower = b.toLowerCase();
|
||||
|
||||
if (hasQuery) {
|
||||
const aPrefix = aLower.startsWith(normalizedQuery);
|
||||
const bPrefix = bLower.startsWith(normalizedQuery);
|
||||
const aPrefix = a.toLowerCase().startsWith(normalizedQuery);
|
||||
const bPrefix = b.toLowerCase().startsWith(normalizedQuery);
|
||||
if (aPrefix !== bPrefix) {
|
||||
return aPrefix ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
const aIsLocal = localBranchNames.has(a);
|
||||
const bIsLocal = localBranchNames.has(b);
|
||||
if (aIsLocal !== bIsLocal) {
|
||||
return aIsLocal ? -1 : 1;
|
||||
const aMeta = branchMeta.get(a);
|
||||
const bMeta = branchMeta.get(b);
|
||||
const aDate = aMeta?.committerDate ?? 0;
|
||||
const bDate = bMeta?.committerDate ?? 0;
|
||||
if (aDate !== bDate) {
|
||||
return bDate - aDate;
|
||||
}
|
||||
|
||||
return a.localeCompare(b);
|
||||
@@ -272,41 +281,40 @@ export async function listBranchSuggestions(
|
||||
listGitRefs(cwd, "refs/remotes/origin"),
|
||||
]);
|
||||
|
||||
const merged = new Map<string, Set<BranchSuggestionRefOrigin>>();
|
||||
for (const localRef of localRefs) {
|
||||
const normalized = normalizeBranchSuggestionName(localRef);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
const origins = merged.get(normalized) ?? new Set<BranchSuggestionRefOrigin>();
|
||||
origins.add("local");
|
||||
merged.set(normalized, origins);
|
||||
}
|
||||
for (const remoteRef of remoteRefs) {
|
||||
const normalized = normalizeBranchSuggestionName(remoteRef);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
const origins = merged.get(normalized) ?? new Set<BranchSuggestionRefOrigin>();
|
||||
origins.add("remote");
|
||||
merged.set(normalized, origins);
|
||||
const branchMeta = new Map<string, { isLocal: boolean; committerDate: number }>();
|
||||
|
||||
for (const ref of localRefs) {
|
||||
const normalized = normalizeBranchSuggestionName(ref.name);
|
||||
if (!normalized) continue;
|
||||
const existing = branchMeta.get(normalized);
|
||||
branchMeta.set(normalized, {
|
||||
isLocal: true,
|
||||
committerDate: Math.max(ref.committerDate, existing?.committerDate ?? 0),
|
||||
});
|
||||
}
|
||||
|
||||
const filteredNames = Array.from(merged.keys()).filter((name) =>
|
||||
for (const ref of remoteRefs) {
|
||||
const normalized = normalizeBranchSuggestionName(ref.name);
|
||||
if (!normalized) continue;
|
||||
const existing = branchMeta.get(normalized);
|
||||
if (!existing) {
|
||||
branchMeta.set(normalized, { isLocal: false, committerDate: ref.committerDate });
|
||||
} else {
|
||||
branchMeta.set(normalized, {
|
||||
...existing,
|
||||
committerDate: Math.max(ref.committerDate, existing.committerDate),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const filteredNames = Array.from(branchMeta.keys()).filter((name) =>
|
||||
query ? name.toLowerCase().includes(query) : true,
|
||||
);
|
||||
if (filteredNames.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const localBranchNames = new Set<string>();
|
||||
for (const [name, origins] of merged) {
|
||||
if (origins.has("local")) {
|
||||
localBranchNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
const ordered = sortBranchSuggestions(filteredNames, localBranchNames, query);
|
||||
const ordered = sortBranchSuggestions(filteredNames, branchMeta, query);
|
||||
return ordered.slice(0, limit);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user