feat: migrate to skills-based approach
This commit is contained in:
344
.agents/skills/project-sync/SKILL.md
Normal file
344
.agents/skills/project-sync/SKILL.md
Normal file
@@ -0,0 +1,344 @@
|
||||
---
|
||||
name: project-sync
|
||||
description: Sync a project timeline using subagents for parallelism. Splits work by week and datasource to stay within context limits. Handles both first-time and incremental syncs.
|
||||
disable-model-invocation: true
|
||||
argument-hint: [project-name]
|
||||
---
|
||||
|
||||
# Project Sync
|
||||
|
||||
**When to use:** Keep a project timeline up to date. Works whether the project has been synced before or not.
|
||||
|
||||
**Precondition:** `projects/$0/datasources.md` must exist. If it doesn't, run `/project-init $0` first.
|
||||
|
||||
## Architecture: Coordinator + Subagents
|
||||
|
||||
This skill is designed for **subagent execution** to stay within context limits. The main agent acts as a **coordinator** that delegates data-intensive work to subagents.
|
||||
|
||||
```
|
||||
Coordinator
|
||||
├── Phase 1: Gather (parallel subagents, one per datasource)
|
||||
│ ├── Subagent: Zulip → writes tmp/$0-sync/zulip.md
|
||||
│ ├── Subagent: Git → writes tmp/$0-sync/git.md
|
||||
│ └── Subagent: Meetings → writes tmp/$0-sync/meetings.md
|
||||
│
|
||||
├── Phase 2: Synthesize (parallel subagents, one per week)
|
||||
│ ├── Subagent: Week 1 → writes timeline/{year-month}/week-{n}.md
|
||||
│ ├── Subagent: Week 2 → writes timeline/{year-month}/week-{n}.md
|
||||
│ └── ...
|
||||
│
|
||||
└── Phase 3: Finalize (coordinator directly)
|
||||
├── timeline/index.md (add links to new weeks)
|
||||
├── project.md (update living document)
|
||||
└── sync-state.md (update sync status)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Coordinator Steps
|
||||
|
||||
### Step 1: Determine Sync Range
|
||||
|
||||
Check whether `projects/$0/sync-state.md` exists.
|
||||
|
||||
**Case A — First sync (no sync-state.md):**
|
||||
Default range is **last 12 months through today**. If the user provided explicit dates as extra arguments (`$1`, `$2`), use those instead.
|
||||
|
||||
**Case B — Incremental sync (sync-state.md exists):**
|
||||
Read `last_sync_date` from `projects/$0/sync-state.md`. Range is `last_sync_date` to today.
|
||||
|
||||
### Step 2: Read Datasources
|
||||
|
||||
Read `projects/$0/datasources.md` to determine:
|
||||
- Zulip stream IDs and search terms
|
||||
- Git repository URL
|
||||
- Meeting room names
|
||||
- Entity types to prioritize
|
||||
|
||||
### Step 3: Prepare Scratch Directory
|
||||
|
||||
```bash
|
||||
mkdir -p tmp/$0-sync
|
||||
```
|
||||
|
||||
This directory holds intermediate outputs from Phase 1 subagents. It is ephemeral — delete it after the sync completes.
|
||||
|
||||
### Step 4: Compute Week Boundaries
|
||||
|
||||
Split the sync range into ISO calendar weeks (Monday–Sunday). Produce a list of `(week_number, week_start, week_end, year_month)` tuples. This list drives Phase 2.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Gather Data (parallel subagents)
|
||||
|
||||
Launch **one subagent per datasource**, all in parallel. Each subagent covers the **full sync range** and writes its output to a scratch file. The output must be organized by week so Phase 2 subagents can consume it.
|
||||
|
||||
### Subagent: Zulip
|
||||
|
||||
**Input:** Sync range, PRIMARY stream IDs and search terms from datasources.md.
|
||||
|
||||
**Important:** `threaded_conversation` entities only contain the **last 50 messages** in a topic. To get complete message history for a week, you must query `conversation_message` entities.
|
||||
|
||||
**Task:** Two-step process for each PRIMARY stream:
|
||||
|
||||
**Step 1:** List all thread IDs in the stream using `id_prefix`:
|
||||
```
|
||||
GET /api/v1/query
|
||||
entity_types=threaded_conversation
|
||||
connector_ids=zulip
|
||||
id_prefix=zulip:stream:{stream_id}
|
||||
limit=100
|
||||
offset=0
|
||||
```
|
||||
|
||||
This returns all thread entities (e.g., `zulip:stream:155:topic_name`). Save these IDs.
|
||||
|
||||
**Step 2:** For each week in the sync range, query messages from each thread:
|
||||
```
|
||||
GET /api/v1/query
|
||||
entity_types=conversation_message
|
||||
connector_ids=zulip
|
||||
parent_id={thread_id} # e.g., zulip:stream:155:standalone
|
||||
date_from={week_start}
|
||||
date_to={week_end}
|
||||
limit=100
|
||||
offset=0
|
||||
```
|
||||
|
||||
Paginate through all messages for each thread/week combination.
|
||||
|
||||
**Output:** Write `tmp/$0-sync/zulip.md` with results grouped by week:
|
||||
|
||||
```markdown
|
||||
## Week {n} ({week_start} to {week_end})
|
||||
|
||||
### Stream: {stream_name}
|
||||
- **Topic:** {topic} ({date}, {message_count} messages, {participant_count} participants)
|
||||
{brief summary or key quote}
|
||||
```
|
||||
|
||||
### Subagent: Git
|
||||
|
||||
**Input:** Sync range, git repository URL from datasources.md.
|
||||
|
||||
**Task:**
|
||||
|
||||
**Important:** Git commands may fail due to gitconfig permission issues. Use a temporary HOME directory:
|
||||
|
||||
```bash
|
||||
# Set temporary HOME to avoid gitconfig permission issues
|
||||
export HOME=$(pwd)/.tmp-home
|
||||
mkdir -p ./tmp
|
||||
|
||||
# Clone if needed, pull if exists
|
||||
if [ -d ./tmp/$0-clone ]; then
|
||||
export HOME=$(pwd)/.tmp-home && cd ./tmp/$0-clone && git pull
|
||||
else
|
||||
export HOME=$(pwd)/.tmp-home && git clone --depth 500 {url} ./tmp/$0-clone
|
||||
cd ./tmp/$0-clone
|
||||
fi
|
||||
|
||||
# Get commits in the date range
|
||||
export HOME=$(pwd)/.tmp-home && git log --since="{range_start}" --until="{range_end}" --format="%H|%an|%ae|%ad|%s" --date=short
|
||||
|
||||
# Get contributor statistics
|
||||
export HOME=$(pwd)/.tmp-home && git log --since="{range_start}" --until="{range_end}" --format="%an" | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
**Output:** Write `tmp/$0-sync/git.md` with results grouped by week:
|
||||
|
||||
```markdown
|
||||
## Week {n} ({week_start} to {week_end})
|
||||
|
||||
**Commits:** {count}
|
||||
**Contributors:** {name} ({count}), {name} ({count})
|
||||
|
||||
### Key Commits
|
||||
- `{short_hash}` {subject} — {author} ({date})
|
||||
```
|
||||
|
||||
### Subagent: Meetings
|
||||
|
||||
**Input:** Sync range, meeting room names from datasources.md.
|
||||
|
||||
**Task:** For each PRIMARY room, query meetings and run semantic search:
|
||||
|
||||
```
|
||||
GET /api/v1/query
|
||||
entity_types=meeting
|
||||
date_from={range_start}
|
||||
date_to={range_end}
|
||||
room_name={room-name}
|
||||
limit=100
|
||||
|
||||
POST /api/v1/search
|
||||
search_text={project-name}
|
||||
entity_types=["meeting"]
|
||||
date_from={range_start}
|
||||
date_to={range_end}
|
||||
limit=50
|
||||
```
|
||||
|
||||
**Output:** Write `tmp/$0-sync/meetings.md` with results grouped by week:
|
||||
|
||||
```markdown
|
||||
## Week {n} ({week_start} to {week_end})
|
||||
|
||||
### Meeting: {title} ({date}, {room})
|
||||
**Participants:** {names}
|
||||
**Summary:** {brief summary}
|
||||
**Key points:**
|
||||
- {point}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Synthesize Week Files (parallel subagents)
|
||||
|
||||
After all Phase 1 subagents complete, launch **one subagent per week**, all in parallel. Each produces a single week file.
|
||||
|
||||
### Subagent: Week {n}
|
||||
|
||||
**Input:** The relevant `## Week {n}` sections extracted from each of:
|
||||
- `tmp/$0-sync/zulip.md`
|
||||
- `tmp/$0-sync/git.md`
|
||||
- `tmp/$0-sync/meetings.md`
|
||||
|
||||
Pass only the sections for this specific week — do NOT pass the full files.
|
||||
|
||||
**Task:** Merge and analyze the data from all three sources. Categorize into:
|
||||
|
||||
1. **Key Decisions** — Technology migrations, architecture changes, vendor switches, security incidents, strategic pivots
|
||||
2. **Technical Work** — Feature implementations, bug fixes, infrastructure changes
|
||||
3. **Team Activity** — Core vs. occasional contributors, role changes
|
||||
4. **Blockers** — Issues, delays, dependencies
|
||||
|
||||
**Milestones:** When documenting milestones, capture BOTH:
|
||||
- **WHAT** — The technical achievement (e.g., "PostgreSQL migration")
|
||||
- **WHY** — The business objective (e.g., "to improve query performance from 107ms to 27ms and enable concurrent access for scaling")
|
||||
|
||||
Search for business objectives in: meeting discussions about roadmap, Zulip threads about planning, PR descriptions, release notes, and any "why are we doing this" conversations.
|
||||
|
||||
**Skip unless meaningful:** Routine check-ins, minor documentation updates, social chat.
|
||||
|
||||
**Output:** Write `projects/$0/timeline/{year-month}/week-{n}.md` using the week file template from [project-history](../project-history/SKILL.md). Also return a **3-5 line summary** to the coordinator for use in Phase 3.
|
||||
|
||||
Create the month directory first if needed: `mkdir -p projects/$0/timeline/{year-month}`
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Finalize (coordinator directly)
|
||||
|
||||
The coordinator collects the summaries returned by all Phase 2 subagents. These summaries are small enough to fit in the coordinator's context.
|
||||
|
||||
### Step 5: Update Timeline Index
|
||||
|
||||
Add links to new week files in `projects/$0/timeline/index.md`. Append entries under the appropriate year/quarter sections. Update milestones if any were reached.
|
||||
|
||||
### Step 6: Update Project Dashboard (project.md)
|
||||
|
||||
**File:** `projects/$0/project.md`
|
||||
|
||||
This is the **living document** — update it with current status from the week summaries:
|
||||
|
||||
**Update these sections:**
|
||||
|
||||
1. **This Week's Focus** - What the team is actively working on now
|
||||
2. **Last Week's Focus** - What was completed in the most recent week
|
||||
3. **Team** - Current contributors and their focus areas
|
||||
4. **Milestones** - Update status and add new ones with business objectives
|
||||
5. **Recent Decisions** - Key decisions from the last 2-3 weeks
|
||||
|
||||
**Milestone Format:**
|
||||
```markdown
|
||||
### In Progress 🔄
|
||||
| Milestone | Target | Business Objective |
|
||||
|-----------|--------|-------------------|
|
||||
| Standalone deployment | Feb 2026 | Enable non-developers to self-host without complex setup |
|
||||
|
||||
### Recently Completed ✅
|
||||
| Milestone | Date | Business Objective |
|
||||
|-----------|------|-------------------|
|
||||
| PostgreSQL migration | Mar 2025 | Improve performance (107ms→27ms) and enable scaling |
|
||||
|
||||
### Lost in Sight / Paused ⏸️
|
||||
| Milestone | Status | Reason |
|
||||
|-----------|--------|--------|
|
||||
| Feature X | Paused | Resources reallocated to higher priority |
|
||||
```
|
||||
|
||||
**Note:** Milestones in this company change frequently — update status (in progress/done/paused) as needed.
|
||||
|
||||
### Step 7: Update Sync State
|
||||
|
||||
Create or update `projects/$0/sync-state.md`:
|
||||
|
||||
**First sync (Case A):**
|
||||
|
||||
```markdown
|
||||
# Sync State
|
||||
|
||||
status: synced
|
||||
created_at: {today's date}
|
||||
last_sync_date: {today's date}
|
||||
initial_history_from: {range_start}
|
||||
initial_history_to: {range_end}
|
||||
last_incremental_sync: {today's date}
|
||||
```
|
||||
|
||||
**Incremental sync (Case B):**
|
||||
|
||||
```markdown
|
||||
# Sync State
|
||||
|
||||
status: synced
|
||||
created_at: {original value}
|
||||
last_sync_date: {today's date}
|
||||
initial_history_from: {original value}
|
||||
initial_history_to: {original value}
|
||||
last_incremental_sync: {today's date}
|
||||
```
|
||||
|
||||
### Step 8: Cleanup
|
||||
|
||||
```bash
|
||||
rm -rf tmp/$0-sync
|
||||
```
|
||||
|
||||
### Step 9: Summary Report
|
||||
|
||||
Output a brief summary:
|
||||
|
||||
```markdown
|
||||
## Sync Summary: {Date}
|
||||
|
||||
### Period Covered
|
||||
{range_start} to {range_end}
|
||||
|
||||
### Key Changes
|
||||
1. Decision: {brief description}
|
||||
2. Feature: {what was built}
|
||||
3. Team: {who joined/left}
|
||||
|
||||
### Metrics
|
||||
- {n} new commits
|
||||
- {n} active contributors
|
||||
- {n} weeks analyzed
|
||||
- {n} new Zulip threads
|
||||
- {n} meetings recorded
|
||||
|
||||
### Current Status
|
||||
[Status description]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Rules
|
||||
|
||||
- **Link to sources**: Always reference commit hashes, PR numbers, Zulip topic names, meeting dates
|
||||
- **Be explicit about exclusions**: Document what you're NOT analyzing and why
|
||||
- **Write once**: Week files are historical records — don't modify existing ones, only create new ones
|
||||
- **Paginate all queries**: Always loop through all pages of results
|
||||
- **Distinguish contributor types**: Core (regular activity) vs. occasional (sporadic)
|
||||
- **Subagent isolation**: Each subagent should be self-contained. Pass only the data it needs — never the full scratch files
|
||||
- **Fail gracefully**: If a datasource subagent fails (e.g., git clone errors, API down), the coordinator should continue with available data and note the gap in the summary
|
||||
Reference in New Issue
Block a user