@forwardimpact/outpost 3.12.0 → 3.13.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/README.md +2 -2
- package/bin/fit-outpost.js +3 -2
- package/package.json +6 -5
- package/src/agent-path.js +10 -6
- package/src/agent-runner.js +28 -24
- package/src/index.js +2 -3
- package/src/kb-manager.js +75 -20
- package/src/kb-validator.js +762 -0
- package/src/outpost.js +108 -39
- package/src/posture.js +3 -3
- package/src/privilege.js +11 -11
- package/src/scheduler.js +14 -12
- package/src/socket-server.js +19 -18
- package/src/spawn-env.js +7 -6
- package/src/state-manager.js +2 -2
- package/templates/.claude/agents/chief-of-staff.md +32 -24
- package/templates/.claude/agents/concierge.md +26 -17
- package/templates/.claude/agents/head-hunter.md +34 -26
- package/templates/.claude/agents/librarian.md +26 -18
- package/templates/.claude/agents/postman.md +26 -18
- package/templates/.claude/agents/recruiter.md +38 -27
- package/templates/.claude/skills/anarlog-follow/SKILL.md +91 -66
- package/templates/.claude/skills/anarlog-follow/references/coaching.md +22 -21
- package/templates/.claude/skills/anarlog-follow/references/sessions.md +52 -0
- package/templates/.claude/skills/anarlog-process/SKILL.md +108 -62
- package/templates/.claude/skills/anarlog-process/references/extraction.md +41 -29
- package/templates/.claude/skills/anarlog-process/references/sessions.md +92 -58
- package/templates/.claude/skills/anarlog-process/scripts/scan.mjs +402 -160
- package/templates/.claude/skills/candidate-report/SKILL.md +41 -36
- package/templates/.claude/skills/candidate-report/references/report-template.html +10 -10
- package/templates/.claude/skills/candidate-report/references/report.css +4 -4
- package/templates/.claude/skills/candidate-report/references/rubric.md +15 -14
- package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +2 -2
- package/templates/.claude/skills/changelog/SKILL.md +122 -67
- package/templates/.claude/skills/deck-create/SKILL.md +90 -32
- package/templates/.claude/skills/deck-create/references/slide.css +4 -4
- package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +6 -6
- package/templates/.claude/skills/deck-review/SKILL.md +63 -58
- package/templates/.claude/skills/deck-review/assets/slide-annotator.js +58 -49
- package/templates/.claude/skills/deck-summarize/SKILL.md +55 -45
- package/templates/.claude/skills/deck-summarize/references/brief-template.md +5 -4
- package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +8 -7
- package/templates/.claude/skills/doc-collab/SKILL.md +39 -34
- package/templates/.claude/skills/doc-create/SKILL.md +27 -23
- package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +6 -6
- package/templates/.claude/skills/draft-emails/SKILL.md +63 -53
- package/templates/.claude/skills/draft-emails/references/template.md +2 -2
- package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +15 -8
- package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +19 -11
- package/templates/.claude/skills/extract-entities/SKILL.md +76 -75
- package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +6 -5
- package/templates/.claude/skills/extract-entities/references/conditions.md +20 -18
- package/templates/.claude/skills/extract-entities/references/content.md +10 -9
- package/templates/.claude/skills/extract-entities/references/links.md +37 -13
- package/templates/.claude/skills/extract-entities/references/recruitment.md +30 -24
- package/templates/.claude/skills/extract-entities/references/resolution.md +14 -14
- package/templates/.claude/skills/extract-entities/references/sources.md +9 -9
- package/templates/.claude/skills/extract-entities/references/templates-conditions.md +12 -6
- package/templates/.claude/skills/extract-entities/references/templates-people-orgs.md +29 -10
- package/templates/.claude/skills/extract-entities/references/templates-priorities.md +10 -4
- package/templates/.claude/skills/extract-entities/references/templates-projects-topics.md +19 -7
- package/templates/.claude/skills/extract-entities/scripts/state.mjs +9 -8
- package/templates/.claude/skills/meeting-prep/SKILL.md +42 -36
- package/templates/.claude/skills/organize-files/SKILL.md +27 -22
- package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +5 -4
- package/templates/.claude/skills/organize-files/scripts/summarize.mjs +4 -4
- package/templates/.claude/skills/person-identify/SKILL.md +77 -27
- package/templates/.claude/skills/person-identify/scripts/identify.sh +125 -26
- package/templates/.claude/skills/person-lookup/SKILL.md +34 -30
- package/templates/.claude/skills/person-lookup/scripts/lookup.sh +46 -17
- package/templates/.claude/skills/req-assess/SKILL.md +44 -36
- package/templates/.claude/skills/req-assess/references/interview-template.md +7 -2
- package/templates/.claude/skills/req-assess/references/panel-template.md +7 -2
- package/templates/.claude/skills/req-assess/references/rubric.md +13 -13
- package/templates/.claude/skills/req-bundle/SKILL.md +164 -0
- package/templates/.claude/skills/req-bundle/references/matching.md +70 -0
- package/templates/.claude/skills/req-bundle/references/pdf-structure.md +74 -0
- package/templates/.claude/skills/req-bundle/scripts/split-bundle.mjs +377 -0
- package/templates/.claude/skills/req-decide/SKILL.md +52 -41
- package/templates/.claude/skills/req-decide/references/rubric.md +11 -10
- package/templates/.claude/skills/req-decide/references/template.md +2 -2
- package/templates/.claude/skills/req-forget/SKILL.md +58 -39
- package/templates/.claude/skills/req-forget/references/classify.md +15 -13
- package/templates/.claude/skills/req-forget/references/locations.md +27 -22
- package/templates/.claude/skills/req-forget/references/report-template.md +24 -16
- package/templates/.claude/skills/req-scan/SKILL.md +27 -25
- package/templates/.claude/skills/req-scan/references/fallbacks.md +9 -9
- package/templates/.claude/skills/req-scan/references/filters.md +13 -13
- package/templates/.claude/skills/req-scan/references/sources.md +7 -7
- package/templates/.claude/skills/req-scan/references/state.md +1 -1
- package/templates/.claude/skills/req-scan/references/template.md +11 -1
- package/templates/.claude/skills/req-scan/scripts/state.mjs +3 -2
- package/templates/.claude/skills/req-screen/SKILL.md +96 -50
- package/templates/.claude/skills/req-screen/references/rubric.md +46 -50
- package/templates/.claude/skills/req-screen/references/scoring.md +79 -0
- package/templates/.claude/skills/req-screen/references/template.md +29 -17
- package/templates/.claude/skills/req-track/SKILL.md +76 -76
- package/templates/.claude/skills/req-track/references/fields.md +31 -27
- package/templates/.claude/skills/req-track/references/overlays.md +42 -0
- package/templates/.claude/skills/req-track/references/signals.md +12 -12
- package/templates/.claude/skills/req-track/references/statuses.md +3 -2
- package/templates/.claude/skills/req-track/references/templates.md +28 -27
- package/templates/.claude/skills/req-workday/SKILL.md +69 -40
- package/templates/.claude/skills/req-workday/references/brief.md +81 -0
- package/templates/.claude/skills/req-workday/references/status-mapping.md +5 -5
- package/templates/.claude/skills/req-workday/references/templates.md +11 -78
- package/templates/.claude/skills/req-workday/references/xlsx-format.md +26 -7
- package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +113 -35
- package/templates/.claude/skills/send-chat/SKILL.md +58 -54
- package/templates/.claude/skills/sync-apple-calendar/SKILL.md +23 -19
- package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +7 -7
- package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +6 -6
- package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +9 -8
- package/templates/.claude/skills/sync-apple-mail/SKILL.md +20 -14
- package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +10 -10
- package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +9 -9
- package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +16 -13
- package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +3 -3
- package/templates/.claude/skills/sync-teams/SKILL.md +46 -43
- package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +22 -21
- package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +4 -4
- package/templates/.claude/skills/sync-teams/scripts/sync.mjs +4 -4
- package/templates/.claude/skills/upstream-instructions/SKILL.md +43 -41
- package/templates/.claude/skills/upstream-instructions/references/examples.md +5 -5
- package/templates/CLAUDE.md +97 -85
- package/templates/MIGRATION.md +359 -0
- package/templates/registry.yaml +25 -0
- package/templates/.claude/skills/anarlog-follow/scripts/follow.mjs +0 -243
- package/templates/.claude/skills/anarlog-trim/SKILL.md +0 -183
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doc-create
|
|
3
|
-
description: Generate PDF documents from user requests
|
|
3
|
+
description: Generate PDF documents from user requests. Playwright renders the HTML to an A4 PDF. Use when the user asks to create a document, proposal, report, or any multi-page PDF that is not a slide deck. Pulls context from the knowledge base for company info, project details, and people.
|
|
4
4
|
compatibility: Requires Node.js installed. Playwright is installed on first use.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Create Documents
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Write tier: `0-Draft`
|
|
10
|
+
Frontmatter: none
|
|
11
|
+
|
|
12
|
+
Generate multi-page A4 PDF documents from user requests. This skill uses
|
|
13
|
+
Playwright to render self-contained HTML to PDF. It can pull context from the
|
|
14
|
+
knowledge base for company info, project details, and people.
|
|
12
15
|
|
|
13
16
|
## Trigger
|
|
14
17
|
|
|
@@ -18,39 +21,39 @@ submission, brief, or any multi-page PDF that is not a slide deck.
|
|
|
18
21
|
## Prerequisites
|
|
19
22
|
|
|
20
23
|
- Node.js installed
|
|
21
|
-
- Playwright
|
|
24
|
+
- Playwright installs on first use
|
|
22
25
|
|
|
23
26
|
## Inputs
|
|
24
27
|
|
|
25
28
|
- User's description of the document
|
|
26
|
-
- `
|
|
29
|
+
- `3-Team/` — optional context about company, product, team, projects
|
|
27
30
|
|
|
28
31
|
## Outputs
|
|
29
32
|
|
|
30
33
|
- An HTML file and a PDF rendered from it, placed where the user specifies
|
|
31
|
-
(default: `
|
|
34
|
+
(default: `0-Draft/`; the human promotes a finished document into its tier)
|
|
32
35
|
|
|
33
36
|
---
|
|
34
37
|
|
|
35
38
|
## Workflow
|
|
36
39
|
|
|
37
|
-
1. Check `
|
|
40
|
+
1. Check `3-Team/` for relevant context about the company, product, team,
|
|
38
41
|
projects, or people mentioned.
|
|
39
|
-
2.
|
|
42
|
+
2. Make sure Playwright is installed:
|
|
40
43
|
`bun install playwright && bunx playwright install chromium`
|
|
41
44
|
3. Create a self-contained HTML file with all CSS inlined. The HTML must handle
|
|
42
|
-
its own page layout
|
|
45
|
+
its own page layout. See **HTML Document Rules** below.
|
|
43
46
|
4. Run the conversion script:
|
|
44
47
|
|
|
45
48
|
```text
|
|
46
49
|
node .claude/skills/doc-create/scripts/convert-to-pdf.mjs <input.html> [output.pdf]
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
If output
|
|
50
|
-
same name.
|
|
52
|
+
If you omit the output path, the script writes the PDF next to the HTML file
|
|
53
|
+
with the same name.
|
|
51
54
|
5. Read the PDF back to visually verify it renders correctly. Check each page
|
|
52
|
-
for overflow, clipped content, and correct page breaks.
|
|
53
|
-
|
|
55
|
+
for overflow, clipped content, and correct page breaks. If you find a
|
|
56
|
+
problem, fix it and re-render.
|
|
54
57
|
|
|
55
58
|
**Do NOT show HTML code to the user. Just create the PDF and deliver it.**
|
|
56
59
|
|
|
@@ -60,8 +63,9 @@ submission, brief, or any multi-page PDF that is not a slide deck.
|
|
|
60
63
|
|
|
61
64
|
- Each page is a `<div class="page">` sized to exactly 210mm × 297mm (A4)
|
|
62
65
|
- Use `page-break-after: always` on every `.page` except the last
|
|
63
|
-
- Handle margins with padding inside `.page
|
|
64
|
-
|
|
66
|
+
- Handle margins with padding inside `.page` rather than with PDF margin
|
|
67
|
+
settings
|
|
68
|
+
- Playwright renders the PDF with zero margins, so the HTML owns all spacing
|
|
65
69
|
|
|
66
70
|
**Print colours:**
|
|
67
71
|
|
|
@@ -70,16 +74,16 @@ submission, brief, or any multi-page PDF that is not a slide deck.
|
|
|
70
74
|
|
|
71
75
|
**Fonts:**
|
|
72
76
|
|
|
73
|
-
- Use system fonts only
|
|
77
|
+
- Use system fonts only, and do not load an external font
|
|
74
78
|
- Monospace stack: `'SF Mono', 'Menlo', 'Monaco', 'Consolas', monospace`
|
|
75
79
|
- Sans-serif stack:
|
|
76
80
|
`-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`
|
|
77
81
|
|
|
78
|
-
**Content
|
|
82
|
+
**Content fit:**
|
|
79
83
|
|
|
80
84
|
- After the first render, visually check every page for overflow
|
|
81
|
-
- Content must not bleed past the `.page` boundary
|
|
82
|
-
or font sizes
|
|
85
|
+
- Content must not bleed past the `.page` boundary. If it does, reduce spacing
|
|
86
|
+
or font sizes, then re-render
|
|
83
87
|
- Page numbers, if used, must not overlap with content. Position them in a
|
|
84
88
|
corner that has whitespace
|
|
85
89
|
|
|
@@ -93,14 +97,14 @@ submission, brief, or any multi-page PDF that is not a slide deck.
|
|
|
93
97
|
|
|
94
98
|
- Use absolute `file://` paths for local images
|
|
95
99
|
- Inline small images as base64 data URIs when possible
|
|
96
|
-
- Verify images appear in the rendered PDF
|
|
97
|
-
missing images
|
|
100
|
+
- Verify images appear in the rendered PDF, because Playwright can fail silently
|
|
101
|
+
on missing images
|
|
98
102
|
|
|
99
103
|
## Design Principles
|
|
100
104
|
|
|
101
105
|
- Clean, professional typography with clear hierarchy
|
|
102
106
|
- Use monospace for section headers and numbers for a technical/engineering feel
|
|
103
|
-
-
|
|
107
|
+
- Keep tables compact and readable, and right-align monetary values
|
|
104
108
|
- Use colour sparingly: one accent colour, one dark, lots of white space
|
|
105
109
|
- Dark-background panels (timelines, hero sections) create visual contrast
|
|
106
110
|
- Callout boxes with left borders draw attention to key statements
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* Convert an HTML document to A4 PDF
|
|
3
|
+
* Convert an HTML document to an A4 PDF with Playwright.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* zero margins
|
|
7
|
-
* @page rules and page-break-after
|
|
8
|
-
* the
|
|
5
|
+
* The script renders an HTML file into an A4 PDF. It keeps the background
|
|
6
|
+
* colours. It sets zero margins. The script expects the HTML to handle its own
|
|
7
|
+
* page layout through CSS @page rules and page-break-after. Without an output
|
|
8
|
+
* path, the script writes the PDF next to the input file.
|
|
9
9
|
*
|
|
10
10
|
* Requires: bun install playwright && bunx playwright install chromium
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { resolve, dirname, basename, join } from "node:path";
|
|
14
14
|
|
|
15
|
-
const HELP = `convert-to-pdf — render an HTML document to A4 PDF
|
|
15
|
+
const HELP = `convert-to-pdf — render an HTML document to A4 PDF with Playwright
|
|
16
16
|
|
|
17
17
|
Usage: bun scripts/convert-to-pdf.mjs <input.html> [output.pdf] [-h|--help]
|
|
18
18
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: draft-emails
|
|
3
|
-
description: Draft and send email responses
|
|
3
|
+
description: Draft and send email responses with context from the knowledge base and calendar. Use when the user asks to draft, reply to, respond to, or send an email.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Draft Emails
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Write tier: `0-Draft`
|
|
9
|
+
Frontmatter: none
|
|
10
|
+
|
|
11
|
+
Draft and send email responses. Use the knowledge base and calendar for full
|
|
12
|
+
context on every person and conversation. Every draft needs explicit user
|
|
13
|
+
approval before you send it.
|
|
11
14
|
|
|
12
15
|
## Trigger
|
|
13
16
|
|
|
@@ -20,32 +23,36 @@ The user asks to draft, reply to, respond to, or send an email.
|
|
|
20
23
|
|
|
21
24
|
## Data locations
|
|
22
25
|
|
|
23
|
-
| Data | Location
|
|
24
|
-
| --------------- |
|
|
25
|
-
| People | `
|
|
26
|
-
| Organizations | `
|
|
27
|
-
| Email threads | `~/.cache/fit/outpost/apple_mail/*.md`
|
|
28
|
-
| Calendar events | `~/.cache/fit/outpost/apple_calendar/*.json`
|
|
29
|
-
| Handled IDs |
|
|
30
|
-
| Ignored IDs |
|
|
31
|
-
| Draft files | `
|
|
26
|
+
| Data | Location |
|
|
27
|
+
| --------------- | --------------------------------------------------- |
|
|
28
|
+
| People | `3-Team/People/*.md` |
|
|
29
|
+
| Organizations | `3-Team/Organizations/*.md` |
|
|
30
|
+
| Email threads | `~/.cache/fit/outpost/apple_mail/*.md` |
|
|
31
|
+
| Calendar events | `~/.cache/fit/outpost/apple_calendar/*.json` |
|
|
32
|
+
| Handled IDs | `~/.cache/fit/outpost/drafts/handled` (one ID/line) |
|
|
33
|
+
| Ignored IDs | `~/.cache/fit/outpost/drafts/ignored` (one ID/line) |
|
|
34
|
+
| Draft files | `0-Draft/{email_id}_draft.md` |
|
|
32
35
|
|
|
33
36
|
`handled` and `ignored` both exclude threads from `scan-emails.mjs`. Use
|
|
34
|
-
`handled` for resolved threads (sent here, replied manually, resolved
|
|
35
|
-
`ignored` for threads that need no response (newsletters, spam,
|
|
36
|
-
reply).
|
|
37
|
+
`handled` for resolved threads (sent here, replied manually, resolved through a
|
|
38
|
+
DM). Use `ignored` for threads that need no response (newsletters, spam,
|
|
39
|
+
outbound with no reply). The ledgers are agent state, not knowledge. They live
|
|
40
|
+
in the cache `drafts/` directory, never in `state/` (a daemon-owned trust
|
|
41
|
+
root).
|
|
37
42
|
|
|
38
43
|
<do_confirm_checklist goal="Verify a draft is safe and ready before sending">
|
|
39
44
|
|
|
40
|
-
- [ ]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- [ ]
|
|
45
|
-
|
|
46
|
-
- [ ]
|
|
47
|
-
|
|
48
|
-
- [ ]
|
|
45
|
+
- [ ] You looked up the sender and the organization in `3-Team/` before you
|
|
46
|
+
drafted.
|
|
47
|
+
- [ ] The draft is a single email (not multiple variants). It matches the
|
|
48
|
+
incoming tone.
|
|
49
|
+
- [ ] The body has no sign-off, name, or "Best". The Apple Mail signature
|
|
50
|
+
handles it.
|
|
51
|
+
- [ ] In a recruitment thread, the candidate is excluded from internal
|
|
52
|
+
recipients. Any direct-to-candidate draft carries `⚠️ RECRUITER ONLY`.
|
|
53
|
+
- [ ] The draft includes no sensitive personal data (health, politics, etc.).
|
|
54
|
+
- [ ] The user explicitly approved the draft before any send.
|
|
55
|
+
- [ ] Send used `--draft <path>` so cleanup and the `handled` ledger happen
|
|
49
56
|
automatically.
|
|
50
57
|
|
|
51
58
|
</do_confirm_checklist>
|
|
@@ -58,65 +65,67 @@ reply).
|
|
|
58
65
|
node scripts/scan-emails.mjs
|
|
59
66
|
```
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
`
|
|
68
|
+
The script outputs `email_id<TAB>subject` for unprocessed emails (those not in
|
|
69
|
+
the `handled` or `ignored` ledger).
|
|
63
70
|
|
|
64
71
|
### 2. Classify
|
|
65
72
|
|
|
66
|
-
**Ignore** (append ID to
|
|
67
|
-
notifications, spam, outbound with no reply.
|
|
73
|
+
**Ignore** (append the ID to `~/.cache/fit/outpost/drafts/ignored`):
|
|
74
|
+
newsletters, marketing, automated notifications, spam, outbound with no reply.
|
|
68
75
|
|
|
69
76
|
**Draft a response**: meeting requests, personal mail from known contacts,
|
|
70
77
|
business inquiries or follow-ups, requests for information or action.
|
|
71
78
|
|
|
72
|
-
Be conservative with ignore
|
|
79
|
+
Be conservative with ignore. When in doubt, draft.
|
|
73
80
|
|
|
74
81
|
### 3. Gather context
|
|
75
82
|
|
|
76
|
-
Before
|
|
83
|
+
Before you draft, look up the sender and the organization in the graph:
|
|
77
84
|
|
|
78
85
|
```bash
|
|
79
|
-
rg -l "sender_name"
|
|
80
|
-
cat "
|
|
81
|
-
cat "
|
|
86
|
+
rg -l "sender_name" [0-9]-*/
|
|
87
|
+
cat "3-Team/People/Sender Name.md"
|
|
88
|
+
cat "3-Team/Organizations/Company Name.md"
|
|
82
89
|
```
|
|
83
90
|
|
|
84
|
-
For
|
|
91
|
+
For an email that arranges a meeting time, also read the relevant calendar
|
|
92
|
+
event:
|
|
85
93
|
|
|
86
94
|
```bash
|
|
87
95
|
ls ~/.cache/fit/outpost/apple_calendar/ 2>/dev/null
|
|
88
96
|
cat "$HOME/.cache/fit/outpost/apple_calendar/event123.json"
|
|
89
97
|
```
|
|
90
98
|
|
|
91
|
-
Extract role, organization, relationship history, and open items. If intent
|
|
92
|
-
unclear or the person has multiple contexts, **ask** rather than guess.
|
|
99
|
+
Extract role, organization, relationship history, and open items. If the intent
|
|
100
|
+
is unclear or the person has multiple contexts, **ask** rather than guess.
|
|
93
101
|
|
|
94
102
|
### 4. Write the draft
|
|
95
103
|
|
|
96
|
-
Save to `
|
|
104
|
+
Save to `0-Draft/{email_id}_draft.md` with the template in
|
|
97
105
|
[references/template.md](references/template.md). Reference past interactions
|
|
98
|
-
naturally
|
|
106
|
+
naturally. To arrange a meeting, propose specific times from the calendar
|
|
107
|
+
availability.
|
|
99
108
|
|
|
100
109
|
### 5. Recruitment & staffing emails
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
You **must never** copy candidates on internal threads about them.
|
|
103
112
|
|
|
104
|
-
- Identify the candidate from the thread and `
|
|
105
|
-
- Strip the candidate from To/CC
|
|
113
|
+
- Identify the candidate from the thread and `2-Confidential/Candidates/`.
|
|
114
|
+
- Strip the candidate from To/CC. Draft to internal stakeholders only.
|
|
106
115
|
- Direct-to-candidate emails carry the warning header
|
|
107
116
|
`⚠️ RECRUITER ONLY — This email goes directly to the candidate.`
|
|
108
117
|
|
|
109
118
|
If a thread mentions a candidate and includes multiple internal recipients,
|
|
110
|
-
treat it as internal
|
|
119
|
+
treat it as internal. Exclude the candidate.
|
|
111
120
|
|
|
112
121
|
### 6. Present and approve
|
|
113
122
|
|
|
114
|
-
Show the draft to the user. Wait for explicit approval before
|
|
115
|
-
edits
|
|
123
|
+
Show the draft to the user. Wait for explicit approval before you send. Apply
|
|
124
|
+
edits. Present the draft again as needed.
|
|
116
125
|
|
|
117
126
|
### 7. Send
|
|
118
127
|
|
|
119
|
-
After
|
|
128
|
+
After the user approves, send the email through Apple Mail:
|
|
120
129
|
|
|
121
130
|
```bash
|
|
122
131
|
node scripts/send-email.mjs \
|
|
@@ -124,18 +133,19 @@ node scripts/send-email.mjs \
|
|
|
124
133
|
--cc "other@example.com" \
|
|
125
134
|
--subject "Re: Subject" \
|
|
126
135
|
--body "Plain text body" \
|
|
127
|
-
--draft "
|
|
136
|
+
--draft "0-Draft/12345_draft.md"
|
|
128
137
|
```
|
|
129
138
|
|
|
130
139
|
Required: `--to`, `--subject`, `--body` (plain text). Optional: `--cc`, `--bcc`,
|
|
131
|
-
`--draft`. With `--draft`, the
|
|
132
|
-
|
|
140
|
+
`--draft`. With `--draft`, the script deletes the draft file. It also appends
|
|
141
|
+
the email ID to the `handled` ledger automatically.
|
|
133
142
|
|
|
134
|
-
### 8. Mark handled
|
|
143
|
+
### 8. Mark handled and do not send
|
|
135
144
|
|
|
136
|
-
When
|
|
145
|
+
When other channels resolve a thread:
|
|
137
146
|
|
|
138
147
|
```bash
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
mkdir -p ~/.cache/fit/outpost/drafts
|
|
149
|
+
echo "$EMAIL_ID" >> ~/.cache/fit/outpost/drafts/handled
|
|
150
|
+
rm -f "0-Draft/${EMAIL_ID}_draft.md"
|
|
141
151
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Draft File Template
|
|
2
2
|
|
|
3
|
-
Save each draft to `
|
|
3
|
+
Save each draft to `0-Draft/{email_id}_draft.md` with this layout:
|
|
4
4
|
|
|
5
5
|
```markdown
|
|
6
6
|
# Draft Response
|
|
@@ -22,5 +22,5 @@ Save each draft to `Drafts/{email_id}_draft.md` using this layout:
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
The body must end with the last sentence of content. Apple Mail appends the
|
|
25
|
-
user's configured signature automatically
|
|
25
|
+
user's configured signature automatically. So do **not** add a sign-off, "Best",
|
|
26
26
|
"Cheers", or the user's name.
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Scan for unprocessed emails and output their IDs and subjects.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The script checks ~/.cache/fit/outpost/apple_mail/ for email thread markdown
|
|
6
|
+
* files. It skips a file that the ~/.cache/fit/outpost/drafts/handled or
|
|
7
|
+
* ~/.cache/fit/outpost/drafts/ignored ledger already lists. It outputs one
|
|
8
|
+
* tab-separated line per unprocessed thread: email_id<TAB>subject. The
|
|
9
|
+
* draft-emails skill uses this script to find threads that need a reply.
|
|
10
|
+
*
|
|
11
|
+
* The ledgers live in the cache drafts/ directory. Never move them into
|
|
12
|
+
* ~/.cache/fit/outpost/state/ — that directory is a daemon-owned trust root.
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
15
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
12
16
|
import { basename, join } from "node:path";
|
|
13
17
|
import { homedir } from "node:os";
|
|
14
18
|
|
|
@@ -17,7 +21,8 @@ const HELP = `scan-emails — list unprocessed email threads
|
|
|
17
21
|
Usage: node scripts/scan-emails.mjs [-h|--help]
|
|
18
22
|
|
|
19
23
|
Scans ~/.cache/fit/outpost/apple_mail/ for .md thread files not yet
|
|
20
|
-
recorded in
|
|
24
|
+
recorded in ~/.cache/fit/outpost/drafts/handled or
|
|
25
|
+
~/.cache/fit/outpost/drafts/ignored. Outputs one line per
|
|
21
26
|
unprocessed thread as: email_id<TAB>subject`;
|
|
22
27
|
|
|
23
28
|
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
@@ -27,6 +32,7 @@ if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
|
27
32
|
|
|
28
33
|
const HOME = homedir();
|
|
29
34
|
const MAIL_DIR = join(HOME, ".cache/fit/outpost/apple_mail");
|
|
35
|
+
const DRAFTS_DIR = join(HOME, ".cache/fit/outpost/drafts");
|
|
30
36
|
|
|
31
37
|
/** Load a file of IDs (one per line) into a Set. */
|
|
32
38
|
function loadIdSet(path) {
|
|
@@ -49,8 +55,9 @@ function extractSubject(filePath) {
|
|
|
49
55
|
function main() {
|
|
50
56
|
if (!existsSync(MAIL_DIR)) return;
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
const
|
|
58
|
+
mkdirSync(DRAFTS_DIR, { recursive: true });
|
|
59
|
+
const handled = loadIdSet(join(DRAFTS_DIR, "handled"));
|
|
60
|
+
const ignored = loadIdSet(join(DRAFTS_DIR, "ignored"));
|
|
54
61
|
|
|
55
62
|
for (const name of readdirSync(MAIL_DIR).sort()) {
|
|
56
63
|
if (!name.endsWith(".md")) continue;
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* Send an email
|
|
3
|
+
* Send an email through Apple Mail with AppleScript.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* Apple Mail. The script writes a temporary
|
|
7
|
-
*
|
|
5
|
+
* The script builds an AppleScript command. That command creates an outgoing
|
|
6
|
+
* message and sends it through Apple Mail. The script writes a temporary
|
|
7
|
+
* .scpt file. It runs the file with osascript. It then deletes the file.
|
|
8
|
+
* Mail.app must run.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
* or sign-off
|
|
10
|
+
* Use plain text for the body. Do not use HTML. Do NOT include an email
|
|
11
|
+
* signature or sign-off. Apple Mail appends the user's configured signature
|
|
12
|
+
* automatically.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import { execFileSync } from "node:child_process";
|
|
14
16
|
import {
|
|
15
17
|
appendFileSync,
|
|
18
|
+
mkdirSync,
|
|
16
19
|
mkdtempSync,
|
|
17
20
|
writeFileSync,
|
|
18
21
|
unlinkSync,
|
|
19
22
|
} from "node:fs";
|
|
20
23
|
import { basename, join } from "node:path";
|
|
21
|
-
import { tmpdir } from "node:os";
|
|
24
|
+
import { homedir, tmpdir } from "node:os";
|
|
22
25
|
|
|
23
|
-
const HELP = `send-email — send an email
|
|
26
|
+
const HELP = `send-email — send an email through Apple Mail
|
|
24
27
|
|
|
25
28
|
Usage: node scripts/send-email.mjs --to <addrs> --subject <subj> --body <text> [options]
|
|
26
29
|
|
|
@@ -30,10 +33,10 @@ Options:
|
|
|
30
33
|
--bcc <addrs> Comma-separated BCC recipients
|
|
31
34
|
--subject <subj> Email subject line (required)
|
|
32
35
|
--body <text> Plain-text email body (required)
|
|
33
|
-
--draft <path> Draft file
|
|
36
|
+
--draft <path> Draft file. The script deletes it after send and appends the ID to ~/.cache/fit/outpost/drafts/handled
|
|
34
37
|
-h, --help Show this help message and exit
|
|
35
38
|
|
|
36
|
-
Mail.app must
|
|
39
|
+
Mail.app must run. You need no signature or sign-off. Apple Mail appends it.`;
|
|
37
40
|
|
|
38
41
|
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
39
42
|
console.log(HELP);
|
|
@@ -102,6 +105,10 @@ function buildAppleScript({ to, cc, bcc, subject, body }) {
|
|
|
102
105
|
.join("\n");
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
// The handled ledger lives in the cache drafts/ directory. Never move it into
|
|
109
|
+
// ~/.cache/fit/outpost/state/ — that directory is a daemon-owned trust root.
|
|
110
|
+
const DRAFTS_DIR = join(homedir(), ".cache/fit/outpost/drafts");
|
|
111
|
+
|
|
105
112
|
function handleDraftCleanup(draft) {
|
|
106
113
|
if (!draft) return;
|
|
107
114
|
try {
|
|
@@ -113,7 +120,8 @@ function handleDraftCleanup(draft) {
|
|
|
113
120
|
const draftBasename = basename(draft, ".md");
|
|
114
121
|
const emailId = draftBasename.replace(/_draft$/, "");
|
|
115
122
|
if (emailId) {
|
|
116
|
-
|
|
123
|
+
mkdirSync(DRAFTS_DIR, { recursive: true });
|
|
124
|
+
appendFileSync(join(DRAFTS_DIR, "handled"), emailId + "\n");
|
|
117
125
|
console.log(`Marked as handled: ${emailId}`);
|
|
118
126
|
}
|
|
119
127
|
}
|