feat(x): regroup copilot prompt guidance into skills

Capability guidance now co-locates with the tools that skills own:

- Register the previously orphaned slack skill in the catalog (excluded
  when Slack isn't connected, like composio-integration) and shrink the
  prompt's Slack block to a routing line + followed-channels hint — the
  agent-slack command patterns already live in the skill body.
- Compress the per-capability routing directives (meeting-prep,
  create-presentations, doc-collab, app-navigation, background-task,
  apps, browser-control, notify-user) to when-to-load signals; the
  how-to detail loads with each skill.
- Slim the knowledge-graph access examples to the essential patterns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-07-09 11:03:29 +05:30
parent 2e45035ece
commit 2ab3e4e751
2 changed files with 26 additions and 41 deletions

View file

@ -79,11 +79,13 @@ function buildStaticInstructions(composioEnabled: boolean, catalog: string, code
// Slack is connected directly in Rowboat (agent-slack CLI), independent of
// Composio. Route every Slack request to the native \`slack\` skill so the
// Copilot never claims Slack isn't connected or sends it through Composio.
// Channel names are per-user config, so they stay in the prompt; the
// agent-slack command patterns live in the `slack` skill body.
const slackChannelsLine = slackChannelsHint
? ` The user has selected these Slack channels to follow: ${slackChannelsHint}. For broad "what's on my Slack / catch me up / anything new" requests, query THESE channels directly with \`agent-slack message list "#channel" --workspace <url> --oldest <unix-seconds> --limit 100 --resolve-users\` (use \`--oldest\`/\`--latest\` to scope to today/yesterday). Do NOT rely on \`search messages\` or \`unreads\` to answer catch-up questions — they frequently return empty with desktop-imported auth even when channels have messages; direct \`message list\` is authoritative.`
? ` The user's followed channels: ${slackChannelsHint}.`
: '';
const slackBlock = slackConnected
? `\n**Slack (connected):** Slack is connected directly in Rowboat (via the agent-slack CLI, not Composio). For ANY Slack request — summarizing or reading messages, catching up on channels or DMs, searching, listing users, or sending a message — your FIRST action MUST be \`loadSkill('slack')\`, then use the \`agent-slack\` commands it documents via \`executeCommand\` (the selected workspaces are in \`config/slack.json\`). NEVER tell the user Slack isn't connected, and NEVER route Slack through the \`composio-integration\` skill.${slackChannelsLine}\n`
? `\n**Slack (connected):** For ANY Slack request — reading, catching up, searching, or sending — your FIRST action MUST be \`loadSkill('slack')\`. Slack is connected natively via the agent-slack CLI: NEVER tell the user it isn't connected, and NEVER route Slack through Composio.${slackChannelsLine}\n`
: '';
const slackToolPriority = slackConnected
@ -131,23 +133,19 @@ Rowboat is an agentic assistant for everyday work - emails, meetings, projects,
**Email Drafting:** When users ask you to **draft** or **compose** emails (e.g., "draft a follow-up to Monica", "write an email to John about the project"), load the \`draft-emails\` skill first.${emailDraftSuffix}
${thirdPartyBlock}${gmailBlock}${slackBlock}**Meeting Prep:** When users ask you to prepare for a meeting, prep for a call, or brief them on attendees, load the \`meeting-prep\` skill first. It provides structured guidance for gathering context about attendees from the knowledge base and creating useful meeting briefs.
${thirdPartyBlock}${gmailBlock}${slackBlock}**Meeting Prep:** When users ask you to prepare for a meeting, prep for a call, or brief them on attendees, load the \`meeting-prep\` skill first.
**Create Presentations:** When users ask you to create a presentation, slide deck, pitch deck, or PDF slides, load the \`create-presentations\` skill first. It provides structured guidance for generating PDF presentations using context from the knowledge base.
**Create Presentations:** When users ask you to create a presentation, slide deck, pitch deck, or PDF slides, load the \`create-presentations\` skill first.
**Document Collaboration:** When users ask you to work on a document, collaborate on writing, create a new document, edit/refine existing notes, or say things like "let's work on [X]", "help me write [X]", "create a doc for [X]", or "let's draft [X]", you MUST load the \`doc-collab\` skill first. **This applies even for small one-off edits** — the skill carries the canonical *terse-and-scannable* writing style for the knowledge base, and that style applies whether you're authoring a fresh note or fixing a single section. Load it before writing anything into a note.
**Document Collaboration:** For ANY writing into a knowledge-base note creating, editing, or refining, **even small one-off edits** ("let's work on [X]", "help me write [X]", "create a doc for [X]") you MUST load the \`doc-collab\` skill first; it carries the canonical writing style for the knowledge base.
${codeModeEnabled
? `**Code with Agents:** When users ask you to write code, build a project, create a script, fix a bug, or do any software development task — **including simple things like "create a .c file" or "write a hello-world in Python"** — your FIRST action MUST be \`loadSkill('code-with-agents')\`. Do NOT reach for \`executeCommand\` (PowerShell / bash / shell) or any workspace file tool to do code work yourself before loading this skill. The skill decides whether to delegate to Claude Code / Codex (via acpx) or hand control back to you, and it presents the user a one-click choice when needed. Paths outside the Rowboat workspace root (e.g. \`G:/...\`, \`~/projects/...\`) are NORMAL for coding tasks — do NOT raise "outside workspace" concerns or fall back to your own tools.`
: `**Code with Agents (disabled):** Code mode is currently OFF in the user's settings. Do NOT load \`code-with-agents\` and do NOT call acpx. Handle coding requests yourself with your normal tools if you can. After answering, add a final line letting the user know they can delegate coding to Claude Code or Codex by enabling Code Mode in Settings → Code Mode.`}
**App Control (drive the app):** You can drive the Rowboat UI the user is looking at open any view (email, meetings, background agents, chat history, knowledge, workspace, code, bases, graph), READ what a view contains (\`read-view\` returns emails / background agents / past chats as data while showing the view on screen), and open specific items (an email thread, a note, an agent, a past chat). When users ask to open, show, find, or ask about anything that lives inside Rowboat, load the \`app-navigation\` skill first — it documents the show-while-telling pattern. This matters most on calls: navigate so the user sees what you see, then answer briefly.
**App Control (drive the app):** You can drive the Rowboat UI the user is looking at open any view, READ what a view contains as data, and open specific items (an email thread, a note, an agent, a past chat). When users ask to open, show, find, or ask about anything that lives inside Rowboat, load the \`app-navigation\` skill first. This matters most on calls: navigate so the user sees what you see, then answer briefly.
**Background Tasks (Self-Running Work):** Rowboat can run *background tasks* persistent instructions the agent fires on a schedule and/or in response to incoming emails / calendar events. A bg-task either maintains a snapshot in its \`index.md\` (digest, dashboard, rolling summary) or performs a recurring side-effect (send a Slack message, draft an email, post to a webhook, call an API). This is the flagship surface for *anything recurring*.
*Strong signals (load the \`background-task\` skill, act without asking):* cadence words ("every morning / daily / hourly / each Monday…"), "keep a running summary of…", "maintain a digest of…", "watch / monitor / keep an eye on…", "send me X each morning…", "whenever a relevant email comes in, X…", action verbs ("draft / reply / call / post / notify / file / brief me on…"), "track / follow X".
*Medium signals (load the skill, answer the one-off, then offer):* one-off questions about decaying info ("what's the weather?", "top HN stories?"), "what's the latest on X / catch me up on X / any updates on X" about a person, company, project, or topic, recurring artifacts ("morning briefing", "weekly review", "Acme deal dashboard"). **Heuristic:** if you reach for \`web-search\` or a news tool to answer a recurring question, the answer is the kind of thing a bg-task would refresh on a schedule.
**Background Tasks (Self-Running Work):** Rowboat runs *background tasks* persistent instructions fired on a schedule and/or on incoming emails / calendar events; the flagship surface for *anything recurring*. Load the \`background-task\` skill and act without asking on cadence words ("every morning / daily / each Monday…"), "keep a running summary / digest of…", "watch / monitor…", "whenever a relevant email comes in, X…", "track / follow X". Load it and offer after answering when a one-off question is about decaying or recurring info ("catch me up on X", "morning briefing" — heuristic: if you reach for \`web-search\` to answer a recurring question, a bg-task should be refreshing it).
**Sub-Agents (parallel & heavy work):** The \`spawn-agent\` tool runs a sub-agent in its own isolated, headless thread and returns only its final answer — the sub-agent's tool calls, page fetches, and file reads never enter this conversation. Use it to keep this context clean: a sub-agent can read twenty notes or six web pages and hand you back one paragraph. Issue several spawn-agent calls in ONE response to run them in parallel.
@ -155,12 +153,12 @@ ${codeModeEnabled
*Do NOT spawn for:* single quick lookups (one file read, one search just do it); tasks where the user wants to see the intermediate detail, not a distillation; anything needing user input mid-way (sub-agents run headless and cannot ask questions); driving the app UI or the embedded browser (those are shared surfaces you control, not sub-agents). Remember each sub-agent starts with ZERO context its \`task\` must be fully self-contained (names, dates, constraints, expected output format).
**Rowboat Apps:** When users ask you to build/make/create an *app* or *dashboard* ("build me an app that…", "make a dashboard for…"), load the \`apps\` skill FIRST — it defines the app contract (manifest, dist/, Host API) and the build flow. For ambiguous requests that could be a one-off answer ("show me my open PRs"), the skill's intent gate says to confirm before building. Do not hand-roll app folders without the skill.
**Rowboat Apps:** When users ask you to build/make/create an *app* or *dashboard*, load the \`apps\` skill FIRST — never hand-roll app folders without it. For ambiguous requests that could be a one-off answer ("show me my open PRs"), it says to confirm before building.
**Live Notes:** If the user explicitly says "live note" or "live-note", load the \`live-note\` skill. Otherwise, do not propose live notes — prefer the \`background-task\` skill for anything recurring.
**Browser Control:** When users ask you to open a website, browse in-app, search the web in the embedded browser, or interact with a live webpage inside Rowboat, load the \`browser-control\` skill first. It explains the \`read-page -> indexed action -> refreshed page\` workflow for the browser pane.
**Browser Control:** When users ask you to open a website, browse in-app, or interact with a live webpage inside Rowboat, load the \`browser-control\` skill first.
**Notifications:** When you need to send a desktop notification completion alert after a long task, time-sensitive update, or a clickable result that lands the user on a specific note/view load the \`notify-user\` skill first. It documents the \`notify-user\` tool and the \`rowboat://\` deep links you can attach to it.
**Notifications:** To send a desktop notification completion alert, time-sensitive update, or a clickable result that lands on a specific note/view load the \`notify-user\` skill first.
## Learning About the User (save-to-memory)
@ -213,33 +211,10 @@ Users can interact with the knowledge graph through you, open it directly in Obs
- **WRONG:** \`file-grep({ pattern: "John", searchPath: "" })\` or \`searchPath: "."\` or any absolute path to the workspace root
- **CORRECT:** \`file-grep({ pattern: "John", searchPath: "knowledge/" })\`
Use the builtin file tools to search and read the knowledge base:
**Finding notes:**
\`\`\`
# List all people notes
file-list("knowledge/People")
# Search for a person by name - MUST include knowledge/ in path
file-grep({ pattern: "Sarah Chen", searchPath: "knowledge/" })
# Find notes mentioning a company - MUST include knowledge/ in path
file-grep({ pattern: "Acme Corp", searchPath: "knowledge/" })
\`\`\`
**Reading notes:**
\`\`\`
# Read a specific person's note
file-readText("knowledge/People/Sarah Chen.md")
# Read an organization note
file-readText("knowledge/Organizations/Acme Corp.md")
\`\`\`
**When a user mentions someone by name:**
1. First, search for them: \`file-grep({ pattern: "John", searchPath: "knowledge/" })\`
2. Read their note to get full context: \`file-readText("knowledge/People/John Smith.md")\`
3. Use the context (role, organization, past interactions, commitments) in your response
Use the base file tools to search and read it:
- Find: \`file-grep({ pattern: "Sarah Chen", searchPath: "knowledge/" })\`; list a category with \`file-list("knowledge/People")\`.
- Read: \`file-readText("knowledge/People/Sarah Chen.md")\`.
- When a user mentions someone by name: grep for them in \`knowledge/\`, read their note, and use that context (role, organization, past interactions, commitments) in your response.
**NEVER use an empty search path or root path for knowledge lookup. ALWAYS set searchPath to \`knowledge/\` or a subfolder like \`knowledge/People/\`.**
@ -437,6 +412,7 @@ export async function buildCopilotInstructions(): Promise<string> {
const excludeIds: string[] = [];
if (!composioEnabled) excludeIds.push('composio-integration');
if (!codeModeEnabled) excludeIds.push('code-with-agents');
if (!slackConnected) excludeIds.push('slack');
// Always build from the live skill set so disk skills added/removed at
// runtime (after refreshDiskSkills + cache invalidation) are reflected.
const catalog = buildSkillCatalog({ excludeIds });

View file

@ -18,6 +18,7 @@ import liveNoteSkill from "./live-note/skill.js";
import backgroundTaskSkill from "./background-task/skill.js";
import notifyUserSkill from "./notify-user/skill.js";
import appsSkill from "./apps/skill.js";
import slackSkill from "./slack/skill.js";
const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url));
const CATALOG_PREFIX = "src/application/assistant/skills";
@ -176,6 +177,14 @@ const definitions: SkillDefinition[] = [
content: browserControlSkill,
tools: ["browser-control", "load-browser-skill"],
},
{
// Excluded from the catalog when Slack isn't connected (see
// buildCopilotInstructions excludeIds), mirroring composio-integration.
id: "slack",
title: "Slack",
summary: "Read, search, and send Slack messages via the agent-slack CLI — catch up on channels, summarize threads, list users. Load FIRST for ANY Slack request; Slack is connected natively, never through Composio.",
content: slackSkill,
},
{
id: "notify-user",
title: "Notify User",