fix(settings): tighten Linear skill section behavior

This commit is contained in:
Brennan Benson
2026-07-17 13:04:43 -07:00
parent e5bbb6bce7
commit f0fec5dedf
11 changed files with 230 additions and 33 deletions
@@ -101,6 +101,8 @@ describe('LinearAgentSkillPane', () => {
expect(examples).toHaveLength(5)
for (const example of examples) {
expect(markup).toContain(example.title)
expect(example.prompt).toContain('/orca-linear')
expect(example.prompt).not.toContain('{{value0}}')
}
})
@@ -102,8 +102,14 @@ export function LinearAgentSkillPane(): React.JSX.Element {
)}
command={installCommand}
installedCommand={updateCommand}
terminalTitle="Linear skill setup"
terminalAriaLabel="Linear skill install terminal"
terminalTitle={translate(
'auto.components.settings.LinearAgentSkillPane.terminalTitle',
'Linear skill setup'
)}
terminalAriaLabel={translate(
'auto.components.settings.LinearAgentSkillPane.terminalAriaLabel',
'Linear skill install terminal'
)}
terminalWorktreeId="settings-linear-skill-terminal"
terminalShellOverride={activeSkillRuntime.terminalShellOverride}
installed={linearSkillInstalled}
@@ -1,5 +1,6 @@
import { useState } from 'react'
import type { LucideIcon } from 'lucide-react'
import { Button } from '@/components/ui/button'
import type { SkillUsageExample } from '@/lib/skill-usage-example'
import { SkillUsageExampleDialog } from './SkillUsageExampleDialog'
@@ -31,10 +32,11 @@ export function SkillUsageExamplesSection({
{examples.map((example) => {
const Icon = resolveIcon(example)
return (
<button
<Button
key={example.id}
type="button"
className="rounded-md border border-border/60 bg-muted/20 px-4 py-3 text-left transition-colors hover:bg-muted/35 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
variant="ghost"
className="h-auto w-full justify-start whitespace-normal rounded-md border border-border/60 bg-muted/20 px-4 py-3 text-left hover:bg-muted/35 hover:text-foreground"
onClick={() => setSelectedExampleId(example.id)}
>
<div className="flex items-start gap-3">
@@ -46,7 +48,7 @@ export function SkillUsageExamplesSection({
<p className="text-xs leading-relaxed text-muted-foreground">{example.summary}</p>
</div>
</div>
</button>
</Button>
)
})}
</div>
@@ -0,0 +1,90 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, describe, expect, it } from 'vitest'
import type { LinearConnectionStatus } from '../../../shared/types'
import { getProviderRuntimeContextKey } from '@/lib/provider-runtime-context'
import { useAppStore } from '@/store'
import { useLinearProviderConnected } from './useLinearProviderConnected'
let root: Root | null = null
let container: HTMLDivElement | null = null
describe('useLinearProviderConnected', () => {
afterEach(() => {
if (root) {
act(() => root?.unmount())
}
root = null
container?.remove()
container = null
useAppStore.setState({
linearStatus: { connected: false, viewer: null },
linearStatusContextKey: null
})
})
it('does not rerender for Linear metadata changes that preserve section visibility', () => {
const settings = useAppStore.getState().settings
useAppStore.setState({
linearStatus: { connected: true, viewer: null },
linearStatusContextKey: getProviderRuntimeContextKey(settings)
})
let renders = 0
let connected = false
function Probe(): null {
renders += 1
connected = useLinearProviderConnected()
return null
}
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
act(() => root?.render(<Probe />))
expect(connected).toBe(true)
expect(renders).toBe(1)
act(() => {
useAppStore.setState({
linearStatus: {
...useAppStore.getState().linearStatus,
selectedWorkspaceId: 'workspace-2'
} satisfies LinearConnectionStatus
})
})
expect(connected).toBe(true)
expect(renders).toBe(1)
act(() => {
useAppStore.setState({ linearStatus: { connected: false, viewer: null } })
})
expect(connected).toBe(false)
expect(renders).toBe(2)
})
it('rejects a connected status from a different runtime context', () => {
useAppStore.setState({
linearStatus: { connected: true, viewer: null },
linearStatusContextKey: 'stale-runtime-context'
})
let connected = true
function Probe(): null {
connected = useLinearProviderConnected()
return null
}
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
act(() => root?.render(<Probe />))
expect(connected).toBe(false)
})
})
@@ -6,8 +6,11 @@ import { useAppStore } from '@/store'
// the rendered section from drifting. The context-key guard rejects a status
// fetched for a different runtime environment than the active one.
export function useLinearProviderConnected(): boolean {
const linearStatus = useAppStore((s) => s.linearStatus)
const linearStatusContextKey = useAppStore((s) => s.linearStatusContextKey)
const settings = useAppStore((s) => s.settings)
return linearStatusContextKey === getProviderRuntimeContextKey(settings) && linearStatus.connected
// Why: Cmd+J keeps this hook mounted globally; project to the visibility bit so
// unrelated Linear metadata and settings updates do not rerender the palette.
return useAppStore(
(state) =>
state.linearStatusContextKey === getProviderRuntimeContextKey(state.settings) &&
state.linearStatus.connected
)
}
+14 -2
View File
@@ -619,7 +619,17 @@
"postUpdate": "Post a progress update",
"moveState": "Move the ticket forward",
"attachPr": "Attach the PR link",
"triageFollowups": "Triage and create follow-ups"
"triageFollowups": "Triage and create follow-ups",
"readTicketSummary": "Pull the linked Linear issue's full context before starting work.",
"readTicketPrompt": "Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.",
"postUpdateSummary": "Comment progress or a completion summary back to the Linear issue.",
"postUpdatePrompt": "Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.",
"moveStateSummary": "Advance the Linear workflow state as the work progresses.",
"moveStatePrompt": "Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.",
"attachPrSummary": "Link the pull request to the Linear issue when you open it.",
"attachPrPrompt": "Use {{value0}} to attach this PR to the linked Linear issue with a PR link.",
"triageFollowupsSummary": "Set assignee, priority, or estimate, and file parented follow-up tickets.",
"triageFollowupsPrompt": "Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup."
}
}
}
@@ -9217,7 +9227,9 @@
"skillTitle": "Linear skill",
"skillDescription": "Enables agents to read linked tickets and post updates to Linear through Orca.",
"howToUse": "How to use it",
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR."
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR.",
"terminalTitle": "Linear skill setup",
"terminalAriaLabel": "Linear skill install terminal"
}
},
"right": {
+14 -2
View File
@@ -596,7 +596,17 @@
"postUpdate": "Post a progress update",
"moveState": "Move the ticket forward",
"attachPr": "Attach the PR link",
"triageFollowups": "Triage and create follow-ups"
"triageFollowups": "Triage and create follow-ups",
"readTicketSummary": "Pull the linked Linear issue's full context before starting work.",
"readTicketPrompt": "Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.",
"postUpdateSummary": "Comment progress or a completion summary back to the Linear issue.",
"postUpdatePrompt": "Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.",
"moveStateSummary": "Advance the Linear workflow state as the work progresses.",
"moveStatePrompt": "Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.",
"attachPrSummary": "Link the pull request to the Linear issue when you open it.",
"attachPrPrompt": "Use {{value0}} to attach this PR to the linked Linear issue with a PR link.",
"triageFollowupsSummary": "Set assignee, priority, or estimate, and file parented follow-up tickets.",
"triageFollowupsPrompt": "Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup."
}
}
}
@@ -9194,7 +9204,9 @@
"skillTitle": "Linear skill",
"skillDescription": "Enables agents to read linked tickets and post updates to Linear through Orca.",
"howToUse": "How to use it",
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR."
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR.",
"terminalTitle": "Linear skill setup",
"terminalAriaLabel": "Linear skill install terminal"
}
},
"right": {
+14 -2
View File
@@ -596,7 +596,17 @@
"postUpdate": "Post a progress update",
"moveState": "Move the ticket forward",
"attachPr": "Attach the PR link",
"triageFollowups": "Triage and create follow-ups"
"triageFollowups": "Triage and create follow-ups",
"readTicketSummary": "Pull the linked Linear issue's full context before starting work.",
"readTicketPrompt": "Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.",
"postUpdateSummary": "Comment progress or a completion summary back to the Linear issue.",
"postUpdatePrompt": "Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.",
"moveStateSummary": "Advance the Linear workflow state as the work progresses.",
"moveStatePrompt": "Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.",
"attachPrSummary": "Link the pull request to the Linear issue when you open it.",
"attachPrPrompt": "Use {{value0}} to attach this PR to the linked Linear issue with a PR link.",
"triageFollowupsSummary": "Set assignee, priority, or estimate, and file parented follow-up tickets.",
"triageFollowupsPrompt": "Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup."
}
}
}
@@ -9194,7 +9204,9 @@
"skillTitle": "Linear skill",
"skillDescription": "Enables agents to read linked tickets and post updates to Linear through Orca.",
"howToUse": "How to use it",
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR."
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR.",
"terminalTitle": "Linear skill setup",
"terminalAriaLabel": "Linear skill install terminal"
}
},
"right": {
+14 -2
View File
@@ -596,7 +596,17 @@
"postUpdate": "Post a progress update",
"moveState": "Move the ticket forward",
"attachPr": "Attach the PR link",
"triageFollowups": "Triage and create follow-ups"
"triageFollowups": "Triage and create follow-ups",
"readTicketSummary": "Pull the linked Linear issue's full context before starting work.",
"readTicketPrompt": "Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.",
"postUpdateSummary": "Comment progress or a completion summary back to the Linear issue.",
"postUpdatePrompt": "Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.",
"moveStateSummary": "Advance the Linear workflow state as the work progresses.",
"moveStatePrompt": "Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.",
"attachPrSummary": "Link the pull request to the Linear issue when you open it.",
"attachPrPrompt": "Use {{value0}} to attach this PR to the linked Linear issue with a PR link.",
"triageFollowupsSummary": "Set assignee, priority, or estimate, and file parented follow-up tickets.",
"triageFollowupsPrompt": "Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup."
}
}
}
@@ -9194,7 +9204,9 @@
"skillTitle": "Linear skill",
"skillDescription": "Enables agents to read linked tickets and post updates to Linear through Orca.",
"howToUse": "How to use it",
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR."
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR.",
"terminalTitle": "Linear skill setup",
"terminalAriaLabel": "Linear skill install terminal"
}
},
"right": {
+14 -2
View File
@@ -596,7 +596,17 @@
"postUpdate": "Post a progress update",
"moveState": "Move the ticket forward",
"attachPr": "Attach the PR link",
"triageFollowups": "Triage and create follow-ups"
"triageFollowups": "Triage and create follow-ups",
"readTicketSummary": "Pull the linked Linear issue's full context before starting work.",
"readTicketPrompt": "Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.",
"postUpdateSummary": "Comment progress or a completion summary back to the Linear issue.",
"postUpdatePrompt": "Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.",
"moveStateSummary": "Advance the Linear workflow state as the work progresses.",
"moveStatePrompt": "Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.",
"attachPrSummary": "Link the pull request to the Linear issue when you open it.",
"attachPrPrompt": "Use {{value0}} to attach this PR to the linked Linear issue with a PR link.",
"triageFollowupsSummary": "Set assignee, priority, or estimate, and file parented follow-up tickets.",
"triageFollowupsPrompt": "Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup."
}
}
}
@@ -9194,7 +9204,9 @@
"skillTitle": "Linear skill",
"skillDescription": "Enables agents to read linked tickets and post updates to Linear through Orca.",
"howToUse": "How to use it",
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR."
"howToUseDescription": "Ask an agent working a Linear-linked worktree to read context, post updates, move the ticket, or attach the PR.",
"terminalTitle": "Linear skill setup",
"terminalAriaLabel": "Linear skill install terminal"
}
},
"right": {
+48 -14
View File
@@ -1,34 +1,62 @@
import { translate } from '@/i18n/i18n'
import { createLocalizedCatalog } from '@/i18n/localized-catalog'
import { ORCA_LINEAR_SKILL_NAME } from './agent-feature-install-commands'
import type { SkillUsageExample } from './skill-usage-example'
const LINEAR_SLASH_COMMAND = `/${ORCA_LINEAR_SKILL_NAME}`
export const getLinearUsageExamples = createLocalizedCatalog((): SkillUsageExample[] => [
{
id: 'read-ticket',
title: translate('auto.lib.linear.usage.examples.readTicket', 'Read the linked ticket'),
summary: "Pull the linked Linear issue's full context before starting work.",
prompt:
'Use /orca-linear to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.'
summary: translate(
'auto.lib.linear.usage.examples.readTicketSummary',
"Pull the linked Linear issue's full context before starting work."
),
prompt: translate(
'auto.lib.linear.usage.examples.readTicketPrompt',
'Use {{value0}} to read the linked Linear issue for this worktree, then summarize the goal and acceptance criteria before you start.',
{ value0: LINEAR_SLASH_COMMAND }
)
},
{
id: 'post-update',
title: translate('auto.lib.linear.usage.examples.postUpdate', 'Post a progress update'),
summary: 'Comment progress or a completion summary back to the Linear issue.',
prompt:
'Use /orca-linear to post a completion update on the linked Linear issue with what changed and how it was verified.'
summary: translate(
'auto.lib.linear.usage.examples.postUpdateSummary',
'Comment progress or a completion summary back to the Linear issue.'
),
prompt: translate(
'auto.lib.linear.usage.examples.postUpdatePrompt',
'Use {{value0}} to post a completion update on the linked Linear issue with what changed and how it was verified.',
{ value0: LINEAR_SLASH_COMMAND }
)
},
{
id: 'move-state',
title: translate('auto.lib.linear.usage.examples.moveState', 'Move the ticket forward'),
summary: 'Advance the Linear workflow state as the work progresses.',
prompt:
'Use /orca-linear to move the linked Linear issue to In Review now that the change is ready.'
summary: translate(
'auto.lib.linear.usage.examples.moveStateSummary',
'Advance the Linear workflow state as the work progresses.'
),
prompt: translate(
'auto.lib.linear.usage.examples.moveStatePrompt',
'Use {{value0}} to move the linked Linear issue to In Review now that the change is ready.',
{ value0: LINEAR_SLASH_COMMAND }
)
},
{
id: 'attach-pr',
title: translate('auto.lib.linear.usage.examples.attachPr', 'Attach the PR link'),
summary: 'Link the pull request to the Linear issue when you open it.',
prompt: 'Use /orca-linear to attach this PR to the linked Linear issue with a PR link.'
summary: translate(
'auto.lib.linear.usage.examples.attachPrSummary',
'Link the pull request to the Linear issue when you open it.'
),
prompt: translate(
'auto.lib.linear.usage.examples.attachPrPrompt',
'Use {{value0}} to attach this PR to the linked Linear issue with a PR link.',
{ value0: LINEAR_SLASH_COMMAND }
)
},
{
id: 'triage-followups',
@@ -36,8 +64,14 @@ export const getLinearUsageExamples = createLocalizedCatalog((): SkillUsageExamp
'auto.lib.linear.usage.examples.triageFollowups',
'Triage and create follow-ups'
),
summary: 'Set assignee, priority, or estimate, and file parented follow-up tickets.',
prompt:
'Use /orca-linear to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup.'
summary: translate(
'auto.lib.linear.usage.examples.triageFollowupsSummary',
'Set assignee, priority, or estimate, and file parented follow-up tickets.'
),
prompt: translate(
'auto.lib.linear.usage.examples.triageFollowupsPrompt',
'Use {{value0}} to triage the linked Linear issue — set priority and estimate — and create a parented follow-up ticket for the deferred cleanup.',
{ value0: LINEAR_SLASH_COMMAND }
)
}
])