Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .claude/skills/activity-summary/SKILL.md
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix yesterday.md mutability label to match the digest contract.

yesterday.md is 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
-yesterday.md      # rolling — generated at 00:00 local for the prior calendar day
+yesterday.md      # closing — generated at 00:00 local for the prior calendar day; immutable afterward
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
yesterday.md # rolling — generated at 00:00 local for the prior calendar day
yesterday.md # closing — generated at 00:00 local for the prior calendar day; immutable afterward
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/activity-summary/SKILL.md at line 30, Update the mutability
label and description for the `yesterday.md` entry in SKILL.md so it matches the
digest contract: change the "rolling" tag to indicate immutability (e.g.,
"immutable" or "generated at day close") and clarify the text to state that
`yesterday.md` is generated once at the prior calendar day's close and does not
change thereafter; update the line containing `yesterday.md      # rolling —
generated at 00:00 local for the prior calendar day` to reflect the
immutable/once-generated semantics so agents read the correct contract.

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## slack
- 7 messages in #gtm-prospects mentioning "ACME"
```
## slack
- 7 messages in `#gtm-prospects` mentioning "ACME" — [/slack/channels/C0ADE9B71CN__gtm-prospects/messages/2026-05-12.jsonl]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/activity-summary/SKILL.md around lines 55 - 57, Under the "##
slack" section update the lone bullet "- 7 messages in `#gtm-prospects` mentioning
"ACME"" to follow the file's "canonical link per bullet" pattern: replace the
plain channel name with a markdown link that points to the canonical mount URL
for the Slack channel (i.e., use the same canonical-link format used by other
providers in this file). Edit the bullet under the "## slack" header so it
includes the canonical mount link for `#gtm-prospects` while preserving the rest
of the text.


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.
104 changes: 104 additions & 0 deletions .claude/skills/daily-digest/SKILL.md
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.
116 changes: 116 additions & 0 deletions .claude/skills/workspace-layout/SKILL.md
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.
Loading
Loading