mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 18:01:22 +00:00
Move skill command handling to AgentSession, update docs
- Skill commands (/skill:name) now expanded in AgentSession instead of interactive mode, enabling them in RPC and print modes - Input event can now intercept /skill:name before expansion - Updated extensions.md with clearer input event docs and processing order - Updated rpc.md: hook -> extension terminology, added skill expansion mentions - Added PR attribution to changelog entries for #761
This commit is contained in:
parent
3e5d91f287
commit
b4a05cbcab
7 changed files with 211 additions and 420 deletions
|
|
@ -13,6 +13,7 @@
|
|||
* Modes use this class and add their own I/O layer on top.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import type {
|
||||
Agent,
|
||||
AgentEvent,
|
||||
|
|
@ -25,6 +26,7 @@ import type { AssistantMessage, ImageContent, Message, Model, TextContent } from
|
|||
import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai";
|
||||
import { getAuthPath } from "../config.js";
|
||||
import { theme } from "../modes/interactive/theme/theme.js";
|
||||
import { stripFrontmatter } from "../utils/frontmatter.js";
|
||||
import { type BashResult, executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
|
||||
import {
|
||||
type CompactionResult,
|
||||
|
|
@ -569,7 +571,7 @@ export class AgentSession {
|
|||
}
|
||||
}
|
||||
|
||||
// Emit input event for extension interception (before template expansion)
|
||||
// Emit input event for extension interception (before skill/template expansion)
|
||||
let currentText = text;
|
||||
let currentImages = options?.images;
|
||||
if (this._extensionRunner?.hasHandlers("input")) {
|
||||
|
|
@ -587,10 +589,12 @@ export class AgentSession {
|
|||
}
|
||||
}
|
||||
|
||||
// Expand file-based prompt templates if requested
|
||||
const expandedText = expandPromptTemplates
|
||||
? expandPromptTemplate(currentText, [...this._promptTemplates])
|
||||
: currentText;
|
||||
// Expand skill commands (/skill:name args) and prompt templates (/template args)
|
||||
let expandedText = currentText;
|
||||
if (expandPromptTemplates) {
|
||||
expandedText = this._expandSkillCommand(expandedText);
|
||||
expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
|
||||
}
|
||||
|
||||
// If streaming, queue via steer() or followUp() based on option
|
||||
if (this.isStreaming) {
|
||||
|
|
@ -718,10 +722,42 @@ export class AgentSession {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand skill commands (/skill:name args) to their full content.
|
||||
* Returns the expanded text, or the original text if not a skill command or skill not found.
|
||||
* Emits errors via extension runner if file read fails.
|
||||
*/
|
||||
private _expandSkillCommand(text: string): string {
|
||||
if (!text.startsWith("/skill:")) return text;
|
||||
|
||||
const spaceIndex = text.indexOf(" ");
|
||||
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
||||
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
||||
|
||||
const skill = this._skills.find((s) => s.name === skillName);
|
||||
if (!skill) return text; // Unknown skill, pass through
|
||||
|
||||
try {
|
||||
const content = readFileSync(skill.filePath, "utf-8");
|
||||
const body = stripFrontmatter(content).trim();
|
||||
const header = `Skill location: ${skill.filePath}\nReferences are relative to ${skill.baseDir}.`;
|
||||
const skillMessage = `${header}\n\n${body}`;
|
||||
return args ? `${skillMessage}\n\n---\n\nUser: ${args}` : skillMessage;
|
||||
} catch (err) {
|
||||
// Emit error like extension commands do
|
||||
this._extensionRunner?.emitError({
|
||||
extensionPath: skill.filePath,
|
||||
event: "skill_expansion",
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
});
|
||||
return text; // Return original on error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a steering message to interrupt the agent mid-run.
|
||||
* Delivered after current tool execution, skips remaining tools.
|
||||
* Expands file-based prompt templates. Errors on extension commands.
|
||||
* Expands skill commands and prompt templates. Errors on extension commands.
|
||||
* @throws Error if text is an extension command
|
||||
*/
|
||||
async steer(text: string): Promise<void> {
|
||||
|
|
@ -730,8 +766,9 @@ export class AgentSession {
|
|||
this._throwIfExtensionCommand(text);
|
||||
}
|
||||
|
||||
// Expand file-based prompt templates
|
||||
const expandedText = expandPromptTemplate(text, [...this._promptTemplates]);
|
||||
// Expand skill commands and prompt templates
|
||||
let expandedText = this._expandSkillCommand(text);
|
||||
expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
|
||||
|
||||
await this._queueSteer(expandedText);
|
||||
}
|
||||
|
|
@ -739,7 +776,7 @@ export class AgentSession {
|
|||
/**
|
||||
* Queue a follow-up message to be processed after the agent finishes.
|
||||
* Delivered only when agent has no more tool calls or steering messages.
|
||||
* Expands file-based prompt templates. Errors on extension commands.
|
||||
* Expands skill commands and prompt templates. Errors on extension commands.
|
||||
* @throws Error if text is an extension command
|
||||
*/
|
||||
async followUp(text: string): Promise<void> {
|
||||
|
|
@ -748,8 +785,9 @@ export class AgentSession {
|
|||
this._throwIfExtensionCommand(text);
|
||||
}
|
||||
|
||||
// Expand file-based prompt templates
|
||||
const expandedText = expandPromptTemplate(text, [...this._promptTemplates]);
|
||||
// Expand skill commands and prompt templates
|
||||
let expandedText = this._expandSkillCommand(text);
|
||||
expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
|
||||
|
||||
await this._queueFollowUp(expandedText);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue