mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-18 21:21:11 +02:00
moved assistant to use skills; added agent monitoring
This commit is contained in:
parent
80dae17fd1
commit
144bbe5878
7 changed files with 396 additions and 157 deletions
|
|
@ -1,164 +1,27 @@
|
||||||
|
import { skillCatalog } from "./skills/index.js";
|
||||||
import { WorkDir as BASE_DIR } from "../config/config.js";
|
import { WorkDir as BASE_DIR } from "../config/config.js";
|
||||||
|
|
||||||
export const CopilotInstructions = `You are an intelligent workflow assistant helping users manage their workflows in ${BASE_DIR}.
|
export const CopilotInstructions = `You are an intelligent workflow assistant helping users manage their workflows in ${BASE_DIR}
|
||||||
|
|
||||||
WORKFLOW KNOWLEDGE:
|
Use the catalog below to decide which skills to load for each user request. Before acting:
|
||||||
- Workflows are JSON files that orchestrate multiple agents
|
- Call the \`loadSkill\` tool with the skill's name or path so you can read its guidance string.
|
||||||
- Agents are JSON files defining AI assistants with specific tools and instructions
|
- Apply the instructions from every loaded skill while working on the request.
|
||||||
- Tools can be built-in functions or MCP (Model Context Protocol) integrations
|
|
||||||
|
|
||||||
NOTE: Comments with // in the formats below are for explanation only - do NOT include them in actual JSON files
|
${skillCatalog}
|
||||||
|
|
||||||
CORRECT WORKFLOW FORMAT:
|
Always consult this catalog first so you load the right skills before taking action.
|
||||||
{
|
|
||||||
"name": "workflow_name", // REQUIRED - must match filename
|
|
||||||
"description": "Description...", // REQUIRED - must be a description of the workflow
|
|
||||||
"steps": [ // REQUIRED - array of steps
|
|
||||||
{
|
|
||||||
"type": "agent", // REQUIRED - always "agent"
|
|
||||||
"id": "agent_name" // REQUIRED - must match agent filename
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "agent",
|
|
||||||
"id": "another_agent_name"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
CORRECT AGENT FORMAT (with detailed tool structure):
|
# Communication & Execution Style
|
||||||
{
|
|
||||||
"name": "agent_name", // REQUIRED - must match filename
|
|
||||||
"description": "What agent does", // REQUIRED - must be a description of the agent
|
|
||||||
"model": "gpt-4.1", // REQUIRED - model to use
|
|
||||||
"instructions": "Instructions...", // REQUIRED - agent instructions
|
|
||||||
"tools": { // OPTIONAL - can be empty {} or omitted
|
|
||||||
"descriptive_tool_name": {
|
|
||||||
"type": "mcp", // REQUIRED - always "mcp" for MCP tools
|
|
||||||
"name": "actual_mcp_tool_name", // REQUIRED - exact tool name from MCP server
|
|
||||||
"description": "What tool does", // REQUIRED - clear description
|
|
||||||
"mcpServerName": "server_name", // REQUIRED - name from mcp.json config
|
|
||||||
"inputSchema": { // REQUIRED - full JSON schema
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"param1": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Description of param" // description is optional but helpful
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["param1"] // OPTIONAL - only include if params are required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IMPORTANT NOTES:
|
## Communication principles
|
||||||
- Agent tools need: type, name, description, mcpServerName, and inputSchema (all REQUIRED)
|
- Break complex efforts into clear, sequential steps the user can follow.
|
||||||
- Tool keys in agents should be descriptive (like "search", "fetch", "analyze") not the exact tool name
|
- Explain reasoning briefly as you work, and confirm outcomes before moving on.
|
||||||
- Agents can have empty tools {} if they don't need external tools
|
- Be proactive about understanding missing context; ask clarifying questions when needed.
|
||||||
- The "required" array in inputSchema is OPTIONAL - only include it if the tool has required parameters
|
- Summarize completed work and suggest logical next steps at the end of a task.
|
||||||
- If all parameters are optional, you can omit the "required" field entirely
|
- Always ask for confirmation before taking destructive actions.
|
||||||
- Property descriptions in inputSchema are optional but helpful for clarity
|
|
||||||
- All other fields marked REQUIRED must always be present
|
|
||||||
|
|
||||||
EXAMPLE 1 - Firecrawl Search Tool (with required params):
|
## Execution reminders
|
||||||
{
|
- Explore existing files and structure before creating new assets.
|
||||||
"tools": {
|
- Use relative paths (no \${BASE_DIR} prefixes) when running commands or referencing files.
|
||||||
"search": {
|
- Keep user data safe—double-check before editing or deleting important resources.
|
||||||
"type": "mcp",
|
`;
|
||||||
"name": "firecrawl_search",
|
|
||||||
"description": "Search the web",
|
|
||||||
"mcpServerName": "firecrawl",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"query": {"type": "string", "description": "Search query"},
|
|
||||||
"limit": {"type": "number", "description": "Number of results"},
|
|
||||||
"sources": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {"type": "string", "enum": ["web", "images", "news"]}
|
|
||||||
},
|
|
||||||
"required": ["type"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["query"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EXAMPLE 2 - ElevenLabs Text-to-Speech (without required array):
|
|
||||||
{
|
|
||||||
"tools": {
|
|
||||||
"text_to_speech": {
|
|
||||||
"type": "mcp",
|
|
||||||
"name": "text_to_speech",
|
|
||||||
"description": "Generate audio from text",
|
|
||||||
"mcpServerName": "elevenLabs",
|
|
||||||
"inputSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"text": {"type": "string"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CRITICAL NAMING AND ORGANIZATION RULES:
|
|
||||||
- Agent filenames MUST match the "name" field in their JSON (e.g., agent_name.json → "name": "agent_name")
|
|
||||||
- Workflow filenames MUST match the "name" field in their JSON (e.g., workflow_name.json → "name": "workflow_name")
|
|
||||||
- When referencing agents in workflow steps, the "id" field MUST match the agent's name (e.g., {"type": "agent", "id": "agent_name"})
|
|
||||||
- All three must be identical: filename, JSON "name" field, and workflow step "id" field
|
|
||||||
- ALL workflows MUST be placed in the "workflows/" folder (e.g., workflows/workflow_name.json)
|
|
||||||
- ALL agents MUST be placed in the "agents/" folder (e.g., agents/agent_name.json)
|
|
||||||
- NEVER create workflows or agents outside these designated folders
|
|
||||||
- Always maintain this naming and organizational consistency when creating or updating files
|
|
||||||
|
|
||||||
YOUR CAPABILITIES:
|
|
||||||
1. Explore the directory structure to understand existing workflows/agents
|
|
||||||
2. Create new workflows and agents following best practices
|
|
||||||
3. Update existing files intelligently
|
|
||||||
4. Read and analyze file contents to maintain consistency
|
|
||||||
5. Suggest improvements and ask clarifying questions when needed
|
|
||||||
6. Execute shell commands to perform system operations
|
|
||||||
- Use executeCommand to run bash/shell commands
|
|
||||||
- Can list files, check system info, run scripts, etc.
|
|
||||||
- Commands execute in the .rowboat directory by default
|
|
||||||
7. List and explore MCP (Model Context Protocol) servers and their available tools
|
|
||||||
- Use listMcpServers to see all configured MCP servers
|
|
||||||
- Use listMcpTools to see what tools are available in a specific MCP server
|
|
||||||
- This helps users understand what external integrations they can use in their workflows
|
|
||||||
|
|
||||||
MCP INTEGRATION:
|
|
||||||
- MCP servers provide external tools that agents can use (e.g., web scraping, database access, APIs)
|
|
||||||
- MCP configuration is stored in config/mcp.json
|
|
||||||
- When users ask about available integrations or tools, check MCP servers
|
|
||||||
- Help users understand which MCP tools they can add to their agents
|
|
||||||
|
|
||||||
DELETION RULES:
|
|
||||||
- When a user asks to delete a WORKFLOW, you MUST:
|
|
||||||
1. First read/analyze the workflow to identify which agents it uses
|
|
||||||
2. List those agents to the user
|
|
||||||
3. Ask the user if they want to delete those agents as well
|
|
||||||
4. Wait for their response before proceeding with any deletions
|
|
||||||
5. Only delete what the user confirms
|
|
||||||
- When a user asks to delete an AGENT, you MUST:
|
|
||||||
1. First read/analyze the agent to identify which workflows it is used in
|
|
||||||
2. List those workflows to the user
|
|
||||||
3. Ask the user if they want to delete/modify those workflows as well
|
|
||||||
4. Wait for their response before proceeding with any deletions
|
|
||||||
5. Only delete/modify what the user confirms
|
|
||||||
|
|
||||||
COMMUNICATION STYLE:
|
|
||||||
- Break down complex tasks into clear steps
|
|
||||||
- Explore existing files/structure before creating new ones
|
|
||||||
- Explain your reasoning as you work through tasks
|
|
||||||
- Be proactive in understanding context
|
|
||||||
- Confirm what you've done and suggest next steps
|
|
||||||
- Always ask for confirmation before destructive operations!!
|
|
||||||
|
|
||||||
Always use relative paths (no ${BASE_DIR} prefix) when calling tools.`;
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
export const skill = String.raw`
|
||||||
|
# Deletion Guardrails
|
||||||
|
|
||||||
|
Load this skill when a user asks to delete agents or workflows so you follow the required confirmation steps.
|
||||||
|
|
||||||
|
## Workflow deletion protocol
|
||||||
|
1. Read the workflow file to identify every agent it references.
|
||||||
|
2. Report those agents to the user and ask whether they should be deleted too.
|
||||||
|
3. Wait for explicit confirmation before deleting anything.
|
||||||
|
4. Only remove the workflow and/or agents the user authorizes.
|
||||||
|
|
||||||
|
## Agent deletion protocol
|
||||||
|
1. Inspect the agent file to discover which workflows reference it.
|
||||||
|
2. List those workflows to the user and ask whether they should be updated or deleted.
|
||||||
|
3. Pause for confirmation before modifying workflows or removing the agent.
|
||||||
|
4. Perform only the deletions the user approves.
|
||||||
|
|
||||||
|
## Safety checklist
|
||||||
|
- Never delete cascaded resources automatically.
|
||||||
|
- Keep a clear audit trail in your responses describing what was removed.
|
||||||
|
- If the user’s instructions are ambiguous, ask clarifying questions before taking action.
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default skill;
|
||||||
143
apps/cli/src/application/assistant/skills/index.ts
Normal file
143
apps/cli/src/application/assistant/skills/index.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import deletionGuardrailsSkill from "./deletion-guardrails/skill.js";
|
||||||
|
import mcpIntegrationSkill from "./mcp-integration/skill.js";
|
||||||
|
import workflowAuthoringSkill from "./workflow-authoring/skill.js";
|
||||||
|
import workflowRunOpsSkill from "./workflow-run-ops/skill.js";
|
||||||
|
|
||||||
|
const CURRENT_FILE = fileURLToPath(import.meta.url);
|
||||||
|
const CURRENT_DIR = path.dirname(CURRENT_FILE);
|
||||||
|
const CATALOG_PREFIX = "src/application/assistant/skills";
|
||||||
|
|
||||||
|
type SkillDefinition = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
folder: string;
|
||||||
|
summary: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ResolvedSkill = {
|
||||||
|
id: string;
|
||||||
|
catalogPath: string;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const definitions: SkillDefinition[] = [
|
||||||
|
{
|
||||||
|
id: "workflow-authoring",
|
||||||
|
title: "Workflow Authoring",
|
||||||
|
folder: "workflow-authoring",
|
||||||
|
summary: "Creating or editing workflows/agents, validating schema rules, and keeping filenames aligned with JSON ids.",
|
||||||
|
content: workflowAuthoringSkill,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "mcp-integration",
|
||||||
|
title: "MCP Integration Guidance",
|
||||||
|
folder: "mcp-integration",
|
||||||
|
summary: "Listing MCP servers/tools and embedding their schemas in agent definitions.",
|
||||||
|
content: mcpIntegrationSkill,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "deletion-guardrails",
|
||||||
|
title: "Deletion Guardrails",
|
||||||
|
folder: "deletion-guardrails",
|
||||||
|
summary: "Following the confirmation process before removing workflows or agents and their dependencies.",
|
||||||
|
content: deletionGuardrailsSkill,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "workflow-run-ops",
|
||||||
|
title: "Workflow Run Operations",
|
||||||
|
folder: "workflow-run-ops",
|
||||||
|
summary: "Commands that list workflow runs, inspect paused executions, or manage cron schedules for workflows.",
|
||||||
|
content: workflowRunOpsSkill,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const skillEntries = definitions.map((definition) => ({
|
||||||
|
...definition,
|
||||||
|
catalogPath: `${CATALOG_PREFIX}/${definition.folder}/skill.ts`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const catalogSections = skillEntries.map((entry) => [
|
||||||
|
`## ${entry.title}`,
|
||||||
|
`- **Skill file:** \`${entry.catalogPath}\``,
|
||||||
|
`- **Use it for:** ${entry.summary}`,
|
||||||
|
].join("\n"));
|
||||||
|
|
||||||
|
export const skillCatalog = [
|
||||||
|
"# Rowboat Skill Catalog",
|
||||||
|
"",
|
||||||
|
"Use this catalog to see which specialized skills you can load. Each entry lists the exact skill file plus a short description of when it helps.",
|
||||||
|
"",
|
||||||
|
catalogSections.join("\n\n"),
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
const normalizeIdentifier = (value: string) =>
|
||||||
|
value.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
||||||
|
|
||||||
|
const aliasMap = new Map<string, ResolvedSkill>();
|
||||||
|
|
||||||
|
const registerAlias = (alias: string, entry: ResolvedSkill) => {
|
||||||
|
const normalized = normalizeIdentifier(alias);
|
||||||
|
if (!normalized) return;
|
||||||
|
aliasMap.set(normalized, entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerAliasVariants = (alias: string, entry: ResolvedSkill) => {
|
||||||
|
const normalized = normalizeIdentifier(alias);
|
||||||
|
if (!normalized) return;
|
||||||
|
|
||||||
|
const variants = new Set<string>([normalized]);
|
||||||
|
|
||||||
|
if (/\.(ts|js)$/i.test(normalized)) {
|
||||||
|
variants.add(normalized.replace(/\.(ts|js)$/i, ""));
|
||||||
|
variants.add(
|
||||||
|
normalized.endsWith(".ts") ? normalized.replace(/\.ts$/i, ".js") : normalized.replace(/\.js$/i, ".ts"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
variants.add(`${normalized}.ts`);
|
||||||
|
variants.add(`${normalized}.js`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const variant of variants) {
|
||||||
|
registerAlias(variant, entry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const entry of skillEntries) {
|
||||||
|
const absoluteTs = path.join(CURRENT_DIR, entry.folder, "skill.ts");
|
||||||
|
const absoluteJs = path.join(CURRENT_DIR, entry.folder, "skill.js");
|
||||||
|
const resolvedEntry: ResolvedSkill = {
|
||||||
|
id: entry.id,
|
||||||
|
catalogPath: entry.catalogPath,
|
||||||
|
content: entry.content,
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseAliases = [
|
||||||
|
entry.id,
|
||||||
|
entry.folder,
|
||||||
|
`${entry.folder}/skill`,
|
||||||
|
`${entry.folder}/skill.ts`,
|
||||||
|
`${entry.folder}/skill.js`,
|
||||||
|
`skills/${entry.folder}/skill.ts`,
|
||||||
|
`skills/${entry.folder}/skill.js`,
|
||||||
|
`${CATALOG_PREFIX}/${entry.folder}/skill.ts`,
|
||||||
|
`${CATALOG_PREFIX}/${entry.folder}/skill.js`,
|
||||||
|
absoluteTs,
|
||||||
|
absoluteJs,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const alias of baseAliases) {
|
||||||
|
registerAliasVariants(alias, resolvedEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const availableSkills = skillEntries.map((entry) => entry.id);
|
||||||
|
|
||||||
|
export function resolveSkill(identifier: string): ResolvedSkill | null {
|
||||||
|
const normalized = normalizeIdentifier(identifier);
|
||||||
|
if (!normalized) return null;
|
||||||
|
|
||||||
|
return aliasMap.get(normalized) ?? null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
export const skill = String.raw`
|
||||||
|
# MCP Integration Guidance
|
||||||
|
|
||||||
|
Load this skill whenever a user asks about external tools, MCP servers, or how to extend an agent’s capabilities.
|
||||||
|
|
||||||
|
## Key concepts
|
||||||
|
- MCP servers expose tools (web scraping, APIs, databases, etc.) declared in \`config/mcp.json\`.
|
||||||
|
- Agents reference MCP tools through the \`"tools"\` block by specifying \`type\`, \`name\`, \`description\`, \`mcpServerName\`, and a full \`inputSchema\`.
|
||||||
|
- Tool schemas can include optional property descriptions; only include \`"required"\` when parameters are mandatory.
|
||||||
|
|
||||||
|
## Operator actions
|
||||||
|
1. Use \`listMcpServers\` to enumerate configured servers.
|
||||||
|
2. Use \`listMcpTools\` for a server to understand the available operations and schemas.
|
||||||
|
3. Explain which MCP tools match the user’s needs before editing agent definitions.
|
||||||
|
4. When adding a tool to an agent, document what it does and ensure the schema mirrors the MCP definition.
|
||||||
|
|
||||||
|
## Example snippets to reference
|
||||||
|
- Firecrawl search (required param):
|
||||||
|
\`\`\`
|
||||||
|
"tools": {
|
||||||
|
"search": {
|
||||||
|
"type": "mcp",
|
||||||
|
"name": "firecrawl_search",
|
||||||
|
"description": "Search the web",
|
||||||
|
"mcpServerName": "firecrawl",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"query": {"type": "string", "description": "Search query"},
|
||||||
|
"limit": {"type": "number", "description": "Number of results"}
|
||||||
|
},
|
||||||
|
"required": ["query"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
- ElevenLabs text-to-speech (no required array):
|
||||||
|
\`\`\`
|
||||||
|
"tools": {
|
||||||
|
"text_to_speech": {
|
||||||
|
"type": "mcp",
|
||||||
|
"name": "text_to_speech",
|
||||||
|
"description": "Generate audio from text",
|
||||||
|
"mcpServerName": "elevenLabs",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {"type": "string"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Safety reminders
|
||||||
|
- Only recommend MCP tools that are actually configured.
|
||||||
|
- Clarify any missing details (required parameters, server names) before modifying files.
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default skill;
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
export const skill = String.raw`
|
||||||
|
# Workflow Authoring
|
||||||
|
|
||||||
|
Load this skill whenever a user wants to inspect, create, or update workflows or agents inside the Rowboat workspace.
|
||||||
|
|
||||||
|
## Workflow knowledge
|
||||||
|
- Workflows (\`workflows/*.json\`) orchestrate multiple agents and define their order through \`"steps"\`.
|
||||||
|
- Agents (\`agents/*.json\`) configure a single model, its instructions, and the MCP tools it may use.
|
||||||
|
- Tools can be Rowboat built-ins or MCP integrations declared in the agent definition.
|
||||||
|
|
||||||
|
## Workflow format
|
||||||
|
\`\`\`
|
||||||
|
{
|
||||||
|
"name": "workflow_name",
|
||||||
|
"description": "Description of the workflow",
|
||||||
|
"steps": [
|
||||||
|
{"type": "agent", "id": "agent_name"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Agent format
|
||||||
|
\`\`\`
|
||||||
|
{
|
||||||
|
"name": "agent_name",
|
||||||
|
"description": "Description of the agent",
|
||||||
|
"model": "gpt-4.1",
|
||||||
|
"instructions": "Instructions for the agent",
|
||||||
|
"tools": {
|
||||||
|
"descriptive_tool_key": {
|
||||||
|
"type": "mcp",
|
||||||
|
"name": "actual_mcp_tool_name",
|
||||||
|
"description": "What the tool does",
|
||||||
|
"mcpServerName": "server_name_from_config",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"param1": {"type": "string", "description": "What the parameter means"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
- Tool keys should be descriptive (e.g., \`"search"\`, \`"fetch"\`, \`"analyze"\`) rather than the MCP tool name.
|
||||||
|
- Include \`required\` in the \`inputSchema\` only when parameters are actually required.
|
||||||
|
|
||||||
|
## Naming and organization rules
|
||||||
|
- Agent filenames must match the \`"name"\` field and the workflow step \`"id"\`.
|
||||||
|
- Workflow filenames must match the \`"name"\` field.
|
||||||
|
- Agents live under \`agents/\`, workflows under \`workflows/\`—never place them elsewhere.
|
||||||
|
- Always keep filenames, \`"name"\`, and referenced ids perfectly aligned.
|
||||||
|
- Use relative paths (no \${BASE_DIR} prefixes) when calling tools from the CLI.
|
||||||
|
|
||||||
|
## Capabilities checklist
|
||||||
|
1. Explore the repository to understand existing workflows/agents before editing.
|
||||||
|
2. Update files carefully to maintain schema validity.
|
||||||
|
3. Suggest improvements and ask clarifying questions.
|
||||||
|
4. List and explore MCP servers/tools when users need new capabilities.
|
||||||
|
5. Confirm work done and outline next steps once changes are complete.
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default skill;
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
export const skill = String.raw`
|
||||||
|
# Workflow Run Operations
|
||||||
|
|
||||||
|
Package of repeatable commands for inspecting workflow run history under ~/.rowboat/runs and managing cron schedules that trigger Rowboat workflows. Load this skill whenever a user asks about workflow run files, paused executions, or cron-based scheduling/unscheduling.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
- User wants to list or filter workflow runs (all runs, by workflow, time range, or paused for input).
|
||||||
|
- User wants to inspect cron jobs or change the workflow schedule.
|
||||||
|
- User asks how to set up monitoring for waiting runs or confirm a cron entry exists.
|
||||||
|
|
||||||
|
## Run monitoring examples
|
||||||
|
Operate from ~/.rowboat (Rowboat tools already set this as the working directory). Use executeCommand with the sample Bash snippets below, modifying placeholders as needed.
|
||||||
|
|
||||||
|
Each run file name starts with a timestamp like '2025-11-12T08-02-41Z'. You can use this to filter for date/time ranges.
|
||||||
|
|
||||||
|
Each line of the run file contains a running log with the first line containing informatin of the workflow. E.g. '{"type":"start","runId":"2025-11-12T08-02-41Z-0014322-000","workflowId":"exa-search","workflow":{"name":"example_workflow","description":"An example workflow","steps":[{"type":"agent","id":"exa-search"}]},"interactive":true,"ts":"2025-11-12T08:02:41.168Z"}'
|
||||||
|
|
||||||
|
If a run is waiting for human input the last line will contain 'paused_for_human_input'. See examples below.
|
||||||
|
|
||||||
|
1. **List all runs**
|
||||||
|
|
||||||
|
ls ~/.rowboat/runs
|
||||||
|
|
||||||
|
|
||||||
|
2. **Filter by workflow**
|
||||||
|
|
||||||
|
grep -rl '"workflowId":"<workflow-id>"' ~/.rowboat/runs | xargs -n1 basename | sed 's/\.jsonl$//' | sort -r
|
||||||
|
|
||||||
|
Replace <workflow-id> with the desired id.
|
||||||
|
|
||||||
|
3. **Filter by time window**
|
||||||
|
To the previous commands add the below through unix pipe
|
||||||
|
|
||||||
|
awk -F'/' '$NF >= "2025-11-12T08-03" && $NF <= "2025-11-12T08-10"'
|
||||||
|
|
||||||
|
Use the correct timestamps.
|
||||||
|
|
||||||
|
4. **Show runs waiting for human input**
|
||||||
|
|
||||||
|
awk 'FNR==1{if (NR>1) print fn, last; fn=FILENAME} {last=$0} END{print fn, last}' ~/.rowboat/runs/*.jsonl | grep 'pause-for-human-input' | awk '{print $1}'
|
||||||
|
|
||||||
|
Prints the files whose last line equals 'pause-for-human-input'.
|
||||||
|
|
||||||
|
## Cron management examples
|
||||||
|
1. **View current cron schedule**
|
||||||
|
|
||||||
|
bash -lc "crontab -l 2>/dev/null || echo 'No crontab entries configured for this user.'"
|
||||||
|
|
||||||
|
2. **Schedule a new workflow**
|
||||||
|
|
||||||
|
crontab -l 2>/dev/null; echo '0 10 * * * /usr/local/bin/node dist/app.js exa-search "what is the weather in tokyo" >> /Users/arjun/.rowboat/logs/exa_search.log 2>&1' ) | crontab -
|
||||||
|
|
||||||
|
|
||||||
|
3. **Unschedule/remove a workflow**
|
||||||
|
|
||||||
|
crontab -l | grep -v 'exa-search' | crontab -
|
||||||
|
|
||||||
|
Removes cron lines containing the workflow id.
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default skill;
|
||||||
|
|
@ -7,6 +7,7 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
||||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
||||||
import { Client } from "@modelcontextprotocol/sdk/client";
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||||
|
import { resolveSkill, availableSkills } from "../assistant/skills/index.js";
|
||||||
|
|
||||||
const BuiltinToolsSchema = z.record(z.string(), z.object({
|
const BuiltinToolsSchema = z.record(z.string(), z.object({
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
|
|
@ -18,6 +19,30 @@ const BuiltinToolsSchema = z.record(z.string(), z.object({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
||||||
|
loadSkill: {
|
||||||
|
description: "Load a Rowboat skill definition into context by fetching its guidance string",
|
||||||
|
inputSchema: z.object({
|
||||||
|
skillName: z.string().describe("Skill identifier or path (e.g., 'workflow-run-ops' or 'src/application/assistant/skills/workflow-run-ops/skill.ts')"),
|
||||||
|
}),
|
||||||
|
execute: async ({ skillName }: { skillName: string }) => {
|
||||||
|
const resolved = resolveSkill(skillName);
|
||||||
|
|
||||||
|
if (!resolved) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Skill '${skillName}' not found. Available skills: ${availableSkills.join(", ")}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
skillName: resolved.id,
|
||||||
|
path: resolved.catalogPath,
|
||||||
|
content: resolved.content,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
exploreDirectory: {
|
exploreDirectory: {
|
||||||
description: 'Recursively explore directory structure to understand existing workflows, agents, and file organization',
|
description: 'Recursively explore directory structure to understand existing workflows, agents, and file organization',
|
||||||
inputSchema: z.object({
|
inputSchema: z.object({
|
||||||
|
|
@ -421,4 +446,4 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue