Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { findRichTextTriggerQuery } from "@tutti-os/ui-rich-text/editor";
|
|
import type { RichTextTriggerConfig } from "@tutti-os/ui-rich-text/types";
|
|
import type { AgentGUIProviderSkillOption } from "./agentGuiNodeTypes";
|
|
import { skillTriggerForPrefix } from "./agentSkillOptions";
|
|
|
|
const agentComposerTriggerConfigs: readonly RichTextTriggerConfig[] = [
|
|
{ trigger: "/", boundary: "whitespace" },
|
|
{ trigger: "$", boundary: "whitespace" }
|
|
];
|
|
|
|
export interface AgentComposerTriggerQueryMatch {
|
|
end: number;
|
|
prefix: "$" | "/";
|
|
query: string;
|
|
start: number;
|
|
}
|
|
|
|
export function getAgentComposerTriggerQueryMatch(
|
|
draft: string
|
|
): AgentComposerTriggerQueryMatch | null {
|
|
const query = findRichTextTriggerQuery(
|
|
draft,
|
|
draft.length,
|
|
agentComposerTriggerConfigs
|
|
);
|
|
if (!query || (query.trigger !== "/" && query.trigger !== "$")) {
|
|
return null;
|
|
}
|
|
return {
|
|
end: query.to,
|
|
prefix: query.trigger,
|
|
query: query.keyword,
|
|
start: query.from
|
|
};
|
|
}
|
|
|
|
export function getPromptStartSlashCommandQuery(draft: string): string | null {
|
|
const match = getAgentComposerTriggerQueryMatch(draft);
|
|
if (!match || match.prefix !== "/") {
|
|
return null;
|
|
}
|
|
return match.start === leadingWhitespaceLength(draft) ? match.query : null;
|
|
}
|
|
|
|
export function draftForSlashCommandTrigger(input: {
|
|
commandName: string;
|
|
currentDraft?: string;
|
|
}): string {
|
|
const commandDraft = `/${input.commandName.trim()} `;
|
|
const currentDraft = input.currentDraft ?? "";
|
|
const match = getAgentComposerTriggerQueryMatch(currentDraft);
|
|
return match?.prefix === "/" &&
|
|
match.start === leadingWhitespaceLength(currentDraft)
|
|
? `${currentDraft.slice(0, match.start)}${commandDraft}`
|
|
: commandDraft;
|
|
}
|
|
|
|
export function filterProviderSkillsForTrigger(input: {
|
|
skills: readonly AgentGUIProviderSkillOption[];
|
|
query: string;
|
|
triggerPrefix: "$" | "/";
|
|
}): AgentGUIProviderSkillOption[] {
|
|
const normalizedQuery = input.query.trim().toLowerCase();
|
|
return input.skills.filter((skill) => {
|
|
const trigger = skillTriggerForPrefix(skill, input.triggerPrefix);
|
|
if (!normalizedQuery) {
|
|
return true;
|
|
}
|
|
const name = skill.name.trim().toLowerCase();
|
|
const normalizedTrigger = trigger.trim().toLowerCase();
|
|
const description = skill.description?.trim().toLowerCase() ?? "";
|
|
return (
|
|
name.startsWith(normalizedQuery) ||
|
|
normalizedTrigger.slice(1).startsWith(normalizedQuery) ||
|
|
description.includes(normalizedQuery)
|
|
);
|
|
});
|
|
}
|
|
|
|
export function draftForProviderSkillTrigger(input: {
|
|
skill: AgentGUIProviderSkillOption;
|
|
currentDraft?: string;
|
|
match?: AgentComposerTriggerQueryMatch | null;
|
|
}): string {
|
|
const currentDraft = input.currentDraft ?? "";
|
|
const match =
|
|
input.match === undefined
|
|
? getAgentComposerTriggerQueryMatch(currentDraft)
|
|
: input.match;
|
|
const trigger = skillTriggerForPrefix(input.skill, match?.prefix);
|
|
if (!trigger) {
|
|
return currentDraft;
|
|
}
|
|
if (!match) {
|
|
return `${trigger} `;
|
|
}
|
|
return `${currentDraft.slice(0, match.start)}${trigger} ${currentDraft.slice(
|
|
match.end
|
|
)}`;
|
|
}
|
|
|
|
function leadingWhitespaceLength(value: string): number {
|
|
const match = /^(\s*)/.exec(value);
|
|
return match?.[1]?.length ?? 0;
|
|
}
|