mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix(settings): preserve Linear freshness and dialog translations
This commit is contained in:
@@ -16,7 +16,8 @@ const UPDATE_COMMAND = 'npx skills update orca-linear --global'
|
||||
const mocks = vi.hoisted(() => ({
|
||||
panelProps: [] as Record<string, unknown>[],
|
||||
runtime: 'native' as 'native' | 'wsl',
|
||||
skillInstalled: true
|
||||
skillInstalled: true,
|
||||
updateSkillName: 'orca-linear'
|
||||
}))
|
||||
|
||||
vi.mock('./AgentSkillSetupPanel', () => ({
|
||||
@@ -41,7 +42,10 @@ vi.mock('./CliSkillRuntimeSetup', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/linear-agent-skill-update-command', () => ({
|
||||
getLinearAgentSkillUpdateCommand: () => UPDATE_COMMAND
|
||||
getLinearAgentSkillUpdateTarget: () => ({
|
||||
command: UPDATE_COMMAND,
|
||||
skillName: mocks.updateSkillName
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useInstalledAgentSkills', () => ({
|
||||
@@ -90,6 +94,7 @@ describe('LinearAgentSkillPane', () => {
|
||||
mocks.panelProps.length = 0
|
||||
mocks.runtime = 'native'
|
||||
mocks.skillInstalled = true
|
||||
mocks.updateSkillName = 'orca-linear'
|
||||
})
|
||||
|
||||
it('renders the Linear skill card and the usage examples', () => {
|
||||
@@ -124,4 +129,11 @@ describe('LinearAgentSkillPane', () => {
|
||||
|
||||
expect(mocks.panelProps.at(-1)?.freshnessSkillName).toBeUndefined()
|
||||
})
|
||||
|
||||
it('checks freshness under the legacy name when that is the installed update target', async () => {
|
||||
mocks.updateSkillName = 'linear-tickets'
|
||||
await renderPane()
|
||||
|
||||
expect(mocks.panelProps.at(-1)?.freshnessSkillName).toBe('linear-tickets')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
AGENT_SKILL_CLI_PREREQUISITE_NOTICE,
|
||||
ensureOrcaCliAvailableForAgentSkillTerminal
|
||||
} from '@/lib/agent-skill-cli-prerequisite'
|
||||
import { getLinearAgentSkillUpdateCommand } from '@/lib/linear-agent-skill-update-command'
|
||||
import { getLinearAgentSkillUpdateTarget } from '@/lib/linear-agent-skill-update-command'
|
||||
import { getLinearUsageExamples } from '@/lib/linear-usage-examples'
|
||||
import type { SkillUsageExample } from '@/lib/skill-usage-example'
|
||||
import {
|
||||
@@ -69,16 +69,19 @@ export function LinearAgentSkillPane(): React.JSX.Element {
|
||||
),
|
||||
[activeSkillRuntime.agentRuntime, activeSkillRuntime.installDisabledReason]
|
||||
)
|
||||
const updateTarget = useMemo(
|
||||
() => getLinearAgentSkillUpdateTarget(linearSkills, linearSkillInstalled),
|
||||
[linearSkillInstalled, linearSkills]
|
||||
)
|
||||
const updateCommand = useMemo(() => {
|
||||
const command = getLinearAgentSkillUpdateCommand(linearSkills, linearSkillInstalled)
|
||||
const command = updateTarget.command
|
||||
return activeSkillRuntime.installDisabledReason
|
||||
? command
|
||||
: buildSkillCommandForRuntime(command, activeSkillRuntime.agentRuntime)
|
||||
}, [
|
||||
activeSkillRuntime.agentRuntime,
|
||||
activeSkillRuntime.installDisabledReason,
|
||||
linearSkillInstalled,
|
||||
linearSkills
|
||||
updateTarget.command
|
||||
])
|
||||
|
||||
return (
|
||||
@@ -134,7 +137,7 @@ export function LinearAgentSkillPane(): React.JSX.Element {
|
||||
// Why: the local-host-only freshness scan cannot vouch for a WSL runtime,
|
||||
// so fall back to the presence-only pill there (mirrors the other skills).
|
||||
freshnessSkillName={
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl' ? undefined : ORCA_LINEAR_SKILL_NAME
|
||||
activeSkillRuntime.agentRuntime?.runtime === 'wsl' ? undefined : updateTarget.skillName
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ const updateCapableCallers = new Map<string, readonly string[]>([
|
||||
['COMPUTER_USE_SKILL_UPDATE_COMMAND', 'installedCommand={updateCommand}']
|
||||
],
|
||||
[
|
||||
// Why: the Linear settings section shares getLinearAgentSkillUpdateCommand so
|
||||
// legacy-only installs still update through the installed skill name.
|
||||
// Why: the Linear settings section shares the update-target resolver so
|
||||
// legacy-only installs keep the command and freshness identity aligned.
|
||||
'src/renderer/src/components/settings/LinearAgentSkillPane.tsx',
|
||||
['getLinearAgentSkillUpdateCommand', 'installedCommand={updateCommand}']
|
||||
['getLinearAgentSkillUpdateTarget', 'installedCommand={updateCommand}']
|
||||
],
|
||||
[
|
||||
'src/renderer/src/components/settings/EphemeralVmsPane.tsx',
|
||||
|
||||
@@ -9192,11 +9192,11 @@
|
||||
"androidApk": "APK de Android"
|
||||
},
|
||||
"SkillUsageExampleDialog": {
|
||||
"copiedPrompt": "Copied example prompt.",
|
||||
"copyFailed": "Failed to copy prompt.",
|
||||
"copyExampleAria": "Copy {{value0}} example prompt",
|
||||
"done": "Done",
|
||||
"copyPrompt": "Copy prompt"
|
||||
"copiedPrompt": "Prompt de ejemplo copiado.",
|
||||
"copyFailed": "No se pudo copiar el prompt.",
|
||||
"copyExampleAria": "Copiar prompt de ejemplo {{value0}}",
|
||||
"done": "Hecho",
|
||||
"copyPrompt": "Copiar prompt"
|
||||
},
|
||||
"LinearAgentSkillPane": {
|
||||
"title": "Linear",
|
||||
|
||||
@@ -9192,11 +9192,11 @@
|
||||
"androidApk": "Android APK"
|
||||
},
|
||||
"SkillUsageExampleDialog": {
|
||||
"copiedPrompt": "Copied example prompt.",
|
||||
"copyFailed": "Failed to copy prompt.",
|
||||
"copyExampleAria": "Copy {{value0}} example prompt",
|
||||
"done": "Done",
|
||||
"copyPrompt": "Copy prompt"
|
||||
"copiedPrompt": "プロンプトの例をコピーしました。",
|
||||
"copyFailed": "プロンプトのコピーに失敗しました。",
|
||||
"copyExampleAria": "{{value0}} プロンプトの例をコピーします",
|
||||
"done": "完了",
|
||||
"copyPrompt": "プロンプトのコピー"
|
||||
},
|
||||
"LinearAgentSkillPane": {
|
||||
"title": "Linear",
|
||||
|
||||
@@ -9192,11 +9192,11 @@
|
||||
"androidApk": "Android APK"
|
||||
},
|
||||
"SkillUsageExampleDialog": {
|
||||
"copiedPrompt": "Copied example prompt.",
|
||||
"copyFailed": "Failed to copy prompt.",
|
||||
"copyExampleAria": "Copy {{value0}} example prompt",
|
||||
"done": "Done",
|
||||
"copyPrompt": "Copy prompt"
|
||||
"copiedPrompt": "예시 프롬프트를 복사했습니다.",
|
||||
"copyFailed": "프롬프트를 복사하지 못했습니다.",
|
||||
"copyExampleAria": "{{value0}} 예시 프롬프트 복사",
|
||||
"done": "완료",
|
||||
"copyPrompt": "프롬프트 복사"
|
||||
},
|
||||
"LinearAgentSkillPane": {
|
||||
"title": "Linear",
|
||||
|
||||
@@ -9192,11 +9192,11 @@
|
||||
"androidApk": "Android APK"
|
||||
},
|
||||
"SkillUsageExampleDialog": {
|
||||
"copiedPrompt": "Copied example prompt.",
|
||||
"copyFailed": "Failed to copy prompt.",
|
||||
"copyExampleAria": "Copy {{value0}} example prompt",
|
||||
"done": "Done",
|
||||
"copyPrompt": "Copy prompt"
|
||||
"copiedPrompt": "复制示例提示。",
|
||||
"copyFailed": "复制提示失败。",
|
||||
"copyExampleAria": "复制 {{value0}} 示例提示",
|
||||
"done": "完成",
|
||||
"copyPrompt": "复制提示"
|
||||
},
|
||||
"LinearAgentSkillPane": {
|
||||
"title": "Linear",
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { DiscoveredSkill } from '../../../shared/skills'
|
||||
import {
|
||||
LINEAR_TICKETS_SKILL_UPDATE_COMMAND,
|
||||
ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
} from './agent-feature-install-commands'
|
||||
import { getLinearAgentSkillUpdateTarget } from './linear-agent-skill-update-command'
|
||||
|
||||
function skill(name: string): DiscoveredSkill {
|
||||
return {
|
||||
id: name,
|
||||
name,
|
||||
description: null,
|
||||
providers: ['agent-skills'],
|
||||
sourceKind: 'home',
|
||||
sourceLabel: 'Agent skills home',
|
||||
rootPath: '/home/test/.agents/skills',
|
||||
directoryPath: `/home/test/.agents/skills/${name}`,
|
||||
skillFilePath: `/home/test/.agents/skills/${name}/SKILL.md`,
|
||||
installed: true,
|
||||
fileCount: 1,
|
||||
updatedAt: null
|
||||
}
|
||||
}
|
||||
|
||||
describe('getLinearAgentSkillUpdateTarget', () => {
|
||||
it('targets the canonical skill for missing, canonical, and dual-name installs', () => {
|
||||
expect(getLinearAgentSkillUpdateTarget([], false)).toEqual({
|
||||
skillName: 'orca-linear',
|
||||
command: ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
})
|
||||
expect(getLinearAgentSkillUpdateTarget([skill('orca-linear')], true)).toEqual({
|
||||
skillName: 'orca-linear',
|
||||
command: ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
})
|
||||
expect(
|
||||
getLinearAgentSkillUpdateTarget([skill('orca-linear'), skill('linear-tickets')], true)
|
||||
).toEqual({
|
||||
skillName: 'orca-linear',
|
||||
command: ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps the update and freshness identity on a legacy-only install', () => {
|
||||
expect(getLinearAgentSkillUpdateTarget([skill('linear-tickets')], true)).toEqual({
|
||||
skillName: 'linear-tickets',
|
||||
command: LINEAR_TICKETS_SKILL_UPDATE_COMMAND
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -10,12 +10,17 @@ import {
|
||||
ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
} from '@/lib/agent-feature-install-commands'
|
||||
|
||||
// Why: legacy-only installs must update the installed legacy skill, while
|
||||
// fresh/canonical/both-name states should move through the canonical name.
|
||||
export function getLinearAgentSkillUpdateCommand(
|
||||
export type LinearAgentSkillUpdateTarget = {
|
||||
skillName: typeof ORCA_LINEAR_SKILL_NAME | typeof LINEAR_TICKETS_SKILL_NAME
|
||||
command: string
|
||||
}
|
||||
|
||||
// Why: legacy-only installs must update and report freshness for the installed
|
||||
// legacy skill, while fresh/canonical/both-name states use the canonical name.
|
||||
export function getLinearAgentSkillUpdateTarget(
|
||||
skills: readonly DiscoveredSkill[],
|
||||
installed: boolean
|
||||
): string {
|
||||
): LinearAgentSkillUpdateTarget {
|
||||
const canonicalSkillInstalled = hasInstalledAgentSkill(skills, ORCA_LINEAR_SKILL_NAME, {
|
||||
sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
})
|
||||
@@ -23,6 +28,13 @@ export function getLinearAgentSkillUpdateCommand(
|
||||
sourceKinds: GLOBAL_AGENT_SKILL_SOURCE_KINDS
|
||||
})
|
||||
return !installed || canonicalSkillInstalled || !legacySkillInstalled
|
||||
? ORCA_LINEAR_SKILL_UPDATE_COMMAND
|
||||
: LINEAR_TICKETS_SKILL_UPDATE_COMMAND
|
||||
? { skillName: ORCA_LINEAR_SKILL_NAME, command: ORCA_LINEAR_SKILL_UPDATE_COMMAND }
|
||||
: { skillName: LINEAR_TICKETS_SKILL_NAME, command: LINEAR_TICKETS_SKILL_UPDATE_COMMAND }
|
||||
}
|
||||
|
||||
export function getLinearAgentSkillUpdateCommand(
|
||||
skills: readonly DiscoveredSkill[],
|
||||
installed: boolean
|
||||
): string {
|
||||
return getLinearAgentSkillUpdateTarget(skills, installed).command
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user