-
Notifications
You must be signed in to change notification settings - Fork 0
docs(skills): add four workspace-primitive skills #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,96 @@ | ||||||||||||
| --- | ||||||||||||
| name: activity-summary | ||||||||||||
| description: Use when an agent is asked "what did I (or my team) work on yesterday / this week / today" across provider data in a relayfile mount (Linear, GitHub, Notion, Slack, Confluence, Jira, etc.). Tells the agent to consult the pre-computed `digests/yesterday.md` (and sibling digest files) at the workspace root BEFORE doing manual exploration with `ls`/`grep`/`find`. The digest is deterministic, exhaustive over the window, and costs one file read instead of dozens of provider queries. | ||||||||||||
| --- | ||||||||||||
|
|
||||||||||||
| # Activity Summary — Read the Digest First | ||||||||||||
|
|
||||||||||||
| ## Overview | ||||||||||||
|
|
||||||||||||
| A relayfile workspace pre-computes deterministic daily activity digests at `<mount>/digests/`. These are produced by relayfile itself from the raw provider data, so they are **complete over the time window** (no API pagination gaps) and **free for the agent to consume** (one file read, no LLM generation step). | ||||||||||||
|
|
||||||||||||
| If you've been asked an activity-summary question, **read the digest before doing anything else.** Reaching for `ls`, `grep`, or per-provider exploration first is the most common reason these answers cost 20+ tool calls when they could cost 1. | ||||||||||||
|
|
||||||||||||
| ## When to use this skill | ||||||||||||
|
|
||||||||||||
| Trigger phrases from the user — read the digest first: | ||||||||||||
|
|
||||||||||||
| - "what did I work on yesterday / today / this week" | ||||||||||||
| - "what changed across {GitHub, Linear, Notion, Slack, ...} {yesterday, since Friday, etc.}" | ||||||||||||
| - "summarize my activity" | ||||||||||||
| - "give me a standup update" | ||||||||||||
| - "what did $team_member ship recently" | ||||||||||||
|
|
||||||||||||
| If the user's question is **not** windowed by time (e.g. "find the Notion page about onboarding"), the digest is not the right entry point — use `by-title/` or `by-id/` indexes instead. See the `workspace-layout` skill. | ||||||||||||
|
|
||||||||||||
| ## What digests look like | ||||||||||||
|
|
||||||||||||
| ```bash | ||||||||||||
| $ ls $MOUNT/digests/ | ||||||||||||
| yesterday.md # rolling — generated at 00:00 local for the prior calendar day | ||||||||||||
| today.md # rolling — updated continuously as the day progresses | ||||||||||||
| 2026-05-12.md # date-stamped, immutable once the day closes | ||||||||||||
| 2026-05-11.md | ||||||||||||
| ... | ||||||||||||
| this-week.md # rolling — Mon→now of the current ISO week | ||||||||||||
| last-week.md # immutable, prior ISO week | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| The body is plain Markdown with one section per provider: | ||||||||||||
|
|
||||||||||||
| ```markdown | ||||||||||||
| # Activity for 2026-05-12 | ||||||||||||
|
|
||||||||||||
| ## linear | ||||||||||||
| - AGE-16 moved to Blocked (waiting on design) — [/linear/issues/AGE-16__87389837-...] | ||||||||||||
| - AGE-9 closed — [/linear/issues/AGE-9__2bb2c00f-...] | ||||||||||||
|
|
||||||||||||
| ## github | ||||||||||||
| - relayfile-adapters#412 merged to main — [/github/repos/.../pulls/412.json] | ||||||||||||
| - relayfile#287 opened — [/github/repos/.../pulls/287.json] | ||||||||||||
|
|
||||||||||||
| ## notion | ||||||||||||
| - "Khaliq's To Dos" edited — [/notion/pages/3566800c-.../content.md] | ||||||||||||
|
|
||||||||||||
| ## slack | ||||||||||||
| - 7 messages in #gtm-prospects mentioning "ACME" | ||||||||||||
| ``` | ||||||||||||
|
Comment on lines
+55
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep provider examples consistent with the “canonical link per bullet” rule. The Slack bullet is the only one without a canonical mount link, which contradicts the format guidance in the same file. Suggested doc fix ## slack
-- 7 messages in `#gtm-prospects` mentioning "ACME"
+- 7 messages in `#gtm-prospects` mentioning "ACME" — [/slack/channels/C0ADE9B71CN__gtm-prospects/messages/2026-05-12.jsonl]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
| Each bullet links to the canonical file in the mount, so a follow-up question ("what changed about AGE-16?") is one `cat` away. | ||||||||||||
|
|
||||||||||||
| ## How to use it | ||||||||||||
|
|
||||||||||||
| ```bash | ||||||||||||
| # 1. Check the digest covers the date the user asked about. | ||||||||||||
| ls $MOUNT/digests/ | ||||||||||||
| cat $MOUNT/digests/yesterday.md | ||||||||||||
|
|
||||||||||||
| # 2. If the user asked about a specific date, prefer the date-stamped file. | ||||||||||||
| cat $MOUNT/digests/2026-05-12.md | ||||||||||||
|
|
||||||||||||
| # 3. Confirm coverage before answering. The digest header includes the window | ||||||||||||
| # it spans; if the user's window is wider, read multiple digest files | ||||||||||||
| # rather than re-deriving from raw provider data. | ||||||||||||
| head -5 $MOUNT/digests/this-week.md | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| That is usually the entire workflow: read → quote → done. Four tool calls or fewer. | ||||||||||||
|
|
||||||||||||
| ## When to fall back to exploration | ||||||||||||
|
|
||||||||||||
| The digest is the right answer when: | ||||||||||||
|
|
||||||||||||
| - The window matches a digest file (yesterday, today, a specific past date, this/last week). | ||||||||||||
| - The user wants **everything** in the window, not a filtered slice. | ||||||||||||
|
|
||||||||||||
| Fall back to direct exploration via `by-edited/<date>/` index subtrees when: | ||||||||||||
|
|
||||||||||||
| - The window is unusual (e.g. "the last 36 hours"). Use `by-edited/` indexes; see `workspace-layout`. | ||||||||||||
| - The user wants a filter the digest doesn't pre-compute (e.g. "only Linear issues assigned to me"). | ||||||||||||
| - The digest file is missing or its header indicates incomplete provider coverage. | ||||||||||||
|
|
||||||||||||
| ## Why this matters | ||||||||||||
|
|
||||||||||||
| In our published benchmarks, the activity-summary question dropped from ~20 turns and $0.30+ to 4 turns and ~$0.07 once the digest existed and the agent read it first. The digest is one file read that replaces ~25 individual provider queries. | ||||||||||||
|
|
||||||||||||
| If you find yourself listing more than 2-3 directories to answer an activity question, stop and check `digests/` — you're almost certainly working harder than you need to. | ||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| --- | ||
| name: daily-digest | ||
| description: Use when authoring or extending the digest set in a relayfile workspace - covers the contract for files under `<mount>/digests/` (`yesterday.md`, `today.md`, `this-week.md`, date-stamped daily files), the per-provider section format, link conventions back into the canonical mount tree, when digests are regenerated, and how adapter authors expose new provider data to the digest pipeline. NOT for agents answering activity questions — those should use the `activity-summary` skill to consume digests, not produce them. | ||
| --- | ||
|
|
||
| # Daily Digest — Authoring Contract | ||
|
|
||
| ## Overview | ||
|
|
||
| `<mount>/digests/` is a relayfile-managed directory containing deterministic, pre-computed Markdown summaries of provider activity over fixed time windows. Digests are the cheap entry point for activity-summary queries (see the `activity-summary` skill). This skill is for **producers** — adapter authors, provider integrators, and anyone extending what shows up in a digest. | ||
|
|
||
| ## When to use this skill | ||
|
|
||
| - You are writing a new relayfile adapter (Notion, Linear, …) and need to wire it into the digest pipeline. | ||
| - You want a new digest window (e.g. `last-30-days.md`) and need to know the contract. | ||
| - A digest is missing a provider's content and you need to know why. | ||
| - You need to debug why `yesterday.md` was empty or stale this morning. | ||
|
|
||
| If you're an agent **reading** the digest to answer a user question, switch to the `activity-summary` skill instead. | ||
|
|
||
| ## Digest file taxonomy | ||
|
|
||
| | File | Window | Mutability | | ||
| |---|---|---| | ||
| | `today.md` | 00:00 local → now | Updated on every change-event for the current day | | ||
| | `yesterday.md` | full prior calendar day | Generated at 00:00 local, immutable for the rest of the day | | ||
| | `YYYY-MM-DD.md` | that calendar day | Immutable once the day closes | | ||
| | `this-week.md` | Mon 00:00 → now (ISO week) | Updated on every change-event | | ||
| | `last-week.md` | full prior ISO week | Immutable | | ||
|
|
||
| A digest file is **always present** for each window even if the window contains no activity (the body says `_no activity_`). Agents can detect empty windows without retrying. | ||
|
|
||
| ## File header | ||
|
|
||
| Every digest starts with a frontmatter-style header that callers can verify before trusting the body: | ||
|
|
||
| ```markdown | ||
| # Activity for 2026-05-12 | ||
|
|
||
| > window: 2026-05-12T00:00:00-07:00 → 2026-05-13T00:00:00-07:00 | ||
| > generated: 2026-05-13T00:01:14Z | ||
| > providers: linear, github, notion, slack | ||
| > events: 47 | ||
| ``` | ||
|
|
||
| - `window` is the half-open interval in the workspace's configured timezone. | ||
| - `providers` lists every adapter that contributed (or attempted to). A missing provider here means the adapter never ran, not that it had zero activity. | ||
| - `events` is the raw change-event count over the window — useful for sanity-checking against the body. | ||
|
|
||
| ## Per-provider section format | ||
|
|
||
| Each provider gets exactly one `## <provider>` section, in alphabetical order. Bullets within a section are sorted by event time, ascending. Each bullet must: | ||
|
|
||
| 1. Start with the canonical record identifier the provider uses (`AGE-16`, `#412`, page title, etc.). | ||
| 2. Describe the change in past tense — "moved to Blocked", "merged to main", "edited". | ||
| 3. End with a Markdown link to the canonical file path in the mount, in square brackets. | ||
|
|
||
| ```markdown | ||
| ## linear | ||
| - AGE-16 moved to Blocked (waiting on design) — [/linear/issues/AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json] | ||
| - AGE-9 closed — [/linear/issues/AGE-9__2bb2c00f-ee93-4c73-a793-df5b725d9a1a.json] | ||
| ``` | ||
|
|
||
| The link target is what makes the digest interactive — a follow-up "tell me more about AGE-16" is one `cat` away from the digest line. | ||
|
|
||
| ## Adapter contract | ||
|
|
||
| For an adapter to contribute to digests it must export a `digest()` function in addition to its sync/writeback handlers: | ||
|
|
||
| ```typescript | ||
| import type { DigestContext, DigestSection } from '@relayfile/adapter-sdk'; | ||
|
|
||
| export async function digest(ctx: DigestContext): Promise<DigestSection | null> { | ||
| const events = await ctx.changeEvents({ | ||
| window: ctx.window, // { from: ISO8601, to: ISO8601 } | ||
| providers: [ctx.provider], | ||
| }); | ||
|
|
||
| if (events.length === 0) return null; | ||
|
|
||
| return { | ||
| provider: ctx.provider, | ||
| bullets: events.map((e) => ({ | ||
| text: renderBullet(e), | ||
| canonicalPath: e.resource.path, | ||
| })), | ||
| }; | ||
| } | ||
| ``` | ||
|
|
||
| Returning `null` is correct for "ran successfully, no activity"; throwing is reserved for "could not produce a digest" and surfaces as a warning in the digest header. | ||
|
|
||
| ## Regeneration | ||
|
|
||
| - **Rolling windows** (`today.md`, `this-week.md`) are regenerated on every change event for the current window, coalesced to a max of one rebuild per 30 seconds. | ||
| - **Closing windows** (`yesterday.md`, `YYYY-MM-DD.md`, `last-week.md`) are produced once at window close (00:00 local for daily, Monday 00:00 for weekly) and never modified afterward. This is what makes them safe to cache and quote verbatim. | ||
| - If you need to force a rebuild (e.g. after fixing an adapter bug), `relayfile digest rebuild --window yesterday` re-derives from the change log. | ||
|
|
||
| ## Common mistakes when adding a new digest | ||
|
|
||
| - **Don't inline raw provider payloads.** Bullets are one line. Anything larger belongs in the canonical file the bullet links to. | ||
| - **Don't summarize across providers in one section.** Cross-provider correlation is the agent's job; the digest's job is exhaustive per-provider listing. | ||
| - **Don't omit the canonical link.** A digest line without a link is dead weight — the agent can't follow up without re-deriving the path. | ||
| - **Don't generate non-deterministic content.** Two runs over the same change-event window must produce byte-identical output. LLM-generated prose belongs in the agent, not the digest. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| --- | ||
| name: workspace-layout | ||
| description: Use when an agent is exploring a relayfile mount for the first time or trying to locate a specific resource (Notion page, Linear issue, Slack channel, GitHub PR). Tells the agent to start with `<mount>/LAYOUT.md` and `<provider>/.layout.md` rather than guessing paths from memory, and to use the `by-title/`, `by-id/`, `by-name/`, `by-edited/<date>/`, `by-state/` alias subtrees instead of recursively grepping. Filename convention is `<identifier>__<uuid>` (ticket number / slug first so listings are scannable). NOT for activity-summary questions, which should use the `activity-summary` skill instead. | ||
| --- | ||
|
|
||
| # Workspace Layout — Start With LAYOUT.md | ||
|
|
||
| ## Overview | ||
|
|
||
| A relayfile mount is **self-describing**. Every workspace has a `LAYOUT.md` at its root, and every provider has a `.layout.md` at its provider root, that together describe the directory shape, the filename conventions, and the indexes available for fast lookup. Read these first. Do not guess paths from memory — provider layouts can be customized per workspace and the indexes available may differ. | ||
|
|
||
| ## When to use this skill | ||
|
|
||
| - You just connected to a relayfile mount and have no prior context about its shape. | ||
| - You need to find a specific resource (a Notion page by title, a Linear issue by number, a Slack channel by name). | ||
| - You're tempted to run `find` or `grep -r` across the mount — almost always there is an index that does it cheaper. | ||
| - You see paths in someone else's code or in a digest and want to understand them. | ||
|
|
||
| If the user is asking an activity-summary question ("what did I work on yesterday"), use the `activity-summary` skill instead. This skill is for resource lookup, not time-windowed queries. | ||
|
|
||
| ## Step 1: Read the root layout | ||
|
|
||
| ```bash | ||
| $ cat $MOUNT/LAYOUT.md | ||
| ``` | ||
|
|
||
| The root `LAYOUT.md` lists the connected providers, the digests directory, the skills directory, and any cross-provider conventions in effect for this workspace. | ||
|
|
||
| ## Step 2: Read the provider layout | ||
|
|
||
| ```bash | ||
| $ cat $MOUNT/linear/.layout.md | ||
| ``` | ||
|
|
||
| The per-provider layout covers: | ||
|
|
||
| - Top-level directories under the provider root (e.g. `issues/`, `projects/`, `cycles/`) | ||
| - Filename conventions in use (`<identifier>__<uuid>.json`, plain UUID, slug-based, …) | ||
| - Which `by-*` alias indexes are populated | ||
| - Writeback directories and their schemas (see the `writeback-as-files` skill) | ||
| - Whether content is paginated and how | ||
|
|
||
| ## Step 3: Use alias indexes, not recursive search | ||
|
|
||
| Canonical records are keyed by UUID for stability. Alias indexes live under `by-*/` and point back to the canonical files. Reach for an index that matches your query shape: | ||
|
|
||
| ```bash | ||
| # Find a Notion page by title | ||
| $ ls $MOUNT/notion/pages/by-title/ | grep -i "onboarding" | ||
| onboarding-runbook__c24642bb.json | ||
| $ jq -r '.id' $MOUNT/notion/pages/by-title/onboarding-runbook__c24642bb.json | ||
|
|
||
| # Find a Linear issue by ticket number (identifier is part of the canonical | ||
| # filename, so a direct ls is sufficient — no by-id/ needed) | ||
| $ ls $MOUNT/linear/issues/ | grep "^AGE-16" | ||
|
|
||
| # Find what was edited yesterday in Notion | ||
| $ ls $MOUNT/notion/pages/by-edited/2026-05-12/ | ||
|
|
||
| # Find all Linear issues currently In Progress | ||
| $ ls $MOUNT/linear/issues/by-state/in-progress/ | ||
|
|
||
| # Find a Slack channel by name | ||
| $ ls $MOUNT/slack/channels/by-name/ | grep "gtm" | ||
| ``` | ||
|
|
||
| Indexes are symlinks (or directory listings on filesystems without symlink support) — they don't duplicate the underlying content, so they stay cheap to enumerate even on workspaces with thousands of records. | ||
|
|
||
| ## Filename convention | ||
|
|
||
| Canonical files use **`<identifier>__<uuid>.<ext>`** — identifier first so directory listings are immediately scannable: | ||
|
|
||
| ```bash | ||
| $ ls $MOUNT/linear/issues/ | ||
| AGE-9__2bb2c00f-ee93-4c73-a793-df5b725d9a1a.json | ||
| AGE-10__8c313d70-9800-4539-820f-96a481c09ce0.json | ||
| AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json | ||
| ``` | ||
|
|
||
| The `__` (double underscore) separator is reserved — provider data must not produce it in the identifier portion. If you see a filename without `__` it's an alias-index symlink or a metadata file, not a canonical record. | ||
|
|
||
| We borrowed the `<identifier>__<uuid>` shape from [Mirage](https://github.com/strukto-ai/mirage) after seeing the pattern in their mount. | ||
|
|
||
| ## Common patterns | ||
|
|
||
| ### "Where does X live?" | ||
|
|
||
| ```bash | ||
| cat $MOUNT/LAYOUT.md # provider list and cross-cutting layout | ||
| cat $MOUNT/<provider>/.layout.md # provider-specific shape | ||
| ls $MOUNT/<provider>/ # top-level resource directories | ||
| ls $MOUNT/<provider>/<resource>/by-*/ # available indexes | ||
| ``` | ||
|
|
||
| Three to four `cat`/`ls` calls and you have the full map. | ||
|
|
||
| ### "I have a UUID, what is it?" | ||
|
|
||
| The UUID is in the filename. `ls` and `grep` find it without needing to know which directory: | ||
|
|
||
| ```bash | ||
| $ find $MOUNT -name "*87389837-62b1-4e1a-a237-59218bab2974*" -type f | ||
| $MOUNT/linear/issues/AGE-16__87389837-62b1-4e1a-a237-59218bab2974.json | ||
| ``` | ||
|
|
||
| Use `find` here because UUIDs are globally unique — the result is one file. | ||
|
|
||
| ### "I have a slug or title, what is it?" | ||
|
|
||
| Use `by-title/` or `by-name/` rather than `find`. The index is sorted and bounded; `find` walks the full tree. | ||
|
|
||
| ## What NOT to do | ||
|
|
||
| - **Don't `grep -r` over the mount** for a title or name. There's an index. Use it. | ||
| - **Don't hardcode paths from a different workspace's LAYOUT.md.** Workspaces can customize which adapters and indexes are mounted. | ||
| - **Don't ignore `.layout.md`.** If you wrote a Notion-specific path from memory and it doesn't exist, the provider's `.layout.md` will tell you the actual shape in one read. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix
yesterday.mdmutability label to match the digest contract.yesterday.mdis described as “rolling” here, but the contract defines it as generated at day close and then immutable. This mismatch can lead to wrong agent behavior.Suggested doc fix
📝 Committable suggestion
🤖 Prompt for AI Agents