Merge branch 'nwparker/term-speed-2-side-effect-cleanup' into nwparker/term-speed-2-hidden-delivery-gate

This commit is contained in:
Neil
2026-06-16 16:32:24 -07:00
144 changed files with 7979 additions and 1094 deletions
+5 -3
View File
@@ -1,8 +1,10 @@
import { describe, expect, it } from 'vitest'
import { getExecutionHostLabel } from './execution-host'
import { MIN_COMPATIBLE_RUNTIME_SERVER_VERSION, RUNTIME_PROTOCOL_VERSION } from './protocol-version'
import { getLocalExecutionHostLabel } from './execution-host'
import { buildExecutionHostRegistry } from './execution-host-registry'
const LOCAL_HOST_LABEL = getExecutionHostLabel('local')
describe('execution host registry', () => {
it('returns only the local host for local-only state', () => {
expect(
@@ -14,7 +16,7 @@ describe('execution host registry', () => {
{
id: 'local',
kind: 'local',
label: getLocalExecutionHostLabel(),
label: LOCAL_HOST_LABEL,
detail: 'This computer',
health: 'local'
}
@@ -189,7 +191,7 @@ describe('execution host registry', () => {
})
expect(hosts).toMatchObject([
{ id: 'local', label: getLocalExecutionHostLabel() },
{ id: 'local', label: LOCAL_HOST_LABEL },
{ id: 'ssh:repo-ssh', label: 'Derived SSH' }
])
})
+6
View File
@@ -71,4 +71,10 @@ describe('execution host identity', () => {
'runtime:runtime-1'
)
})
it('labels local execution hosts by platform', () => {
expect(getLocalExecutionHostLabel('darwin')).toBe('Local Mac')
expect(getLocalExecutionHostLabel('linux')).toBe('Local Linux')
expect(getLocalExecutionHostLabel('win32')).toBe('Local Windows')
})
})
+30 -31
View File
@@ -13,42 +13,41 @@ export type ParsedExecutionHost =
| { kind: 'ssh'; id: `ssh:${string}`; targetId: string }
| { kind: 'runtime'; id: `runtime:${string}`; environmentId: string }
function getCurrentLocalPlatform(): NodeJS.Platform | null {
const globalNavigator = (globalThis as { navigator?: { userAgent?: string; platform?: string } })
.navigator
const userAgent = globalNavigator?.userAgent || globalNavigator?.platform || ''
if (/Windows/i.test(userAgent)) {
return 'win32'
}
if (/Mac/i.test(userAgent)) {
return 'darwin'
}
if (/Linux|X11/i.test(userAgent)) {
return 'linux'
}
return typeof process === 'undefined' ? null : process.platform
}
export function getLocalExecutionHostLabel(platform: NodeJS.Platform | null = null): string {
const localPlatform = platform ?? getCurrentLocalPlatform()
if (localPlatform === 'darwin') {
return 'Local Mac'
}
if (localPlatform === 'win32') {
return 'Local Windows'
}
if (localPlatform === 'linux') {
return 'Local Linux'
}
return 'This computer'
}
function normalizeHostPart(value: string | null | undefined): string | null {
const trimmed = value?.trim()
return trimmed ? trimmed : null
}
function getCurrentHostPlatform(): string {
if (typeof process !== 'undefined' && typeof process.platform === 'string') {
return process.platform
}
if (typeof navigator !== 'undefined') {
if (navigator.userAgent.includes('Windows')) {
return 'win32'
}
if (navigator.userAgent.includes('Linux')) {
return 'linux'
}
if (navigator.userAgent.includes('Mac')) {
return 'darwin'
}
}
return ''
}
export function getLocalExecutionHostLabel(platform = getCurrentHostPlatform()): string {
switch (platform) {
case 'darwin':
return 'Local Mac'
case 'win32':
return 'Local Windows'
case 'linux':
return 'Local Linux'
default:
return 'This computer'
}
}
export function toSshExecutionHostId(targetId: string): `ssh:${string}` {
return `ssh:${encodeURIComponent(targetId)}`
}
+2
View File
@@ -63,6 +63,8 @@ export type MRInfo = {
authorAvatarUrl?: string | null
/** GitLab MR head SHA — pipeline status is keyed off the head commit. */
headSha?: string
/** Target branch name for review-created worktree compare-base repair. */
baseRefName?: string
conflictSummary?: PRConflictSummary
}
@@ -0,0 +1,20 @@
import type { HostedReviewProvider } from './hosted-review'
export type HostedReviewCreationProvider = 'github' | 'gitlab' | 'azure-devops' | 'gitea'
export function supportsHostedReviewCreation(
provider: HostedReviewProvider | null | undefined
): provider is HostedReviewCreationProvider {
return (
provider === 'github' ||
provider === 'gitlab' ||
provider === 'azure-devops' ||
provider === 'gitea'
)
}
export function resolveHostedReviewCreationProvider(
provider: HostedReviewProvider | null | undefined
): HostedReviewCreationProvider {
return supportsHostedReviewCreation(provider) ? provider : 'github'
}
+2
View File
@@ -25,6 +25,8 @@ export type HostedReviewInfo = {
mergeQueueRequired?: boolean | null
mergeStateStatus?: string | null
headSha?: string
/** Target branch name for review-created worktree compare-base repair. */
baseRefName?: string
conflictSummary?: PRConflictSummary
}
@@ -27,6 +27,19 @@ describe('buildPullRequestFieldsPrompt', () => {
expect(prompt).toContain('Additional user prompt:')
expect(prompt).toContain('Use conventional PR titles.')
})
it('tells the agent to preserve existing review templates', () => {
const prompt = buildPullRequestFieldsPrompt(
{
...context,
currentBody: '## Summary\n\n## Testing\n\n- [ ] Required checks'
},
''
)
expect(prompt).toContain('preserve its headings, required sections, and checklists')
expect(prompt).toContain('Leave genuinely unknown template items as TODO or unchecked')
})
})
describe('parseGeneratedPullRequestFields', () => {
+2
View File
@@ -41,6 +41,8 @@ export function buildPullRequestFieldsPrompt(
'- Keep the base branch as the current base unless the diff clearly targets a different branch.',
'- Title: concise, specific, no trailing period.',
'- Body: useful Markdown summary for reviewers. Include testing notes only when evidence exists.',
'- If Current description contains a pull request or merge request template, preserve its headings, required sections, and checklists while filling relevant sections from the branch changes.',
'- Leave genuinely unknown template items as TODO or unchecked instead of deleting them.',
'- draft: true only when the changes clearly look unfinished, WIP, or unsafe to review.',
'- Do not include labels, reviewers, code fences, prose, or any keys beyond base/title/body/draft.',
'',
+8 -1
View File
@@ -4,13 +4,20 @@
const SHELL_NAMES = new Set(
'|bash|zsh|sh|fish|cmd|cmd.exe|powershell|powershell.exe|pwsh|pwsh.exe|nu'.split('|')
)
const WINDOWS_PROCESS_EXTENSION_RE = /\.(?:exe|cmd|bat|ps1)$/i
export function isShellProcess(processName: string): boolean {
const normalized = processName
.trim()
.replace(/^["']|["']$/g, '')
.toLowerCase()
const basename = normalized.split(/[\\/]/).pop() ?? normalized
// Why: Windows node-pty reports Git Bash and similar shells as `bash.exe`;
// those still need the same shell-safe handling as their POSIX basenames.
const basenameWithoutWindowsExtension = basename.replace(WINDOWS_PROCESS_EXTENSION_RE, '')
return (
SHELL_NAMES.has(normalized) || SHELL_NAMES.has(normalized.split(/[\\/]/).pop() ?? normalized)
SHELL_NAMES.has(normalized) ||
SHELL_NAMES.has(basename) ||
SHELL_NAMES.has(basenameWithoutWindowsExtension)
)
}
+6
View File
@@ -494,6 +494,8 @@ export type GitPushTarget = {
export type GitHubPrStartPoint = {
baseBranch: string
/** Review target branch to use for Source Control compare after creating from a PR head SHA. */
compareBaseRef?: string
pushTarget?: GitPushTarget
/** Verified PR head commit. Present when checkout can be tied to a stable SHA. */
headSha?: string
@@ -1054,6 +1056,8 @@ export type PRInfo = {
// Keeping the head SHA in cached PR metadata lets the checks panel poll the
// correct commit without re-querying GitHub or guessing from local branch refs.
headSha?: string
/** Target branch name for PR-created worktree compare-base repair. */
baseRefName?: string
prRepo?: GitHubRepositoryIdentity
headRepo?: GitHubRepositoryIdentity
conflictSummary?: PRConflictSummary
@@ -1894,6 +1898,8 @@ export type CreateWorktreeArgs = {
* Linear artifact whose title should remain readable in the sidebar. */
displayName?: string
baseBranch?: string
/** Source Control compare target when it differs from the checkout start point. */
compareBaseRef?: string
/** Optional git branch to create, separate from the filesystem-safe worktree
* name. Used when creating from an existing branch whose local branch name
* legitimately contains `/` while the worktree directory must not. */