mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-24 00:55:35 +00:00
* feat(cli): add AstronStudio agent profile Signed-off-by: dongmucat <1127093059@qq.com> * test(cli): make AstronStudio path assertions portable Signed-off-by: dongmucat <1127093059@qq.com> * docs(cli): document AstronStudio install target Signed-off-by: dongmucat <1127093059@qq.com> * fix(cli): rename AstronStudio profile to AStudio Signed-off-by: dongmucat <1127093059@qq.com> * fix(cli): use stable lowercase AStudio agent id Signed-off-by: dongmucat <1127093059@qq.com> --------- Signed-off-by: dongmucat <1127093059@qq.com>
133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, describe, expect, mock, test } from 'bun:test'
|
|
import type { AgentCandidate } from '../../../src/agents/types'
|
|
|
|
interface PromptChoice {
|
|
title: string
|
|
value: AgentCandidate
|
|
}
|
|
|
|
interface PromptOptions {
|
|
choices?: PromptChoice[]
|
|
onRender?: (this: { cursor?: number }) => void
|
|
format?: (selectedTargets: AgentCandidate[]) => AgentCandidate[]
|
|
}
|
|
|
|
const defaultSelectedTargets = (options: PromptOptions): AgentCandidate[] => options.format?.([]) ?? []
|
|
let selectPromptTargets = defaultSelectedTargets
|
|
let renderedChoices: PromptChoice[] = []
|
|
|
|
mock.module('prompts', () => ({
|
|
default: (options: PromptOptions) => {
|
|
renderedChoices = options.choices ?? []
|
|
options.onRender?.call({ cursor: 1 })
|
|
return { selected: selectPromptTargets(options) }
|
|
}
|
|
}))
|
|
|
|
afterEach(() => {
|
|
selectPromptTargets = defaultSelectedTargets
|
|
renderedChoices = []
|
|
})
|
|
|
|
const { resolveInstallTargets } = await import('../../../src/agents/resolver')
|
|
|
|
describe('resolveInstallTargets interactive prompt', () => {
|
|
test('renders AStudio by display name when its directory was detected', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'skillhub-astudio-resolver-'))
|
|
const nativeRootDir = join(home, '.acode', 'skills')
|
|
const profileRootDir = nativeRootDir.replace(/\\/g, '/')
|
|
|
|
try {
|
|
await mkdir(nativeRootDir, { recursive: true })
|
|
await resolveInstallTargets({
|
|
cwd: '/repo',
|
|
home,
|
|
agents: [],
|
|
scope: 'user',
|
|
json: false,
|
|
interactive: true
|
|
})
|
|
|
|
expect(renderedChoices[0]?.title).toBe(`AStudio (${profileRootDir})`)
|
|
} finally {
|
|
await rm(home, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('does not render AStudio when .acode skills is a regular file', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'skillhub-astudio-file-'))
|
|
const rootDir = join(home, '.acode', 'skills')
|
|
|
|
try {
|
|
await mkdir(join(home, '.acode'), { recursive: true })
|
|
await writeFile(rootDir, 'not a directory')
|
|
const targets = await resolveInstallTargets({
|
|
cwd: '/repo',
|
|
home,
|
|
agents: [],
|
|
scope: 'user',
|
|
json: false,
|
|
interactive: true
|
|
})
|
|
|
|
expect(renderedChoices.some(choice => choice.value.agent === 'astudio')).toBe(false)
|
|
expect(targets).toEqual([{
|
|
agent: 'generic',
|
|
rootDir: `${home}/.agents/skills`,
|
|
scope: 'user',
|
|
source: 'fallback'
|
|
}])
|
|
} finally {
|
|
await rm(home, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test('uses the highlighted target when Enter submits an empty multiselect', async () => {
|
|
const detected: AgentCandidate[] = [
|
|
{ agent: 'codex', rootDir: '/repo/.codex/skills', scope: 'project', source: 'detected' },
|
|
{ agent: 'claude-code', rootDir: '/repo/.claude/skills', scope: 'project', source: 'detected' }
|
|
]
|
|
const highlighted = detected[1]!
|
|
|
|
const targets = await resolveInstallTargets({
|
|
cwd: '/repo',
|
|
agents: [],
|
|
json: false,
|
|
interactive: true,
|
|
detected
|
|
})
|
|
|
|
expect(targets).toEqual([highlighted])
|
|
})
|
|
|
|
test('allows selecting generic alongside detected user targets', async () => {
|
|
selectPromptTargets = options => options.choices?.map(choice => choice.value) ?? []
|
|
const codex: AgentCandidate = {
|
|
agent: 'codex',
|
|
rootDir: '/home/u/.codex/skills',
|
|
scope: 'user',
|
|
source: 'detected'
|
|
}
|
|
const generic: AgentCandidate = {
|
|
agent: 'generic',
|
|
rootDir: '/home/u/.agents/skills',
|
|
scope: 'user',
|
|
source: 'fallback'
|
|
}
|
|
|
|
const targets = await resolveInstallTargets({
|
|
cwd: '/repo',
|
|
home: '/home/u',
|
|
agents: [],
|
|
scope: 'user',
|
|
json: false,
|
|
interactive: true,
|
|
detected: [codex]
|
|
})
|
|
|
|
expect(targets).toEqual([codex, generic])
|
|
})
|
|
})
|