Feature/at agent mention (#8571)

Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-04-22 10:17:43 -04:00
committed by GitHub
parent 2bbf4dd229
commit 04514c6d20
7 changed files with 99 additions and 18 deletions
+2 -1
View File
@@ -4165,7 +4165,8 @@
"enum": [
"Builtin",
"Recipe",
"Skill"
"Skill",
"Agent"
]
},
"ConfigKey": {
+1 -1
View File
@@ -80,7 +80,7 @@ export type CheckProviderRequest = {
provider: string;
};
export type CommandType = 'Builtin' | 'Recipe' | 'Skill';
export type CommandType = 'Builtin' | 'Recipe' | 'Skill' | 'Agent';
/**
* Configuration key metadata for provider setup
+2
View File
@@ -36,6 +36,8 @@ export const getItemIcon = (item: DisplayItem): IconInfo => {
return { Icon: BookOpen, color: '#10b981' }; // Green
case 'Skill':
return { Icon: Sparkles, color: '#8b5cf6' }; // Purple
case 'Agent':
return { Icon: Terminal, color: '#f59e0b' }; // Amber
case 'Directory':
return { Icon: Folder, color: '#f59e0b' }; // Amber
default: {
+37 -14
View File
@@ -38,11 +38,12 @@ const i18n = defineMessages({
type DisplayItemType = CommandType | 'Directory' | 'File';
const typeOrder: Record<DisplayItemType, number> = {
Directory: 0,
File: 1,
Builtin: 2,
Skill: 3,
Recipe: 4,
Agent: 0,
Directory: 1,
File: 2,
Builtin: 3,
Skill: 4,
Recipe: 5,
};
export interface DisplayItem {
@@ -426,7 +427,9 @@ const MentionPopover = forwardRef<
);
let finalScore = bestMatch.score;
if (finalScore > 0 && currentWorkingDir) {
if (finalScore > 0 && file.itemType === 'Agent') {
finalScore += 100;
} else if (finalScore > 0 && currentWorkingDir) {
const depth = file.extra.replace(currentWorkingDir, '').split('/').length - 1;
finalScore += depth <= 1 ? 50 : depth <= 2 ? 30 : depth <= 3 ? 15 : 0;
}
@@ -449,6 +452,9 @@ const MentionPopover = forwardRef<
}, [items, query, currentWorkingDir]);
const getSelectionText = (item: DisplayItem): string => {
if (item.itemType === 'Agent') {
return '@' + item.name + ' ';
}
if (item.itemType === 'Skill') {
return `Use the ${item.name} skill to `;
}
@@ -486,17 +492,34 @@ const MentionPopover = forwardRef<
throwOnError: true,
});
if (cancelled) return;
const commandItems: DisplayItem[] = (response.data?.commands || []).map((cmd) => ({
name: cmd.command,
extra: cmd.help,
itemType: cmd.command_type,
relativePath: cmd.command,
}));
const commandItems: DisplayItem[] = (response.data?.commands || [])
.filter((cmd) => cmd.command_type !== 'Agent')
.map((cmd) => ({
name: cmd.command,
extra: cmd.help,
itemType: cmd.command_type,
relativePath: cmd.command,
}));
setItems(commandItems);
} else {
const scannedFiles = await scanDirectoryFromRoot(currentWorkingDir || getDefaultStartPath());
// Fetch agents from server and scan files in parallel
const [agentResponse, scannedFiles] = await Promise.all([
getSlashCommands({
query: { working_dir: currentWorkingDir },
throwOnError: true,
}).catch(() => null),
scanDirectoryFromRoot(currentWorkingDir || getDefaultStartPath()),
]);
if (cancelled) return;
setItems(scannedFiles);
const agentItems: DisplayItem[] = (agentResponse?.data?.commands || [])
.filter((cmd) => cmd.command_type === 'Agent')
.map((cmd) => ({
name: cmd.command,
extra: cmd.help,
itemType: cmd.command_type,
relativePath: cmd.command,
}));
setItems([...agentItems, ...scannedFiles]);
}
} catch (error) {
if (!cancelled) {