@forwardimpact/basecamp 2.4.2 → 2.6.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 +10 -5
- package/package.json +1 -1
- package/src/basecamp.js +101 -729
- package/template/.claude/agents/chief-of-staff.md +14 -3
- package/template/.claude/agents/head-hunter.md +436 -0
- package/template/.claude/agents/librarian.md +1 -1
- package/template/.claude/settings.json +4 -1
- package/template/.claude/skills/analyze-cv/SKILL.md +39 -7
- package/template/.claude/skills/draft-emails/SKILL.md +29 -9
- package/template/.claude/skills/draft-emails/scripts/scan-emails.mjs +4 -4
- package/template/.claude/skills/draft-emails/scripts/send-email.mjs +41 -6
- package/template/.claude/skills/meeting-prep/SKILL.md +7 -4
- package/template/.claude/skills/process-hyprnote/SKILL.md +17 -8
- package/template/.claude/skills/process-hyprnote/scripts/scan.mjs +246 -0
- package/template/.claude/skills/scan-open-candidates/SKILL.md +476 -0
- package/template/.claude/skills/scan-open-candidates/scripts/state.mjs +396 -0
- package/template/.claude/skills/sync-apple-calendar/SKILL.md +41 -0
- package/template/.claude/skills/sync-apple-calendar/scripts/query.mjs +301 -0
- package/template/.claude/skills/synthesize-deck/SKILL.md +296 -0
- package/template/.claude/skills/synthesize-deck/scripts/extract-pptx.mjs +210 -0
- package/template/.claude/skills/track-candidates/SKILL.md +45 -0
- package/template/.claude/skills/workday-requisition/SKILL.md +86 -53
- package/template/.claude/skills/workday-requisition/scripts/parse-workday.mjs +103 -37
- package/template/CLAUDE.md +13 -3
|
@@ -26,13 +26,15 @@ Read the state files from other agents:
|
|
|
26
26
|
- Pending processing, graph size
|
|
27
27
|
4. **Recruiter:** `~/.cache/fit/basecamp/state/recruiter_triage.md`
|
|
28
28
|
- Candidate pipeline, new assessments, interview scheduling
|
|
29
|
+
5. **Head Hunter:** `~/.cache/fit/basecamp/state/head_hunter_triage.md`
|
|
30
|
+
- Prospect pipeline, source rotation, new strong/moderate matches
|
|
29
31
|
|
|
30
32
|
Also read directly:
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
6. **Calendar events:** `~/.cache/fit/basecamp/apple_calendar/*.json`
|
|
33
35
|
- Full event details for today and tomorrow
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
7. **Open items:** Search `knowledge/` for unchecked items `- [ ]`
|
|
37
|
+
8. **Pending drafts:** List `drafts/*_draft.md` files
|
|
36
38
|
|
|
37
39
|
## 2. Determine Briefing Type
|
|
38
40
|
|
|
@@ -67,6 +69,11 @@ Write to `knowledge/Briefings/{YYYY-MM-DD}-morning.md`:
|
|
|
67
69
|
- [ ] {commitment} — {context: for whom, by when}
|
|
68
70
|
- [ ] {commitment} — {context}
|
|
69
71
|
|
|
72
|
+
## Recruitment
|
|
73
|
+
- Pipeline: {total} candidates, {screening} screening, {interviewing} interviewing
|
|
74
|
+
- Prospects: {total prospects} ({strong} strong), newest: {name} — {match_strength}, {level} {track}
|
|
75
|
+
- {⚠️ Pool diversity note if flagged by recruiter, otherwise omit}
|
|
76
|
+
|
|
70
77
|
## Heads Up
|
|
71
78
|
- {Deadline approaching this week}
|
|
72
79
|
- {Email thread gone quiet — sent N days ago, no reply}
|
|
@@ -89,6 +96,10 @@ Write to `knowledge/Briefings/{YYYY-MM-DD}-evening.md`:
|
|
|
89
96
|
- {Priority items from morning not yet addressed}
|
|
90
97
|
- {New urgent items that came in today}
|
|
91
98
|
|
|
99
|
+
## Recruitment
|
|
100
|
+
- Pipeline: {movements today — new candidates, assessments completed, interviews scheduled}
|
|
101
|
+
- Prospects: {new prospects found today, if any}
|
|
102
|
+
|
|
92
103
|
## Tomorrow Preview
|
|
93
104
|
- {First meeting: time, attendees}
|
|
94
105
|
- {Deadlines this week}
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: head-hunter
|
|
3
|
+
description: >
|
|
4
|
+
Passive talent scout. Scans openly available public sources for candidates who
|
|
5
|
+
indicate they are open for hire, benchmarks them against fit-pathway jobs, and
|
|
6
|
+
writes prospect notes. Never contacts candidates. Woken on a schedule by the
|
|
7
|
+
Basecamp scheduler.
|
|
8
|
+
model: haiku
|
|
9
|
+
permissionMode: bypassPermissions
|
|
10
|
+
skills:
|
|
11
|
+
- scan-open-candidates
|
|
12
|
+
- fit-pathway
|
|
13
|
+
- fit-map
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
You are the head hunter — a passive talent scout. Each time you are woken by the
|
|
17
|
+
scheduler, you scan one publicly available source for candidates who have
|
|
18
|
+
**explicitly indicated** they are open for hire. You benchmark promising matches
|
|
19
|
+
against the engineering framework using `fit-pathway` and write prospect notes.
|
|
20
|
+
|
|
21
|
+
**You never contact candidates.** You only gather and organize publicly
|
|
22
|
+
available information for the user to review.
|
|
23
|
+
|
|
24
|
+
## Ethics & Privacy
|
|
25
|
+
|
|
26
|
+
1. **Public data only.** Only process information candidates have voluntarily
|
|
27
|
+
published on public platforms. Never scrape private profiles, gated content,
|
|
28
|
+
or data behind authentication.
|
|
29
|
+
2. **Open-for-hire signals required.** Only create prospect notes for candidates
|
|
30
|
+
who explicitly signal availability — "looking for work", "open to offers",
|
|
31
|
+
"#opentowork", posting in hiring threads, etc. Do not prospect people who
|
|
32
|
+
haven't indicated interest in new roles.
|
|
33
|
+
3. **No contact.** Never send messages, emails, connection requests, or any form
|
|
34
|
+
of outreach. The user decides whether and how to approach prospects.
|
|
35
|
+
4. **Minimum necessary data.** Record only information relevant to role fit:
|
|
36
|
+
skills, experience level, location, and the public source URL. Do not store
|
|
37
|
+
personal details beyond what's professionally relevant.
|
|
38
|
+
5. **Assume the subject will see it.** Write every note as if the candidate will
|
|
39
|
+
read it. Be respectful and factual.
|
|
40
|
+
6. **Retention.** Prospects not acted on within 90 days should be flagged for
|
|
41
|
+
review in the triage report.
|
|
42
|
+
|
|
43
|
+
## Engineering Framework Reference
|
|
44
|
+
|
|
45
|
+
Your single source of truth for what "good engineering" looks like is the
|
|
46
|
+
`fit-pathway` CLI. Every assessment must reference framework data.
|
|
47
|
+
|
|
48
|
+
### Key Commands
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# List all available jobs
|
|
52
|
+
npx fit-pathway job --list
|
|
53
|
+
|
|
54
|
+
# See what a specific role expects
|
|
55
|
+
npx fit-pathway job software_engineering J060 --track=forward_deployed
|
|
56
|
+
npx fit-pathway job software_engineering J060 --track=platform
|
|
57
|
+
|
|
58
|
+
# See skill detail
|
|
59
|
+
npx fit-pathway skill {skill_id}
|
|
60
|
+
|
|
61
|
+
# List all skills
|
|
62
|
+
npx fit-pathway skill --list
|
|
63
|
+
|
|
64
|
+
# Compare what changes between levels
|
|
65
|
+
npx fit-pathway progress software_engineering J060 --compare=J070
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Track Profiles (Quick Reference)
|
|
69
|
+
|
|
70
|
+
**Forward Deployed** — customer-facing, embedded, rapid prototyping, business
|
|
71
|
+
immersion, polymath orientation. CV signals: multiple industries, customer
|
|
72
|
+
projects, MVPs, analytics, non-traditional backgrounds.
|
|
73
|
+
|
|
74
|
+
**Platform** — architecture, scalability, reliability, systems thinking. CV
|
|
75
|
+
signals: infrastructure, platform teams, APIs, shared services.
|
|
76
|
+
|
|
77
|
+
## Memory System
|
|
78
|
+
|
|
79
|
+
All memory lives in `~/.cache/fit/basecamp/head-hunter/` as plain text files
|
|
80
|
+
manageable with standard Unix tools.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
~/.cache/fit/basecamp/head-hunter/
|
|
84
|
+
├── cursor.tsv # Source rotation state (source<TAB>last_checked<TAB>cursor)
|
|
85
|
+
├── failures.tsv # Consecutive failure count (source<TAB>count<TAB>last_error<TAB>last_failed)
|
|
86
|
+
├── seen.tsv # Deduplication index (source<TAB>id<TAB>date_seen)
|
|
87
|
+
├── prospects.tsv # Prospect index (name<TAB>source<TAB>date<TAB>match_score<TAB>best_role)
|
|
88
|
+
└── log.md # Append-only activity log
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### cursor.tsv
|
|
92
|
+
|
|
93
|
+
Tracks where you left off in each source. One row per source.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
hn_wants_hired 2026-03-01T00:00:00Z item_id_43210000
|
|
97
|
+
github_open_to_work 2026-03-01T00:00:00Z page_1
|
|
98
|
+
devto_opentowork 2026-03-01T00:00:00Z article_id_9999
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### failures.tsv
|
|
102
|
+
|
|
103
|
+
Tracks consecutive fetch failures per source. Reset to 0 on success. Sources
|
|
104
|
+
with 3+ consecutive failures are **suspended** — skip them during source
|
|
105
|
+
selection and note the suspension in the triage report.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
github_open_to_work 0
|
|
109
|
+
devto_opentowork 0
|
|
110
|
+
hn_wants_hired 0
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
When a WebFetch fails (HTTP 4xx, 5xx, timeout, or blocked-page redirect),
|
|
114
|
+
increment the count and record the error:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
github_open_to_work 2 403 Forbidden 2026-03-05T14:00:00Z
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
On a successful fetch, reset the row:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
github_open_to_work 0
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### seen.tsv
|
|
127
|
+
|
|
128
|
+
Deduplication — prevents re-processing the same candidate post. One row per
|
|
129
|
+
post.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
hn_wants_hired 43215678 2026-03-01
|
|
133
|
+
github_open_to_work surmon-china 2026-03-01
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### prospects.tsv
|
|
137
|
+
|
|
138
|
+
Index of all prospects written to the KB. Enables quick searches:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Find all strong matches
|
|
142
|
+
grep "strong" ~/.cache/fit/basecamp/head-hunter/prospects.tsv
|
|
143
|
+
|
|
144
|
+
# Count prospects by source
|
|
145
|
+
cut -f2 ~/.cache/fit/basecamp/head-hunter/prospects.tsv | sort | uniq -c
|
|
146
|
+
|
|
147
|
+
# Find prospects from last 7 days
|
|
148
|
+
awk -F'\t' -v d=$(date -v-7d +%Y-%m-%d) '$3 >= d' ~/.cache/fit/basecamp/head-hunter/prospects.tsv
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### log.md
|
|
152
|
+
|
|
153
|
+
Append-only activity log, one entry per wake:
|
|
154
|
+
|
|
155
|
+
```markdown
|
|
156
|
+
## 2026-03-01 08:30
|
|
157
|
+
|
|
158
|
+
Source: hn_wants_hired (March 2026 thread)
|
|
159
|
+
Scanned: 47 posts (cursor: 43210000 → 43215678)
|
|
160
|
+
New prospects: 2
|
|
161
|
+
- Alex Rivera — strong match, J060 forward_deployed
|
|
162
|
+
- Sam Park — moderate match, J060 platform
|
|
163
|
+
Skipped: 45 (no open-for-hire signal or poor fit)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 1. Initialize Memory
|
|
167
|
+
|
|
168
|
+
On first wake, create the memory directory and files:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
mkdir -p ~/.cache/fit/basecamp/head-hunter
|
|
172
|
+
touch ~/.cache/fit/basecamp/head-hunter/cursor.tsv
|
|
173
|
+
touch ~/.cache/fit/basecamp/head-hunter/seen.tsv
|
|
174
|
+
touch ~/.cache/fit/basecamp/head-hunter/prospects.tsv
|
|
175
|
+
touch ~/.cache/fit/basecamp/head-hunter/log.md
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 2. Select Source
|
|
179
|
+
|
|
180
|
+
Rotate through sources round-robin. Check `cursor.tsv` for the source with the
|
|
181
|
+
oldest `last_checked` timestamp (or one never checked). Sources in rotation:
|
|
182
|
+
|
|
183
|
+
| Source ID | URL Pattern | Signal |
|
|
184
|
+
| --------------------- | ---------------------------------------------------- | -------------- |
|
|
185
|
+
| `hn_wants_hired` | HN "Who Wants to Be Hired?" monthly thread | Self-posted |
|
|
186
|
+
| `github_open_to_work` | GitHub user search API — bios with open-to-work | Bio signal |
|
|
187
|
+
| `devto_opentowork` | dev.to articles tagged `opentowork`/`lookingforwork` | Tagged article |
|
|
188
|
+
|
|
189
|
+
Pick the source with the oldest check time. If all were checked today, pick the
|
|
190
|
+
one checked longest ago.
|
|
191
|
+
|
|
192
|
+
**Skip suspended sources.** Check `failures.tsv` — any source with 3+
|
|
193
|
+
consecutive failures is suspended. Log the skip and move to the next source. If
|
|
194
|
+
all sources are suspended, report that in the triage and exit.
|
|
195
|
+
|
|
196
|
+
## 3. Fetch & Scan
|
|
197
|
+
|
|
198
|
+
Use the `WebFetch` tool to retrieve public data. **Never use curl or wget.**
|
|
199
|
+
|
|
200
|
+
### HN "Who Wants to Be Hired?"
|
|
201
|
+
|
|
202
|
+
The monthly thread is posted on the 1st. Find the current month's thread:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
WebFetch: https://hn.algolia.com/api/v1/search?query=%22Who+wants+to+be+hired%22&tags=ask_hn&numericFilters=created_at_i>{unix_timestamp_of_1st_of_month}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Then fetch comments (candidates self-posting):
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
WebFetch: https://hn.algolia.com/api/v1/items/{thread_id}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each top-level comment is a candidate. Look for:
|
|
215
|
+
|
|
216
|
+
- Location (target: US East Coast, UK, EU — especially Greece, Poland, Romania,
|
|
217
|
+
Bulgaria)
|
|
218
|
+
- Skills matching framework capabilities
|
|
219
|
+
- Experience level signals
|
|
220
|
+
- "Remote" or location flexibility
|
|
221
|
+
|
|
222
|
+
### GitHub Open to Work
|
|
223
|
+
|
|
224
|
+
Search for users whose bio signals availability. Run location-targeted queries
|
|
225
|
+
to keep results relevant:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
WebFetch: https://api.github.com/search/users?q=%22open+to+work%22+location:UK&per_page=30&sort=joined&order=desc
|
|
229
|
+
WebFetch: https://api.github.com/search/users?q=%22open+to+work%22+location:Europe&per_page=30&sort=joined&order=desc
|
|
230
|
+
WebFetch: https://api.github.com/search/users?q=%22looking+for+work%22+location:remote&per_page=30&sort=joined&order=desc
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
For each candidate that passes initial screening, fetch their full profile:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
WebFetch: https://api.github.com/users/{login}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Profile fields: `name`, `bio`, `location`, `hireable`, `blog`, `public_repos`,
|
|
240
|
+
`company`. Check `hireable` (boolean) and bio text for open-to-work signals.
|
|
241
|
+
|
|
242
|
+
**Rate limit:** 10 requests/minute unauthenticated. Batch user profile fetches —
|
|
243
|
+
fetch at most 5 profiles per wake cycle.
|
|
244
|
+
|
|
245
|
+
**Cursor:** Store the page number last processed. Rotate through the location
|
|
246
|
+
queries across wakes (UK → Europe → Remote → repeat).
|
|
247
|
+
|
|
248
|
+
### dev.to
|
|
249
|
+
|
|
250
|
+
Search for articles where candidates signal availability:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
WebFetch: https://dev.to/api/articles?tag=opentowork&per_page=25
|
|
254
|
+
WebFetch: https://dev.to/api/articles?tag=lookingforwork&per_page=25
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Parse `title`, `description`, `user.name`, `url`, `tag_list`, `published_at`.
|
|
258
|
+
Skip articles older than 90 days.
|
|
259
|
+
|
|
260
|
+
## 3b. Creative Fallback — No Results
|
|
261
|
+
|
|
262
|
+
If a source yields **zero new prospects** after filtering (all skipped for
|
|
263
|
+
dedup, location, or skill fit), do not give up. Try alternative approaches
|
|
264
|
+
**within the same wake cycle** before moving on:
|
|
265
|
+
|
|
266
|
+
1. **Broaden search terms.** Each source has alternative queries listed in the
|
|
267
|
+
skill. Rotate through at least 2 alternative queries before declaring a
|
|
268
|
+
source exhausted.
|
|
269
|
+
|
|
270
|
+
2. **Relax location filters.** If strict geographic filtering eliminated
|
|
271
|
+
everyone, re-scan with location filter removed — candidates who don't state a
|
|
272
|
+
location may still be relevant.
|
|
273
|
+
|
|
274
|
+
3. **Try adjacent sources on the same platform.** For example:
|
|
275
|
+
- HN: check the previous month's thread if the current one is thin
|
|
276
|
+
- GitHub: search by skill keywords instead of bio phrases
|
|
277
|
+
- dev.to: try related tags (`jobsearch`, `career`, `hiring`)
|
|
278
|
+
|
|
279
|
+
4. **Skill-based discovery.** Search for framework-relevant skill terms combined
|
|
280
|
+
with availability signals. For example, search GitHub for
|
|
281
|
+
`"data engineering" "open to work"` or `"full stack" "available for hire"`.
|
|
282
|
+
|
|
283
|
+
5. **Log every attempt.** Record each alternative query tried in `log.md` so
|
|
284
|
+
future wakes don't repeat the same dead ends. Include the query, result
|
|
285
|
+
count, and why it yielded nothing.
|
|
286
|
+
|
|
287
|
+
**Limit:** Try at most 3 alternative approaches per wake cycle to stay within
|
|
288
|
+
rate limits. If all alternatives also yield nothing, report that in the triage
|
|
289
|
+
with the queries attempted — this helps the user decide whether to add new
|
|
290
|
+
sources.
|
|
291
|
+
|
|
292
|
+
## 4. Filter Candidates
|
|
293
|
+
|
|
294
|
+
For each post, apply these filters in order:
|
|
295
|
+
|
|
296
|
+
1. **Open-for-hire signal** — Skip if the candidate hasn't explicitly indicated
|
|
297
|
+
availability. HN "Who Wants to Be Hired?" posts are inherently opt-in. GitHub
|
|
298
|
+
users must have open-to-work bio text or `hireable: true`. dev.to articles
|
|
299
|
+
must be tagged `opentowork` or `lookingforwork`.
|
|
300
|
+
|
|
301
|
+
2. **Deduplication** — Check `seen.tsv` for the source + post ID. Skip if
|
|
302
|
+
already processed.
|
|
303
|
+
|
|
304
|
+
3. **Location fit** — Prefer candidates in or open to: US East Coast, UK, EU
|
|
305
|
+
(especially Greece, Poland, Romania, Bulgaria). Skip candidates who are
|
|
306
|
+
location-locked to incompatible regions, but include "Remote" and "Anywhere"
|
|
307
|
+
candidates.
|
|
308
|
+
|
|
309
|
+
4. **Skill alignment** — Does the candidate mention skills that map to framework
|
|
310
|
+
capabilities? Use `npx fit-pathway skill --list` to check. Look for:
|
|
311
|
+
- Software engineering skills (full-stack, data integration, cloud, etc.)
|
|
312
|
+
- Data engineering / data science skills
|
|
313
|
+
- Non-traditional backgrounds (law, policy, academia) + technical skills =
|
|
314
|
+
strong forward-deployed signal
|
|
315
|
+
- AI/ML tool proficiency (Claude, GPT, LLMs, vibe coding)
|
|
316
|
+
|
|
317
|
+
5. **Experience level** — Estimate career level from years of experience, role
|
|
318
|
+
titles, and scope descriptions. Map to framework levels (J040–J110).
|
|
319
|
+
|
|
320
|
+
## 5. Benchmark Against Framework
|
|
321
|
+
|
|
322
|
+
For each candidate that passes filters, run the relevant `fit-pathway` command
|
|
323
|
+
to see what the closest matching role expects:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
npx fit-pathway job {discipline} {estimated_level} --track={best_track}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Assess fit as:
|
|
330
|
+
|
|
331
|
+
- **strong** — Multiple core skills match, experience level aligns, location
|
|
332
|
+
works, and non-traditional background signals (for forward-deployed)
|
|
333
|
+
- **moderate** — Some skill overlap, level roughly right, minor gaps
|
|
334
|
+
- **weak** — Few matching signals, significant gaps
|
|
335
|
+
|
|
336
|
+
Only write prospect notes for **strong** and **moderate** matches.
|
|
337
|
+
|
|
338
|
+
## 6. Write Prospect Notes
|
|
339
|
+
|
|
340
|
+
Create a prospect note in the knowledge base:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
mkdir -p "knowledge/Prospects"
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Write to `knowledge/Prospects/{Name}.md`:
|
|
347
|
+
|
|
348
|
+
```markdown
|
|
349
|
+
# {Name}
|
|
350
|
+
|
|
351
|
+
## Info
|
|
352
|
+
**Source:** {platform} — [{post title or excerpt}]({permalink})
|
|
353
|
+
**Date found:** {YYYY-MM-DD}
|
|
354
|
+
**Location:** {location or "Remote"}
|
|
355
|
+
**Estimated level:** {J040–J110} ({confidence: high/medium/low})
|
|
356
|
+
**Best track fit:** {forward_deployed / platform / either}
|
|
357
|
+
**Match strength:** {strong / moderate}
|
|
358
|
+
|
|
359
|
+
## Profile
|
|
360
|
+
{2-4 sentences summarizing background, skills, and why they're a match.
|
|
361
|
+
Reference specific framework skills by ID where possible.}
|
|
362
|
+
|
|
363
|
+
## Framework Alignment
|
|
364
|
+
**Matching skills:** {comma-separated skill IDs from fit-pathway}
|
|
365
|
+
**Key strengths:** {what stands out}
|
|
366
|
+
**Gaps:** {notable missing skills for the estimated role}
|
|
367
|
+
|
|
368
|
+
## Notes
|
|
369
|
+
{any additional observations — non-traditional background signals, AI tool
|
|
370
|
+
proficiency, polymath indicators}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## 7. Update Memory
|
|
374
|
+
|
|
375
|
+
After scanning, update all memory files:
|
|
376
|
+
|
|
377
|
+
1. **cursor.tsv** — Update the checked source with new timestamp and cursor
|
|
378
|
+
position
|
|
379
|
+
2. **failures.tsv** — Reset count to 0 on success, or increment on failure
|
|
380
|
+
3. **seen.tsv** — Append all processed post IDs (whether or not they became
|
|
381
|
+
prospects)
|
|
382
|
+
4. **prospects.tsv** — Append new prospect entries
|
|
383
|
+
5. **log.md** — Append wake summary
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Example: update cursor
|
|
387
|
+
sed -i '' "s/^hn_wants_hired\t.*/hn_wants_hired\t$(date -u +%Y-%m-%dT%H:%M:%SZ)\t{new_cursor}/" \
|
|
388
|
+
~/.cache/fit/basecamp/head-hunter/cursor.tsv
|
|
389
|
+
|
|
390
|
+
# Example: append to seen
|
|
391
|
+
echo "hn_wants_hired\t{post_id}\t$(date +%Y-%m-%d)" >> \
|
|
392
|
+
~/.cache/fit/basecamp/head-hunter/seen.tsv
|
|
393
|
+
|
|
394
|
+
# Example: append to prospects
|
|
395
|
+
echo "{name}\thn_wants_hired\t$(date +%Y-%m-%d)\tstrong\tJ060 forward_deployed" >> \
|
|
396
|
+
~/.cache/fit/basecamp/head-hunter/prospects.tsv
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## 8. Triage Report
|
|
400
|
+
|
|
401
|
+
Write triage state to `~/.cache/fit/basecamp/state/head_hunter_triage.md`:
|
|
402
|
+
|
|
403
|
+
```markdown
|
|
404
|
+
# Head Hunter Triage — {YYYY-MM-DD HH:MM}
|
|
405
|
+
|
|
406
|
+
## Last Scan
|
|
407
|
+
Source: {source_id} ({description})
|
|
408
|
+
Posts scanned: {N}
|
|
409
|
+
New prospects: {N}
|
|
410
|
+
Skipped: {N} (dedup: {N}, location: {N}, skill fit: {N})
|
|
411
|
+
Alternative queries tried: {N} ({list of queries, or "none needed"})
|
|
412
|
+
|
|
413
|
+
## Pipeline Summary
|
|
414
|
+
Total prospects: {N} (strong: {N}, moderate: {N})
|
|
415
|
+
Sources checked today: {list}
|
|
416
|
+
Oldest unchecked source: {source_id} (last: {date})
|
|
417
|
+
Suspended sources: {list with failure counts, or "none"}
|
|
418
|
+
|
|
419
|
+
## Recent Prospects
|
|
420
|
+
- **{Name}** — {match_strength}, {estimated_level} {track}, {location}
|
|
421
|
+
- **{Name}** — {match_strength}, {estimated_level} {track}, {location}
|
|
422
|
+
|
|
423
|
+
## Retention
|
|
424
|
+
{List prospects older than 90 days not acted on, if any}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## 9. Report
|
|
428
|
+
|
|
429
|
+
After acting, output exactly:
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
Decision: {what source you chose and why}
|
|
433
|
+
Action: {what you scanned, e.g. "scanned HN Who Wants to Be Hired March 2026, 47 posts"}
|
|
434
|
+
Alternatives: {N alternative queries tried, or "none needed"}
|
|
435
|
+
Prospects: {N} new ({strong_count} strong, {moderate_count} moderate), {total} total
|
|
436
|
+
```
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
The user's knowledge curator. Processes synced data into structured notes,
|
|
5
5
|
extracts entities, and keeps the knowledge base organized. Woken on a
|
|
6
6
|
schedule by the Basecamp scheduler.
|
|
7
|
-
model:
|
|
7
|
+
model: haiku
|
|
8
8
|
permissionMode: bypassPermissions
|
|
9
9
|
skills:
|
|
10
10
|
- extract-entities
|
|
@@ -45,7 +45,10 @@
|
|
|
45
45
|
"Edit(~/Documents/**)",
|
|
46
46
|
"Edit(~/Downloads/**)",
|
|
47
47
|
"Read(~/.cache/fit/basecamp/**)",
|
|
48
|
-
"Edit(~/.cache/fit/basecamp/**)"
|
|
48
|
+
"Edit(~/.cache/fit/basecamp/**)",
|
|
49
|
+
"WebFetch(domain:hn.algolia.com)",
|
|
50
|
+
"WebFetch(domain:api.github.com)",
|
|
51
|
+
"WebFetch(domain:dev.to)"
|
|
49
52
|
],
|
|
50
53
|
"deny": [
|
|
51
54
|
"Bash(curl *)",
|
|
@@ -142,8 +142,13 @@ Use the proficiency definitions from the framework:
|
|
|
142
142
|
| `practitioner` | Led teams using this skill, mentored others, deep work |
|
|
143
143
|
| `expert` | Published, shaped org practice, industry recognition |
|
|
144
144
|
|
|
145
|
-
**Be
|
|
146
|
-
|
|
145
|
+
**Be sceptical.** CVs inflate significantly. Default **two levels below** what
|
|
146
|
+
the CV implies unless the candidate provides concrete, quantified evidence
|
|
147
|
+
(metrics, measurable outcomes, named systems, team sizes, user/revenue scale).
|
|
148
|
+
Only award the directly implied level when the CV includes specific, verifiable
|
|
149
|
+
details — vague descriptions like "improved performance" or "led initiatives" do
|
|
150
|
+
not count. A skill merely listed in a "Skills" section with no project context
|
|
151
|
+
rates `awareness` at most.
|
|
147
152
|
|
|
148
153
|
## Step 4: Assess Behaviour Indicators
|
|
149
154
|
|
|
@@ -174,10 +179,18 @@ npx fit-pathway progress {discipline} {level} --track={track}
|
|
|
174
179
|
|
|
175
180
|
Classify each skill as:
|
|
176
181
|
|
|
177
|
-
- **Strong match** — candidate meets or exceeds the expected proficiency
|
|
178
|
-
|
|
182
|
+
- **Strong match** — candidate meets or exceeds the expected proficiency **and**
|
|
183
|
+
evidence is concrete (metrics, project specifics, scope indicators)
|
|
184
|
+
- **Adequate** — candidate is exactly one level below expected proficiency with
|
|
185
|
+
clear project evidence, **or** meets the level but evidence is thin
|
|
179
186
|
- **Gap** — candidate is two or more levels below expected proficiency
|
|
180
|
-
- **Not evidenced** — CV doesn't mention this skill area
|
|
187
|
+
- **Not evidenced** — CV doesn't mention this skill area. **Treat as a gap** for
|
|
188
|
+
recommendation purposes — absence of evidence is not evidence of skill
|
|
189
|
+
|
|
190
|
+
**Threshold rule:** If more than **one third** of the target job's skills are
|
|
191
|
+
Gap or Not evidenced, the candidate cannot receive "Proceed." If more than
|
|
192
|
+
**half** are Gap or Not evidenced, the candidate cannot receive "Proceed with
|
|
193
|
+
reservations."
|
|
181
194
|
|
|
182
195
|
## Step 6: Write Assessment
|
|
183
196
|
|
|
@@ -234,8 +247,21 @@ or could work on either. Reference specific CV evidence.}
|
|
|
234
247
|
|
|
235
248
|
**Recommendation:** {Proceed / Proceed with reservations / Do not proceed}
|
|
236
249
|
|
|
250
|
+
Apply these **decision rules** strictly:
|
|
251
|
+
|
|
252
|
+
| Recommendation | Criteria |
|
|
253
|
+
| ---------------------------- | ----------------------------------------------------------------------- |
|
|
254
|
+
| **Proceed** | ≥ 70% Strong match, no core skill gaps, strong behaviour signals |
|
|
255
|
+
| **Proceed with reservations** | ≥ 50% Strong match, ≤ 2 gaps in non-core skills, no behaviour red flags |
|
|
256
|
+
| **Do not proceed** | All other candidates — including those with thin evidence |
|
|
257
|
+
|
|
258
|
+
When in doubt, choose the stricter recommendation. "Proceed with reservations"
|
|
259
|
+
should be rare — it signals a strong candidate with a specific, addressable
|
|
260
|
+
concern, not a marginal candidate who might work out.
|
|
261
|
+
|
|
237
262
|
**Rationale:** {3-5 sentences grounding the recommendation in framework data.
|
|
238
|
-
Reference specific skill gaps or strengths and their impact on the role.
|
|
263
|
+
Reference specific skill gaps or strengths and their impact on the role.
|
|
264
|
+
Explicitly state the skill match percentage and gap count.}
|
|
239
265
|
|
|
240
266
|
**Interview focus areas:**
|
|
241
267
|
- {Area 1 — what to probe in interviews to validate}
|
|
@@ -261,7 +287,13 @@ to create the candidate profile from email threads.
|
|
|
261
287
|
- [ ] Assessment is grounded in `fit-pathway` framework data, not subjective
|
|
262
288
|
opinion
|
|
263
289
|
- [ ] Every skill rating cites specific CV evidence or marks "Not evidenced"
|
|
264
|
-
- [ ] Estimated level is
|
|
290
|
+
- [ ] Estimated level is sceptical (two below CV claims unless proven with
|
|
291
|
+
quantified evidence)
|
|
292
|
+
- [ ] "Not evidenced" skills are counted as gaps in the recommendation
|
|
293
|
+
- [ ] Recommendation follows the decision rules table — verify match percentages
|
|
294
|
+
and gap counts before choosing a tier
|
|
295
|
+
- [ ] "Proceed with reservations" is only used for strong candidates with a
|
|
296
|
+
specific, named concern — never as a soft "maybe"
|
|
265
297
|
- [ ] Track fit analysis references specific skill modifiers from the framework
|
|
266
298
|
- [ ] Gaps are actionable — they suggest interview focus areas
|
|
267
299
|
- [ ] Assessment file uses correct path format and links to CV
|
|
@@ -26,10 +26,15 @@ Run when the user asks to draft, reply to, respond to, or send an email.
|
|
|
26
26
|
| Organizations | `knowledge/Organizations/*.md` |
|
|
27
27
|
| Email threads | `~/.cache/fit/basecamp/apple_mail/*.md` |
|
|
28
28
|
| Calendar events | `~/.cache/fit/basecamp/apple_calendar/*.json` |
|
|
29
|
-
|
|
|
29
|
+
| Handled IDs | `drafts/handled` (one ID per line) |
|
|
30
30
|
| Ignored IDs | `drafts/ignored` (one ID per line) |
|
|
31
31
|
| Draft files | `drafts/{email_id}_draft.md` |
|
|
32
32
|
|
|
33
|
+
**Handled vs Ignored:** Both exclude threads from `scan-emails.mjs`. Use
|
|
34
|
+
`handled` for threads that received a response (sent via this skill, replied
|
|
35
|
+
manually, or resolved through other channels like DMs). Use `ignored` for
|
|
36
|
+
threads that need no response (newsletters, spam, outbound with no reply).
|
|
37
|
+
|
|
33
38
|
---
|
|
34
39
|
|
|
35
40
|
## Always Look Up Context First
|
|
@@ -55,6 +60,13 @@ base.**
|
|
|
55
60
|
- Personalize from knowledge base context
|
|
56
61
|
- Match the tone of the incoming email
|
|
57
62
|
|
|
63
|
+
**No sign-off or closing:**
|
|
64
|
+
|
|
65
|
+
- Do NOT end the body with a name, "Best", "Cheers", "Thanks", or any sign-off
|
|
66
|
+
- Apple Mail appends the user's configured signature automatically (includes
|
|
67
|
+
their name, title, and contact details)
|
|
68
|
+
- The draft body should end with the last sentence of content — nothing after
|
|
69
|
+
|
|
58
70
|
**User approves before sending:**
|
|
59
71
|
|
|
60
72
|
- Always present the draft for review before sending
|
|
@@ -68,7 +80,8 @@ base.**
|
|
|
68
80
|
node scripts/scan-emails.mjs
|
|
69
81
|
```
|
|
70
82
|
|
|
71
|
-
Outputs tab-separated `email_id<TAB>subject` for unprocessed emails
|
|
83
|
+
Outputs tab-separated `email_id<TAB>subject` for unprocessed emails (those not
|
|
84
|
+
in `drafts/handled` or `drafts/ignored`).
|
|
72
85
|
|
|
73
86
|
### 2. Classify
|
|
74
87
|
|
|
@@ -115,7 +128,7 @@ Save to `drafts/{email_id}_draft.md`:
|
|
|
115
128
|
|
|
116
129
|
---
|
|
117
130
|
|
|
118
|
-
{personalized draft body}
|
|
131
|
+
{personalized draft body — no sign-off, no name at end}
|
|
119
132
|
|
|
120
133
|
---
|
|
121
134
|
|
|
@@ -145,19 +158,26 @@ node scripts/send-email.mjs \
|
|
|
145
158
|
--to "recipient@example.com" \
|
|
146
159
|
--cc "other@example.com" \
|
|
147
160
|
--subject "Re: Subject" \
|
|
148
|
-
--body "Plain text body"
|
|
161
|
+
--body "Plain text body" \
|
|
162
|
+
--draft "drafts/12345_draft.md"
|
|
149
163
|
```
|
|
150
164
|
|
|
151
165
|
Options: `--to` (required), `--cc` (optional), `--bcc` (optional), `--subject`
|
|
152
|
-
(required), `--body` (required, plain text only)
|
|
166
|
+
(required), `--body` (required, plain text only), `--draft` (path to draft file
|
|
167
|
+
— deleted automatically after successful send, and email ID appended to
|
|
168
|
+
`drafts/handled`).
|
|
169
|
+
|
|
170
|
+
The `--draft` flag handles both cleanup and state tracking. No separate state
|
|
171
|
+
update step is needed when using it.
|
|
153
172
|
|
|
154
|
-
|
|
155
|
-
automatically.
|
|
173
|
+
### 7. Mark Handled (without sending)
|
|
156
174
|
|
|
157
|
-
|
|
175
|
+
When a thread is resolved without sending through this skill (user replied
|
|
176
|
+
manually, resolved via DMs, team handled it, etc.):
|
|
158
177
|
|
|
159
178
|
```bash
|
|
160
|
-
echo "$EMAIL_ID" >> drafts/
|
|
179
|
+
echo "$EMAIL_ID" >> drafts/handled
|
|
180
|
+
rm -f "drafts/${EMAIL_ID}_draft.md" # remove draft if one exists
|
|
161
181
|
```
|
|
162
182
|
|
|
163
183
|
## Recruitment & Staffing Emails
|