mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix: hide scratch worktrees from linked checkouts
This commit is contained in:
@@ -2315,6 +2315,48 @@ describe('registerWorktreeHandlers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('hides agent scratch created inside a linked checkout from desktop listings', async () => {
|
||||
const linkedCheckoutPath = '/workspace/feature-x'
|
||||
const scratchPath = `${linkedCheckoutPath}/.claude/worktrees/agent-a04ccaaa`
|
||||
listWorktreesMock.mockResolvedValue([
|
||||
{
|
||||
path: '/workspace/repo',
|
||||
head: 'main-head',
|
||||
branch: 'refs/heads/main',
|
||||
isBare: false,
|
||||
isMainWorktree: true
|
||||
},
|
||||
{
|
||||
path: linkedCheckoutPath,
|
||||
head: 'feature-head',
|
||||
branch: 'refs/heads/feature-x',
|
||||
isBare: false,
|
||||
isMainWorktree: false
|
||||
},
|
||||
{
|
||||
path: scratchPath,
|
||||
head: 'scratch-head',
|
||||
branch: 'refs/heads/worktree-agent-a04ccaaa',
|
||||
isBare: false,
|
||||
isMainWorktree: false
|
||||
}
|
||||
])
|
||||
|
||||
const detected = (await handlers['worktrees:listDetected'](null, {
|
||||
repoId: 'repo-1'
|
||||
})) as { worktrees: (Worktree & { ownership: string; visible: boolean })[] }
|
||||
const visible = (await handlers['worktrees:list'](null, { repoId: 'repo-1' })) as Worktree[]
|
||||
|
||||
expect(detected.worktrees.find((worktree) => worktree.path === scratchPath)).toMatchObject({
|
||||
ownership: 'agent-scratch',
|
||||
visible: false
|
||||
})
|
||||
expect(visible.map((worktree) => worktree.path)).toEqual([
|
||||
'/workspace/repo',
|
||||
linkedCheckoutPath
|
||||
])
|
||||
})
|
||||
|
||||
it('does not reuse host detected worktree scans for a selected WSL runtime', async () => {
|
||||
listWorktreesMock
|
||||
.mockResolvedValueOnce([
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
isLegacyRepoForExternalWorktreeVisibility,
|
||||
toDetectedWorktree
|
||||
} from '../../shared/worktree-ownership'
|
||||
import { createAgentScratchWorktreePathMatcher } from '../../shared/agent-scratch-worktrees'
|
||||
import {
|
||||
assertWorktreeCleanForRemoval,
|
||||
forceDeleteLocalBranch,
|
||||
@@ -754,8 +755,14 @@ function buildDetectedGitWorktrees(
|
||||
const isLegacyRepoForVisibility = isLegacyRepoForExternalWorktreeVisibility(repo)
|
||||
// Why: a prunable registration has no working directory (issue #8389); only
|
||||
// this listing omits it — removal/cleanup flows list worktrees separately.
|
||||
const liveWorktrees = gitWorktrees.filter((gitWorktree) => !gitWorktree.prunable)
|
||||
return dedupeWorktreesByPath(liveWorktrees).map((gitWorktree) => {
|
||||
const liveWorktrees = dedupeWorktreesByPath(
|
||||
gitWorktrees.filter((gitWorktree) => !gitWorktree.prunable)
|
||||
)
|
||||
const agentScratchWorktreePathMatcher = createAgentScratchWorktreePathMatcher([
|
||||
repo.path,
|
||||
...liveWorktrees.map((worktree) => worktree.path)
|
||||
])
|
||||
return liveWorktrees.map((gitWorktree) => {
|
||||
const worktreeId = `${repo.id}::${gitWorktree.path}`
|
||||
let meta = store.getWorktreeMeta(worktreeId)
|
||||
const worktree = mergeWorktree(repo.id, gitWorktree, meta, repo.displayName)
|
||||
@@ -765,7 +772,8 @@ function buildDetectedGitWorktrees(
|
||||
meta,
|
||||
settings,
|
||||
knownOrcaLayouts,
|
||||
isLegacyRepoForVisibility
|
||||
isLegacyRepoForVisibility,
|
||||
agentScratchWorktreePathMatcher
|
||||
})
|
||||
if (!detected.visible) {
|
||||
return detected
|
||||
@@ -778,7 +786,8 @@ function buildDetectedGitWorktrees(
|
||||
meta,
|
||||
settings,
|
||||
knownOrcaLayouts,
|
||||
isLegacyRepoForVisibility
|
||||
isLegacyRepoForVisibility,
|
||||
agentScratchWorktreePathMatcher
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -970,6 +979,10 @@ function buildDisconnectedDetectedWorktrees(
|
||||
worktrees: Worktree[]
|
||||
): DetectedWorktree[] {
|
||||
const settings = store.getSettings()
|
||||
const agentScratchWorktreePathMatcher = createAgentScratchWorktreePathMatcher([
|
||||
repo.path,
|
||||
...worktrees.map((worktree) => worktree.path)
|
||||
])
|
||||
return worktrees.map((worktree) => {
|
||||
const meta = store.getWorktreeMeta(worktree.id)
|
||||
const detected = toDetectedWorktree({
|
||||
@@ -978,7 +991,8 @@ function buildDisconnectedDetectedWorktrees(
|
||||
meta,
|
||||
settings,
|
||||
knownOrcaLayouts: [],
|
||||
isLegacyRepoForVisibility: true
|
||||
isLegacyRepoForVisibility: true,
|
||||
agentScratchWorktreePathMatcher
|
||||
})
|
||||
return applyMetadataFallbackVisibility(detected)
|
||||
})
|
||||
|
||||
@@ -26388,6 +26388,31 @@ describe('OrcaRuntimeService', () => {
|
||||
expect(removeWorktreeLineage).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('hides agent scratch created inside a linked checkout from runtime listings', async () => {
|
||||
const linkedCheckoutPath = '/tmp/worktree-a'
|
||||
const scratchPath = `${linkedCheckoutPath}/.claude/worktrees/agent-a04ccaaa`
|
||||
vi.mocked(listWorktrees).mockClear()
|
||||
vi.mocked(listWorktrees).mockResolvedValue([
|
||||
makeWorktreeInfo(TEST_REPO_PATH),
|
||||
makeWorktreeInfo(linkedCheckoutPath),
|
||||
makeWorktreeInfo(scratchPath)
|
||||
])
|
||||
const runtime = createRuntime()
|
||||
|
||||
const detected = await runtime.listDetectedManagedWorktrees(`id:${TEST_REPO_ID}`)
|
||||
const listed = await runtime.listManagedWorktrees(`id:${TEST_REPO_ID}`)
|
||||
|
||||
expect(detected.worktrees.find((worktree) => worktree.path === scratchPath)).toMatchObject({
|
||||
ownership: 'agent-scratch',
|
||||
visible: false
|
||||
})
|
||||
expect(listed.worktrees.map((worktree) => worktree.path)).toEqual([
|
||||
TEST_REPO_PATH,
|
||||
linkedCheckoutPath
|
||||
])
|
||||
expect(listWorktrees).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('bounds repeated detected worktree scans across the reported 15-repo shape', async () => {
|
||||
vi.mocked(listWorktrees).mockReset()
|
||||
const repos = Array.from({ length: 15 }, (_, index) => ({
|
||||
|
||||
@@ -263,6 +263,10 @@ import {
|
||||
isLegacyRepoForExternalWorktreeVisibility,
|
||||
toDetectedWorktree
|
||||
} from '../../shared/worktree-ownership'
|
||||
import {
|
||||
createAgentScratchWorktreePathMatcher,
|
||||
type AgentScratchWorktreePathMatcher
|
||||
} from '../../shared/agent-scratch-worktrees'
|
||||
import {
|
||||
BROWSER_HEADLESS_RUNTIME_CAPABILITY,
|
||||
BROWSER_CERTIFICATE_TRUST_RUNTIME_CAPABILITY,
|
||||
@@ -15207,11 +15211,29 @@ export class OrcaRuntimeService {
|
||||
}
|
||||
const resolved = await this.listResolvedWorktrees()
|
||||
const repoId = repoSelector ? (await this.resolveRepoSelector(repoSelector)).id : null
|
||||
const checkoutPathsByRepoId = new Map<string, string[]>()
|
||||
for (const worktree of resolved) {
|
||||
const checkoutPaths = checkoutPathsByRepoId.get(worktree.repoId) ?? []
|
||||
checkoutPaths.push(worktree.path)
|
||||
checkoutPathsByRepoId.set(worktree.repoId, checkoutPaths)
|
||||
}
|
||||
const agentScratchMatchersByRepoId = new Map(
|
||||
(this.store?.getRepos() ?? []).map((repo) => [
|
||||
repo.id,
|
||||
createAgentScratchWorktreePathMatcher([
|
||||
repo.path,
|
||||
...(checkoutPathsByRepoId.get(repo.id) ?? [])
|
||||
])
|
||||
])
|
||||
)
|
||||
const worktrees = resolved.filter((worktree) => {
|
||||
if (repoId && worktree.repoId !== repoId) {
|
||||
return false
|
||||
}
|
||||
return this.isRuntimeWorktreeVisible(worktree)
|
||||
return this.isRuntimeWorktreeVisible(
|
||||
worktree,
|
||||
agentScratchMatchersByRepoId.get(worktree.repoId)
|
||||
)
|
||||
})
|
||||
return {
|
||||
worktrees: worktrees.slice(0, limit),
|
||||
@@ -15240,11 +15262,19 @@ export class OrcaRuntimeService {
|
||||
if (scan.ok) {
|
||||
this.pruneLineageForMissingRepoWorktrees(repo, scan.worktrees)
|
||||
}
|
||||
const agentScratchWorktreePathMatcher = createAgentScratchWorktreePathMatcher([
|
||||
repo.path,
|
||||
...scan.worktrees.map((worktree) => worktree.path)
|
||||
])
|
||||
const detected = scan.worktrees.map((gitWorktree) => {
|
||||
const worktreeId = `${repo.id}::${gitWorktree.path}`
|
||||
const meta = this.store?.getWorktreeMeta(worktreeId)
|
||||
const worktree = mergeWorktree(repo.id, gitWorktree, meta, repo.displayName)
|
||||
const detectedWorktree = this.toRuntimeDetectedWorktree(repo, worktree)
|
||||
const detectedWorktree = this.toRuntimeDetectedWorktree(
|
||||
repo,
|
||||
worktree,
|
||||
agentScratchWorktreePathMatcher
|
||||
)
|
||||
if (scan.ok) {
|
||||
return detectedWorktree
|
||||
}
|
||||
@@ -15258,15 +15288,22 @@ export class OrcaRuntimeService {
|
||||
}
|
||||
}
|
||||
|
||||
private isRuntimeWorktreeVisible(worktree: Worktree): boolean {
|
||||
private isRuntimeWorktreeVisible(
|
||||
worktree: Worktree,
|
||||
agentScratchWorktreePathMatcher?: AgentScratchWorktreePathMatcher
|
||||
): boolean {
|
||||
const repo = this.store?.getRepo(worktree.repoId)
|
||||
if (!repo || !this.store) {
|
||||
return true
|
||||
}
|
||||
return this.toRuntimeDetectedWorktree(repo, worktree).visible
|
||||
return this.toRuntimeDetectedWorktree(repo, worktree, agentScratchWorktreePathMatcher).visible
|
||||
}
|
||||
|
||||
private toRuntimeDetectedWorktree(repo: Repo, worktree: Worktree): DetectedWorktree {
|
||||
private toRuntimeDetectedWorktree(
|
||||
repo: Repo,
|
||||
worktree: Worktree,
|
||||
agentScratchWorktreePathMatcher?: AgentScratchWorktreePathMatcher
|
||||
): DetectedWorktree {
|
||||
const settings = this.store?.getSettings()
|
||||
if (!settings) {
|
||||
return {
|
||||
@@ -15282,7 +15319,8 @@ export class OrcaRuntimeService {
|
||||
meta: this.store?.getWorktreeMeta(worktree.id),
|
||||
settings,
|
||||
knownOrcaLayouts: buildKnownOrcaWorkspaceLayouts(settings, repo),
|
||||
isLegacyRepoForVisibility: isLegacyRepoForExternalWorktreeVisibility(repo)
|
||||
isLegacyRepoForVisibility: isLegacyRepoForExternalWorktreeVisibility(repo),
|
||||
agentScratchWorktreePathMatcher
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { getHiddenExternalWorktrees } from '../../../../shared/external-worktree-inbox'
|
||||
import {
|
||||
getHiddenExternalWorktrees,
|
||||
getVisibleExternalWorktrees
|
||||
} from '../../../../shared/external-worktree-inbox'
|
||||
import { isGitRepoKind } from '../../../../shared/repo-kind'
|
||||
import {
|
||||
effectiveExternalWorktreeVisibility,
|
||||
@@ -34,13 +37,7 @@ export default function WorktreeVisibilityDialog(): React.JSX.Element | null {
|
||||
'show'
|
||||
: false
|
||||
const hiddenCount = getHiddenExternalWorktrees(detected).length
|
||||
const otherCount =
|
||||
detected?.authoritative === true
|
||||
? detected.worktrees.filter(
|
||||
(worktree) =>
|
||||
worktree.visible && !worktree.selectedCheckout && worktree.ownership !== 'orca-managed'
|
||||
).length
|
||||
: 0
|
||||
const otherCount = getVisibleExternalWorktrees(detected).length
|
||||
const hiddenWorktreeLabel = `${hiddenCount} ${hiddenCount === 1 ? 'worktree' : 'worktrees'}`
|
||||
const shownWorktreeLabel = `${otherCount} ${otherCount === 1 ? 'worktree' : 'worktrees'}`
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isAgentScratchWorktreePath } from './agent-scratch-worktrees'
|
||||
import {
|
||||
createAgentScratchWorktreePathMatcher,
|
||||
isAgentScratchWorktreePath
|
||||
} from './agent-scratch-worktrees'
|
||||
|
||||
describe('isAgentScratchWorktreePath', () => {
|
||||
const repoPath = '/Users/dev/app'
|
||||
@@ -19,6 +22,22 @@ describe('isAgentScratchWorktreePath', () => {
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('matches scratch worktrees created from a linked checkout', () => {
|
||||
const matchesAgentScratch = createAgentScratchWorktreePathMatcher([
|
||||
repoPath,
|
||||
'/Users/dev/orca/workspaces/app/feature-x'
|
||||
])
|
||||
|
||||
expect(
|
||||
matchesAgentScratch(
|
||||
'/Users/dev/orca/workspaces/app/feature-x/.claude/worktrees/agent-a04ccaaa'
|
||||
)
|
||||
).toBe(true)
|
||||
expect(matchesAgentScratch('/Users/dev/other/feature-x/.claude/worktrees/agent-a04ccaaa')).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('matches Windows path separators and casing', () => {
|
||||
expect(
|
||||
isAgentScratchWorktreePath(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { isWindowsAbsolutePathLike, relativePathInsideRoot } from './cross-platform-path'
|
||||
import { isWslUncPath } from './wsl-paths'
|
||||
import { normalizeRuntimePathForComparison } from './cross-platform-path'
|
||||
|
||||
/** Why: agent CLIs reserve these repo-root paths for scratch; broader matches
|
||||
* can hide legitimate user worktrees (#9388). */
|
||||
@@ -8,19 +7,32 @@ const AGENT_SCRATCH_PATH_PREFIXES: readonly (readonly string[])[] = [
|
||||
['.gsd-workspaces']
|
||||
]
|
||||
|
||||
export function isAgentScratchWorktreePath(repoPath: string, worktreePath: string): boolean {
|
||||
const relativePath = relativePathInsideRoot(repoPath, worktreePath)
|
||||
if (!relativePath) {
|
||||
export type AgentScratchWorktreePathMatcher = (worktreePath: string) => boolean
|
||||
|
||||
export function createAgentScratchWorktreePathMatcher(
|
||||
checkoutPaths: readonly string[]
|
||||
): AgentScratchWorktreePathMatcher {
|
||||
const checkoutPathKeys = new Set(checkoutPaths.map(normalizeRuntimePathForComparison))
|
||||
return (worktreePath) => {
|
||||
const segments = normalizeRuntimePathForComparison(worktreePath).split('/')
|
||||
for (const prefix of AGENT_SCRATCH_PATH_PREFIXES) {
|
||||
for (let index = 0; index + prefix.length < segments.length; index += 1) {
|
||||
if (!prefix.every((segment, offset) => segments[index + offset] === segment)) {
|
||||
continue
|
||||
}
|
||||
const checkoutPath = segments.slice(0, index).join('/')
|
||||
const checkoutPathKey = /^[a-z]:$/i.test(checkoutPath)
|
||||
? `${checkoutPath}/`
|
||||
: checkoutPath || '/'
|
||||
if (checkoutPathKeys.has(checkoutPathKey)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const caseInsensitive = isWindowsAbsolutePathLike(worktreePath) && !isWslUncPath(worktreePath)
|
||||
const segments = relativePath
|
||||
.split('/')
|
||||
.filter(Boolean)
|
||||
.map((segment) => (caseInsensitive ? segment.toLowerCase() : segment))
|
||||
return AGENT_SCRATCH_PATH_PREFIXES.some(
|
||||
(prefix) =>
|
||||
segments.length > prefix.length &&
|
||||
prefix.every((segment, index) => segments[index] === segment)
|
||||
)
|
||||
}
|
||||
|
||||
export function isAgentScratchWorktreePath(repoPath: string, worktreePath: string): boolean {
|
||||
return createAgentScratchWorktreePathMatcher([repoPath])(worktreePath)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
import {
|
||||
getHiddenExternalWorktrees,
|
||||
getNewExternalWorktreeInboxWorktrees,
|
||||
getVisibleExternalWorktrees,
|
||||
mergeExternalWorktreeInboxPaths,
|
||||
shouldOfferNewExternalWorktreeInbox
|
||||
} from './external-worktree-inbox'
|
||||
@@ -162,6 +163,17 @@ describe('external worktree inbox', () => {
|
||||
).toEqual([hidden])
|
||||
})
|
||||
|
||||
it('excludes explicitly visible agent scratch from visibility-control counts', () => {
|
||||
const visible = detectedWorktree({ id: 'visible', visible: true })
|
||||
const scratch = detectedWorktree({
|
||||
id: 'agent-scratch',
|
||||
ownership: 'agent-scratch',
|
||||
visible: true
|
||||
})
|
||||
|
||||
expect(getVisibleExternalWorktrees(detectedResult([visible, scratch]))).toEqual([visible])
|
||||
})
|
||||
|
||||
it('offers metadata-free nested Orca workspace worktrees through the inbox', () => {
|
||||
const settings = makeSettings()
|
||||
const manual = toDetectedWorktree({
|
||||
|
||||
@@ -39,13 +39,28 @@ export function getHiddenExternalWorktrees(
|
||||
return []
|
||||
}
|
||||
return detected.worktrees.filter(
|
||||
(worktree) =>
|
||||
!worktree.visible &&
|
||||
!worktree.selectedCheckout &&
|
||||
worktree.ownership !== 'orca-managed' &&
|
||||
// Why: agent scratch is never importable, so it must not feed the
|
||||
// discovery card or the new-worktrees inbox.
|
||||
worktree.ownership !== 'agent-scratch'
|
||||
(worktree) => !worktree.visible && isUserFacingExternalWorktree(worktree)
|
||||
)
|
||||
}
|
||||
|
||||
export function getVisibleExternalWorktrees(
|
||||
detected: DetectedWorktreeListResult | undefined
|
||||
): DetectedWorktree[] {
|
||||
if (detected?.authoritative !== true) {
|
||||
return []
|
||||
}
|
||||
return detected.worktrees.filter(
|
||||
(worktree) => worktree.visible && isUserFacingExternalWorktree(worktree)
|
||||
)
|
||||
}
|
||||
|
||||
function isUserFacingExternalWorktree(worktree: DetectedWorktree): boolean {
|
||||
// Why: an explicit scratch import may be visible, but agent plumbing must
|
||||
// stay outside repo-wide discovery and visibility controls (#9388).
|
||||
return (
|
||||
!worktree.selectedCheckout &&
|
||||
worktree.ownership !== 'orca-managed' &&
|
||||
worktree.ownership !== 'agent-scratch'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { GlobalSettings, Repo, Worktree, WorktreeMeta } from './types'
|
||||
import { createAgentScratchWorktreePathMatcher } from './agent-scratch-worktrees'
|
||||
import {
|
||||
applyMetadataFallbackVisibility,
|
||||
buildKnownOrcaWorkspaceLayouts,
|
||||
@@ -441,6 +442,27 @@ describe('agent scratch worktrees', () => {
|
||||
).toBe('agent-scratch')
|
||||
})
|
||||
|
||||
it('classifies scratch worktrees created inside another linked checkout', () => {
|
||||
const repo = makeRepo()
|
||||
const settings = makeSettings()
|
||||
const linkedCheckoutPath = '/orca/workspaces/app/feature-x'
|
||||
expect(
|
||||
classifyWorktreeOwnership({
|
||||
repo,
|
||||
settings,
|
||||
worktree: makeWorktree({
|
||||
path: `${linkedCheckoutPath}/.claude/worktrees/agent-a04ccaaa`,
|
||||
isMainWorktree: false
|
||||
}),
|
||||
knownOrcaLayouts: buildKnownOrcaWorkspaceLayouts(settings, repo),
|
||||
agentScratchWorktreePathMatcher: createAgentScratchWorktreePathMatcher([
|
||||
repo.path,
|
||||
linkedCheckoutPath
|
||||
])
|
||||
})
|
||||
).toBe('agent-scratch')
|
||||
})
|
||||
|
||||
it('keeps strong Orca metadata authoritative over the scratch path match', () => {
|
||||
const repo = makeRepo()
|
||||
const settings = makeSettings()
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
resolveRuntimePath
|
||||
} from './cross-platform-path'
|
||||
import { parseWslUncPath } from './wsl-paths'
|
||||
import { isAgentScratchWorktreePath } from './agent-scratch-worktrees'
|
||||
import {
|
||||
isAgentScratchWorktreePath,
|
||||
type AgentScratchWorktreePathMatcher
|
||||
} from './agent-scratch-worktrees'
|
||||
import { isExplicitlyImportedExternalWorktreePath } from './external-worktree-inbox'
|
||||
import type {
|
||||
DetectedWorktree,
|
||||
@@ -155,6 +158,7 @@ export function classifyWorktreeOwnership(args: {
|
||||
meta?: WorktreeMeta
|
||||
settings: Pick<GlobalSettings, 'workspaceDir' | 'nestWorkspaces' | 'workspaceDirHistory'>
|
||||
knownOrcaLayouts: OrcaWorkspaceLayout[]
|
||||
agentScratchWorktreePathMatcher?: AgentScratchWorktreePathMatcher
|
||||
}): WorktreeOwnership {
|
||||
if (hasStrongOrcaMetadata(args.meta)) {
|
||||
return 'orca-managed'
|
||||
@@ -162,7 +166,10 @@ export function classifyWorktreeOwnership(args: {
|
||||
|
||||
// Why: sub-agent scratch worktrees (e.g. .claude/worktrees) are tool
|
||||
// plumbing, not workspaces; classify before layout heuristics (#9388).
|
||||
if (isAgentScratchWorktreePath(args.repo.path, args.worktree.path)) {
|
||||
if (
|
||||
args.agentScratchWorktreePathMatcher?.(args.worktree.path) ??
|
||||
isAgentScratchWorktreePath(args.repo.path, args.worktree.path)
|
||||
) {
|
||||
return 'agent-scratch'
|
||||
}
|
||||
|
||||
@@ -186,6 +193,7 @@ export function toDetectedWorktree(args: {
|
||||
settings: Pick<GlobalSettings, 'workspaceDir' | 'nestWorkspaces' | 'workspaceDirHistory'>
|
||||
knownOrcaLayouts: OrcaWorkspaceLayout[]
|
||||
isLegacyRepoForVisibility?: boolean
|
||||
agentScratchWorktreePathMatcher?: AgentScratchWorktreePathMatcher
|
||||
}): DetectedWorktree {
|
||||
const ownership = classifyWorktreeOwnership(args)
|
||||
const selectedCheckout = areRuntimePathsEqual(args.worktree.path, args.repo.path)
|
||||
|
||||
Reference in New Issue
Block a user