@forwardimpact/basecamp 0.1.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/LICENSE +201 -0
- package/README.md +229 -0
- package/build.js +124 -0
- package/config/scheduler.json +28 -0
- package/package.json +37 -0
- package/scheduler.js +552 -0
- package/scripts/build-pkg.sh +117 -0
- package/scripts/compile.sh +26 -0
- package/scripts/install.sh +108 -0
- package/scripts/pkg-resources/conclusion.html +62 -0
- package/scripts/pkg-resources/welcome.html +64 -0
- package/scripts/postinstall +46 -0
- package/scripts/uninstall.sh +56 -0
- package/template/.claude/settings.json +40 -0
- package/template/.claude/skills/create-presentations/SKILL.md +75 -0
- package/template/.claude/skills/create-presentations/references/slide.css +35 -0
- package/template/.claude/skills/create-presentations/scripts/convert-to-pdf.js +32 -0
- package/template/.claude/skills/doc-collab/SKILL.md +112 -0
- package/template/.claude/skills/draft-emails/SKILL.md +191 -0
- package/template/.claude/skills/draft-emails/scripts/scan-emails.sh +33 -0
- package/template/.claude/skills/extract-entities/SKILL.md +466 -0
- package/template/.claude/skills/extract-entities/references/TEMPLATES.md +131 -0
- package/template/.claude/skills/extract-entities/scripts/state.py +100 -0
- package/template/.claude/skills/meeting-prep/SKILL.md +135 -0
- package/template/.claude/skills/organize-files/SKILL.md +146 -0
- package/template/.claude/skills/organize-files/scripts/organize-by-type.sh +42 -0
- package/template/.claude/skills/organize-files/scripts/summarize.sh +21 -0
- package/template/.claude/skills/sync-apple-calendar/SKILL.md +101 -0
- package/template/.claude/skills/sync-apple-calendar/references/SCHEMA.md +80 -0
- package/template/.claude/skills/sync-apple-calendar/scripts/sync.py +233 -0
- package/template/.claude/skills/sync-apple-mail/SKILL.md +131 -0
- package/template/.claude/skills/sync-apple-mail/references/SCHEMA.md +88 -0
- package/template/.claude/skills/sync-apple-mail/scripts/parse-emlx.py +104 -0
- package/template/.claude/skills/sync-apple-mail/scripts/sync.py +348 -0
- package/template/CLAUDE.md +152 -0
- package/template/USER.md +5 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: draft-emails
|
|
3
|
+
description: Draft email responses using the knowledge base and calendar for full context on every person and conversation. Use when the user asks to draft, reply to, or respond to an email. Looks up people and organizations in the knowledge base before drafting.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Draft Emails
|
|
7
|
+
|
|
8
|
+
Help the user draft email responses. Uses the knowledge base and calendar for
|
|
9
|
+
full context on every person and conversation. This is an interactive skill —
|
|
10
|
+
the user triggers it by asking to draft or reply to emails.
|
|
11
|
+
|
|
12
|
+
## Trigger
|
|
13
|
+
|
|
14
|
+
Run when the user asks to draft, reply to, or respond to an email.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- Knowledge base populated (from `extract-entities` skill)
|
|
19
|
+
- Synced email data in `~/.cache/fit/basecamp/apple_mail/`
|
|
20
|
+
|
|
21
|
+
## Inputs
|
|
22
|
+
|
|
23
|
+
- `knowledge/People/*.md` — person context
|
|
24
|
+
- `knowledge/Organizations/*.md` — organization context
|
|
25
|
+
- `~/.cache/fit/basecamp/apple_mail/*.md` — email threads
|
|
26
|
+
- `~/.cache/fit/basecamp/apple_calendar/*.json` — calendar events (for
|
|
27
|
+
scheduling)
|
|
28
|
+
- `drafts/last_processed` — timestamp of last processing run
|
|
29
|
+
- `drafts/drafted` — list of drafted email IDs (one per line)
|
|
30
|
+
- `drafts/ignored` — list of ignored email IDs (one per line)
|
|
31
|
+
|
|
32
|
+
## Outputs
|
|
33
|
+
|
|
34
|
+
- `drafts/{email_id}_draft.md` — draft email files
|
|
35
|
+
- `drafts/last_processed` — updated timestamp
|
|
36
|
+
- `drafts/drafted` — updated with newly drafted IDs
|
|
37
|
+
- `drafts/ignored` — updated with newly ignored IDs
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Critical: Always Look Up Context First
|
|
42
|
+
|
|
43
|
+
**BEFORE drafting any email, you MUST look up the person/organization in the
|
|
44
|
+
knowledge base.**
|
|
45
|
+
|
|
46
|
+
When the user says "draft an email to Monica" or mentions ANY person:
|
|
47
|
+
|
|
48
|
+
1. **STOP** — Do not draft anything yet
|
|
49
|
+
2. **SEARCH** — Look them up: `rg -l "Monica" knowledge/`
|
|
50
|
+
3. **READ** — Read their note: `cat "knowledge/People/Monica Smith.md"`
|
|
51
|
+
4. **UNDERSTAND** — Extract role, organization, relationship history, open items
|
|
52
|
+
5. **THEN DRAFT** — Only now draft the email, using this context
|
|
53
|
+
|
|
54
|
+
## Key Principles
|
|
55
|
+
|
|
56
|
+
**Ask, don't guess:**
|
|
57
|
+
|
|
58
|
+
- If intent is unclear, ASK what the email should be about
|
|
59
|
+
- If a person has multiple contexts, ASK which one
|
|
60
|
+
- **WRONG:** "Here are three variants — pick one"
|
|
61
|
+
- **RIGHT:** "I see Akhilesh is involved in Rowboat and banking. Which topic?"
|
|
62
|
+
|
|
63
|
+
**Be decisive, not generic:**
|
|
64
|
+
|
|
65
|
+
- Once you know the context, draft ONE email — no multiple versions
|
|
66
|
+
- Every draft must be personalized from knowledge base context
|
|
67
|
+
- Infer tone and approach from context
|
|
68
|
+
|
|
69
|
+
## Processing Flow
|
|
70
|
+
|
|
71
|
+
### Step 1: Scan for New Emails
|
|
72
|
+
|
|
73
|
+
Find unprocessed emails using the scan script:
|
|
74
|
+
|
|
75
|
+
bash scripts/scan-emails.sh
|
|
76
|
+
|
|
77
|
+
This outputs tab-separated `email_id<TAB>subject` for each email not yet in
|
|
78
|
+
`drafts/drafted` or `drafts/ignored`.
|
|
79
|
+
|
|
80
|
+
### Step 2: Parse Email
|
|
81
|
+
|
|
82
|
+
Each email file is markdown with headers:
|
|
83
|
+
|
|
84
|
+
- `# Subject Line`
|
|
85
|
+
- `**Thread ID:** <id>`
|
|
86
|
+
- `**Message Count:** <count>`
|
|
87
|
+
- `### From: Name <email@example.com>`
|
|
88
|
+
- `**Date:** <date>`
|
|
89
|
+
|
|
90
|
+
### Step 3: Classify Email
|
|
91
|
+
|
|
92
|
+
**IGNORE** (append ID to `drafts/ignored`):
|
|
93
|
+
|
|
94
|
+
- Newsletters, marketing, automated notifications
|
|
95
|
+
- Spam or irrelevant cold outreach
|
|
96
|
+
- Outbound emails from user with no reply
|
|
97
|
+
|
|
98
|
+
**DRAFT response for:**
|
|
99
|
+
|
|
100
|
+
- Meeting requests or scheduling
|
|
101
|
+
- Personal emails from known contacts
|
|
102
|
+
- Business inquiries
|
|
103
|
+
- Follow-ups on existing conversations
|
|
104
|
+
- Emails requesting information or action
|
|
105
|
+
|
|
106
|
+
### Step 4: Gather Context
|
|
107
|
+
|
|
108
|
+
**Knowledge Base (REQUIRED for every draft):**
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
rg -l "sender_name" knowledge/
|
|
112
|
+
cat "knowledge/People/Sender Name.md"
|
|
113
|
+
cat "knowledge/Organizations/Company Name.md"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Calendar (for scheduling emails):**
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
ls ~/.cache/fit/basecamp/apple_calendar/ 2>/dev/null
|
|
120
|
+
cat "$HOME/.cache/fit/basecamp/apple_calendar/event123.json"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Step 5: Create Draft
|
|
124
|
+
|
|
125
|
+
Write draft to `drafts/{email_id}_draft.md`:
|
|
126
|
+
|
|
127
|
+
```markdown
|
|
128
|
+
# Draft Response
|
|
129
|
+
|
|
130
|
+
**Original Email ID:** {id}
|
|
131
|
+
**Original Subject:** {subject}
|
|
132
|
+
**From:** {sender}
|
|
133
|
+
**Date Processed:** {date}
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Context Used
|
|
138
|
+
- Calendar: {relevant info or N/A}
|
|
139
|
+
- Knowledge: {relevant notes or N/A}
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Draft Response
|
|
144
|
+
|
|
145
|
+
Subject: Re: {subject}
|
|
146
|
+
|
|
147
|
+
{personalized draft body}
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Notes
|
|
152
|
+
{why this response was crafted this way}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Guidelines:**
|
|
156
|
+
|
|
157
|
+
- Draft ONE email — no multiple versions
|
|
158
|
+
- Reference past interactions naturally
|
|
159
|
+
- Match the tone of the incoming email
|
|
160
|
+
- For scheduling: propose specific times from calendar
|
|
161
|
+
- If unsure about intent, ask a clarifying question
|
|
162
|
+
|
|
163
|
+
### Step 6: Update State
|
|
164
|
+
|
|
165
|
+
After each email, update the state files:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
echo "$EMAIL_ID" >> drafts/drafted # or drafts/ignored
|
|
169
|
+
date -u '+%Y-%m-%dT%H:%M:%SZ' > drafts/last_processed
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Step 7: Summary
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
## Processing Summary
|
|
176
|
+
**Emails Scanned:** X
|
|
177
|
+
**Drafts Created:** Y
|
|
178
|
+
**Ignored:** Z
|
|
179
|
+
|
|
180
|
+
### Drafts Created:
|
|
181
|
+
- {id}: {subject} — {reason}
|
|
182
|
+
|
|
183
|
+
### Ignored:
|
|
184
|
+
- {id}: {subject} — {reason}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Constraints
|
|
188
|
+
|
|
189
|
+
- Never actually send emails — only create drafts
|
|
190
|
+
- Be conservative with ignore — when in doubt, create a draft
|
|
191
|
+
- For ambiguous emails, create a draft with a note explaining the ambiguity
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Scan for unprocessed emails and output their IDs and subjects.
|
|
3
|
+
#
|
|
4
|
+
# Usage: bash scripts/scan-emails.sh
|
|
5
|
+
#
|
|
6
|
+
# Checks ~/.cache/fit/basecamp/apple_mail/ for email files
|
|
7
|
+
# not yet listed in drafts/drafted or drafts/ignored.
|
|
8
|
+
# Outputs tab-separated: email_id<TAB>subject
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
MAIL_DIRS=(
|
|
13
|
+
"$HOME/.cache/fit/basecamp/apple_mail"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
for dir in "${MAIL_DIRS[@]}"; do
|
|
17
|
+
[ -d "$dir" ] || continue
|
|
18
|
+
for file in "$dir"/*.md; do
|
|
19
|
+
[ -f "$file" ] || continue
|
|
20
|
+
|
|
21
|
+
# Extract ID from filename (without extension)
|
|
22
|
+
EMAIL_ID="$(basename "$file" .md)"
|
|
23
|
+
|
|
24
|
+
# Skip if already drafted or ignored
|
|
25
|
+
rg -qxF "$EMAIL_ID" drafts/drafted 2>/dev/null && continue
|
|
26
|
+
rg -qxF "$EMAIL_ID" drafts/ignored 2>/dev/null && continue
|
|
27
|
+
|
|
28
|
+
# Extract subject from first H1 heading
|
|
29
|
+
SUBJECT="$(rg -m1 '^# ' "$file" 2>/dev/null | sed 's/^# //')"
|
|
30
|
+
|
|
31
|
+
printf '%s\t%s\n' "$EMAIL_ID" "$SUBJECT"
|
|
32
|
+
done
|
|
33
|
+
done
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extract-entities
|
|
3
|
+
description: Process synced email/calendar files from ~/.cache/fit/basecamp/ and ad-hoc document files (e.g. from ~/Desktop/ or ~/Downloads/) to extract structured knowledge into knowledge/ as Obsidian-compatible markdown notes. Use on a schedule, when the user asks to process/extract entities, or when invoked by another skill (e.g. organize-files). Builds the core knowledge graph from raw data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Extract Entities
|
|
7
|
+
|
|
8
|
+
Process synced email and calendar files from `~/.cache/fit/basecamp/` and
|
|
9
|
+
extract structured knowledge into `knowledge/` as Obsidian-compatible markdown
|
|
10
|
+
notes. This is the core knowledge graph builder — it transforms raw data from
|
|
11
|
+
the sync skills into actionable, linked notes.
|
|
12
|
+
|
|
13
|
+
Also accepts **ad-hoc document files** passed by other skills (e.g. the
|
|
14
|
+
**`organize-files`** skill passes documents found in `~/Desktop/` and
|
|
15
|
+
`~/Downloads/`).
|
|
16
|
+
|
|
17
|
+
## Trigger
|
|
18
|
+
|
|
19
|
+
Run this skill:
|
|
20
|
+
|
|
21
|
+
- On a schedule (every 15 minutes) for synced data
|
|
22
|
+
- When the user asks to process/extract entities from synced data
|
|
23
|
+
- When invoked by another skill with ad-hoc file paths (e.g.
|
|
24
|
+
**`organize-files`** after organizing `~/Desktop/` and `~/Downloads/`)
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
- Synced data in `~/.cache/fit/basecamp/` (from `sync-apple-mail` or
|
|
29
|
+
`sync-apple-calendar` skills), **and/or**
|
|
30
|
+
- Ad-hoc file paths provided by the calling skill or user
|
|
31
|
+
- User identity configured in `USER.md` (Name, Email, Domain)
|
|
32
|
+
|
|
33
|
+
## Inputs
|
|
34
|
+
|
|
35
|
+
### Synced data (scheduled processing)
|
|
36
|
+
|
|
37
|
+
- `~/.cache/fit/basecamp/apple_mail/*.md` — synced email threads
|
|
38
|
+
- `~/.cache/fit/basecamp/apple_calendar/*.json` — synced calendar events
|
|
39
|
+
|
|
40
|
+
### Ad-hoc files (from other skills or user)
|
|
41
|
+
|
|
42
|
+
- Arbitrary file paths passed as input (e.g.
|
|
43
|
+
`~/Downloads/Documents/Proposal.pdf`, `~/Desktop/Meeting_Notes.md`)
|
|
44
|
+
- Supported formats: `.pdf`, `.txt`, `.md`, `.rtf`, `.doc`, `.docx`, `.csv`,
|
|
45
|
+
`.xlsx`
|
|
46
|
+
- Typically provided by the **`organize-files`** skill after organizing
|
|
47
|
+
`~/Desktop/` and `~/Downloads/`
|
|
48
|
+
|
|
49
|
+
### State tracking
|
|
50
|
+
|
|
51
|
+
- `~/.cache/fit/basecamp/state/graph_processed` — tracks which files have been
|
|
52
|
+
processed (TSV)
|
|
53
|
+
- `USER.md` — user identity (Name, Email, Domain) for self-exclusion
|
|
54
|
+
|
|
55
|
+
## Outputs
|
|
56
|
+
|
|
57
|
+
- `knowledge/People/*.md` — person notes
|
|
58
|
+
- `knowledge/Organizations/*.md` — organization notes
|
|
59
|
+
- `knowledge/Projects/*.md` — project notes
|
|
60
|
+
- `knowledge/Topics/*.md` — topic notes
|
|
61
|
+
- `~/.cache/fit/basecamp/state/graph_processed` — updated with newly processed
|
|
62
|
+
files
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Before Starting
|
|
67
|
+
|
|
68
|
+
1. Read `USER.md` to get the user's name, email, and domain
|
|
69
|
+
2. Find new/changed files to process:
|
|
70
|
+
|
|
71
|
+
python3 scripts/state.py check
|
|
72
|
+
|
|
73
|
+
This outputs one file path per line for all source files that are new or
|
|
74
|
+
have changed since last processing.
|
|
75
|
+
|
|
76
|
+
### Ad-hoc file inputs
|
|
77
|
+
|
|
78
|
+
When invoked with ad-hoc file paths (e.g. by the **`organize-files`** skill),
|
|
79
|
+
process those files directly instead of scanning `~/.cache/fit/basecamp/`. Check
|
|
80
|
+
each file against `graph_processed` the same way — skip if the hash hasn't
|
|
81
|
+
changed.
|
|
82
|
+
|
|
83
|
+
**Process in batches of 10 files per run.**
|
|
84
|
+
|
|
85
|
+
## Step 0: Build Knowledge Index
|
|
86
|
+
|
|
87
|
+
Before processing, scan all existing notes to build an index:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
find knowledge/People knowledge/Organizations knowledge/Projects knowledge/Topics -name "*.md" 2>/dev/null
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For each note, extract key fields:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
head -20 "knowledge/People/Sarah Chen.md"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Build a mental index:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
PEOPLE:
|
|
103
|
+
| Name | Email | Organization | Role | Aliases |
|
|
104
|
+
|
|
105
|
+
ORGANIZATIONS:
|
|
106
|
+
| Name | Domain | Aliases |
|
|
107
|
+
|
|
108
|
+
PROJECTS:
|
|
109
|
+
| Name | Status | Aliases |
|
|
110
|
+
|
|
111
|
+
TOPICS:
|
|
112
|
+
| Name | Keywords | Aliases |
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Step 1: Determine Source Type and Filter
|
|
116
|
+
|
|
117
|
+
### Determine type
|
|
118
|
+
|
|
119
|
+
- Has `Meeting:` or `Attendees:` or `Transcript:` → **meeting** (can create
|
|
120
|
+
notes)
|
|
121
|
+
- Has `From:` and `To:` or `Subject:` → **email** (can only update existing
|
|
122
|
+
notes)
|
|
123
|
+
- Is in `Voice Memos/` folder → **voice memo** (can create notes)
|
|
124
|
+
- Is in `apple_calendar/` → **calendar event** (enrich existing notes only)
|
|
125
|
+
- Is an **ad-hoc document** (from `~/Desktop/`, `~/Downloads/`, or passed by
|
|
126
|
+
another skill) → **document** (can create notes)
|
|
127
|
+
|
|
128
|
+
### Filter: Skip These Sources
|
|
129
|
+
|
|
130
|
+
**ALWAYS process — never skip:**
|
|
131
|
+
|
|
132
|
+
- Calendar events — always process regardless of whether attendees are internal
|
|
133
|
+
or external. Internal-only meetings are valuable for enriching project and
|
|
134
|
+
topic notes (meeting context, decisions, agenda items). Only skip all-day
|
|
135
|
+
placeholder events with no attendees and no description (e.g. "Block", "OOO").
|
|
136
|
+
|
|
137
|
+
**SKIP entirely (don't process):**
|
|
138
|
+
|
|
139
|
+
- Newsletters (unsubscribe links, "View in browser", bulk sender indicators)
|
|
140
|
+
- Marketing emails (promotional language, no-reply senders)
|
|
141
|
+
- Automated notifications (GitHub, Jira, Slack, CI/CD, shipping updates)
|
|
142
|
+
- Spam or cold outreach from unknown senders with no existing relationship
|
|
143
|
+
- Product update emails, release notes, changelogs
|
|
144
|
+
- Social media notifications
|
|
145
|
+
- Receipts and order confirmations
|
|
146
|
+
- Calendar invite emails that are just logistics
|
|
147
|
+
- Mass emails (many recipients, mailing list headers)
|
|
148
|
+
|
|
149
|
+
**PROCESS (but only update existing notes):**
|
|
150
|
+
|
|
151
|
+
- Emails from people who already have notes in `knowledge/People/`
|
|
152
|
+
- Emails that reference existing projects or organizations
|
|
153
|
+
|
|
154
|
+
**PROCESS (can create new notes):**
|
|
155
|
+
|
|
156
|
+
- Meeting transcripts with external attendees
|
|
157
|
+
- Voice memos
|
|
158
|
+
|
|
159
|
+
**Exception — Warm Intros:** If an email is a warm introduction from someone who
|
|
160
|
+
has a note, AND they're introducing a new person, create a note for the
|
|
161
|
+
introduced person.
|
|
162
|
+
|
|
163
|
+
Warm intro signals:
|
|
164
|
+
|
|
165
|
+
- Subject contains "Intro:", "Introduction:", "Meet", "Connecting"
|
|
166
|
+
- Body contains "introduce you to", "want to connect", "meet [Name]"
|
|
167
|
+
- New person is CC'd
|
|
168
|
+
|
|
169
|
+
## Step 2: Read and Parse Source File
|
|
170
|
+
|
|
171
|
+
### For emails
|
|
172
|
+
|
|
173
|
+
Extract: Date, Subject, From, To/Cc, Thread ID, Body.
|
|
174
|
+
|
|
175
|
+
### For meetings
|
|
176
|
+
|
|
177
|
+
Extract: Date, Attendees, Transcript/Notes.
|
|
178
|
+
|
|
179
|
+
### For ad-hoc documents
|
|
180
|
+
|
|
181
|
+
Extract: Date (file modification date), Filename, Source path, Content.
|
|
182
|
+
|
|
183
|
+
- `.md`, `.txt`, `.rtf` — read directly
|
|
184
|
+
- `.pdf` — extract text (`pdftotext` or `mdcat` if available)
|
|
185
|
+
- `.csv` — read as-is, look for names/emails/orgs in columns
|
|
186
|
+
- `.doc`, `.docx` — extract text (`textutil -convert txt` on macOS)
|
|
187
|
+
|
|
188
|
+
Ad-hoc documents follow **meeting** rules: they **can create** new entity notes.
|
|
189
|
+
|
|
190
|
+
### 2a: Exclude Self
|
|
191
|
+
|
|
192
|
+
Never create or update notes for:
|
|
193
|
+
|
|
194
|
+
- The user (matches name, email, or @domain from `USER.md`)
|
|
195
|
+
- Anyone @{user.domain} (colleagues at user's company)
|
|
196
|
+
|
|
197
|
+
### 2b: Extract All Name Variants
|
|
198
|
+
|
|
199
|
+
Collect every way entities are referenced:
|
|
200
|
+
|
|
201
|
+
**People:** Full names, first names, last names, initials, email addresses,
|
|
202
|
+
roles/titles, pronouns with clear antecedents.
|
|
203
|
+
|
|
204
|
+
**Organizations:** Full names, short names, abbreviations, email domains.
|
|
205
|
+
|
|
206
|
+
**Projects:** Explicit names, descriptive references ("the pilot", "the deal").
|
|
207
|
+
|
|
208
|
+
## Step 3: Look Up Existing Notes
|
|
209
|
+
|
|
210
|
+
For each variant, search the knowledge index built in Step 0.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
rg -l "Sarah Chen|sarah@acme.com" knowledge/
|
|
214
|
+
cat "knowledge/People/Sarah Chen.md"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Matching criteria:**
|
|
218
|
+
|
|
219
|
+
| Source has | Note has | Match if |
|
|
220
|
+
| ------------------------ | ------------------------ | ------------------------- |
|
|
221
|
+
| First name "Sarah" | Full name "Sarah Chen" | Same organization context |
|
|
222
|
+
| Email "sarah@acme.com" | Email field | Exact match |
|
|
223
|
+
| Email domain "@acme.com" | Organization "Acme Corp" | Domain matches org |
|
|
224
|
+
| Any variant | Aliases field | Listed in aliases |
|
|
225
|
+
|
|
226
|
+
## Step 4: Resolve Entities to Canonical Names
|
|
227
|
+
|
|
228
|
+
Build a resolution map from every source reference to its canonical form:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
RESOLVED:
|
|
232
|
+
- "Sarah Chen" → [[People/Sarah Chen]]
|
|
233
|
+
- "sarah@acme.com" → [[People/Sarah Chen]]
|
|
234
|
+
- "Acme" → [[Organizations/Acme Corp]]
|
|
235
|
+
|
|
236
|
+
NEW ENTITIES (meeting — create notes):
|
|
237
|
+
- "Jennifer" (CTO) → Create [[People/Jennifer]]
|
|
238
|
+
|
|
239
|
+
NEW ENTITIES (email — do NOT create):
|
|
240
|
+
- "Random Person" → Skip
|
|
241
|
+
|
|
242
|
+
AMBIGUOUS:
|
|
243
|
+
- "Mike" (no context) → Skip
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Disambiguation priority:** Email match > Organization context > Role match >
|
|
247
|
+
Aliases > Recency.
|
|
248
|
+
|
|
249
|
+
## Step 5: Identify New Entities (Meetings Only)
|
|
250
|
+
|
|
251
|
+
For entities not resolved to existing notes, apply the **"Would I prep for this
|
|
252
|
+
person?"** test:
|
|
253
|
+
|
|
254
|
+
**CREATE a note for:**
|
|
255
|
+
|
|
256
|
+
- Decision makers or key contacts at customers, prospects, partners
|
|
257
|
+
- Investors or potential investors
|
|
258
|
+
- Candidates being interviewed
|
|
259
|
+
- Advisors or mentors with ongoing relationships
|
|
260
|
+
- Introducers who connect you to valuable contacts
|
|
261
|
+
|
|
262
|
+
**DO NOT create notes for:**
|
|
263
|
+
|
|
264
|
+
- Transactional service providers (bank employees, support reps)
|
|
265
|
+
- One-time administrative contacts
|
|
266
|
+
- Large group meeting attendees you didn't interact with
|
|
267
|
+
- Internal colleagues (@user.domain)
|
|
268
|
+
- Assistants handling only logistics
|
|
269
|
+
|
|
270
|
+
For people who don't get their own note, add to the Organization note's
|
|
271
|
+
`## Contacts` section instead.
|
|
272
|
+
|
|
273
|
+
### Role Inference
|
|
274
|
+
|
|
275
|
+
If role is not explicit, infer from context:
|
|
276
|
+
|
|
277
|
+
- Organizer of cross-company meeting → likely senior or partnerships
|
|
278
|
+
- Technical questions → likely engineering
|
|
279
|
+
- Pricing questions → likely procurement or finance
|
|
280
|
+
- "I'll need to check with my team" → manager
|
|
281
|
+
- "I can make that call" → decision maker
|
|
282
|
+
|
|
283
|
+
Format: `**Role:** Product Lead (inferred from evaluation discussions)`
|
|
284
|
+
|
|
285
|
+
## Step 6: Extract Content
|
|
286
|
+
|
|
287
|
+
For each entity that has or will have a note, extract:
|
|
288
|
+
|
|
289
|
+
### Decisions
|
|
290
|
+
|
|
291
|
+
Signals: "We decided...", "We agreed...", "Let's go with...", "Approved",
|
|
292
|
+
"Confirmed"
|
|
293
|
+
|
|
294
|
+
### Commitments
|
|
295
|
+
|
|
296
|
+
Signals: "I'll...", "We'll...", "Can you...", "Please send...", "By Friday"
|
|
297
|
+
Extract: Owner, action, deadline, status (open).
|
|
298
|
+
|
|
299
|
+
### Key Facts
|
|
300
|
+
|
|
301
|
+
Extract **substantive** information only:
|
|
302
|
+
|
|
303
|
+
- Specific numbers (budget, team size, timeline)
|
|
304
|
+
- Preferences or working style
|
|
305
|
+
- Background information
|
|
306
|
+
- Technical requirements
|
|
307
|
+
- What was discussed or proposed
|
|
308
|
+
|
|
309
|
+
**NEVER include:** Meta-commentary about missing data, placeholder text, or data
|
|
310
|
+
quality observations. If no key facts exist, leave the section empty.
|
|
311
|
+
|
|
312
|
+
### Open Items
|
|
313
|
+
|
|
314
|
+
Include commitments and next steps only:
|
|
315
|
+
|
|
316
|
+
```markdown
|
|
317
|
+
- [ ] Send API documentation — by Friday
|
|
318
|
+
- [ ] Schedule follow-up call with CTO
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**NEVER include:** "Find their email", "Add their role", "Research company
|
|
322
|
+
background"
|
|
323
|
+
|
|
324
|
+
### Activity Summary
|
|
325
|
+
|
|
326
|
+
One line per source:
|
|
327
|
+
|
|
328
|
+
```markdown
|
|
329
|
+
- **2025-01-15** (meeting): Kickoff for [[Projects/Acme Integration]]. [[People/David Kim]] needs API access.
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Always use canonical names with absolute paths (`[[People/Name]]`,
|
|
333
|
+
`[[Organizations/Name]]`).
|
|
334
|
+
|
|
335
|
+
### Summary
|
|
336
|
+
|
|
337
|
+
2-3 sentences answering: "Who is this person and why do I know them?" Focus on
|
|
338
|
+
the relationship, not the communication method.
|
|
339
|
+
|
|
340
|
+
**Good:** "VP Engineering at [[Organizations/Acme Corp]] leading the
|
|
341
|
+
[[Projects/Acme Integration]] pilot." **Bad:** "Attendee on the scheduled
|
|
342
|
+
meeting (Aug 12, 2024)."
|
|
343
|
+
|
|
344
|
+
## Step 7: Detect State Changes
|
|
345
|
+
|
|
346
|
+
Review extracted content for signals that existing note fields need updating:
|
|
347
|
+
|
|
348
|
+
### Project Status Changes
|
|
349
|
+
|
|
350
|
+
| Signal | New Status |
|
|
351
|
+
| ------------------------------------- | ---------- |
|
|
352
|
+
| "approved" / "signed" / "green light" | active |
|
|
353
|
+
| "on hold" / "pausing" / "delayed" | on hold |
|
|
354
|
+
| "cancelled" / "not proceeding" | cancelled |
|
|
355
|
+
| "launched" / "completed" / "shipped" | completed |
|
|
356
|
+
| "exploring" / "considering" | planning |
|
|
357
|
+
|
|
358
|
+
### Open Item Resolution
|
|
359
|
+
|
|
360
|
+
| Signal | Action |
|
|
361
|
+
| ---------------------------- | --------------- |
|
|
362
|
+
| "Here's the X you requested" | Mark X complete |
|
|
363
|
+
| "I've sent the X" | Mark X complete |
|
|
364
|
+
| "X is done" / "X is ready" | Mark X complete |
|
|
365
|
+
|
|
366
|
+
Change `- [ ]` to `- [x]` with completion date.
|
|
367
|
+
|
|
368
|
+
### Role/Title Changes
|
|
369
|
+
|
|
370
|
+
- New title in email signature
|
|
371
|
+
- "I've been promoted to..."
|
|
372
|
+
- Different role than what's in the note
|
|
373
|
+
|
|
374
|
+
### Relationship Changes
|
|
375
|
+
|
|
376
|
+
- "I've joined [New Company]"
|
|
377
|
+
- "We signed the contract" → prospect → customer
|
|
378
|
+
- New email domain for known person
|
|
379
|
+
|
|
380
|
+
**Be conservative:** Only apply clear, unambiguous state changes. If uncertain,
|
|
381
|
+
add to activity log but don't change fields.
|
|
382
|
+
|
|
383
|
+
Log state changes in activity with `[Field → value]` notation:
|
|
384
|
+
|
|
385
|
+
```markdown
|
|
386
|
+
- **2025-01-20** (email): Leadership approved pilot. [Status → active]
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Step 8: Check for Duplicates
|
|
390
|
+
|
|
391
|
+
Before writing:
|
|
392
|
+
|
|
393
|
+
- Check activity log for existing entries on this date from this source
|
|
394
|
+
- Compare key facts against existing — skip duplicates
|
|
395
|
+
- Check open items — don't add same item twice
|
|
396
|
+
- If new info contradicts existing, note both versions with "(needs
|
|
397
|
+
clarification)"
|
|
398
|
+
|
|
399
|
+
## Step 9: Write Updates
|
|
400
|
+
|
|
401
|
+
**Write one file at a time. Do not batch writes.**
|
|
402
|
+
|
|
403
|
+
### For NEW entities (meetings only)
|
|
404
|
+
|
|
405
|
+
Create the note file using the templates in
|
|
406
|
+
[references/TEMPLATES.md](references/TEMPLATES.md).
|
|
407
|
+
|
|
408
|
+
### For EXISTING entities
|
|
409
|
+
|
|
410
|
+
Read the current note, then apply targeted edits:
|
|
411
|
+
|
|
412
|
+
- Add new activity entry at the TOP of the Activity section (reverse
|
|
413
|
+
chronological)
|
|
414
|
+
- Update Last seen date
|
|
415
|
+
- Add new key facts (if not duplicates)
|
|
416
|
+
- Update open items (mark completed, add new ones)
|
|
417
|
+
- Apply state changes to fields
|
|
418
|
+
|
|
419
|
+
Use precise edits — don't rewrite the entire file.
|
|
420
|
+
|
|
421
|
+
## Step 10: Ensure Bidirectional Links
|
|
422
|
+
|
|
423
|
+
After writing, verify links go both ways:
|
|
424
|
+
|
|
425
|
+
| If you add... | Then also add... |
|
|
426
|
+
| ---------------------- | -------------------------------------------- |
|
|
427
|
+
| Person → Organization | Organization → Person (in People section) |
|
|
428
|
+
| Person → Project | Project → Person (in People section) |
|
|
429
|
+
| Project → Organization | Organization → Project (in Projects section) |
|
|
430
|
+
|
|
431
|
+
Always use absolute links: `[[People/Sarah Chen]]`,
|
|
432
|
+
`[[Organizations/Acme Corp]]`, `[[Projects/Acme Integration]]`.
|
|
433
|
+
|
|
434
|
+
## Step 11: Update Graph State
|
|
435
|
+
|
|
436
|
+
After processing each file, update the state:
|
|
437
|
+
|
|
438
|
+
python3 scripts/state.py update "$FILE"
|
|
439
|
+
|
|
440
|
+
## Source Type Rules Summary
|
|
441
|
+
|
|
442
|
+
| Source Type | Creates Notes? | Updates Notes? | Detects State Changes? |
|
|
443
|
+
| ----------------------- | --------------- | -------------- | ---------------------- |
|
|
444
|
+
| Calendar event | No | Yes (always) | Yes |
|
|
445
|
+
| Meeting | Yes | Yes | Yes |
|
|
446
|
+
| Voice memo | Yes | Yes | Yes |
|
|
447
|
+
| Ad-hoc document | Yes | Yes | Yes |
|
|
448
|
+
| Email (known contact) | No | Yes | Yes |
|
|
449
|
+
| Email (unknown contact) | No (SKIP) | No | No |
|
|
450
|
+
| Email (warm intro) | Yes (exception) | Yes | Yes |
|
|
451
|
+
|
|
452
|
+
## Quality Checklist
|
|
453
|
+
|
|
454
|
+
Before completing, verify:
|
|
455
|
+
|
|
456
|
+
- [ ] Correctly identified source as meeting or email
|
|
457
|
+
- [ ] Applied correct rules (meetings create, emails only update)
|
|
458
|
+
- [ ] Excluded self and @user.domain from entity extraction
|
|
459
|
+
- [ ] Applied "Would I prep?" test to each person
|
|
460
|
+
- [ ] Used absolute paths `[[Folder/Name]]` in ALL links
|
|
461
|
+
- [ ] Summaries describe relationship, not communication method
|
|
462
|
+
- [ ] Key facts are substantive (no filler)
|
|
463
|
+
- [ ] Open items are commitments (no "find their email" tasks)
|
|
464
|
+
- [ ] State changes logged with `[Field → value]` notation
|
|
465
|
+
- [ ] Bidirectional links are consistent
|
|
466
|
+
- [ ] Graph state updated for processed files
|