@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,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: right-to-be-forgotten
|
|
3
|
+
description: >
|
|
4
|
+
Process GDPR Article 17 data erasure requests. Finds and removes all personal
|
|
5
|
+
data related to a named individual from the knowledge base, cached data, and
|
|
6
|
+
agent state files. Use when the user receives a right-to-be-forgotten request,
|
|
7
|
+
asks to delete all data about a person, or needs to comply with a data
|
|
8
|
+
erasure obligation.
|
|
9
|
+
compatibility: Requires macOS filesystem access
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Right to Be Forgotten
|
|
13
|
+
|
|
14
|
+
Process data erasure requests under GDPR Article 17 (Right to Erasure). Given a
|
|
15
|
+
person's name, systematically find and remove all personal data from the
|
|
16
|
+
knowledge base, cached synced data, and agent state files.
|
|
17
|
+
|
|
18
|
+
This skill produces an **erasure report** documenting what was found, what was
|
|
19
|
+
deleted, and what requires manual action — providing an audit trail for
|
|
20
|
+
compliance.
|
|
21
|
+
|
|
22
|
+
## Trigger
|
|
23
|
+
|
|
24
|
+
Run this skill:
|
|
25
|
+
|
|
26
|
+
- When the user receives a formal GDPR erasure request
|
|
27
|
+
- When the user asks to delete all data about a specific person
|
|
28
|
+
- When a candidate withdraws from a recruitment process and requests data
|
|
29
|
+
deletion
|
|
30
|
+
- When the user asks to "forget" someone
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
- The person's full name (and any known aliases or email addresses)
|
|
35
|
+
- User confirmation before deletion proceeds
|
|
36
|
+
|
|
37
|
+
## Inputs
|
|
38
|
+
|
|
39
|
+
- **Name**: Full name of the data subject (required)
|
|
40
|
+
- **Aliases**: Alternative names, maiden names, nicknames (optional)
|
|
41
|
+
- **Email addresses**: Known email addresses (optional, improves search coverage)
|
|
42
|
+
- **Scope**: `all` (default) or `recruitment-only` (limits to candidate data)
|
|
43
|
+
|
|
44
|
+
## Outputs
|
|
45
|
+
|
|
46
|
+
- `knowledge/Erasure/{Name}--{YYYY-MM-DD}.md` — erasure report (audit trail)
|
|
47
|
+
- Deleted files and redacted references across the knowledge base
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Step 0: Confirm Intent
|
|
52
|
+
|
|
53
|
+
Before proceeding, clearly state to the user:
|
|
54
|
+
|
|
55
|
+
> **Data erasure request for: {Name}**
|
|
56
|
+
>
|
|
57
|
+
> This will permanently delete all personal data related to {Name} from:
|
|
58
|
+
> - Knowledge base notes (People, Candidates, Organizations mentions)
|
|
59
|
+
> - Cached email threads and attachments
|
|
60
|
+
> - Agent state and triage files
|
|
61
|
+
>
|
|
62
|
+
> This action cannot be undone. Proceed?
|
|
63
|
+
|
|
64
|
+
**Wait for explicit confirmation before continuing.**
|
|
65
|
+
|
|
66
|
+
## Step 1: Discovery — Find All References
|
|
67
|
+
|
|
68
|
+
Search systematically across every data location. Record every match.
|
|
69
|
+
|
|
70
|
+
### 1a. Knowledge Base — Direct Notes
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Candidate directory (recruitment data)
|
|
74
|
+
ls -d "knowledge/Candidates/{Name}/" 2>/dev/null
|
|
75
|
+
|
|
76
|
+
# People note
|
|
77
|
+
ls "knowledge/People/{Name}.md" 2>/dev/null
|
|
78
|
+
|
|
79
|
+
# Try common name variations
|
|
80
|
+
ls "knowledge/People/{First} {Last}.md" 2>/dev/null
|
|
81
|
+
ls "knowledge/People/{Last}, {First}.md" 2>/dev/null
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 1b. Knowledge Base — Backlinks and Mentions
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Search for all mentions across the entire knowledge graph
|
|
88
|
+
rg -l "{Name}" knowledge/
|
|
89
|
+
rg -l "{First name} {Last name}" knowledge/
|
|
90
|
+
|
|
91
|
+
# Search for Obsidian-style links
|
|
92
|
+
rg -l "\[\[.*{Name}.*\]\]" knowledge/
|
|
93
|
+
|
|
94
|
+
# Search by email address if known
|
|
95
|
+
rg -l "{email}" knowledge/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 1c. Cached Data — Email Threads
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Search synced email threads for mentions
|
|
102
|
+
rg -l "{Name}" ~/.cache/fit/basecamp/apple_mail/ 2>/dev/null
|
|
103
|
+
rg -l "{email}" ~/.cache/fit/basecamp/apple_mail/ 2>/dev/null
|
|
104
|
+
|
|
105
|
+
# Check for attachment directories containing their files
|
|
106
|
+
find ~/.cache/fit/basecamp/apple_mail/attachments/ -iname "*{Name}*" 2>/dev/null
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 1d. Cached Data — Calendar Events
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Search calendar events
|
|
113
|
+
rg -l "{Name}" ~/.cache/fit/basecamp/apple_calendar/ 2>/dev/null
|
|
114
|
+
rg -l "{email}" ~/.cache/fit/basecamp/apple_calendar/ 2>/dev/null
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 1e. Agent State Files
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Search triage files for mentions
|
|
121
|
+
rg -l "{Name}" ~/.cache/fit/basecamp/state/ 2>/dev/null
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 1f. Drafts
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Search email drafts
|
|
128
|
+
rg -l "{Name}" drafts/ 2>/dev/null
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Compile a complete inventory of every file and reference found.
|
|
132
|
+
|
|
133
|
+
## Step 2: Classify References
|
|
134
|
+
|
|
135
|
+
For each discovered reference, classify the required action:
|
|
136
|
+
|
|
137
|
+
| Reference Type | Action | Example |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| **Dedicated note** (sole subject) | Delete entire file | `knowledge/People/{Name}.md` |
|
|
140
|
+
| **Dedicated directory** | Delete entire directory | `knowledge/Candidates/{Name}/` |
|
|
141
|
+
| **Mention in another note** | Redact: remove lines referencing the person | Backlink in `knowledge/Organizations/Agency.md` |
|
|
142
|
+
| **Email thread** (sole subject) | Delete file | `~/.cache/fit/basecamp/apple_mail/thread.md` |
|
|
143
|
+
| **Email thread** (multiple people) | Redact: remove paragraphs about the person | Thread discussing multiple candidates |
|
|
144
|
+
| **Attachment** (their CV, etc.) | Delete file | `attachments/{thread}/CV.pdf` |
|
|
145
|
+
| **Triage/state file** | Redact: remove lines mentioning them | `recruiter_triage.md` |
|
|
146
|
+
| **Insights file** | Redact: remove bullets mentioning them | `knowledge/Candidates/Insights.md` |
|
|
147
|
+
|
|
148
|
+
## Step 3: Execute Deletions
|
|
149
|
+
|
|
150
|
+
Process in order from most specific to most general:
|
|
151
|
+
|
|
152
|
+
### 3a. Delete Dedicated Files and Directories
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Remove candidate directory (CV, brief, assessment — everything)
|
|
156
|
+
rm -rf "knowledge/Candidates/{Name}/"
|
|
157
|
+
|
|
158
|
+
# Remove people note
|
|
159
|
+
rm -f "knowledge/People/{Name}.md"
|
|
160
|
+
|
|
161
|
+
# Remove any attachments
|
|
162
|
+
find ~/.cache/fit/basecamp/apple_mail/attachments/ -iname "*{Name}*" -delete
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 3b. Redact Mentions in Other Notes
|
|
166
|
+
|
|
167
|
+
For each file that **mentions** the person but isn't dedicated to them:
|
|
168
|
+
|
|
169
|
+
1. Read the file
|
|
170
|
+
2. Remove lines, bullets, or sections that reference the person
|
|
171
|
+
3. Remove broken `[[backlinks]]` to deleted notes
|
|
172
|
+
4. Write the updated file
|
|
173
|
+
|
|
174
|
+
**Redaction rules:**
|
|
175
|
+
|
|
176
|
+
- Remove entire bullet points that mention the person by name
|
|
177
|
+
- Remove table rows containing the person's name
|
|
178
|
+
- Remove `## Connected to` entries linking to their deleted note
|
|
179
|
+
- If a section becomes empty after redaction, remove the section header too
|
|
180
|
+
- Do NOT remove surrounding context that doesn't identify the person
|
|
181
|
+
|
|
182
|
+
### 3c. Handle Email Threads
|
|
183
|
+
|
|
184
|
+
For threads where the person is the **sole subject** (e.g., a recruitment email
|
|
185
|
+
about only them):
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
rm -f "~/.cache/fit/basecamp/apple_mail/{thread}.md"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
For threads with **multiple people**, redact only the paragraphs about this
|
|
192
|
+
person — leave the rest intact.
|
|
193
|
+
|
|
194
|
+
### 3d. Clean Agent State
|
|
195
|
+
|
|
196
|
+
Remove mentions from triage files:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Regenerate triage files on next agent wake — just remove current mentions
|
|
200
|
+
for f in ~/.cache/fit/basecamp/state/*_triage.md; do
|
|
201
|
+
if rg -q "{Name}" "$f" 2>/dev/null; then
|
|
202
|
+
# Read, remove lines mentioning the person, write back
|
|
203
|
+
rg -v "{Name}" "$f" > "$f.tmp" && mv "$f.tmp" "$f"
|
|
204
|
+
fi
|
|
205
|
+
done
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 3e. Clean Processing State
|
|
209
|
+
|
|
210
|
+
Remove entries from the graph_processed state so deleted files aren't
|
|
211
|
+
incorrectly tracked:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Remove processed-file entries for deleted paths
|
|
215
|
+
rg -v "{deleted_path}" ~/.cache/fit/basecamp/state/graph_processed \
|
|
216
|
+
> ~/.cache/fit/basecamp/state/graph_processed.tmp \
|
|
217
|
+
&& mv ~/.cache/fit/basecamp/state/graph_processed.tmp \
|
|
218
|
+
~/.cache/fit/basecamp/state/graph_processed
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Step 4: Write Erasure Report
|
|
222
|
+
|
|
223
|
+
Create the audit trail at `knowledge/Erasure/{Name}--{YYYY-MM-DD}.md`:
|
|
224
|
+
|
|
225
|
+
```markdown
|
|
226
|
+
# Data Erasure Report — {Full Name}
|
|
227
|
+
|
|
228
|
+
**Date:** {YYYY-MM-DD HH:MM}
|
|
229
|
+
**Requested by:** {user or "GDPR Article 17 request"}
|
|
230
|
+
**Scope:** {all / recruitment-only}
|
|
231
|
+
|
|
232
|
+
## Data Subject
|
|
233
|
+
- **Name:** {Full Name}
|
|
234
|
+
- **Known aliases:** {aliases or "none"}
|
|
235
|
+
- **Known emails:** {emails or "none"}
|
|
236
|
+
|
|
237
|
+
## Actions Taken
|
|
238
|
+
|
|
239
|
+
### Deleted Files
|
|
240
|
+
- `knowledge/Candidates/{Name}/brief.md`
|
|
241
|
+
- `knowledge/Candidates/{Name}/CV.pdf`
|
|
242
|
+
- `knowledge/Candidates/{Name}/assessment.md`
|
|
243
|
+
- `knowledge/People/{Name}.md`
|
|
244
|
+
- {list all deleted files}
|
|
245
|
+
|
|
246
|
+
### Redacted References
|
|
247
|
+
- `knowledge/Organizations/{Agency}.md` — removed backlink
|
|
248
|
+
- `knowledge/Candidates/Insights.md` — removed {N} bullet(s)
|
|
249
|
+
- {list all redacted files and what was removed}
|
|
250
|
+
|
|
251
|
+
### Cached Data Removed
|
|
252
|
+
- `~/.cache/fit/basecamp/apple_mail/{thread}.md` — deleted (sole subject)
|
|
253
|
+
- `~/.cache/fit/basecamp/apple_mail/{thread2}.md` — redacted (multi-person)
|
|
254
|
+
- {list all cache actions}
|
|
255
|
+
|
|
256
|
+
### State Files Cleaned
|
|
257
|
+
- `~/.cache/fit/basecamp/state/recruiter_triage.md` — redacted
|
|
258
|
+
- {list all state file actions}
|
|
259
|
+
|
|
260
|
+
## Requires Manual Action
|
|
261
|
+
|
|
262
|
+
The following data sources are outside this tool's reach:
|
|
263
|
+
|
|
264
|
+
- **Apple Mail** — original emails remain in the user's mailbox. Search for
|
|
265
|
+
"{Name}" in Mail.app and delete threads manually.
|
|
266
|
+
- **Apple Calendar** — original events remain. Check Calendar.app for events
|
|
267
|
+
mentioning "{Name}".
|
|
268
|
+
- **Recruitment agencies** — notify {Agency} that the candidate's data has been
|
|
269
|
+
deleted and request they do the same.
|
|
270
|
+
- **Interview notes** — check physical notebooks or other apps for handwritten
|
|
271
|
+
or external notes.
|
|
272
|
+
- **Shared documents** — check Google Drive, SharePoint, or other shared
|
|
273
|
+
platforms for documents mentioning the person.
|
|
274
|
+
|
|
275
|
+
## Verification
|
|
276
|
+
|
|
277
|
+
After erasure, verify no traces remain:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
rg "{Name}" knowledge/ ~/.cache/fit/basecamp/
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Expected result: no matches (except this erasure report).
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**IMPORTANT:** The erasure report itself must NOT contain personal data beyond
|
|
287
|
+
the name and the fact that data was deleted. Do not copy CV content, skill
|
|
288
|
+
assessments, or candidate details into the report. Record only what was deleted,
|
|
289
|
+
not what it contained.
|
|
290
|
+
|
|
291
|
+
## Step 5: Verify
|
|
292
|
+
|
|
293
|
+
Run a final search to confirm no references were missed:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
rg "{Name}" knowledge/ ~/.cache/fit/basecamp/ drafts/
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The only match should be the erasure report itself. If other matches remain,
|
|
300
|
+
process them and update the report.
|
|
301
|
+
|
|
302
|
+
## Scope Variants
|
|
303
|
+
|
|
304
|
+
### recruitment-only
|
|
305
|
+
|
|
306
|
+
When scope is `recruitment-only`, limit erasure to:
|
|
307
|
+
|
|
308
|
+
- `knowledge/Candidates/{Name}/` directory
|
|
309
|
+
- `knowledge/Candidates/Insights.md` mentions
|
|
310
|
+
- Recruitment-related email threads (from known agency domains)
|
|
311
|
+
- `recruiter_triage.md` state file
|
|
312
|
+
|
|
313
|
+
Leave `knowledge/People/{Name}.md` and general knowledge graph references
|
|
314
|
+
intact — the person may be a colleague or contact outside of recruitment.
|
|
315
|
+
|
|
316
|
+
### all (default)
|
|
317
|
+
|
|
318
|
+
Full erasure across all knowledge base locations, cached data, and state files.
|
|
319
|
+
|
|
320
|
+
## Quality Checklist
|
|
321
|
+
|
|
322
|
+
- [ ] User confirmed intent before any deletion
|
|
323
|
+
- [ ] Searched all data locations (knowledge, cache, state, drafts)
|
|
324
|
+
- [ ] All dedicated files/directories deleted
|
|
325
|
+
- [ ] All backlinks and mentions redacted from other notes
|
|
326
|
+
- [ ] Cached email threads and attachments handled
|
|
327
|
+
- [ ] Agent state files cleaned
|
|
328
|
+
- [ ] Processing state updated for deleted files
|
|
329
|
+
- [ ] Erasure report created with full audit trail
|
|
330
|
+
- [ ] Report does NOT contain personal data (only file paths and actions)
|
|
331
|
+
- [ ] Manual action items listed (Mail.app, Calendar.app, agencies)
|
|
332
|
+
- [ ] Final verification search shows no remaining references
|
|
333
|
+
- [ ] Broken backlinks cleaned up in referencing notes
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: send-chat
|
|
3
|
+
description: Send messages to people via chat platforms (e.g. Microsoft Teams, Slack) using browser automation. Resolves people by name using the knowledge graph, drafts messages for approval, and sends via the web app. Use when the user asks to message, ping, or chat with someone.
|
|
4
|
+
compatibility:
|
|
5
|
+
requires:
|
|
6
|
+
- browser-automation
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Send Chat
|
|
10
|
+
|
|
11
|
+
Send chat messages to people using browser automation against a web-based chat
|
|
12
|
+
platform (Microsoft Teams, Slack, or similar). Resolves recipients by name from
|
|
13
|
+
the knowledge graph so the user can say "message Sarah about the standup"
|
|
14
|
+
without needing exact display names.
|
|
15
|
+
|
|
16
|
+
## Trigger
|
|
17
|
+
|
|
18
|
+
Run when the user asks to:
|
|
19
|
+
|
|
20
|
+
- Send a message on Teams / Slack / chat
|
|
21
|
+
- Ping / chat / DM someone
|
|
22
|
+
- Follow up with someone via chat
|
|
23
|
+
- Send a message about a topic
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
- Chat platform web app open and authenticated in the browser
|
|
28
|
+
- Browser automation available (e.g. Chrome MCP, Playwright)
|
|
29
|
+
- Knowledge base populated with people notes
|
|
30
|
+
|
|
31
|
+
## Critical: Always Look Up Context First
|
|
32
|
+
|
|
33
|
+
**BEFORE messaging anyone, you MUST look up the person in the knowledge base.**
|
|
34
|
+
|
|
35
|
+
When the user mentions ANY person:
|
|
36
|
+
|
|
37
|
+
1. **STOP** — Do not open the chat platform yet
|
|
38
|
+
2. **SEARCH** — Look them up: `rg -l "{name}" knowledge/People/`
|
|
39
|
+
3. **READ** — Read their note to understand context, role, recent interactions
|
|
40
|
+
4. **UNDERSTAND** — Know who they are, what you've been working on together
|
|
41
|
+
5. **THEN PROCEED** — Only now compose the message and use browser automation
|
|
42
|
+
|
|
43
|
+
This context is essential for:
|
|
44
|
+
|
|
45
|
+
- Finding the right person if the name is ambiguous
|
|
46
|
+
- Drafting an appropriate message if the user gave a loose prompt
|
|
47
|
+
- Knowing the person's role and relationship for tone
|
|
48
|
+
|
|
49
|
+
## Resolving People
|
|
50
|
+
|
|
51
|
+
The user will refer to people by first name, last name, or nickname. Resolve to
|
|
52
|
+
a full name using the knowledge graph:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Find person by partial name
|
|
56
|
+
rg -l -i "{name}" knowledge/People/
|
|
57
|
+
|
|
58
|
+
# If ambiguous, read candidates to disambiguate
|
|
59
|
+
cat "knowledge/People/{Candidate}.md"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**If ambiguous** (multiple matches), ask the user which person they mean — list
|
|
63
|
+
the matches with roles/orgs to help them pick.
|
|
64
|
+
|
|
65
|
+
**If no match**, tell the user you don't have this person in the knowledge base
|
|
66
|
+
and ask for their full name as it appears in the chat platform.
|
|
67
|
+
|
|
68
|
+
## Composing the Message
|
|
69
|
+
|
|
70
|
+
**Every message MUST be drafted as a text file first.** This ensures the user
|
|
71
|
+
can review and edit the exact message before it's sent.
|
|
72
|
+
|
|
73
|
+
### Draft Workflow
|
|
74
|
+
|
|
75
|
+
1. **Compose the message** based on context and user intent.
|
|
76
|
+
2. **Write it to a draft file** at `drafts/chat-{recipient-slug}-{date}.md`
|
|
77
|
+
- `{recipient-slug}` = lowercase, hyphenated full name (e.g. `sarah-chen`)
|
|
78
|
+
- `{date}` = ISO date (e.g. `2026-02-19`)
|
|
79
|
+
3. **Show the user the draft** — display the file path and contents.
|
|
80
|
+
4. **Wait for approval** — the user may edit the file or ask for changes.
|
|
81
|
+
5. **Only after approval**, proceed to send.
|
|
82
|
+
|
|
83
|
+
**Draft file format:**
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
To: {Full Name}
|
|
87
|
+
Via: {Platform name}
|
|
88
|
+
Date: {YYYY-MM-DD}
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
{message body}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The message body (everything below the `---` separator) is what gets pasted into
|
|
96
|
+
the chat.
|
|
97
|
+
|
|
98
|
+
**Message guidelines:**
|
|
99
|
+
|
|
100
|
+
- Match the user's usual tone — casual for peers, professional for leadership
|
|
101
|
+
- Keep it concise — chat is informal, not email
|
|
102
|
+
- Reference specific context naturally (project names, recent decisions)
|
|
103
|
+
- If the user provides exact wording, use it verbatim
|
|
104
|
+
- If the user said "ping {name}" without detail, ask what they want to say
|
|
105
|
+
- Draft one message based on context — don't offer multiple options
|
|
106
|
+
- **Keep messages on a single line with no formatting.** No line breaks, no
|
|
107
|
+
markdown. Use inline separators (e.g. `•`, `—`) to keep structure. Multi-line
|
|
108
|
+
formatting is unreliable via browser automation.
|
|
109
|
+
|
|
110
|
+
## Browser Automation Flow
|
|
111
|
+
|
|
112
|
+
Once the user has approved the draft, send it as a **single submission** — paste
|
|
113
|
+
the entire message at once rather than typing line by line.
|
|
114
|
+
|
|
115
|
+
### Step 1: Identify the Chat Platform
|
|
116
|
+
|
|
117
|
+
Check which platform is available:
|
|
118
|
+
|
|
119
|
+
- Look for an open tab matching the configured chat URL
|
|
120
|
+
- If no tab is open, ask the user which platform to use and navigate to it
|
|
121
|
+
|
|
122
|
+
### Step 2: Open a Chat with the Recipient
|
|
123
|
+
|
|
124
|
+
1. Use the platform's search or "New chat" feature
|
|
125
|
+
2. Type the recipient's full name
|
|
126
|
+
3. Wait for search results to populate (take a screenshot to verify)
|
|
127
|
+
4. Click the correct person from the results
|
|
128
|
+
|
|
129
|
+
If the person doesn't appear in search, inform the user — they may not be in the
|
|
130
|
+
same organization.
|
|
131
|
+
|
|
132
|
+
### Step 3: Send the Approved Message
|
|
133
|
+
|
|
134
|
+
1. Read the approved draft file to get the message body (below the `---`)
|
|
135
|
+
2. Click the message compose box
|
|
136
|
+
3. Paste the entire message as a single submission
|
|
137
|
+
4. Press Enter or click Send
|
|
138
|
+
5. Take a screenshot to confirm the message was sent
|
|
139
|
+
|
|
140
|
+
### Step 4: Update Knowledge Graph (Optional)
|
|
141
|
+
|
|
142
|
+
If the message is substantive (not just "hey" or "thanks"), note the interaction
|
|
143
|
+
on the person's knowledge note:
|
|
144
|
+
|
|
145
|
+
```markdown
|
|
146
|
+
- {YYYY-MM-DD}: Messaged on {Platform} re: {topic}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Error Handling
|
|
150
|
+
|
|
151
|
+
- **Platform not loaded / auth required:** Tell the user to sign in first, then
|
|
152
|
+
retry
|
|
153
|
+
- **Person not found in search:** Report back — they may be external or using a
|
|
154
|
+
different display name. Ask the user for the exact name
|
|
155
|
+
- **Chat already open:** If a chat with this person is already visible, use it
|
|
156
|
+
directly
|
|
157
|
+
- **UI not as expected:** Take a screenshot and describe what you see. Don't
|
|
158
|
+
click blindly
|
|
159
|
+
|
|
160
|
+
## Constraints
|
|
161
|
+
|
|
162
|
+
- **Always confirm before sending.** Never send a message without explicit user
|
|
163
|
+
approval — this is a hard requirement
|
|
164
|
+
- **One message at a time.** Don't batch-send to multiple people without
|
|
165
|
+
confirming each one
|
|
166
|
+
- **No file attachments.** This skill handles text messages only
|
|
167
|
+
- **No group chats.** Targets 1:1 chats only
|
|
168
|
+
- **No message deletion or editing.** Once sent, it's sent
|
|
169
|
+
- **Respect ethics rules.** Never send messages that contain personal judgments,
|
|
170
|
+
gossip, or sensitive information per the knowledge base ethics policy
|
|
@@ -37,10 +37,11 @@ their calendar.
|
|
|
37
37
|
|
|
38
38
|
## Implementation
|
|
39
39
|
|
|
40
|
-
Run the sync as a single
|
|
41
|
-
per event for attendees) and handles all data
|
|
40
|
+
Run the sync as a single Node.js script with embedded SQLite. This avoids N+1
|
|
41
|
+
process invocations (one per event for attendees) and handles all data
|
|
42
|
+
transformation in one pass:
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
node scripts/sync.mjs [--days N]
|
|
44
45
|
|
|
45
46
|
- `--days N` — how many days back to sync (default: 30)
|
|
46
47
|
|
|
@@ -97,8 +98,7 @@ Each `{event_id}.json` file:
|
|
|
97
98
|
|
|
98
99
|
## Constraints
|
|
99
100
|
|
|
100
|
-
- Open database read-only (
|
|
101
|
+
- Open database read-only (`readOnly: true`)
|
|
101
102
|
- This sync is stateless — always queries the current sliding window
|
|
102
103
|
- All-day events may have null end times — use start date as end date
|
|
103
104
|
- All-day events have timezone `_float` — omit timezone from output
|
|
104
|
-
- Output format matches Google Calendar event format for downstream consistency
|