diff --git a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx index 8cd4fbfcf..742012bff 100644 --- a/packages/opencode/src/cli/cmd/run/footer.prompt.tsx +++ b/packages/opencode/src/cli/cmd/run/footer.prompt.tsx @@ -12,6 +12,7 @@ import fuzzysort from "fuzzysort" import path from "path" import { createEffect, createMemo, createResource, createSignal, onCleanup, onMount, type Accessor } from "solid-js" import * as Locale from "@/util/locale" +import { ConfigReference } from "@/config/reference" import { createPromptHistory, isExitCommand, @@ -283,6 +284,19 @@ export function createPromptState(input: PromptInput): PromptState { }, })) }) + const referenceNames = createMemo(() => + input + .agents() + .filter((item) => item.options.reference !== undefined) + .map((item) => item.name) + .toSorted((a, b) => b.length - a.length), + ) + const referenceFilePath = (value: string) => { + for (const name of referenceNames()) { + if (value.startsWith(`${name}:/`)) return { name, path: value.slice(name.length + 2).replace(/\\/g, "/") } + if (value.startsWith(`${name}/`)) return { name, path: value.slice(name.length + 1).replace(/\\/g, "/") } + } + } const resources = createMemo(() => { return input.resources().map((item) => ({ kind: "mention", @@ -331,7 +345,10 @@ export function createPromptState(input: PromptInput): PromptState { return a.localeCompare(b) }) .map((item): Auto => { - const url = pathToFileURL(path.resolve(input.directory, item)) + const reference = referenceFilePath(item) + const url = reference + ? new URL(ConfigReference.fileUrl(reference.name, reference.path)) + : pathToFileURL(path.resolve(input.directory, item)) let filename = item if (next.line && !item.endsWith("/")) { filename = `${item}#${next.line.start}${next.line.end ? `-${next.line.end}` : ""}` @@ -366,7 +383,9 @@ export function createPromptState(input: PromptInput): PromptState { }, { initialValue: [] as Auto[] }, ) - const mentionOptions = createMemo(() => [...agents(), ...files(), ...resources()]) + const mentionOptions = createMemo(() => + referenceFilePath(removeLineRange(query())) ? files() : [...agents(), ...files(), ...resources()], + ) const slashOptions = createMemo(() => { const builtins = [ { kind: "slash", name: "new", display: "/new", description: "start a new session" } satisfies SlashOption, diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 7f390f0eb..70e981057 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -18,6 +18,7 @@ import { Locale } from "@/util/locale" import type { PromptInfo } from "./history" import { useFrecency } from "./frecency" import { useBindings } from "../../keymap" +import { ConfigReference } from "@/config/reference" function removeLineRange(input: string) { const hashIndex = input.lastIndexOf("#") @@ -225,8 +226,10 @@ export function Autocomplete(props: { function createFilePart(item: string, lineRange?: { startLine: number; endLine?: number }) { const baseDir = (sync.path.directory || process.cwd()).replace(/\/+$/, "") - const fullPath = path.isAbsolute(item) ? item : path.join(baseDir, item) - const urlObj = pathToFileURL(fullPath) + const reference = ConfigReference.parseFilePath(item, sync.data.config.reference) + const urlObj = reference + ? new URL(ConfigReference.fileUrl(reference.name, reference.path)) + : pathToFileURL(path.isAbsolute(item) ? item : path.join(baseDir, item)) const filename = lineRange && !item.endsWith("/") ? `${item}#${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}` @@ -261,6 +264,9 @@ export function Autocomplete(props: { } function normalizeMentionPath(filePath: string) { + const reference = ConfigReference.parseFilePath(filePath, sync.data.config.reference) + if (reference) return ConfigReference.formatFilePath(reference.name, reference.path) + const baseDir = sync.path.directory || process.cwd() const absolute = path.resolve(filePath) const relative = path.relative(baseDir, absolute) @@ -430,11 +436,15 @@ export function Autocomplete(props: { const filesValue = files() const agentsValue = agents() const commandsValue = commands() + const searchValue = search() + const reference = ConfigReference.parseFilePath(removeLineRange(searchValue), sync.data.config.reference) const mixed: AutocompleteOption[] = - store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue] - - const searchValue = search() + store.visible === "@" + ? reference + ? filesValue || [] + : [...agentsValue, ...(filesValue || []), ...mcpResources()] + : [...commandsValue] if (!searchValue) { return mixed @@ -506,7 +516,7 @@ export function Autocomplete(props: { const currentCursorOffset = input.cursorOffset const displayText = selected.display.trimEnd() - const path = displayText.startsWith("@") ? displayText.slice(1) : displayText + const path = selected.path ?? (displayText.startsWith("@") ? displayText.slice(1) : displayText) input.cursorOffset = store.index const startCursor = input.logicalCursor