diff --git a/apps/x/packages/core/src/application/assistant/skills/app-navigation/skill.ts b/apps/skills/app-navigation/SKILL.md similarity index 95% rename from apps/x/packages/core/src/application/assistant/skills/app-navigation/skill.ts rename to apps/skills/app-navigation/SKILL.md index 9dbbce84..b1946da8 100644 --- a/apps/x/packages/core/src/application/assistant/skills/app-navigation/skill.ts +++ b/apps/skills/app-navigation/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: app-navigation +description: >- + Navigate the app UI - open notes, switch views, filter/search the knowledge base, and manage saved views. +metadata: + title: "App Navigation" +--- + # App Navigation Skill You have access to the **app-navigation** tool which lets you control the Rowboat UI directly — opening notes, switching views, filtering the knowledge base, and creating saved views. @@ -77,6 +84,3 @@ Save the current view configuration as a named base. - ` + "`open-note`" + ` validates that the file exists before navigating. - Filter categories and values come from frontmatter in knowledge files. - **Never send ` + "`columns`" + ` or ` + "`sort`" + ` with ` + "`update-base-view`" + ` unless the user specifically asks to change them.** Only pass the parameters you intend to change — omitted parameters are left untouched. -`; - -export default skill; diff --git a/apps/skills/background-task/SKILL.md b/apps/skills/background-task/SKILL.md new file mode 100644 index 00000000..d16808c1 --- /dev/null +++ b/apps/skills/background-task/SKILL.md @@ -0,0 +1,136 @@ +--- +name: background-task +description: >- + Set up a recurring background task — persistent instructions the agent fires on a schedule and/or on matching events (Gmail, Calendar). Either maintains an `index.md` digest (OUTPUT mode) or performs a recurring side-effect like drafting a reply / posting to Slack / calling an API (ACTION mode). Flagship surface for anything recurring. +metadata: + title: "Background Tasks" +--- + +# Background Tasks Skill + +A *background task* is a persistent agent the user configures once and the framework keeps firing — on a schedule, inside time-of-day windows, and/or in response to matching incoming events (Gmail threads, calendar changes). Each task lives at `bg-tasks//` and owns two artifacts: + +- `task.yaml` — the spec (the user's **instructions**, triggers, runtime state). You and the user both treat this as the source of truth. +- `index.md` — the agent-owned body. The runtime never writes here; the bg-task agent does, each run. + +A task is one of two shapes — the agent decides per run from the verbs in `instructions`: + +| Mode | Trigger verbs | Behavior | +|---|---|---| +| **OUTPUT** | "maintain / show / summarize / track / digest" | Rewrite `index.md` to reflect the current state. | +| **ACTION** | "send / draft / post / notify / file / reply / call" | Perform the action, then append a one-line journal entry under `## Journal` in `index.md`. | + +Mixed instructions ("summarize and email it") trigger both. + +## Tools you'll use (and ones you WON'T) + +You have three dedicated builtin tools for this skill: + +- `create-background-task` — materializes a new task on disk. **Use this. Do not write `task.yaml` yourself with `workspace-edit`, and do not search the codebase for IPC channels like `bg-task:create`** — they're renderer-side and not callable from here. +- `patch-background-task` — updates an existing task (instructions / triggers / active / model). Use this for the extend-don't-fork case. +- `run-background-task-agent` — manually fires a task to run now. Always call this immediately after `create-background-task` so the user sees content. + +To inspect what tasks already exist, use `workspace-glob` on `bg-tasks/*/task.yaml` and `workspace-readFile` on candidates. The user's bg-tasks folder is workspace-relative. + +## Mode: act-first + +Bg-task creation is **action-first**. Don't ask "should I?" — read the request, pick a name, call `create-background-task`, then call `run-background-task-agent` with the returned slug. Confirm in one line past-tense at the end. Tell the user the surface name: "Manage it from Background tasks in the sidebar." + +The only exception: if a related bg-task already exists, **extend its instructions** via `patch-background-task` rather than creating a duplicate (see "Extend, don't fork"). + +## When you're loaded + +The host's trigger paragraph loads this skill on: + +- **Cadence**: "every morning", "daily", "hourly", "each Monday" +- **Watch/monitor**: "watch / monitor / keep an eye on / track / follow X" +- **Recurring artifact**: "morning briefing", "weekly review", "Acme deal dashboard" +- **Event-conditional**: "whenever a relevant email comes in, …" +- **Action verbs**: "draft / reply / call / post / notify / file / brief me on" +- **Decay questions**: "what's the weather", "top HN stories", "latest on X" — answer the one-off, then offer + +If the user explicitly says "live note" / "live-note", the host loads the `live-note` skill instead — don't try to handle that case here. + +## Workflow + +1. **Check for existing tasks.** Before creating, glob `bg-tasks/*/task.yaml` and read any candidates whose intent might overlap with the user's ask. If a related task exists, jump to "Extend, don't fork" below. + +2. **Pick a name.** Use a short, friendly title in title-case: "Morning weather", "Q3 deal digest", "HN top stories". The framework slugifies it (lowercase, dashes) for the folder — you don't manage the slug. + +3. **Write the instructions.** Capture the user's intent in their own words, with concrete verbs. Bake any specifics (which source, which audience, output shape) into the instructions — the agent re-reads them on every run. + + - Good: *"Summarize my unread emails since yesterday 6pm into a one-paragraph digest plus a bulleted list of action items. Skip newsletters and automated notifications."* + - Bad: *"Daily email summary."* (vague — agent will improvise unhelpfully) + +4. **Pick triggers.** All three are independently optional; mix freely. + + - `cronExpr` — exact times. `"0 7 * * *"` = 7am daily. + - `windows` — time-of-day bands. Each fires once per day inside the band, anywhere — forgiving when the app was offline. + - `eventMatchCriteria` — a natural-language description of which incoming events should wake the task (e.g. "Emails about Q3 OKRs from the leadership team"). Pass-1 routing matches; the agent does Pass-2 before acting. + + No triggers at all = manual-only. The user clicks Run. + +5. **Call `create-background-task`.** Required: `name`, `instructions`. Optional: `triggers`, `model`, `provider` (leave model/provider unset unless the user explicitly asked). The tool returns a slug. + +6. **Call `run-background-task-agent`** with the slug. The agent runs once and populates `index.md`. + +7. **Confirm.** One line. Name the task. Point at the sidebar. Done. + +## Extend, don't fork + +When the user's new ask overlaps with an existing task — e.g. they say "also include X" or the ask is a refinement of an existing task's intent — call `patch-background-task` instead of creating a duplicate. + +Signals that you should extend: +- The user says "also …" / "and on top of that …" / "while you're at it …" +- The new ask is a refinement of an existing task's intent (different threshold, additional source, slightly different output) + +When extending, pass the full rewritten `instructions` — don't try to surgical-edit a single sentence. The agent rereads instructions every run, so a clean rewrite is fine. After `patch-background-task` returns, call `run-background-task-agent` on the same slug so the user sees the updated output. + +## Worked examples + +### OUTPUT — morning briefing + +User: *"Every morning at 7, give me a one-paragraph summary of overnight news in AI agents."* + +1. `create-background-task` with: + - `name`: "AI agent overnight news" + - `instructions`: "Search the web and Hacker News for news about AI agents (autonomous LLM agents, agentic frameworks, agent benchmarks) published in the last 24 hours. Summarize the top developments in one paragraph (3-5 sentences) followed by a 3-5 item bulleted list of the most significant items with a single-sentence note each. Replace the body of index.md." + - `triggers`: { `cronExpr`: "0 7 * * *" } +2. `run-background-task-agent` slug=ai-agent-overnight-news. +3. "Done — created the **AI agent overnight news** task. It'll run every morning at 7 and you can find it in Background tasks in the sidebar." + +### ACTION — email auto-reply + +User: *"Whenever I get an email about Q3 planning, draft a reply asking when they're free this week."* + +1. `create-background-task` with: + - `name`: "Q3 email auto-reply drafts" + - `instructions`: "When an event arrives describing an email thread about Q3 planning, use the Gmail draft-create tool to draft a reply to the latest message asking the sender when they're free for a 30-minute call this week. Do not send the draft — leave it in Drafts for me to review. After drafting, append a journal entry to index.md noting the thread subject and the draft id." + - `triggers`: { `eventMatchCriteria`: "Emails about Q3 planning (roadmap, OKRs, headcount, exec priorities)" } +2. `run-background-task-agent` slug=q3-email-auto-reply-drafts. +3. "Done — created the **Q3 email auto-reply drafts** task. It'll fire on relevant Gmail threads. Manage it from Background tasks in the sidebar." + +### ACTION + journal — Slack watcher + +User: *"Every weekday morning at 9, post a summary of unresolved high-priority issues to #engineering on Slack."* + +1. `create-background-task` with: + - `name`: "Daily eng triage" + - `instructions`: "Each run, query for unresolved issues labeled priority:high or above. Summarize counts by owner and the three oldest items. Send the summary to #engineering via the Slack tool. After sending, append a journal entry to index.md with the timestamp and the message id." + - `triggers`: { `cronExpr`: "0 9 * * 1-5" } +2. `run-background-task-agent` slug=daily-eng-triage. + +## Canonical Schema + +```yaml +${schemaYaml} +``` + +Notes: +- `active` defaults to true. Patch `{ active: false }` to pause without deleting. +- `createdAt` and `lastRun` are runtime-managed — never write them yourself. +- The `triggers` block reuses Live Notes' `Triggers` schema verbatim. Cron grace and 5-minute backoff semantics are identical. + +## Exceptions + +The `Background tasks` sidebar view has a "New task" button that opens a form-driven flow. If the user is editing fields there or asking about a specific task from that view, *you* are not the right surface — the form is. Point at it ("You can also do this from the New task button in the Background tasks view") and step aside. diff --git a/apps/x/packages/core/src/application/assistant/skills/browser-control/skill.ts b/apps/skills/browser-control/SKILL.md similarity index 96% rename from apps/x/packages/core/src/application/assistant/skills/browser-control/skill.ts rename to apps/skills/browser-control/SKILL.md index 868ce8e8..a3bc216f 100644 --- a/apps/x/packages/core/src/application/assistant/skills/browser-control/skill.ts +++ b/apps/skills/browser-control/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: browser-control +description: >- + Control the embedded browser pane - open sites, inspect page state, and interact with indexed page elements. +metadata: + title: "Browser Control" +--- + # Browser Control Skill You have access to the **browser-control** tool, which controls Rowboat's embedded browser pane directly. @@ -114,6 +121,3 @@ These skills are written against a Python harness, so treat them as **reference - Use Rowboat's browser for live interaction. Use web search tools for research where a live session is unnecessary. - Do not wrap browser URLs or browser pages in ` + "```filepath" + ` blocks. Filepath cards are only for real files on disk, not web pages or browser tabs. - If you mention a page the browser opened, use plain text for the URL/title instead of trying to create a clickable file card. -`; - -export default skill; diff --git a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts b/apps/skills/builtin-tools/SKILL.md similarity index 64% rename from apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts rename to apps/skills/builtin-tools/SKILL.md index 4187584e..f4f5f413 100644 --- a/apps/x/packages/core/src/application/assistant/skills/builtin-tools/skill.ts +++ b/apps/skills/builtin-tools/SKILL.md @@ -1,24 +1,31 @@ -export const skill = String.raw` +--- +name: builtin-tools +description: >- + Understanding and using builtin tools (especially executeCommand for bash/shell) in agent definitions. +metadata: + title: "Builtin Tools Reference" +--- + # Builtin Tools Reference Load this skill when creating or modifying agents that need access to Rowboat's builtin tools (shell execution, file operations, etc.). ## Available Builtin Tools -Agents can use builtin tools by declaring them in the YAML frontmatter \`tools\` section with \`type: builtin\` and the appropriate \`name\`. +Agents can use builtin tools by declaring them in the YAML frontmatter `tools` section with `type: builtin` and the appropriate `name`. ### executeCommand **The most powerful and versatile builtin tool** - Execute any bash/shell command and get the output. -**Security note:** Commands are filtered through \`config/security.json\` in the workspace root. Populate this file with allowed command names (array or dictionary entries). Any command not present is blocked and returns exit code 126 so the agent knows it violated the policy. +**Security note:** Commands are filtered through `config/security.json` in the workspace root. Populate this file with allowed command names (array or dictionary entries). Any command not present is blocked and returns exit code 126 so the agent knows it violated the policy. **Agent tool declaration (YAML frontmatter):** -\`\`\`yaml +```yaml tools: bash: type: builtin name: executeCommand -\`\`\` +``` **What it can do:** - Run package managers (npm, pip, apt, brew, cargo, go get, etc.) @@ -49,8 +56,8 @@ tools: - Full bash shell features are available (variables, loops, conditionals, etc.) - Tools like jq, yq, awk, sed can parse and transform data -**Example agent with executeCommand** (\`agents/arxiv-feed-reader.md\`): -\`\`\`markdown +**Example agent with executeCommand** (`agents/arxiv-feed-reader.md`): +```markdown --- model: gpt-5.1 tools: @@ -64,15 +71,15 @@ Extract latest papers from the arXiv feed and summarize them. Use curl to fetch the RSS feed, then parse it with yq and jq: -\\\`\\\`\\\`bash +\\`\\`\\`bash curl -s https://rss.arxiv.org/rss/cs.AI | yq -p=xml -o=json | jq -r '.rss.channel.item[] | select(.title | test("agent"; "i")) | "\\(.title)\\n\\(.link)\\n\\(.description)\\n"' -\\\`\\\`\\\` +\\`\\`\\` This will give you papers containing 'agent' in the title. -\`\`\` +``` -**Another example - System monitoring agent** (\`agents/system-monitor.md\`): -\`\`\`markdown +**Another example - System monitoring agent** (`agents/system-monitor.md`): +```markdown --- model: gpt-5.1 tools: @@ -89,10 +96,10 @@ Monitor system resources using bash commands: - Use 'ps aux' for process list Parse the output and report any issues. -\`\`\` +``` -**Another example - Git automation agent** (\`agents/git-helper.md\`): -\`\`\`markdown +**Another example - Git automation agent** (`agents/git-helper.md`): +```markdown --- model: gpt-5.1 tools: @@ -109,19 +116,19 @@ Help with git operations. Use commands like: - 'git branch -a' - List branches Can also run 'git add', 'git commit', 'git push' when instructed. -\`\`\` +``` ## Agent-to-Agent Calling Agents can call other agents as tools to create complex multi-step workflows. This is the core mechanism for building multi-agent systems in the CLI. **Tool declaration (YAML frontmatter):** -\`\`\`yaml +```yaml tools: summariser: type: agent name: summariser_agent -\`\`\` +``` **When to use:** - Breaking complex tasks into specialized sub-agents @@ -135,8 +142,8 @@ tools: - Results are returned as tool output - The calling agent can then continue processing or delegate further -**Example - Agent that delegates to a summarizer** (\`agents/paper_analyzer.md\`): -\`\`\`markdown +**Example - Agent that delegates to a summarizer** (`agents/paper_analyzer.md`): +```markdown --- model: gpt-5.1 tools: @@ -148,7 +155,7 @@ tools: Pick 2 interesting papers and summarise each using the summariser tool. Pass the paper URL to the summariser. Don't ask for human input. -\`\`\` +``` **Tips for agent chaining:** - Make instructions explicit about when to call other agents @@ -158,43 +165,43 @@ Pass the paper URL to the summariser. Don't ask for human input. ## Additional Builtin Tools -While \`executeCommand\` is the most versatile, other builtin tools exist for specific Rowboat operations (file management, agent inspection, etc.). These are primarily used by the Rowboat copilot itself and are not typically needed in user agents. If you need file operations, consider using bash commands like \`cat\`, \`echo\`, \`tee\`, etc. through \`executeCommand\`. +While `executeCommand` is the most versatile, other builtin tools exist for specific Rowboat operations (file management, agent inspection, etc.). These are primarily used by the Rowboat copilot itself and are not typically needed in user agents. If you need file operations, consider using bash commands like `cat`, `echo`, `tee`, etc. through `executeCommand`. ### Copilot-Specific Builtin Tools The Rowboat copilot has access to special builtin tools that regular agents don't typically use. These tools help the copilot assist users with workspace management and MCP integration: #### File & Directory Operations -- \`workspace-readdir\` - List directory contents (supports recursive exploration) -- \`workspace-readFile\` - Read file contents -- \`workspace-writeFile\` - Create or update file contents -- \`workspace-edit\` - Make precise edits by replacing specific text (safer than full rewrites) -- \`workspace-remove\` - Remove files or directories -- \`workspace-exists\` - Check if a file or directory exists -- \`workspace-stat\` - Get file/directory statistics -- \`workspace-mkdir\` - Create directories -- \`workspace-rename\` - Rename or move files/directories -- \`workspace-copy\` - Copy files -- \`workspace-getRoot\` - Get workspace root directory path -- \`workspace-glob\` - Find files matching a glob pattern (e.g., "**/*.ts", "agents/*.md") -- \`workspace-grep\` - Search file contents using regex, returns matching files and lines +- `workspace-readdir` - List directory contents (supports recursive exploration) +- `workspace-readFile` - Read file contents +- `workspace-writeFile` - Create or update file contents +- `workspace-edit` - Make precise edits by replacing specific text (safer than full rewrites) +- `workspace-remove` - Remove files or directories +- `workspace-exists` - Check if a file or directory exists +- `workspace-stat` - Get file/directory statistics +- `workspace-mkdir` - Create directories +- `workspace-rename` - Rename or move files/directories +- `workspace-copy` - Copy files +- `workspace-getRoot` - Get workspace root directory path +- `workspace-glob` - Find files matching a glob pattern (e.g., "**/*.ts", "agents/*.md") +- `workspace-grep` - Search file contents using regex, returns matching files and lines #### Agent Operations -- \`analyzeAgent\` - Read and analyze an agent file structure -- \`loadSkill\` - Load a Rowboat skill definition into context +- `analyzeAgent` - Read and analyze an agent file structure +- `loadSkill` - Load a Rowboat skill definition into context #### MCP Operations -- \`addMcpServer\` - Add or update an MCP server configuration (with validation) -- \`listMcpServers\` - List all available MCP servers -- \`listMcpTools\` - List all available tools from a specific MCP server -- \`executeMcpTool\` - **Execute a specific MCP tool on behalf of the user** +- `addMcpServer` - Add or update an MCP server configuration (with validation) +- `listMcpServers` - List all available MCP servers +- `listMcpTools` - List all available tools from a specific MCP server +- `executeMcpTool` - **Execute a specific MCP tool on behalf of the user** #### Using executeMcpTool as Copilot -The \`executeMcpTool\` builtin allows the copilot to directly execute MCP tools without creating an agent. Load the "mcp-integration" skill for complete guidance on discovering and executing MCP tools, including workflows, schema matching, and examples. +The `executeMcpTool` builtin allows the copilot to directly execute MCP tools without creating an agent. Load the "mcp-integration" skill for complete guidance on discovering and executing MCP tools, including workflows, schema matching, and examples. **When to use executeMcpTool vs creating an agent:** -- Use \`executeMcpTool\` for immediate, one-time tasks +- Use `executeMcpTool` for immediate, one-time tasks - Create an agent when the user needs repeated use or autonomous operation - Create an agent for complex multi-step workflows involving multiple tools @@ -211,18 +218,15 @@ The \`executeMcpTool\` builtin allows the copilot to directly execute MCP tools - **Use builtin executeCommand** when you need: CLI tools, system operations, data processing, git operations, any shell command - **Use MCP tools** when you need: Web scraping (firecrawl), text-to-speech (elevenlabs), specialized APIs, external service integrations -- **Use agent tools (\`type: agent\`)** when you need: Complex multi-step logic, task delegation, specialized processing that benefits from LLM reasoning +- **Use agent tools (`type: agent`)** when you need: Complex multi-step logic, task delegation, specialized processing that benefits from LLM reasoning -Many tasks can be accomplished with just \`executeCommand\` and common Unix tools - it's incredibly powerful! +Many tasks can be accomplished with just `executeCommand` and common Unix tools - it's incredibly powerful! ## Key Insight: Multi-Agent Workflows In the CLI, multi-agent workflows are built by: -1. Creating specialized agents as Markdown files in the \`agents/\` directory -2. Creating an orchestrator agent that has other agents in its \`tools\` (YAML frontmatter) -3. Running the orchestrator with \`rowboatx --agent orchestrator_name\` +1. Creating specialized agents as Markdown files in the `agents/` directory +2. Creating an orchestrator agent that has other agents in its `tools` (YAML frontmatter) +3. Running the orchestrator with `rowboatx --agent orchestrator_name` There are no separate "workflow" files - everything is an agent defined in Markdown! -`; - -export default skill; diff --git a/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts b/apps/skills/code-with-agents/SKILL.md similarity index 77% rename from apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts rename to apps/skills/code-with-agents/SKILL.md index c2879228..8ace0df0 100644 --- a/apps/x/packages/core/src/application/assistant/skills/code-with-agents/skill.ts +++ b/apps/skills/code-with-agents/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: code-with-agents +description: >- + Write code, build projects, create scripts, or fix bugs by delegating to Claude Code or Codex via acpx. +metadata: + title: "Code with Agents" +--- + # Code with Agents Skill Use this skill when the user asks you to write code, build a project, create scripts, fix bugs, or do any software development task that should be delegated to a coding agent (Claude Code or Codex). @@ -36,11 +43,11 @@ Before running anything, confirm the following with the user: Once you know the folder and agent, tell the user: -> I'll use [Claude Code / Codex] to [description of the task] in \`[folder]\`. Permission requests from the coding agent itself (file writes, command execution, etc.) will be automatically approved once it starts. Wait for the user's confirmation before you execute anything. +> I'll use [Claude Code / Codex] to [description of the task] in `[folder]`. Permission requests from the coding agent itself (file writes, command execution, etc.) will be automatically approved once it starts. Wait for the user's confirmation before you execute anything. ### Step 3: Execute with acpx -Use the \`executeCommand\` tool to run the coding agent via acpx. The command format is: +Use the `executeCommand` tool to run the coding agent via acpx. The command format is: **For Claude Code:** ` + "`" + ` @@ -54,7 +61,7 @@ npx acpx@latest --approve-all --cwd codex exec "" ### Critical: flag order -The \`--approve-all\` and \`--cwd\` flags are global flags and MUST come before the agent name (\`claude\` or \`codex\`). This is the correct order: +The `--approve-all` and `--cwd` flags are global flags and MUST come before the agent name (`claude` or `codex`). This is the correct order: ` + "`" + ` npx acpx@latest [global flags] exec "" @@ -82,9 +89,6 @@ When constructing the prompt for the coding agent: After the command finishes, look for the summary that the coding agent produced at the end of its output and pass that along to the user as-is. Do not rewrite or add to it. Only add your own explanation if the command failed or the exit code is non-zero. -Do NOT use file reference blocks (e.g. \`\`\`file:path/to/file\`\`\`) when mentioning code files — they may not open correctly. Just refer to file paths as plain text. +Do NOT use file reference blocks (e.g. ```file:path/to/file```) when mentioning code files — they may not open correctly. Just refer to file paths as plain text. -- If the exit code is 5, it means permissions were denied — this should not happen with \`--approve-all\`, but if it does, let the user know -`; - -export default skill; +- If the exit code is 5, it means permissions were denied — this should not happen with `--approve-all`, but if it does, let the user know diff --git a/apps/x/packages/core/src/application/assistant/skills/composio-integration/skill.ts b/apps/skills/composio-integration/SKILL.md similarity index 54% rename from apps/x/packages/core/src/application/assistant/skills/composio-integration/skill.ts rename to apps/skills/composio-integration/SKILL.md index 795daeeb..8b0f5f6b 100644 --- a/apps/x/packages/core/src/application/assistant/skills/composio-integration/skill.ts +++ b/apps/skills/composio-integration/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: composio-integration +description: >- + Interact with third-party services (Gmail, GitHub, Slack, LinkedIn, Notion, Jira, Google Sheets, etc.) via Composio. Search, connect, and execute tools. +metadata: + title: "Composio Integration" +--- + # Composio Integration **Load this skill** when the user asks to interact with ANY third-party service — email, GitHub, Slack, LinkedIn, Notion, Jira, Google Sheets, calendar, etc. This skill provides the complete workflow for discovering, connecting, and executing Composio tools. @@ -16,34 +23,34 @@ export const skill = String.raw` | Service | Slug | |---------|------| -| Gmail | \`gmail\` | -| Google Calendar | \`googlecalendar\` | -| Google Sheets | \`googlesheets\` | -| Google Docs | \`googledocs\` | -| Google Drive | \`googledrive\` | -| Slack | \`slack\` | -| GitHub | \`github\` | -| Notion | \`notion\` | -| Linear | \`linear\` | -| Jira | \`jira\` | -| Asana | \`asana\` | -| Trello | \`trello\` | -| HubSpot | \`hubspot\` | -| Salesforce | \`salesforce\` | -| LinkedIn | \`linkedin\` | -| X (Twitter) | \`twitter\` | -| Reddit | \`reddit\` | -| Dropbox | \`dropbox\` | -| OneDrive | \`onedrive\` | -| Microsoft Outlook | \`microsoft_outlook\` | -| Microsoft Teams | \`microsoft_teams\` | -| Calendly | \`calendly\` | -| Cal.com | \`cal\` | -| Intercom | \`intercom\` | -| Zendesk | \`zendesk\` | -| Airtable | \`airtable\` | +| Gmail | `gmail` | +| Google Calendar | `googlecalendar` | +| Google Sheets | `googlesheets` | +| Google Docs | `googledocs` | +| Google Drive | `googledrive` | +| Slack | `slack` | +| GitHub | `github` | +| Notion | `notion` | +| Linear | `linear` | +| Jira | `jira` | +| Asana | `asana` | +| Trello | `trello` | +| HubSpot | `hubspot` | +| Salesforce | `salesforce` | +| LinkedIn | `linkedin` | +| X (Twitter) | `twitter` | +| Reddit | `reddit` | +| Dropbox | `dropbox` | +| OneDrive | `onedrive` | +| Microsoft Outlook | `microsoft_outlook` | +| Microsoft Teams | `microsoft_teams` | +| Calendly | `calendly` | +| Cal.com | `cal` | +| Intercom | `intercom` | +| Zendesk | `zendesk` | +| Airtable | `airtable` | -**IMPORTANT:** Always use these exact slugs. Do NOT guess — e.g., Google Sheets is \`googlesheets\` (no underscore), not \`google_sheets\`. +**IMPORTANT:** Always use these exact slugs. Do NOT guess — e.g., Google Sheets is `googlesheets` (no underscore), not `google_sheets`. ## Critical: Check First, Connect Second @@ -52,10 +59,10 @@ export const skill = String.raw` **Flow:** 1. Check if the service is in the "Currently connected" list (in the system prompt above) 2. If **connected** → go directly to step 4 -3. If **NOT connected** → call \`composio-connect-toolkit\` once, wait for user to authenticate, then continue -4. Call \`composio-search-tools\` with SHORT keyword queries -5. Read the \`inputSchema\` from results — note \`required\` fields -6. Call \`composio-execute-tool\` with slug, toolkit, and all required arguments +3. If **NOT connected** → call `composio-connect-toolkit` once, wait for user to authenticate, then continue +4. Call `composio-search-tools` with SHORT keyword queries +5. Read the `inputSchema` from results — note `required` fields +6. Call `composio-execute-tool` with slug, toolkit, and all required arguments **NEVER call composio-connect-toolkit for a service that's already connected.** This creates duplicate connect cards in the UI. @@ -74,42 +81,42 @@ If the first search returns 0 results, try a different short query (e.g., "issue ## Passing Arguments -**ALWAYS include the \`arguments\` field** when calling \`composio-execute-tool\`, even if the tool has no required parameters. +**ALWAYS include the `arguments` field** when calling `composio-execute-tool`, even if the tool has no required parameters. -- Read the \`inputSchema\` from search results carefully -- Extract user-provided values into the correct fields (e.g., "rowboatlabs/rowboat" → \`owner: "rowboatlabs", repo: "rowboat"\`) -- For tools with empty \`properties: {}\`, pass \`arguments: {}\` +- Read the `inputSchema` from search results carefully +- Extract user-provided values into the correct fields (e.g., "rowboatlabs/rowboat" → `owner: "rowboatlabs", repo: "rowboat"`) +- For tools with empty `properties: {}`, pass `arguments: {}` - For tools with required fields, pass all of them ### Example: GitHub Issues User says: "Get me the open issues on rowboatlabs/rowboat" -1. \`composio-search-tools({ query: "list issues", toolkitSlug: "github" })\` - → finds \`GITHUB_ISSUES_LIST_FOR_REPO\` with required: ["owner", "repo"] -2. \`composio-execute-tool({ toolSlug: "GITHUB_ISSUES_LIST_FOR_REPO", toolkitSlug: "github", arguments: { owner: "rowboatlabs", repo: "rowboat", state: "open", per_page: 100 } })\` +1. `composio-search-tools({ query: "list issues", toolkitSlug: "github" })` + → finds `GITHUB_ISSUES_LIST_FOR_REPO` with required: ["owner", "repo"] +2. `composio-execute-tool({ toolSlug: "GITHUB_ISSUES_LIST_FOR_REPO", toolkitSlug: "github", arguments: { owner: "rowboatlabs", repo: "rowboat", state: "open", per_page: 100 } })` ### Example: Gmail Fetch User says: "What's my latest email?" -1. \`composio-search-tools({ query: "fetch emails", toolkitSlug: "gmail" })\` - → finds \`GMAIL_FETCH_EMAILS\` -2. \`composio-execute-tool({ toolSlug: "GMAIL_FETCH_EMAILS", toolkitSlug: "gmail", arguments: { user_id: "me", max_results: 5 } })\` +1. `composio-search-tools({ query: "fetch emails", toolkitSlug: "gmail" })` + → finds `GMAIL_FETCH_EMAILS` +2. `composio-execute-tool({ toolSlug: "GMAIL_FETCH_EMAILS", toolkitSlug: "gmail", arguments: { user_id: "me", max_results: 5 } })` ### Example: LinkedIn Profile (no-arg tool) User says: "Get my LinkedIn profile" -1. \`composio-search-tools({ query: "get profile", toolkitSlug: "linkedin" })\` - → finds \`LINKEDIN_GET_MY_INFO\` with properties: {} -2. \`composio-execute-tool({ toolSlug: "LINKEDIN_GET_MY_INFO", toolkitSlug: "linkedin", arguments: {} })\` +1. `composio-search-tools({ query: "get profile", toolkitSlug: "linkedin" })` + → finds `LINKEDIN_GET_MY_INFO` with properties: {} +2. `composio-execute-tool({ toolSlug: "LINKEDIN_GET_MY_INFO", toolkitSlug: "linkedin", arguments: {} })` ## Error Recovery - **If a tool call fails** (missing fields, 500 error): Fix the arguments and retry IMMEDIATELY. Do NOT stop and narrate the error to the user. - **If search returns 0 results**: Try a different short query. If still 0, the tool may not exist for that service. -- **If a tool requires connection**: Call \`composio-connect-toolkit\` once, then retry after connection. +- **If a tool requires connection**: Call `composio-connect-toolkit` once, then retry after connection. ## Multi-Part Requests @@ -122,6 +129,3 @@ When the user says "connect X and then do Y" — complete BOTH parts in one turn - **Read-only actions** (fetch, list, get, search): Execute without asking - **Mutating actions** (send email, create issue, post, delete): Show the user what you're about to do and confirm before executing - **Connecting a toolkit**: Always safe — just do it when needed -`; - -export default skill; diff --git a/apps/x/packages/core/src/application/assistant/skills/create-presentations/skill.ts b/apps/skills/create-presentations/SKILL.md similarity index 98% rename from apps/x/packages/core/src/application/assistant/skills/create-presentations/skill.ts rename to apps/skills/create-presentations/SKILL.md index f3dd4d03..893660fc 100644 --- a/apps/x/packages/core/src/application/assistant/skills/create-presentations/skill.ts +++ b/apps/skills/create-presentations/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: create-presentations +description: >- + Create PDF presentations and slide decks from natural language requests using knowledge base context. +metadata: + title: "Create Presentations" +--- + # PDF Presentation Skill ## Theme Selection @@ -79,13 +86,13 @@ Map each point to a slide layout from the Available Layout Types below. For a ty ## Workflow 1. Use workspace-readFile to check knowledge/ for relevant context about the company, product, team, etc. -2. Ensure Playwright is installed: \`npm install playwright && npx playwright install chromium\` +2. Ensure Playwright is installed: `npm install playwright && npx playwright install chromium` 3. Use workspace-getRoot to get the workspace root path. 4. Plan the narrative arc and slide outline (see Content Planning above). 5. Use workspace-writeFile to create the HTML file at tmp/presentation.html (workspace-relative) with slides (1280x720px each). 6. **Perform the Post-Generation Validation (see below). Fix any issues before proceeding.** 7. Use workspace-writeFile to create the conversion script at tmp/convert.js (workspace-relative) — see Playwright Export section. -8. Run it: \`node /tmp/convert.js\` +8. Run it: `node /tmp/convert.js` 9. Tell the user: "Your presentation is ready at ~/Desktop/presentation.pdf" and note the theme used. **Critical**: Never show HTML code to the user. Never ask the user to run commands, install packages, or make technical decisions. The entire pipeline from content to PDF must be invisible to the user. @@ -96,17 +103,17 @@ Use workspace-writeFile and workspace-readFile for ALL file operations. Do NOT u After generating the slide HTML, perform ALL of these checks before converting to PDF: -1. **Title overflow check**: For every slide, verify that the title text at its set font-size fits within the slide width (1280px) minus padding (120px total). If \`title_chars × 0.6 × font_size > 1160\`, reduce font-size. Use these max sizes: +1. **Title overflow check**: For every slide, verify that the title text at its set font-size fits within the slide width (1280px) minus padding (120px total). If `title_chars × 0.6 × font_size > 1160`, reduce font-size. Use these max sizes: - Short titles (1-3 words): 72px max - Medium titles (4-6 words): 56px max - Long titles (7+ words): 44px max - Apply \`word-wrap: break-word\` and \`overflow-wrap: break-word\` to all title elements. Never use \`white-space: nowrap\` on titles. + Apply `word-wrap: break-word` and `overflow-wrap: break-word` to all title elements. Never use `white-space: nowrap` on titles. 2. **Content bounds check**: Verify no element extends beyond the 1280x720 slide boundary. Look for: long titles, bullet lists with 6+ items, wide tables, long labels on charts, text that wraps more lines than the available height allows. -3. **Broken visuals check**: Confirm no \`\` tags reference external URLs. All visuals must be CSS, SVG, or emoji only. Never use external images — they will fail in PDF rendering. Use CSS shapes, gradients, SVG, or emoji for all visual elements. +3. **Broken visuals check**: Confirm no `` tags reference external URLs. All visuals must be CSS, SVG, or emoji only. Never use external images — they will fail in PDF rendering. Use CSS shapes, gradients, SVG, or emoji for all visual elements. -4. **Font loading check**: Verify the Google Fonts \`\` tag includes ALL font families used in the CSS. Missing fonts cause fallback rendering and broken typography. +4. **Font loading check**: Verify the Google Fonts `` tag includes ALL font families used in the CSS. Missing fonts cause fallback rendering and broken typography. 5. **Theme consistency check**: Confirm all slides use the same palette — no rogue colors in charts, backgrounds, or text that don't belong to the chosen theme. @@ -117,15 +124,15 @@ After generating the slide HTML, perform ALL of these checks before converting t These rules prevent rendering issues in PDF. Violating them causes overlapping rectangles and broken layouts. 1. **No layered elements** — Never create separate elements for backgrounds or shadows. Style content elements directly. -2. **No box-shadow** — Use borders instead: \`border: 1px solid #e5e7eb\` -3. **Bullets via CSS only** — Use \`li::before\` pseudo-elements, not separate DOM elements. -4. **Content must fit** — Slides are 1280x720px with 60px padding. Safe content area is 1160x600px. Use \`overflow: hidden\`. +2. **No box-shadow** — Use borders instead: `border: 1px solid #e5e7eb` +3. **Bullets via CSS only** — Use `li::before` pseudo-elements, not separate DOM elements. +4. **Content must fit** — Slides are 1280x720px with 60px padding. Safe content area is 1160x600px. Use `overflow: hidden`. 5. **No footers or headers** — Never add fixed/absolute-positioned footer or header elements to slides. They overlap with content in PDF rendering. If you need a slide number or title, include it as part of the normal content flow. 6. **No external images** — All visuals must be CSS, SVG, or emoji. External image URLs will render as broken white boxes in PDF. ## Required CSS -\`\`\`css +```css @page { size: 1280px 720px; margin: 0; } html { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; } .slide { @@ -137,11 +144,11 @@ html { -webkit-print-color-adjust: exact !important; print-color-adjust: exact ! page-break-inside: avoid; } .slide:last-child { page-break-after: auto; } -\`\`\` +``` ## Playwright Export -\`\`\`javascript +```javascript // save as tmp/convert.js via workspace-writeFile const { chromium } = require('playwright'); const path = require('path'); @@ -160,9 +167,9 @@ const path = require('path'); await browser.close(); console.log('Done: ~/Desktop/presentation.pdf'); })(); -\`\`\` +``` -Replace \`\` with the actual absolute path returned by workspace-getRoot. +Replace `` with the actual absolute path returned by workspace-getRoot. ## Available Layout Types (35 Templates) @@ -245,7 +252,7 @@ Never use the same layout for consecutive slides. Alternate between dark and lig ### Design Guidelines -- Use Google Fonts loaded via \`\` tag. Recommended pairings: +- Use Google Fonts loaded via `` tag. Recommended pairings: - **Primary pair**: Outfit (headings) + DM Sans (body) — works for most decks - **Editorial pair**: Playfair Display (headings) + DM Sans (body) — for reports/proposals - **Accent fonts**: Space Mono (overlines, labels), Crimson Pro (quotes) @@ -253,7 +260,7 @@ Never use the same layout for consecutive slides. Alternate between dark and lig - Light slides: use warm neutrals, clean borders, and ample whitespace - Charts: use CSS (conic-gradient for donuts, inline styles for bar heights) or inline SVG for line/combo charts - Typography hierarchy: monospace overlines for labels -> sans-serif for headings -> serif for editorial/quotes -- Cards: use \`border-radius: 12-16px\`, subtle borders (\`rgba(255,255,255,0.08)\` on dark), no box-shadow (PDF rule) +- Cards: use `border-radius: 12-16px`, subtle borders (`rgba(255,255,255,0.08)` on dark), no box-shadow (PDF rule) - All visuals must be CSS, SVG, or emoji — no external images ### HTML Template Examples @@ -2738,7 +2745,3 @@ Never use the same layout for consecutive slides. Alternate between dark and lig - -`; - -export default skill; \ No newline at end of file diff --git a/apps/x/packages/core/src/application/assistant/skills/deletion-guardrails/skill.ts b/apps/skills/deletion-guardrails/SKILL.md similarity index 83% rename from apps/x/packages/core/src/application/assistant/skills/deletion-guardrails/skill.ts rename to apps/skills/deletion-guardrails/SKILL.md index e0355b8d..760b0454 100644 --- a/apps/x/packages/core/src/application/assistant/skills/deletion-guardrails/skill.ts +++ b/apps/skills/deletion-guardrails/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: deletion-guardrails +description: >- + Following the confirmation process before removing workflows or agents and their dependencies. +metadata: + title: "Deletion Guardrails" +--- + # Deletion Guardrails Load this skill when a user asks to delete agents or workflows so you follow the required confirmation steps. @@ -19,6 +26,3 @@ Load this skill when a user asks to delete agents or workflows so you follow the - 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; diff --git a/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts b/apps/skills/doc-collab/SKILL.md similarity index 79% rename from apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts rename to apps/skills/doc-collab/SKILL.md index 798fef65..dc6623f7 100644 --- a/apps/x/packages/core/src/application/assistant/skills/doc-collab/skill.ts +++ b/apps/skills/doc-collab/SKILL.md @@ -1,11 +1,19 @@ -import { KNOWLEDGE_NOTE_STYLE_GUIDE } from '../../../lib/knowledge-note-style.js'; +--- +name: doc-collab +description: >- + Collaborate on documents - create, edit, and refine notes and documents in the knowledge base. +metadata: + title: "Document Collaboration" +--- -export const skill = String.raw` # Document Collaboration Skill You are an expert document assistant helping the user create, edit, and refine documents in their knowledge base. -` + KNOWLEDGE_NOTE_STYLE_GUIDE + String.raw` + + +{{include:knowledge-note-style}} + > The writing style above is non-negotiable for any content you author or edit in the knowledge base — even small one-off edits. The user's whole knowledge base is built on it. The rest of this skill covers the *workflow* of collaboration; the style guide above covers the *output*. @@ -54,14 +62,14 @@ You are an expert document assistant helping the user create, edit, and refine d When the user mentions a document name, search for it using multiple approaches: 1. **Search by name pattern** (handles partial matches, different cases): -\`\`\` +``` workspace-glob({ pattern: "knowledge/**/*[name]*", path: "knowledge/" }) -\`\`\` +``` 2. **Search by content** (finds docs that mention the topic): -\`\`\` +``` workspace-grep({ pattern: "[name]", path: "knowledge/" }) -\`\`\` +``` 3. **Try common variations:** - With/without hyphens: "show-hn" vs "showhn" vs "show hn" @@ -78,19 +86,19 @@ workspace-grep({ pattern: "[name]", path: "knowledge/" }) - Ask: "Which document would you like to work on?" **Creating new documents:** -1. Ask simply: "Shall I create [filename]?" (don't ask about location - default to \`knowledge/Notes/\` unless the user specifies a different folder) +1. Ask simply: "Shall I create [filename]?" (don't ask about location - default to `knowledge/Notes/` unless the user specifies a different folder) 2. Create it with just a title - don't pre-populate with structure or outlines 3. Ask: "What would you like in this?" -\`\`\` +``` workspace-createFile({ path: "knowledge/Notes/[Document Name].md", content: "# [Document Title]\n\n" }) -\`\`\` +``` **WRONG approach:** -- "Should this be in Projects/ or Topics/?" - don't ask, just use \`knowledge/Notes/\` +- "Should this be in Projects/ or Topics/?" - don't ask, just use `knowledge/Notes/` - "Here's a proposed outline..." - don't propose, let the user guide - "I'll create a structure with sections for X, Y, Z" - don't assume structure @@ -123,22 +131,22 @@ workspace-createFile({ ### Step 3: Execute Changes **For edits, use workspace-editFile:** -\`\`\` +``` workspace-editFile({ path: "knowledge/[path].md", old_string: "[exact text to replace]", new_string: "[new text]" }) -\`\`\` +``` **For additions at the end:** -\`\`\` +``` workspace-editFile({ path: "knowledge/[path].md", old_string: "[last line or section]", new_string: "[last line or section]\n\n[new content]" }) -\`\`\` +``` **For new sections:** Find the right place in the document structure and insert the new section. @@ -155,16 +163,16 @@ After making changes: When the user mentions people, companies, or projects: **Search for relevant notes:** -\`\`\` +``` workspace-grep({ pattern: "[Name]", path: "knowledge/" }) -\`\`\` +``` **Read relevant notes:** -\`\`\` +``` workspace-readFile("knowledge/People/[Person].md") workspace-readFile("knowledge/Organizations/[Company].md") workspace-readFile("knowledge/Projects/[Project].md") -\`\`\` +``` **Use the context:** - Reference specific facts, dates, and details @@ -173,12 +181,12 @@ workspace-readFile("knowledge/Projects/[Project].md") ## Document Locations -Documents are stored in \`knowledge/\` within the workspace root, with subfolders: -- \`Notes/\` - **Default location for user notes. Create new notes here unless the user specifies a different folder.** -- \`People/\` - Notes about individuals -- \`Organizations/\` - Notes about companies, teams -- \`Projects/\` - Project documentation -- \`Topics/\` - Subject matter notes +Documents are stored in `knowledge/` within the workspace root, with subfolders: +- `Notes/` - **Default location for user notes. Create new notes here unless the user specifies a different folder.** +- `People/` - Notes about individuals +- `Organizations/` - Notes about companies, teams +- `Projects/` - Project documentation +- `Topics/` - Subject matter notes ## Rich Blocks @@ -186,61 +194,61 @@ Notes support rich block types beyond standard Markdown. Blocks are fenced code ### Image Block Displays an image with optional alt text and caption. -\`\`\`image +```image {"src": "https://example.com/photo.png", "alt": "Description", "caption": "Optional caption"} -\`\`\` -- \`src\` (required): URL or relative path to the image -- \`alt\` (optional): Alt text -- \`caption\` (optional): Caption displayed below the image +``` +- `src` (required): URL or relative path to the image +- `alt` (optional): Alt text +- `caption` (optional): Caption displayed below the image ### Embed Block Embeds external content (YouTube videos, Figma designs, tweets, or generic links). -\`\`\`embed +```embed {"provider": "youtube", "url": "https://www.youtube.com/watch?v=VIDEO_ID", "caption": "Video title"} -\`\`\` -- \`provider\` (required): \`"youtube"\`, \`"figma"\`, \`"tweet"\`, or \`"generic"\` -- \`url\` (required): Full URL to the content -- \`caption\` (optional): Caption displayed below the embed +``` +- `provider` (required): `"youtube"`, `"figma"`, `"tweet"`, or `"generic"` +- `url` (required): Full URL to the content +- `caption` (optional): Caption displayed below the embed - YouTube and Figma render as iframes; tweet renders inline from the tweet URL; generic shows a link card ### Iframe Block Embeds an arbitrary web page or a locally-served dashboard in the note. -\`\`\`iframe +```iframe {"url": "http://localhost:3210/sites/example-dashboard/", "title": "Trend Dashboard", "height": 640} -\`\`\` -- \`url\` (required): Full URL to render. Use \`https://\` for remote sites, or \`http://localhost:3210/sites//\` for local dashboards -- \`title\` (optional): Title shown above the iframe -- \`height\` (optional): Height in pixels. Good dashboard defaults are 480-800 -- \`allow\` (optional): Custom iframe \`allow\` attribute when the page needs extra browser capabilities -- Remote sites may refuse to render in iframes because of their own CSP / X-Frame-Options headers. When you need a reliable embed, create a local site in \`sites//\` and use the localhost URL above +``` +- `url` (required): Full URL to render. Use `https://` for remote sites, or `http://localhost:3210/sites//` for local dashboards +- `title` (optional): Title shown above the iframe +- `height` (optional): Height in pixels. Good dashboard defaults are 480-800 +- `allow` (optional): Custom iframe `allow` attribute when the page needs extra browser capabilities +- Remote sites may refuse to render in iframes because of their own CSP / X-Frame-Options headers. When you need a reliable embed, create a local site in `sites//` and use the localhost URL above ### Chart Block Renders a chart from inline data. -\`\`\`chart +```chart {"chart": "bar", "title": "Q1 Revenue", "data": [{"month": "Jan", "revenue": 50000}, {"month": "Feb", "revenue": 62000}], "x": "month", "y": "revenue"} -\`\`\` -- \`chart\` (required): \`"line"\`, \`"bar"\`, or \`"pie"\` -- \`title\` (optional): Chart title -- \`data\` (optional): Array of objects with the data points -- \`source\` (optional): Relative path to a JSON file containing the data array (alternative to inline data) -- \`x\` (required): Key name for the x-axis / label field -- \`y\` (required): Key name for the y-axis / value field +``` +- `chart` (required): `"line"`, `"bar"`, or `"pie"` +- `title` (optional): Chart title +- `data` (optional): Array of objects with the data points +- `source` (optional): Relative path to a JSON file containing the data array (alternative to inline data) +- `x` (required): Key name for the x-axis / label field +- `y` (required): Key name for the y-axis / value field ### Table Block Renders a styled table from structured data. -\`\`\`table +```table {"title": "Team", "columns": ["name", "role"], "data": [{"name": "Alice", "role": "Eng"}, {"name": "Bob", "role": "Design"}]} -\`\`\` -- \`columns\` (required): Array of column names (determines display order) -- \`data\` (required): Array of row objects -- \`title\` (optional): Table title +``` +- `columns` (required): Array of column names (determines display order) +- `data` (required): Array of row objects +- `title` (optional): Table title ### Block Guidelines - The JSON must be valid and on a single line (no pretty-printing) -- Insert blocks using \`workspace-editFile\` just like any other content +- Insert blocks using `workspace-editFile` just like any other content - When the user asks for a chart, table, embed, or live dashboard — use blocks rather than plain Markdown tables or image links - When editing a note that already contains blocks, preserve them unless the user asks to change them -- For local dashboards and mini apps, put the site files in \`sites//\` and point an \`iframe\` block at \`http://localhost:3210/sites//\` +- For local dashboards and mini apps, put the site files in `sites//` and point an `iframe` block at `http://localhost:3210/sites//` ## Best Practices @@ -257,9 +265,9 @@ Renders a styled table from structured data. - Be responsive to feedback **Wiki-links:** -- Use \`[[Person Name]]\` to link to people -- Use \`[[Organization Name]]\` to link to companies -- Use \`[[Project Name]]\` to link to projects +- Use `[[Person Name]]` to link to people +- Use `[[Organization Name]]` to link to companies +- Use `[[Project Name]]` to link to projects - Only link to notes that exist or that you'll create ## Example Interactions @@ -279,10 +287,10 @@ Renders a styled table from structured data. **Approval mode - showing changes first:** **User:** "Add a section about Acme Corp" **You:** "I'll add this after the Overview section: -\`\`\` +``` ## Partnership with Acme Corp [content based on knowledge...] -\`\`\` +``` Ok to add?" **User:** "yes" **You:** *Makes the edit* @@ -302,6 +310,3 @@ Ok to add?" - "In the meantime, let me propose some sections..." ❌ - Switching from approval mode to direct mode without asking ❌ - In approval mode: making edits without showing the change first ❌ -`; - -export default skill; diff --git a/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts b/apps/skills/draft-emails/SKILL.md similarity index 78% rename from apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts rename to apps/skills/draft-emails/SKILL.md index 208e9560..c9b31d29 100644 --- a/apps/x/packages/core/src/application/assistant/skills/draft-emails/skill.ts +++ b/apps/skills/draft-emails/SKILL.md @@ -1,4 +1,11 @@ -export const skill = String.raw` +--- +name: draft-emails +description: >- + Process incoming emails and create draft responses using calendar and knowledge base for context. +metadata: + title: "Draft Emails" +--- + # Email Draft Skill You are helping the user draft email responses. Use their calendar and knowledge base for context. @@ -7,21 +14,21 @@ You are helping the user draft email responses. Use their calendar and knowledge **BEFORE drafting any email, you MUST look up the person/organization in the knowledge base.** -**PATH REQUIREMENT:** Always use \`knowledge/\` as the path (not empty, not the workspace root, not an absolute path). -- **WRONG:** \`path: ""\` or \`path: "."\` -- **CORRECT:** \`path: "knowledge/"\` +**PATH REQUIREMENT:** Always use `knowledge/` as the path (not empty, not the workspace root, not an absolute path). +- **WRONG:** `path: ""` or `path: "."` +- **CORRECT:** `path: "knowledge/"` When the user says "draft an email to Monica" or mentions ANY person, organization, project, or topic: 1. **STOP** - Do not draft anything yet -2. **SEARCH** - Look them up in the knowledge base (path MUST be \`knowledge/\`): - \`\`\` +2. **SEARCH** - Look them up in the knowledge base (path MUST be `knowledge/`): + ``` workspace-grep({ pattern: "Monica", path: "knowledge/" }) - \`\`\` + ``` 3. **READ** - Read their note to understand who they are: - \`\`\` + ``` workspace-readFile("knowledge/People/Monica Smith.md") - \`\`\` + ``` 4. **UNDERSTAND** - Extract their role, organization, relationship history, past interactions, open items 5. **THEN DRAFT** - Only now draft the email, using this context @@ -43,48 +50,48 @@ When the user says "draft an email to Monica" or mentions ANY person, organizati ## State Management -All state is stored in \`pre-built/email-draft/\`: +All state is stored in `pre-built/email-draft/`: -- \`state.json\` - Tracks processing state: - \`\`\`json +- `state.json` - Tracks processing state: + ```json { "lastProcessedTimestamp": "2025-01-10T00:00:00Z", "drafted": ["email_id_1", "email_id_2"], "ignored": ["spam_id_1", "spam_id_2"] } - \`\`\` -- \`drafts/\` - Contains draft email files + ``` +- `drafts/` - Contains draft email files ## Initialization On first run, check if state exists. If not, create it: -1. Check if \`pre-built/email-draft/state.json\` exists -2. If not, create \`pre-built/email-draft/\` and \`pre-built/email-draft/drafts/\` -3. Initialize \`state.json\` with empty arrays and a timestamp of "1970-01-01T00:00:00Z" +1. Check if `pre-built/email-draft/state.json` exists +2. If not, create `pre-built/email-draft/` and `pre-built/email-draft/drafts/` +3. Initialize `state.json` with empty arrays and a timestamp of "1970-01-01T00:00:00Z" ## Processing Flow ### Step 1: Load State -Read \`pre-built/email-draft/state.json\` to get: -- \`lastProcessedTimestamp\` - Only process emails newer than this -- \`drafted\` - List of email IDs already drafted (skip these) -- \`ignored\` - List of email IDs marked as ignored (skip these) +Read `pre-built/email-draft/state.json` to get: +- `lastProcessedTimestamp` - Only process emails newer than this +- `drafted` - List of email IDs already drafted (skip these) +- `ignored` - List of email IDs marked as ignored (skip these) ### Step 2: Scan for New Emails -List emails in \`gmail_sync/\` folder. +List emails in `gmail_sync/` folder. For each email file: -1. Extract the email ID from filename (e.g., \`19048cf9c0317981.md\` -> \`19048cf9c0317981\`) -2. Skip if ID is in \`drafted\` or \`ignored\` lists +1. Extract the email ID from filename (e.g., `19048cf9c0317981.md` -> `19048cf9c0317981`) +2. Skip if ID is in `drafted` or `ignored` lists 3. Read the email content ### Step 3: Parse Email Each email file contains: -\`\`\`markdown +```markdown # Subject Line **Thread ID:** @@ -96,7 +103,7 @@ Each email file contains: **Date:** -\`\`\` +``` Extract: - Thread ID (this is the email ID) @@ -110,7 +117,7 @@ Extract: Determine the email type and action: -**IGNORE these (add to \`ignored\` list):** +**IGNORE these (add to `ignored` list):** - Newsletters (unsubscribe links, "View in browser", bulk sender indicators) - Marketing emails (promotional language, no-reply senders) - Automated notifications (GitHub, Jira, Slack, shipping updates) @@ -130,23 +137,23 @@ Before drafting, gather relevant context. **Always check the knowledge base firs **Knowledge Base Context (REQUIRED):** -First, search for the sender and any mentioned entities (path MUST be \`knowledge/\`): -\`\`\` +First, search for the sender and any mentioned entities (path MUST be `knowledge/`): +``` # Search for the sender by name or email workspace-grep({ pattern: "sender_name_or_email", path: "knowledge/" }) # List all people to find potential matches workspace-readdir("knowledge/People") -\`\`\` +``` Then read the relevant notes: -\`\`\` +``` # Read the sender's note workspace-readFile("knowledge/People/Sender Name.md") # Read their organization's note workspace-readFile("knowledge/Organizations/Company Name.md") -\`\`\` +``` Extract from these notes: - Their role, title, and organization @@ -158,18 +165,18 @@ Extract from these notes: Use this context to provide informed, personalized responses that demonstrate you remember past interactions. **Calendar Context** (for scheduling emails): -- Read calendar events from \`calendar_sync/\` folder +- Read calendar events from `calendar_sync/` folder - Look for events in the relevant time period - Check for conflicts, availability ### Step 6: Create Draft -For emails that need a response, create a draft file in \`pre-built/email-draft/drafts/\`: +For emails that need a response, create a draft file in `pre-built/email-draft/drafts/`: -**Filename:** \`{email_id}_draft.md\` +**Filename:** `{email_id}_draft.md` **Content format:** -\`\`\`markdown +```markdown # Draft Response **Original Email ID:** {email_id} @@ -197,7 +204,7 @@ Subject: Re: {original_subject} ## Notes {any notes about why this response was crafted this way} -\`\`\` +``` **Drafting Guidelines:** - Draft ONE email - do not offer multiple versions or options unless explicitly asked @@ -213,15 +220,15 @@ Subject: Re: {original_subject} ### Step 7: Update State After processing each email: -1. Add the email ID to either \`drafted\` or \`ignored\` list -2. Update \`lastProcessedTimestamp\` to the current time -3. Write updated state to \`pre-built/email-draft/state.json\` +1. Add the email ID to either `drafted` or `ignored` list +2. Update `lastProcessedTimestamp` to the current time +3. Write updated state to `pre-built/email-draft/state.json` ## Output After processing all new emails, provide a summary: -\`\`\` +``` ## Processing Summary **Emails Scanned:** X @@ -233,7 +240,7 @@ After processing all new emails, provide a summary: ### Ignored: - {email_id}: {subject} - {reason for ignoring} -\`\`\` +``` ## Error Handling @@ -247,6 +254,3 @@ After processing all new emails, provide a summary: - The user will review and send drafts manually - Be conservative with ignore - when in doubt, create a draft - For ambiguous emails, create a draft with a note explaining the ambiguity -`; - -export default skill; diff --git a/apps/x/packages/core/src/application/lib/knowledge-note-style.ts b/apps/skills/knowledge-note-style/SKILL.md similarity index 62% rename from apps/x/packages/core/src/application/lib/knowledge-note-style.ts rename to apps/skills/knowledge-note-style/SKILL.md index a70e4186..766839c8 100644 --- a/apps/x/packages/core/src/application/lib/knowledge-note-style.ts +++ b/apps/skills/knowledge-note-style/SKILL.md @@ -1,14 +1,13 @@ -/** - * The canonical writing style for content written into the user's knowledge - * base. Imported by both the `doc-collab` skill (so Copilot picks it up on - * note edits) and the live-note run-agent prompt (so background runs use the - * same rules without having to load the skill on every fire). One source of - * truth, two consumers. - * - * If you change this guide, restart the dev server / rebuild — both consumers - * inline it at module load. - */ -export const KNOWLEDGE_NOTE_STYLE_GUIDE = `# Knowledge-note writing style — terse and scannable +--- +name: knowledge-note-style +description: >- + Canonical terse-and-scannable writing style for content authored into the user's knowledge base. Included by doc-collab and the live-note / background-task agents — not invoked directly. +hidden: true +metadata: + title: "Knowledge Note Style Guide" +--- + +# Knowledge-note writing style — terse and scannable The user's knowledge base is a place they **scan**, not read. Every note competes for attention against many others. Optimize aggressively for **information density and signal-per-line**. These rules apply whether you're authoring a new note, refreshing a live note, or making a one-off edit — they are not optional. @@ -21,32 +20,32 @@ The user's knowledge base is a place they **scan**, not read. Every note compete ## Tightest shape that fits — pick from this ladder **1. Single line** when the answer is one fact. -- Weather: \`24°, Cloudy · NE 8mph · 12% PoP\` -- Price: \`BTC: $67,432 (+1.2% 24h)\` -- Time: \`2:30 PM IST\` -- Status: \`✓ All systems operational\` or \`⚠ db: degraded\` +- Weather: `24°, Cloudy · NE 8mph · 12% PoP` +- Price: `BTC: $67,432 (+1.2% 24h)` +- Time: `2:30 PM IST` +- Status: `✓ All systems operational` or `⚠ db: degraded` **2. Compact table** for 2+ parallel items with the same shape. -\`\`\` +``` | Symbol | Price | Δ24h | |--------|------:|------:| | BTC | $67k | +1.2% | | ETH | $3.2k | −0.8% | -\`\`\` +``` **3. Short bullets** for digests and lists. One line per item, ≤80 chars when possible. Lead with the value, push metadata to the end. -- News: \`- · ·