fix(kimi-code): guard footer PR lookup spawn failures (#89)

Co-authored-by: liruifengv <liruifeng1024@gmail.com>
This commit is contained in:
caiji 2026-05-27 10:42:11 +07:00 committed by GitHub
parent 4e458d6364
commit 61cae592fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 21 deletions

View file

@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---
Prevent the TUI from crashing when pull request lookup fails during startup.

View file

@ -244,28 +244,32 @@ function parseDiffNumstatCount(value: string | undefined): number {
function readPullRequest(workDir: string): Promise<PullRequestInfo | null> {
return new Promise((resolve) => {
execFile(
'gh',
['pr', 'view', '--json', 'number,url'],
{
cwd: workDir,
encoding: 'utf8',
env: {
...process.env,
GH_NO_UPDATE_NOTIFIER: '1',
GH_PROMPT_DISABLED: '1',
try {
execFile(
'gh',
['pr', 'view', '--json', 'number,url'],
{
cwd: workDir,
encoding: 'utf8',
env: {
...process.env,
GH_NO_UPDATE_NOTIFIER: '1',
GH_PROMPT_DISABLED: '1',
},
timeout: PR_SPAWN_TIMEOUT_MS,
maxBuffer: 256 * 1024,
},
timeout: PR_SPAWN_TIMEOUT_MS,
maxBuffer: 256 * 1024,
},
(error, stdout) => {
if (error !== null) {
resolve(null);
return;
}
resolve(parsePullRequest(stdout));
},
);
(error, stdout) => {
if (error !== null) {
resolve(null);
return;
}
resolve(parsePullRequest(stdout));
},
);
} catch {
resolve(null);
}
});
}

View file

@ -149,6 +149,57 @@ describe('git status cache', () => {
});
});
it('keeps footer git status working when gh pull-request lookup throws synchronously', async () => {
const onChange = vi.fn();
mocks.execFile.mockImplementation(() => {
const error = Object.assign(new Error('spawn ENOTDIR'), { code: 'ENOTDIR' });
throw error;
});
mocks.spawnSync.mockImplementation((_cmd: string, args: string[]) => {
if (args.includes('rev-parse')) {
return { status: 0, stdout: 'true\n' };
}
if (args.includes('branch')) {
return { status: 0, stdout: 'main\n' };
}
if (args.includes('status')) {
return {
status: 0,
stdout: '## main...origin/main\n M src/app.ts\n',
};
}
if (args.includes('diff')) {
return { status: 0, stdout: '2\t1\tsrc/app.ts\n' };
}
return { status: 1, stdout: '' };
});
const cache = createGitStatusCache('/tmp/repo', { onChange });
expect(cache.getStatus()).toEqual({
branch: 'main',
dirty: true,
ahead: 0,
behind: 0,
diffAdded: 2,
diffDeleted: 1,
pullRequest: null,
});
await Promise.resolve();
expect(onChange).not.toHaveBeenCalled();
expect(cache.getStatus()).toEqual({
branch: 'main',
dirty: true,
ahead: 0,
behind: 0,
diffAdded: 2,
diffDeleted: 1,
pullRequest: null,
});
});
it('returns null when the working directory is not a git repo and formats badges', () => {
mocks.spawnSync.mockReturnValue({ status: 1, stdout: '' });
expect(createGitStatusCache('/tmp/not-a-repo').getStatus()).toBeNull();