@forwardimpact/basecamp 2.0.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/scheduler.json +5 -0
- package/package.json +1 -1
- package/src/basecamp.js +288 -57
- package/template/.claude/agents/chief-of-staff.md +6 -2
- package/template/.claude/agents/concierge.md +2 -3
- package/template/.claude/agents/librarian.md +4 -6
- package/template/.claude/agents/recruiter.md +269 -0
- package/template/.claude/settings.json +0 -4
- package/template/.claude/skills/analyze-cv/SKILL.md +269 -0
- package/template/.claude/skills/create-presentations/SKILL.md +2 -2
- package/template/.claude/skills/create-presentations/references/slide.css +1 -1
- package/template/.claude/skills/create-presentations/scripts/convert-to-pdf.mjs +47 -0
- package/template/.claude/skills/draft-emails/SKILL.md +85 -123
- package/template/.claude/skills/draft-emails/scripts/scan-emails.mjs +66 -0
- package/template/.claude/skills/draft-emails/scripts/send-email.mjs +118 -0
- package/template/.claude/skills/extract-entities/SKILL.md +2 -2
- package/template/.claude/skills/extract-entities/scripts/state.mjs +130 -0
- package/template/.claude/skills/manage-tasks/SKILL.md +242 -0
- package/template/.claude/skills/organize-files/SKILL.md +3 -3
- package/template/.claude/skills/organize-files/scripts/organize-by-type.mjs +105 -0
- package/template/.claude/skills/organize-files/scripts/summarize.mjs +84 -0
- package/template/.claude/skills/process-hyprnote/SKILL.md +2 -2
- package/template/.claude/skills/right-to-be-forgotten/SKILL.md +333 -0
- package/template/.claude/skills/send-chat/SKILL.md +170 -0
- package/template/.claude/skills/sync-apple-calendar/SKILL.md +5 -5
- package/template/.claude/skills/sync-apple-calendar/scripts/sync.mjs +325 -0
- package/template/.claude/skills/sync-apple-mail/SKILL.md +6 -6
- package/template/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +374 -0
- package/template/.claude/skills/sync-apple-mail/scripts/sync.mjs +629 -0
- package/template/.claude/skills/track-candidates/SKILL.md +376 -0
- package/template/.claude/skills/upstream-skill/SKILL.md +207 -0
- package/template/.claude/skills/weekly-update/SKILL.md +250 -0
- package/template/CLAUDE.md +68 -40
- package/template/.claude/skills/create-presentations/scripts/convert-to-pdf.js +0 -32
- package/template/.claude/skills/draft-emails/scripts/scan-emails.sh +0 -34
- package/template/.claude/skills/extract-entities/scripts/state.py +0 -100
- package/template/.claude/skills/organize-files/scripts/organize-by-type.sh +0 -42
- package/template/.claude/skills/organize-files/scripts/summarize.sh +0 -21
- package/template/.claude/skills/sync-apple-calendar/scripts/sync.py +0 -242
- package/template/.claude/skills/sync-apple-mail/scripts/parse-emlx.py +0 -104
- package/template/.claude/skills/sync-apple-mail/scripts/sync.py +0 -455
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: track-candidates
|
|
3
|
+
description: Scan synced email threads for recruitment candidates, extract structured profiles, and create/update notes in knowledge/Candidates/. Use when the user asks to track candidates, process recruitment emails, or update the hiring pipeline.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Track Candidates
|
|
7
|
+
|
|
8
|
+
Scan synced email threads from `~/.cache/fit/basecamp/apple_mail/` for
|
|
9
|
+
recruitment candidates. Extract structured candidate profiles and create/update
|
|
10
|
+
notes in `knowledge/Candidates/`. This builds a local, searchable recruitment
|
|
11
|
+
pipeline from scattered email threads.
|
|
12
|
+
|
|
13
|
+
## Trigger
|
|
14
|
+
|
|
15
|
+
Run this skill:
|
|
16
|
+
|
|
17
|
+
- When the user asks to track, process, or update candidates
|
|
18
|
+
- When the user asks about recruitment pipeline status
|
|
19
|
+
- After `sync-apple-mail` has pulled new threads
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- Synced email data in `~/.cache/fit/basecamp/apple_mail/` (from
|
|
24
|
+
`sync-apple-mail`)
|
|
25
|
+
- User identity configured in `USER.md`
|
|
26
|
+
|
|
27
|
+
## Inputs
|
|
28
|
+
|
|
29
|
+
- `~/.cache/fit/basecamp/apple_mail/*.md` — synced email threads
|
|
30
|
+
- `~/.cache/fit/basecamp/apple_mail/attachments/` — CV/resume attachments
|
|
31
|
+
- `~/.cache/fit/basecamp/state/graph_processed` — tracks processed files (shared
|
|
32
|
+
with `extract-entities`)
|
|
33
|
+
- `USER.md` — user identity for self-exclusion
|
|
34
|
+
|
|
35
|
+
## Outputs
|
|
36
|
+
|
|
37
|
+
- `knowledge/Candidates/{Full Name}/brief.md` — candidate profile note
|
|
38
|
+
- `knowledge/Candidates/{Full Name}/CV.pdf` — local copy of CV (or `CV.docx`)
|
|
39
|
+
- `~/.cache/fit/basecamp/state/graph_processed` — updated with processed threads
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Before Starting
|
|
44
|
+
|
|
45
|
+
1. Read `USER.md` to get the user's name, email, and domain.
|
|
46
|
+
2. Find new/changed email files to process:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node .claude/skills/extract-entities/scripts/state.mjs check
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This outputs one file path per line for all source files that are new or
|
|
53
|
+
changed. Filter this list to only `apple_mail/*.md` files — calendar events are
|
|
54
|
+
not relevant for candidate tracking.
|
|
55
|
+
|
|
56
|
+
**Process in batches of 10 files per run.**
|
|
57
|
+
|
|
58
|
+
## Step 0: Build Candidate Index
|
|
59
|
+
|
|
60
|
+
Scan existing candidate notes to avoid duplicates:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ls -d knowledge/Candidates/*/
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each candidate has their own directory at `knowledge/Candidates/{Name}/`
|
|
67
|
+
containing standardized files:
|
|
68
|
+
|
|
69
|
+
- `brief.md` — the candidate profile note
|
|
70
|
+
- `CV.pdf` (or `CV.docx`) — local copy of CV (if available)
|
|
71
|
+
|
|
72
|
+
For each existing note, read the header fields (Name, Role, Source, Status) to
|
|
73
|
+
build a mental index of known candidates.
|
|
74
|
+
|
|
75
|
+
Also scan `knowledge/People/`, `knowledge/Organizations/`, and
|
|
76
|
+
`knowledge/Projects/` to resolve recruiter names, agency orgs, and project
|
|
77
|
+
links.
|
|
78
|
+
|
|
79
|
+
## Step 1: Identify Recruitment Emails
|
|
80
|
+
|
|
81
|
+
For each new/changed email thread, determine if it contains candidate
|
|
82
|
+
information. Look for these signals:
|
|
83
|
+
|
|
84
|
+
### CV/Resume Attachments
|
|
85
|
+
|
|
86
|
+
Check `~/.cache/fit/basecamp/apple_mail/attachments/{thread_id}/` for PDF or
|
|
87
|
+
DOCX files with candidate names in filenames.
|
|
88
|
+
|
|
89
|
+
### Recruiter Sender Domains
|
|
90
|
+
|
|
91
|
+
Emails from known recruitment agency domains. Map sender domains to
|
|
92
|
+
organizations using `knowledge/Organizations/` — look for notes tagged as
|
|
93
|
+
recruitment agencies.
|
|
94
|
+
|
|
95
|
+
If no known agencies exist yet, use sender patterns as hints:
|
|
96
|
+
|
|
97
|
+
- Multiple candidates presented by the same sender
|
|
98
|
+
- Structured profile formatting (rate, availability, skills)
|
|
99
|
+
- Forwarding candidate CVs on behalf of others
|
|
100
|
+
|
|
101
|
+
### Profile Presentation Patterns
|
|
102
|
+
|
|
103
|
+
Look for structured candidate descriptions containing:
|
|
104
|
+
|
|
105
|
+
- "Rate:" or rate/cost information
|
|
106
|
+
- "Availability:" or notice period
|
|
107
|
+
- "English:" or language level
|
|
108
|
+
- "Location:" or country/city
|
|
109
|
+
- Candidate name + role formatting (e.g. "Staff Software Engineer")
|
|
110
|
+
- "years of experience" or "YoE"
|
|
111
|
+
- Skills/tech stack listings
|
|
112
|
+
|
|
113
|
+
### Interview Scheduling
|
|
114
|
+
|
|
115
|
+
- "schedule a call", "schedule an interview"
|
|
116
|
+
- "first interview", "second interview", "technical interview"
|
|
117
|
+
- "interview slot", "available for a call"
|
|
118
|
+
|
|
119
|
+
### Follow-up on Existing Candidates
|
|
120
|
+
|
|
121
|
+
Threads that mention a candidate already in `knowledge/Candidates/` by name —
|
|
122
|
+
these update pipeline status.
|
|
123
|
+
|
|
124
|
+
**Skip threads that don't match any signal.** Not all email threads are
|
|
125
|
+
recruitment-related.
|
|
126
|
+
|
|
127
|
+
## Step 2: Extract Candidate Data
|
|
128
|
+
|
|
129
|
+
For each candidate found in a recruitment email, extract:
|
|
130
|
+
|
|
131
|
+
| Field | Source | Required |
|
|
132
|
+
| ----------------- | ------------------------------------------------- | ------------------- |
|
|
133
|
+
| **Name** | Filename, email body, CV | Yes |
|
|
134
|
+
| **Role** | Email body, CV | Yes |
|
|
135
|
+
| **Rate** | Email body (e.g. "$120/hr", "€80/h") | If available |
|
|
136
|
+
| **Availability** | Email body (e.g. "1 month notice", "immediately") | If available |
|
|
137
|
+
| **English** | Email body (e.g. "B2", "Upper-intermediate") | If available |
|
|
138
|
+
| **Location** | Email body, CV | If available |
|
|
139
|
+
| **Source agency** | Sender domain → Organization | Yes |
|
|
140
|
+
| **Recruiter** | Email sender or CC'd recruiter | Yes |
|
|
141
|
+
| **CV path** | Attachment directory | If available |
|
|
142
|
+
| **Skills** | Email body, CV | If available |
|
|
143
|
+
| **Gender** | Name, pronouns, recruiter context | If identifiable |
|
|
144
|
+
| **Summary** | Email body, CV | Yes — 2-3 sentences |
|
|
145
|
+
|
|
146
|
+
### Determining Gender
|
|
147
|
+
|
|
148
|
+
Record the candidate's gender when **explicitly stated** in the email or CV:
|
|
149
|
+
|
|
150
|
+
- Pronouns used by the recruiter ("she is available", "her CV attached")
|
|
151
|
+
- Gendered titles ("Ms.", "Mrs.", "Mr.")
|
|
152
|
+
|
|
153
|
+
Record as `Woman`, `Man`, or `—` (unknown). When uncertain, use `—` — **never
|
|
154
|
+
infer gender from names**, regardless of cultural context. Name-based inference
|
|
155
|
+
is unreliable and culturally biased. This field supports aggregate pool
|
|
156
|
+
diversity tracking; it has **no bearing** on hiring decisions, assessment
|
|
157
|
+
criteria, or candidate visibility.
|
|
158
|
+
|
|
159
|
+
### Determining Source and Recruiter
|
|
160
|
+
|
|
161
|
+
- Map sender email domain to an organization in `knowledge/Organizations/`.
|
|
162
|
+
- The person who sent or forwarded the candidate profile is the recruiter. Look
|
|
163
|
+
them up in `knowledge/People/` and link with `[[People/Name]]`.
|
|
164
|
+
- If the organization or recruiter doesn't exist yet, create notes for them.
|
|
165
|
+
|
|
166
|
+
### CV Attachment Path
|
|
167
|
+
|
|
168
|
+
Check for attachments:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
ls ~/.cache/fit/basecamp/apple_mail/attachments/{thread_id}/
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Match CV files to candidates by name similarity in the filename. Copy the CV
|
|
175
|
+
into the candidate's directory with a standardized name:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
mkdir -p "knowledge/Candidates/{Full Name}"
|
|
179
|
+
cp "~/.cache/fit/basecamp/apple_mail/attachments/{thread_id}/{filename}" \
|
|
180
|
+
"knowledge/Candidates/{Full Name}/CV.pdf"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use `CV.pdf` for PDF files and `CV.docx` for Word documents. The `## CV` link in
|
|
184
|
+
the brief uses a relative path: `./CV.pdf`.
|
|
185
|
+
|
|
186
|
+
## Step 3: Determine Pipeline Status
|
|
187
|
+
|
|
188
|
+
Assign a status based on the email context:
|
|
189
|
+
|
|
190
|
+
| Status | Signal |
|
|
191
|
+
| ------------------ | ----------------------------------------------------- |
|
|
192
|
+
| `new` | CV/profile received, no response yet |
|
|
193
|
+
| `screening` | Under review, questions asked about the candidate |
|
|
194
|
+
| `first-interview` | First interview scheduled or completed |
|
|
195
|
+
| `second-interview` | Second interview scheduled or completed |
|
|
196
|
+
| `offer` | Offer extended |
|
|
197
|
+
| `hired` | Accepted and onboarding |
|
|
198
|
+
| `rejected` | Explicitly passed on ("not a fit", "pass", "decline") |
|
|
199
|
+
| `on-hold` | Paused, waiting on notice period, or deferred |
|
|
200
|
+
|
|
201
|
+
**Default to `new`** if no response signals are found. Read the full thread
|
|
202
|
+
chronologically to determine the most recent status.
|
|
203
|
+
|
|
204
|
+
### Status Advancement Signals
|
|
205
|
+
|
|
206
|
+
Look for these patterns in the hiring manager's replies:
|
|
207
|
+
|
|
208
|
+
- "let's schedule" / "set up an interview" → `first-interview`
|
|
209
|
+
- "second round" / "follow-up interview" → `second-interview`
|
|
210
|
+
- "not what we're looking for" / "pass" → `rejected`
|
|
211
|
+
- "extend an offer" / "make an offer" → `offer`
|
|
212
|
+
- "they've accepted" / "start date" → `hired`
|
|
213
|
+
- "put on hold" / "come back to later" → `on-hold`
|
|
214
|
+
- No response to profile → remains `new`
|
|
215
|
+
|
|
216
|
+
## Step 4: Build Pipeline Timeline
|
|
217
|
+
|
|
218
|
+
Extract a chronological timeline from the thread:
|
|
219
|
+
|
|
220
|
+
```markdown
|
|
221
|
+
## Pipeline
|
|
222
|
+
- **2026-01-29**: Profile shared by {Recruiter} ({Agency})
|
|
223
|
+
- **2026-02-03**: {Recruiter} followed up asking about scheduling
|
|
224
|
+
- **2026-02-10**: {Hiring Manager} requested first interview
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Each entry: `**{date}**: {what happened}` — one line per meaningful event.
|
|
228
|
+
Include who did what. Skip noise (signature blocks, disclaimers, forwarded
|
|
229
|
+
headers).
|
|
230
|
+
|
|
231
|
+
## Step 5: Write Candidate Note
|
|
232
|
+
|
|
233
|
+
### For NEW candidates
|
|
234
|
+
|
|
235
|
+
Create the candidate directory and note:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
mkdir -p "knowledge/Candidates/{Full Name}"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Then create `knowledge/Candidates/{Full Name}/brief.md`:
|
|
242
|
+
|
|
243
|
+
```markdown
|
|
244
|
+
# {Full Name}
|
|
245
|
+
|
|
246
|
+
## Info
|
|
247
|
+
**Role:** {role title}
|
|
248
|
+
**Rate:** {rate or "—"}
|
|
249
|
+
**Availability:** {availability or "—"}
|
|
250
|
+
**English:** {level or "—"}
|
|
251
|
+
**Location:** {location or "—"}
|
|
252
|
+
**Gender:** {Woman / Man / —}
|
|
253
|
+
**Source:** [[Organizations/{Agency}]] via [[People/{Recruiter Name}]]
|
|
254
|
+
**Status:** {pipeline status}
|
|
255
|
+
**First seen:** {date profile was shared, YYYY-MM-DD}
|
|
256
|
+
**Last activity:** {date of most recent thread activity, YYYY-MM-DD}
|
|
257
|
+
|
|
258
|
+
## Summary
|
|
259
|
+
{2-3 sentences: role, experience level, key strengths}
|
|
260
|
+
|
|
261
|
+
## CV
|
|
262
|
+
- [CV.pdf](./CV.pdf)
|
|
263
|
+
|
|
264
|
+
## Connected to
|
|
265
|
+
- [[Organizations/{Agency}]] — sourced by
|
|
266
|
+
- [[People/{Recruiter}]] — recruiter
|
|
267
|
+
|
|
268
|
+
## Pipeline
|
|
269
|
+
- **{date}**: {event}
|
|
270
|
+
|
|
271
|
+
## Skills
|
|
272
|
+
{comma-separated skill tags}
|
|
273
|
+
|
|
274
|
+
## Notes
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
If a CV attachment exists, **copy it into the candidate directory** before
|
|
278
|
+
writing the note.
|
|
279
|
+
|
|
280
|
+
If no CV attachment exists, omit the `## CV` section entirely.
|
|
281
|
+
|
|
282
|
+
### For EXISTING candidates
|
|
283
|
+
|
|
284
|
+
Read `knowledge/Candidates/{Full Name}/brief.md`, then apply targeted edits:
|
|
285
|
+
|
|
286
|
+
- Update **Status** if it has advanced
|
|
287
|
+
- Update **Last activity** date
|
|
288
|
+
- Add new **Pipeline** entries at the bottom (chronological order)
|
|
289
|
+
- Update **Rate**, **Availability**, or other fields if new information is
|
|
290
|
+
available
|
|
291
|
+
- Add new **Skills** if mentioned
|
|
292
|
+
|
|
293
|
+
**Use precise edits — don't rewrite the entire file.**
|
|
294
|
+
|
|
295
|
+
## Step 5b: Capture Key Insights
|
|
296
|
+
|
|
297
|
+
After writing or updating candidate notes, check whether any **high-signal
|
|
298
|
+
observations** belong in `knowledge/Candidates/Insights.md`. This is a shared
|
|
299
|
+
file for cross-candidate strategic thinking.
|
|
300
|
+
|
|
301
|
+
**Add an insight only when:**
|
|
302
|
+
|
|
303
|
+
- A candidate may be better suited for a **different role** than the one they
|
|
304
|
+
applied for
|
|
305
|
+
- A candidate stands out as a **strong match** for a specific team or leader
|
|
306
|
+
- There's a meaningful **comparison between candidates** (e.g. complementary
|
|
307
|
+
strengths, overlapping profiles)
|
|
308
|
+
- A hiring decision or trade-off needs to be **remembered across sessions**
|
|
309
|
+
|
|
310
|
+
**Do NOT add:**
|
|
311
|
+
|
|
312
|
+
- Per-candidate status updates (that's what `brief.md` is for)
|
|
313
|
+
- Generic strengths/weaknesses already captured in interview notes
|
|
314
|
+
- Anything that only matters within a single candidate's context
|
|
315
|
+
|
|
316
|
+
Format: one bullet per insight under `## Placement Notes`, with
|
|
317
|
+
`[[Candidates/Name/brief|Name]]` links and relevant people/org backlinks.
|
|
318
|
+
|
|
319
|
+
## Step 6: Ensure Bidirectional Links
|
|
320
|
+
|
|
321
|
+
After writing candidate notes, verify links go both ways:
|
|
322
|
+
|
|
323
|
+
| If you add... | Then also add... |
|
|
324
|
+
| ------------------------ | ------------------------------------------- |
|
|
325
|
+
| Candidate → Organization | Organization → Candidate |
|
|
326
|
+
| Candidate → Recruiter | Recruiter → Candidate (in Activity section) |
|
|
327
|
+
| Candidate → Project | Project → Candidate (in People section) |
|
|
328
|
+
|
|
329
|
+
Use absolute paths: `[[Candidates/Name/brief|Name]]`,
|
|
330
|
+
`[[Organizations/Agency]]`, `[[People/Recruiter]]`.
|
|
331
|
+
|
|
332
|
+
**Important:** Only add backlinks to Organization/People/Project notes if they
|
|
333
|
+
don't already reference the candidate. Check before editing.
|
|
334
|
+
|
|
335
|
+
## Step 7: Update Graph State
|
|
336
|
+
|
|
337
|
+
After processing each email thread, mark it as processed:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
node .claude/skills/extract-entities/scripts/state.mjs update "{file_path}"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
This uses the same state file as `extract-entities`, so threads processed here
|
|
344
|
+
won't be re-scanned by either skill (unless the file changes).
|
|
345
|
+
|
|
346
|
+
## Step 8: Tag Skills with Framework IDs
|
|
347
|
+
|
|
348
|
+
When a candidate's email or CV mentions technical skills, map them to the
|
|
349
|
+
engineering framework using `fit-pathway`:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
npx fit-pathway skill --list
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Use framework skill IDs (e.g. `data_integration`, `full_stack_development`,
|
|
356
|
+
`architecture_and_design`) in the **Skills** section of the candidate brief
|
|
357
|
+
instead of free-form tags. This enables consistent cross-candidate comparison.
|
|
358
|
+
|
|
359
|
+
If a candidate has a CV attachment, flag them for the `analyze-cv` skill which
|
|
360
|
+
produces a full framework-aligned assessment.
|
|
361
|
+
|
|
362
|
+
## Quality Checklist
|
|
363
|
+
|
|
364
|
+
- [ ] Scanned all new/changed email threads for recruitment signals
|
|
365
|
+
- [ ] Extracted all candidates found (check attachment directories too)
|
|
366
|
+
- [ ] Each candidate has a complete note with all available fields
|
|
367
|
+
- [ ] CV paths are correct and point to actual files
|
|
368
|
+
- [ ] Pipeline status reflects the latest thread activity
|
|
369
|
+
- [ ] Timeline entries are in chronological order
|
|
370
|
+
- [ ] Used `[[absolute/path]]` links throughout
|
|
371
|
+
- [ ] Bidirectional links are consistent
|
|
372
|
+
- [ ] Graph state updated for all processed threads
|
|
373
|
+
- [ ] No duplicate candidate notes created
|
|
374
|
+
- [ ] Key strategic insights added to `Insights.md` where warranted
|
|
375
|
+
- [ ] Skills tagged using framework skill IDs where possible
|
|
376
|
+
- [ ] Gender field populated only from explicit pronouns/titles (never name-inferred)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upstream-skill
|
|
3
|
+
description: Track changes made to skills in this installation and produce a changelog that can be included upstream. Use when skills have been modified, added, or removed locally and those changes should be contributed back to the monorepo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Upstream
|
|
7
|
+
|
|
8
|
+
Track changes made to skills in this installation and produce a structured
|
|
9
|
+
changelog so improvements can be contributed back to the upstream monorepo.
|
|
10
|
+
|
|
11
|
+
## Trigger
|
|
12
|
+
|
|
13
|
+
Run this skill when:
|
|
14
|
+
|
|
15
|
+
- The user asks to prepare local skill changes for upstream contribution
|
|
16
|
+
- Skills in `.claude/skills/` have been modified, added, or removed
|
|
17
|
+
- The user wants to document what changed in the local installation
|
|
18
|
+
- Before syncing with the upstream monorepo
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- A working Basecamp installation with `.claude/skills/` directory
|
|
23
|
+
- Git available for detecting changes
|
|
24
|
+
|
|
25
|
+
## Inputs
|
|
26
|
+
|
|
27
|
+
- `.claude/skills/*/SKILL.md` — current skill files in this installation
|
|
28
|
+
- Git history — to detect what changed and when
|
|
29
|
+
|
|
30
|
+
## Outputs
|
|
31
|
+
|
|
32
|
+
- `.claude/skills/CHANGELOG.md` — structured changelog of all local skill changes
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Process
|
|
37
|
+
|
|
38
|
+
### Step 1: Identify Changed Skills
|
|
39
|
+
|
|
40
|
+
Use git to find all skill files that have been added, modified, or deleted since
|
|
41
|
+
the last changelog entry (or since initial commit if no changelog exists).
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Find the date of the last changelog entry (if any)
|
|
45
|
+
head -20 .claude/skills/CHANGELOG.md 2>/dev/null
|
|
46
|
+
|
|
47
|
+
# List changed skill files since last documented change
|
|
48
|
+
git log --oneline --name-status -- '.claude/skills/'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If `.claude/skills/CHANGELOG.md` already exists, read the most recent entry date
|
|
52
|
+
and only look at changes after that date:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git log --after="<last-entry-date>" --name-status -- '.claude/skills/'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If no changelog exists, consider all commits that touched `.claude/skills/`.
|
|
59
|
+
|
|
60
|
+
### Step 2: Classify Each Change
|
|
61
|
+
|
|
62
|
+
For every changed skill file, determine the type of change:
|
|
63
|
+
|
|
64
|
+
| Type | Description |
|
|
65
|
+
| ----------- | ----------------------------------------------------- |
|
|
66
|
+
| `added` | New skill created that doesn't exist upstream |
|
|
67
|
+
| `modified` | Existing skill updated (workflow, checklists, tools) |
|
|
68
|
+
| `removed` | Skill deleted from the installation |
|
|
69
|
+
| `renamed` | Skill directory or file renamed |
|
|
70
|
+
|
|
71
|
+
For **modified** skills, read the current file and the previous version to
|
|
72
|
+
identify what specifically changed:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Show diff for a specific skill
|
|
76
|
+
git diff HEAD~N -- '.claude/skills/<skill-name>/SKILL.md'
|
|
77
|
+
|
|
78
|
+
# Or compare against a specific commit/date
|
|
79
|
+
git log --oneline -- '.claude/skills/<skill-name>/'
|
|
80
|
+
git diff <commit> -- '.claude/skills/<skill-name>/SKILL.md'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Step 3: Describe Each Change
|
|
84
|
+
|
|
85
|
+
For every changed skill, write a clear description that an upstream maintainer
|
|
86
|
+
can act on. Each entry must answer:
|
|
87
|
+
|
|
88
|
+
1. **What changed?** — The specific section or behaviour that was modified
|
|
89
|
+
2. **Why?** — The problem encountered or improvement discovered during use
|
|
90
|
+
3. **How?** — A summary of the actual change (not a full diff)
|
|
91
|
+
|
|
92
|
+
Good descriptions:
|
|
93
|
+
|
|
94
|
+
- "Added a safety check to Step 3 — agents were skipping validation when the
|
|
95
|
+
source directory was empty, causing silent failures"
|
|
96
|
+
- "Rewrote the Entity Extraction section to process files in batches of 10
|
|
97
|
+
instead of all at once — large inboxes caused context window overflow"
|
|
98
|
+
- "New skill: `process-hyprnote` — transcribes and extracts entities from
|
|
99
|
+
Hyprnote meeting recordings"
|
|
100
|
+
|
|
101
|
+
Bad descriptions:
|
|
102
|
+
|
|
103
|
+
- "Updated SKILL.md" (too vague)
|
|
104
|
+
- "Fixed stuff" (no context)
|
|
105
|
+
- "Changed line 42" (not meaningful to upstream)
|
|
106
|
+
|
|
107
|
+
### Step 4: Write the Changelog
|
|
108
|
+
|
|
109
|
+
Create or update `.claude/skills/CHANGELOG.md` with the following format:
|
|
110
|
+
|
|
111
|
+
```markdown
|
|
112
|
+
# Skill Changelog
|
|
113
|
+
|
|
114
|
+
Changes to skills in this installation that should be considered for upstream
|
|
115
|
+
inclusion in the Forward Impact monorepo.
|
|
116
|
+
|
|
117
|
+
## <YYYY-MM-DD>
|
|
118
|
+
|
|
119
|
+
### <skill-name> (added|modified|removed)
|
|
120
|
+
|
|
121
|
+
**What:** <one-line summary of the change>
|
|
122
|
+
|
|
123
|
+
**Why:** <the problem or improvement that motivated it>
|
|
124
|
+
|
|
125
|
+
**Details:**
|
|
126
|
+
<2-5 lines describing the specific changes made>
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Entries are in **reverse chronological order** (newest first). Group changes
|
|
132
|
+
from the same date under a single date heading.
|
|
133
|
+
|
|
134
|
+
### Step 5: Review the Changelog
|
|
135
|
+
|
|
136
|
+
After writing, read the changelog back and verify:
|
|
137
|
+
|
|
138
|
+
- [ ] Every changed skill has an entry
|
|
139
|
+
- [ ] Each entry has What, Why, and Details sections
|
|
140
|
+
- [ ] Descriptions are specific enough for an upstream maintainer to act on
|
|
141
|
+
- [ ] New skills include a brief description of their purpose
|
|
142
|
+
- [ ] Removed skills explain why they were removed
|
|
143
|
+
- [ ] No duplicate entries for the same change
|
|
144
|
+
- [ ] Dates are accurate (from git history, not guessed)
|
|
145
|
+
|
|
146
|
+
## Example Output
|
|
147
|
+
|
|
148
|
+
```markdown
|
|
149
|
+
# Skill Changelog
|
|
150
|
+
|
|
151
|
+
Changes to skills in this installation that should be considered for upstream
|
|
152
|
+
inclusion in the Forward Impact monorepo.
|
|
153
|
+
|
|
154
|
+
## 2026-03-01
|
|
155
|
+
|
|
156
|
+
### track-candidates (modified)
|
|
157
|
+
|
|
158
|
+
**What:** Added gender field extraction for diversity tracking
|
|
159
|
+
|
|
160
|
+
**Why:** Recruitment pipeline lacked diversity metrics — pool composition was
|
|
161
|
+
invisible without structured gender data.
|
|
162
|
+
|
|
163
|
+
**Details:**
|
|
164
|
+
- Added Gender field to candidate brief template (Woman / Man / —)
|
|
165
|
+
- Added extraction rules: pronouns, gendered titles, culturally unambiguous names
|
|
166
|
+
- Added explicit note that field has no bearing on hiring decisions
|
|
167
|
+
- Updated quality checklist to include gender field verification
|
|
168
|
+
|
|
169
|
+
### process-hyprnote (added)
|
|
170
|
+
|
|
171
|
+
**What:** New skill for processing Hyprnote meeting recordings
|
|
172
|
+
|
|
173
|
+
**Why:** Meeting notes were being lost — Hyprnote captures transcriptions but
|
|
174
|
+
they weren't being integrated into the knowledge base.
|
|
175
|
+
|
|
176
|
+
**Details:**
|
|
177
|
+
- Reads transcription files from `~/.cache/fit/basecamp/hyprnote/`
|
|
178
|
+
- Extracts people, decisions, and action items
|
|
179
|
+
- Creates meeting notes in `knowledge/Meetings/`
|
|
180
|
+
- Links attendees to `knowledge/People/` entries
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 2026-02-15
|
|
185
|
+
|
|
186
|
+
### extract-entities (modified)
|
|
187
|
+
|
|
188
|
+
**What:** Increased batch size from 5 to 10 files per run
|
|
189
|
+
|
|
190
|
+
**Why:** Processing was too slow for large inboxes — 5 files per batch meant
|
|
191
|
+
dozens of runs to catch up after a week of email.
|
|
192
|
+
|
|
193
|
+
**Details:**
|
|
194
|
+
- Changed batch size constant from 5 to 10 in Step 1
|
|
195
|
+
- Added a note about context window limits for batches > 15
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Notes
|
|
201
|
+
|
|
202
|
+
- This skill only **documents** changes — it does not push or merge anything
|
|
203
|
+
- The changelog is consumed by the **downstream** skill in the upstream monorepo
|
|
204
|
+
- Keep descriptions actionable: an upstream maintainer should be able to
|
|
205
|
+
understand and apply each change without access to this installation
|
|
206
|
+
- When in doubt about whether a change is upstream-worthy, include it — the
|
|
207
|
+
upstream maintainer will decide what to incorporate
|