forked from bai/curriculum-project-hub
feat: add slash command help
This commit is contained in:
+26
-43
@@ -38,6 +38,7 @@ import { MessageBatcher, messageBatchKey, type MessageBatcherOptions } from "./m
|
||||
import { ApprovalManager } from "./approval.js";
|
||||
import { SenderNameCache } from "./senderCache.js";
|
||||
import { TriggerQueue, triggerQueue as defaultTriggerQueue, type QueuedTrigger } from "./triggerQueue.js";
|
||||
import { createSlashCommandRegistry, formatSlashHelpTarget, parseSlashHelpSubcommand, parseSlashInvocation } from "./slashCommands.js";
|
||||
|
||||
export { ApprovalManager } from "./approval.js";
|
||||
export type { ApprovalResult, PendingApproval } from "./approval.js";
|
||||
@@ -87,6 +88,12 @@ export function makeTriggerHandler(deps: TriggerDeps): TriggerHandler {
|
||||
const runAgent = deps.runAgent ?? defaultRunAgent;
|
||||
const approvalManager = new ApprovalManager();
|
||||
const triggerQueue = deps.triggerQueue ?? defaultTriggerQueue;
|
||||
const slashCommands = createSlashCommandRegistry({
|
||||
prisma: deps.prisma,
|
||||
settings: deps.settings,
|
||||
logger: deps.logger,
|
||||
triggerQueue,
|
||||
});
|
||||
const batchContexts = new Map<string, TriggerRunContext>();
|
||||
// runId → AbortController for live runs. Registered when a run starts,
|
||||
// used by the interrupt card action to abort the SDK query, cleared in the
|
||||
@@ -676,48 +683,23 @@ export function makeTriggerHandler(deps: TriggerDeps): TriggerHandler {
|
||||
// batcher. Session commands bypass the lock because they don't create runs;
|
||||
// role/unknown slash prompts still use the normal run path and queue if locked.
|
||||
if (cleanPrompt.startsWith("/")) {
|
||||
const cmd = cleanPrompt.split(/\s+/)[0];
|
||||
switch (cmd) {
|
||||
case "/new": {
|
||||
await deps.prisma.agentSession.updateMany({
|
||||
where: { projectId, archivedAt: null },
|
||||
data: { archivedAt: new Date() },
|
||||
});
|
||||
await sendText(rt, chatId, "已开新会话,下次 @bot 将从头开始。");
|
||||
return;
|
||||
}
|
||||
case "/resume": {
|
||||
const latest = await deps.prisma.agentSession.findFirst({
|
||||
where: { projectId, archivedAt: { not: null } },
|
||||
orderBy: { archivedAt: "desc" },
|
||||
select: { id: true },
|
||||
});
|
||||
if (latest === null) {
|
||||
await sendText(rt, chatId, "没有可恢复的会话。");
|
||||
return;
|
||||
}
|
||||
await deps.prisma.agentSession.update({
|
||||
where: { id: latest.id },
|
||||
data: { archivedAt: null },
|
||||
});
|
||||
await sendText(rt, chatId, "已恢复上一个会话。");
|
||||
return;
|
||||
}
|
||||
case "/reset": {
|
||||
await deps.prisma.agentSession.updateMany({
|
||||
where: { projectId, archivedAt: null },
|
||||
data: { archivedAt: new Date() },
|
||||
});
|
||||
const cleared = triggerQueue.clear(projectId);
|
||||
if (cleared > 0) {
|
||||
deps.logger.info({ projectId, cleared }, "feishu trigger: cleared queued triggers on reset");
|
||||
}
|
||||
await sendText(rt, chatId, "已重置,下次 @bot 将从头开始。");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
const invocation = parseSlashInvocation(cleanPrompt);
|
||||
const helpSubcommandTarget = invocation === null ? null : parseSlashHelpSubcommand(invocation);
|
||||
if (helpSubcommandTarget !== null) {
|
||||
// Help is generated on demand because role/tool configuration is runtime data.
|
||||
const models = await deps.settings.modelRegistry({ projectId });
|
||||
await sendText(rt, chatId, formatSlashHelpTarget(helpSubcommandTarget, models, slashCommands));
|
||||
return;
|
||||
}
|
||||
|
||||
if (invocation !== null) {
|
||||
const slashCommand = slashCommands.get(invocation.name);
|
||||
if (slashCommand !== undefined) {
|
||||
await slashCommand.run({ invocation, projectId, chatId, rt });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await startAgentRun(runContext, cleanPrompt);
|
||||
return;
|
||||
}
|
||||
@@ -1037,11 +1019,12 @@ async function downloadPostMessageFiles(args: {
|
||||
await downloadMessageFile(args.rt, args.msg.message_id, fileKey, savePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a leading `/<role>` command from a prompt (e.g. `/draft 帮我写教案`).
|
||||
* Returns the role id and the remaining prompt with the command stripped.
|
||||
* Control commands already handled upstream (`/new`, `/resume`, `/reset`) are
|
||||
* ignored here — they never reach this function.
|
||||
* Control/help commands already handled upstream are ignored here — they never
|
||||
* reach this function.
|
||||
*
|
||||
* Unknown `/foo` that isn't a registered role: returns `null` role and the
|
||||
* original prompt unchanged (the slash is treated as literal text). Role
|
||||
|
||||
Reference in New Issue
Block a user