Address managed skill PR feedback

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-06-22 16:06:30 -07:00
co-authored by Orca
parent eae4cb4dc8
commit f89a0b25cf
10 changed files with 56 additions and 11 deletions
+2 -1
View File
@@ -9,6 +9,7 @@ import { app, BrowserWindow, ipcMain, nativeTheme } from 'electron'
import { electronApp, is } from '@electron-toolkit/utils'
import * as QRCode from 'qrcode'
import { Store, initDataPath } from './persistence'
import { shouldEmitManagedAgentSkillFallback } from '../shared/skills'
import { applyAppIcon } from './app-icon'
import { StatsCollector, initStatsPath } from './stats/collector'
import { ClaudeUsageStore, initClaudeUsagePath } from './claude-usage/store'
@@ -1643,7 +1644,7 @@ app.whenReady().then(async () => {
...(remoteRuntime ? { remoteRuntime } : {}),
...(!remoteRuntime && discoveryTarget ? { discoveryTarget } : {})
})
if (result.status === 'fallback') {
if (result.status === 'fallback' && shouldEmitManagedAgentSkillFallback(result)) {
sendManagedSkillFallback(result)
} else if (result.status === 'updated') {
sendManagedSkillUpdated(result)
@@ -5,6 +5,8 @@ export function isDiscoveredManagedSkill(
skill: DiscoveredSkill,
skillName: ManagedAgentSkillName
): boolean {
// Why: frontmatter names can be display labels, while the folder basename is
// the install identity used by the skills CLI.
const expected = normalizeSkillName(skillName)
return (
normalizeSkillName(skill.name) === expected ||
@@ -68,7 +68,7 @@ export function selectSingleGlobalManagedSkillCandidate(args: {
fallback: { reason: 'project-install', scope: 'project' }
}
}
if (bundledOrPluginCandidates.length > 0 || homeCandidates.length !== 1) {
if (bundledOrPluginCandidates.length > 0) {
return {
status: 'fallback',
fallback: {
@@ -82,6 +82,12 @@ export function selectSingleGlobalManagedSkillCandidate(args: {
}
}
}
if (homeCandidates.length !== 1) {
return {
status: 'fallback',
fallback: { reason: 'ambiguous-install', scope: 'global' }
}
}
const candidate = homeCandidates[0]
if (candidate.directoryIsSymlink || candidate.skillFileIsSymlink) {
@@ -10,6 +10,7 @@ type ManagedSkillLockEntry = {
skillFolderHash?: unknown
}
// Why: managed-skill readiness validation currently trusts lockfile schema v3 only.
const SUPPORTED_LOCKFILE_SCHEMA_VERSION = 3
export async function readManagedSkillLockEntry(args: {
@@ -43,6 +44,7 @@ export async function readManagedSkillLockEntry(args: {
if (!entry) {
return { ok: false, reason: 'lock-entry-missing' }
}
// Why: only trust lock entries tied to Orca-managed source and canonical skill path.
if (!isManagedLockEntryForSkill(entry, args.skillName)) {
return { ok: false, reason: 'lock-entry-unmanaged-source' }
}
@@ -6,6 +6,10 @@ import { createRoot, type Root } from 'react-dom/client'
import type { CliInstallStatus } from '../../../../shared/cli-install-types'
import type { DiscoveredSkill } from '../../../../shared/skills'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
LINEAR_TICKETS_SKILL_UPDATE_COMMAND,
ORCA_LINEAR_SKILL_UPDATE_COMMAND
} from '@/lib/agent-feature-install-commands'
import {
LinearAgentSkillSetupPrompt,
_linearAgentSkillSetupPromptInternalsForTests
@@ -156,7 +160,7 @@ describe('LinearAgentSkillSetupPrompt update command', () => {
await renderPrompt()
expect(mocks.panelProps.at(-1)).toEqual(
expect.objectContaining({ installedCommand: 'npx skills update orca-linear --global' })
expect.objectContaining({ installedCommand: ORCA_LINEAR_SKILL_UPDATE_COMMAND })
)
})
@@ -166,7 +170,7 @@ describe('LinearAgentSkillSetupPrompt update command', () => {
await renderPrompt()
expect(mocks.panelProps.at(-1)).toEqual(
expect.objectContaining({ installedCommand: 'npx skills update linear-tickets --global' })
expect.objectContaining({ installedCommand: LINEAR_TICKETS_SKILL_UPDATE_COMMAND })
)
})
@@ -176,7 +180,7 @@ describe('LinearAgentSkillSetupPrompt update command', () => {
await renderPrompt()
expect(mocks.panelProps.at(-1)).toEqual(
expect.objectContaining({ installedCommand: 'npx skills update orca-linear --global' })
expect.objectContaining({ installedCommand: ORCA_LINEAR_SKILL_UPDATE_COMMAND })
)
})
})
@@ -91,22 +91,42 @@ export function ManagedAgentSkillSetupDialogHost(): React.JSX.Element | null {
)
const recheck = useCallback(async (): Promise<void> => {
if (!active) {
if (!active || rechecking) {
return
}
const recheckUiKey = active.uiKey
setRechecking(true)
try {
const result = await window.api.skills.ensureManagedReady({ ...active.request, force: true })
if (result.status === 'fallback') {
setDialogState((current) => replaceActiveAfterManagedAgentSkillRecheck(current, result))
setDialogState((current) => {
if (current.active?.uiKey !== recheckUiKey) {
return current
}
const previousKey = current.active.uiKey
const next = replaceActiveAfterManagedAgentSkillRecheck(current, result)
if (next.active?.uiKey !== previousKey) {
queuedKeysRef.current.delete(previousKey)
if (next.active) {
queuedKeysRef.current.add(next.active.uiKey)
}
}
return next
})
return
}
setDialogState((current) => {
if (current.active?.uiKey !== recheckUiKey) {
return current
}
queuedKeysRef.current.delete(recheckUiKey)
return advanceManagedAgentSkillFallbackQueue(current)
})
notifyInstalledAgentSkillsChanged()
advanceQueue()
} finally {
setRechecking(false)
}
}, [active, advanceQueue])
}, [active, rechecking])
if (!active) {
return null
@@ -188,7 +208,13 @@ export function ManagedAgentSkillSetupDialogHost(): React.JSX.Element | null {
/>
) : (
<div className="px-6 pt-4 pb-3">
<Button type="button" variant="outline" size="sm" onClick={() => void recheck()}>
<Button
type="button"
variant="outline"
size="sm"
disabled={loading}
onClick={() => void recheck()}
>
<RefreshCw className={loading ? 'size-3.5 animate-spin' : 'size-3.5'} />
{translate(
'auto.components.skills.ManagedAgentSkillSetupDialogHost.recheck',
@@ -95,7 +95,7 @@ export async function discoverInstalledAgentSkills(
const inFlightDiscovery = pendingDiscoveryByTarget.get(key)
if (inFlightDiscovery) {
if (!force || pendingDiscoverySatisfiesForcedRefreshByTarget.get(key)) {
if (!force) {
return inFlightDiscovery
}
try {
+1
View File
@@ -2312,6 +2312,7 @@ function createSkillsApi(): NonNullable<Partial<PreloadApi>['skills']> {
scannedAt: Date.now()
})),
ensureManagedReady: (request: ManagedAgentSkillEnsureRequest) => {
// Why: web clients cannot inspect or mutate trusted host skill installs.
const uiKey = ['remote', '', request.skillName, request.context].join(':')
const cooldownUntil = cooldownUntilByKey.get(uiKey)
if (!request.force && cooldownUntil && cooldownUntil > Date.now()) {
@@ -11,6 +11,7 @@ export function buildAgentFeatureSkillInstallCommand(skillNames: readonly string
if (skillNames.length === 0) {
throw new Error('At least one skill name is required.')
}
// Why: keep setup commands fully non-interactive in both npx and skills layers.
return `npx --yes skills add ${ORCA_SKILLS_REPOSITORY_URL} --skill ${skillNames.join(' ')} --global --yes`
}
@@ -19,6 +20,7 @@ export function buildAgentFeatureSkillUpdateCommand(skillName: string): string {
if (!trimmedSkillName) {
throw new Error('A skill name is required.')
}
// Why: keep setup commands fully non-interactive in both npx and skills layers.
return `npx --yes skills update ${trimmedSkillName} --global --yes`
}
+1
View File
@@ -313,6 +313,7 @@ export function getDefaultSettings(homedir: string): GlobalSettings {
agentDefaultEnv: { ...DEFAULT_TUI_AGENT_ENV },
agentYoloDefaultsMigrated: true,
agentStatusHooksEnabled: true,
// Why: keep managed skill readiness proactive; users can opt out in Agents settings.
managedAgentSkillBackgroundUpdatesEnabled: true,
tabAutoGenerateTitle: false,
confirmClosePinnedTab: true,