fix: avoid repeated macOS privacy prompts (#1524)

* fix: avoid repeated macos privacy prompts

* fix: reduce background worktree permission probes

* chore: pin oxlint for ci

* fix: preserve optional rpc params with zod 4.4

* fix: preserve optional inline rpc params with zod 4.4
This commit is contained in:
Neil
2026-05-06 23:30:03 -07:00
committed by GitHub
parent 43a258951f
commit 7b83b2dcdc
28 changed files with 532 additions and 208 deletions
+24 -1
View File
@@ -294,13 +294,36 @@ describe('getStatus', () => {
// wrapped in double quotes) and the parser would store that literal
// string as entry.path, breaking sidebar display + downstream blob reads.
expect(gitExecFileAsyncMock).toHaveBeenCalledWith(
['-c', 'core.quotePath=false', 'status', '--porcelain=v2', '--untracked-files=all'],
[
'-c',
'core.quotePath=false',
'status',
'--porcelain=v2',
'--branch',
'--untracked-files=all'
],
{ cwd: '/repo' }
)
expect(result.entries).toEqual([
{ path: 'docs/日本語/sample.md', status: 'modified', area: 'unstaged' }
])
})
it('parses branch identity from porcelain v2 branch headers', async () => {
readFileMock.mockResolvedValue('gitdir: /repo/.git/worktrees/feature\n')
existsSyncMock.mockReturnValue(false)
gitExecFileAsyncMock.mockResolvedValueOnce({
stdout:
'# branch.oid abcdef1234567890\n# branch.head feature/prompts\n1 .M N... 100644 100644 100644 ce013625030ba8dba906f756967f9e9ca394464a ce013625030ba8dba906f756967f9e9ca394464a src/app.ts\n'
})
const result = await getStatus('/repo')
expect(result).toMatchObject({
head: 'abcdef1234567890',
branch: 'refs/heads/feature/prompts'
})
})
})
describe('detectConflictOperation', () => {
+15 -2
View File
@@ -23,6 +23,8 @@ const MAX_GIT_SHOW_BYTES = 10 * 1024 * 1024
*/
export async function getStatus(worktreePath: string): Promise<GitStatusResult> {
const entries: GitStatusEntry[] = []
let head: string | undefined
let branch: string | undefined
// Why: detectConflictOperation (4 existsSync + readFile) and git status are
// independent. Running them concurrently saves one round-trip of I/O latency.
@@ -32,7 +34,7 @@ export async function getStatus(worktreePath: string): Promise<GitStatusResult>
// in double quotes. Without it, the parsed entry.path is unreadable in the
// sidebar and downstream `git show :"docs/\346..."` lookups silently miss.
const statusPromise = gitExecFileAsync(
['-c', 'core.quotePath=false', 'status', '--porcelain=v2', '--untracked-files=all'],
['-c', 'core.quotePath=false', 'status', '--porcelain=v2', '--branch', '--untracked-files=all'],
{ cwd: worktreePath }
)
const conflictOperation = await conflictPromise
@@ -47,6 +49,17 @@ export async function getStatus(worktreePath: string): Promise<GitStatusResult>
continue
}
if (line.startsWith('# branch.oid ')) {
head = line.slice('# branch.oid '.length).trim()
continue
}
if (line.startsWith('# branch.head ')) {
const branchHead = line.slice('# branch.head '.length).trim()
branch = branchHead && branchHead !== '(detached)' ? `refs/heads/${branchHead}` : ''
continue
}
if (line.startsWith('1 ') || line.startsWith('2 ')) {
// Changed entries: "1 XY sub mH mI mW hH path" or "2 XY sub mH mI mW hH X\tscore\tpath\torigPath"
const parts = line.split(' ')
@@ -95,7 +108,7 @@ export async function getStatus(worktreePath: string): Promise<GitStatusResult>
// Not a git repo or git not available
}
return { entries, conflictOperation }
return { entries, conflictOperation, head, branch }
}
function parseStatusChar(char: string): GitFileStatus {