mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 13:04:08 +00:00
Add bash mode for executing shell commands
- Add ! prefix in TUI editor to execute shell commands directly
- Output streams in real-time and is added to LLM context
- Supports multiline commands, cancellation (Escape), truncation
- Preview mode shows last 20 lines, Ctrl+O expands full output
- Commands persist in session history as bashExecution messages
- Add bash command to RPC mode via {type:'bash',command:'...'}
- Add RPC tests for bash command execution and context inclusion
- Update docs: rpc.md, session.md, README.md, CHANGELOG.md
Closes #112
Co-authored-by: Markus Ylisiurunen <markus.ylisiurunen@gmail.com>
This commit is contained in:
parent
1608da8770
commit
bd0d0676d4
13 changed files with 917 additions and 126 deletions
102
packages/coding-agent/src/messages.ts
Normal file
102
packages/coding-agent/src/messages.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* Custom message types and transformers for the coding agent.
|
||||
*
|
||||
* Extends the base AppMessage type with coding-agent specific message types,
|
||||
* and provides a transformer to convert them to LLM-compatible messages.
|
||||
*/
|
||||
|
||||
import type { AppMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { Message } from "@mariozechner/pi-ai";
|
||||
|
||||
// ============================================================================
|
||||
// Custom Message Types
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Message type for bash executions via the ! command.
|
||||
*/
|
||||
export interface BashExecutionMessage {
|
||||
role: "bashExecution";
|
||||
command: string;
|
||||
output: string;
|
||||
exitCode: number | null;
|
||||
cancelled: boolean;
|
||||
truncated: boolean;
|
||||
fullOutputPath?: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// Extend CustomMessages via declaration merging
|
||||
declare module "@mariozechner/pi-agent-core" {
|
||||
interface CustomMessages {
|
||||
bashExecution: BashExecutionMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Type Guards
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Type guard for BashExecutionMessage.
|
||||
*/
|
||||
export function isBashExecutionMessage(msg: AppMessage | Message): msg is BashExecutionMessage {
|
||||
return (msg as BashExecutionMessage).role === "bashExecution";
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Message Formatting
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Convert a BashExecutionMessage to user message text for LLM context.
|
||||
*/
|
||||
export function bashExecutionToText(msg: BashExecutionMessage): string {
|
||||
let text = `Ran \`${msg.command}\`\n`;
|
||||
if (msg.output) {
|
||||
text += "```\n" + msg.output + "\n```";
|
||||
} else {
|
||||
text += "(no output)";
|
||||
}
|
||||
if (msg.cancelled) {
|
||||
text += "\n\n(command cancelled)";
|
||||
} else if (msg.exitCode !== null && msg.exitCode !== 0) {
|
||||
text += `\n\nCommand exited with code ${msg.exitCode}`;
|
||||
}
|
||||
if (msg.truncated && msg.fullOutputPath) {
|
||||
text += `\n\n[Output truncated. Full output: ${msg.fullOutputPath}]`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Message Transformer
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Transform AppMessages (including custom types) to LLM-compatible Messages.
|
||||
*
|
||||
* This is used by:
|
||||
* - Agent's messageTransformer option (for prompt calls)
|
||||
* - Compaction's generateSummary (for summarization)
|
||||
*/
|
||||
export function messageTransformer(messages: AppMessage[]): Message[] {
|
||||
return messages
|
||||
.map((m): Message | null => {
|
||||
if (isBashExecutionMessage(m)) {
|
||||
// Convert bash execution to user message
|
||||
return {
|
||||
role: "user",
|
||||
content: [{ type: "text", text: bashExecutionToText(m) }],
|
||||
timestamp: m.timestamp,
|
||||
};
|
||||
}
|
||||
// Pass through standard LLM roles
|
||||
if (m.role === "user" || m.role === "assistant" || m.role === "toolResult") {
|
||||
return m as Message;
|
||||
}
|
||||
// Filter out unknown message types
|
||||
return null;
|
||||
})
|
||||
.filter((m): m is Message => m !== null);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue