mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-04 10:11:46 +03:00
feat(tui): autocomplete skill references
This commit is contained in:
@@ -10,6 +10,7 @@ import { NanobotTui, sessionExitMessage, type AppOptions } from "./app"
|
||||
import type {
|
||||
MessageOptions,
|
||||
RecoveryState,
|
||||
SkillCandidate,
|
||||
SlashCommand,
|
||||
WorkspaceScopePayload,
|
||||
} from "./protocol"
|
||||
@@ -513,6 +514,69 @@ describe("NanobotTui layout", () => {
|
||||
expect(sent).toEqual([])
|
||||
})
|
||||
|
||||
test("completes available skills with arrows, Enter, Tab, and Escape", async () => {
|
||||
setup = await createRenderer({ width: 80, height: 24, screenMode: "alternate-screen" })
|
||||
const sent: string[] = []
|
||||
const app = mount(setup, sent)
|
||||
const ui = app as unknown as {
|
||||
ready: boolean
|
||||
composer: TextareaRenderable
|
||||
skillCandidates: SkillCandidate[]
|
||||
skillMenu: { visible: boolean }
|
||||
}
|
||||
app.accept({ event: "attached", chat_id: "chat" })
|
||||
await waitUntil(() => ui.ready)
|
||||
ui.skillCandidates = [
|
||||
{ name: "simplify", description: "Simplify code", source: "workspace" },
|
||||
{ name: "verify", description: "Verify public behavior", source: "builtin" },
|
||||
]
|
||||
|
||||
await setup.mockInput.typeText("$")
|
||||
expect(ui.skillMenu.visible).toBe(true)
|
||||
setup.mockInput.pressArrow("down")
|
||||
setup.mockInput.pressEnter()
|
||||
await waitUntil(() => ui.composer.plainText === "$verify ")
|
||||
expect(ui.skillMenu.visible).toBe(false)
|
||||
expect(sent).toEqual([])
|
||||
|
||||
ui.composer.setText("")
|
||||
await setup.mockInput.typeText("please $sim")
|
||||
expect(ui.skillMenu.visible).toBe(true)
|
||||
setup.mockInput.pressTab()
|
||||
expect(ui.composer.plainText).toBe("please $simplify ")
|
||||
expect(ui.skillMenu.visible).toBe(false)
|
||||
|
||||
ui.composer.setText("")
|
||||
await setup.mockInput.typeText("$")
|
||||
expect(ui.skillMenu.visible).toBe(true)
|
||||
setup.mockInput.pressEscape()
|
||||
await waitUntil(() => !ui.skillMenu.visible)
|
||||
expect(ui.skillMenu.visible).toBe(false)
|
||||
expect(ui.composer.plainText).toBe("$")
|
||||
|
||||
ui.composer.setText("")
|
||||
await setup.mockInput.typeText("请用 $ver")
|
||||
expect(ui.skillMenu.visible).toBe(true)
|
||||
setup.mockInput.pressTab()
|
||||
expect(ui.composer.plainText).toBe("请用 $verify ")
|
||||
await setup.mockInput.typeText("now")
|
||||
expect(ui.composer.plainText).toBe("请用 $verify now")
|
||||
|
||||
ui.composer.setText("use $verify later")
|
||||
ui.composer.cursorOffset = 8
|
||||
await waitUntil(() => ui.skillMenu.visible)
|
||||
ui.composer.cursorOffset = ui.composer.plainText.length
|
||||
await waitUntil(() => !ui.skillMenu.visible)
|
||||
|
||||
ui.composer.setText("")
|
||||
await setup.mockInput.typeText("$missing")
|
||||
expect(ui.skillMenu.visible).toBe(true)
|
||||
ui.composer.submit()
|
||||
await waitUntil(() => sent.length === 1)
|
||||
expect(sent).toEqual(["$missing"])
|
||||
expect(ui.skillMenu.visible).toBe(false)
|
||||
})
|
||||
|
||||
test("runs bang commands through the gateway without steering the agent", async () => {
|
||||
setup = await createRenderer({ width: 80, height: 24, screenMode: "alternate-screen" })
|
||||
const sent: string[] = []
|
||||
|
||||
Reference in New Issue
Block a user