@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.
Files changed (129) hide show
  1. package/README.md +2 -2
  2. package/bin/fit-outpost.js +3 -2
  3. package/package.json +6 -5
  4. package/src/agent-path.js +10 -6
  5. package/src/agent-runner.js +28 -24
  6. package/src/index.js +2 -3
  7. package/src/kb-manager.js +75 -20
  8. package/src/kb-validator.js +762 -0
  9. package/src/outpost.js +108 -39
  10. package/src/posture.js +3 -3
  11. package/src/privilege.js +11 -11
  12. package/src/scheduler.js +14 -12
  13. package/src/socket-server.js +19 -18
  14. package/src/spawn-env.js +7 -6
  15. package/src/state-manager.js +2 -2
  16. package/templates/.claude/agents/chief-of-staff.md +32 -24
  17. package/templates/.claude/agents/concierge.md +26 -17
  18. package/templates/.claude/agents/head-hunter.md +34 -26
  19. package/templates/.claude/agents/librarian.md +26 -18
  20. package/templates/.claude/agents/postman.md +26 -18
  21. package/templates/.claude/agents/recruiter.md +38 -27
  22. package/templates/.claude/skills/anarlog-follow/SKILL.md +91 -66
  23. package/templates/.claude/skills/anarlog-follow/references/coaching.md +22 -21
  24. package/templates/.claude/skills/anarlog-follow/references/sessions.md +52 -0
  25. package/templates/.claude/skills/anarlog-process/SKILL.md +108 -62
  26. package/templates/.claude/skills/anarlog-process/references/extraction.md +41 -29
  27. package/templates/.claude/skills/anarlog-process/references/sessions.md +92 -58
  28. package/templates/.claude/skills/anarlog-process/scripts/scan.mjs +402 -160
  29. package/templates/.claude/skills/candidate-report/SKILL.md +41 -36
  30. package/templates/.claude/skills/candidate-report/references/report-template.html +10 -10
  31. package/templates/.claude/skills/candidate-report/references/report.css +4 -4
  32. package/templates/.claude/skills/candidate-report/references/rubric.md +15 -14
  33. package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +2 -2
  34. package/templates/.claude/skills/changelog/SKILL.md +122 -67
  35. package/templates/.claude/skills/deck-create/SKILL.md +90 -32
  36. package/templates/.claude/skills/deck-create/references/slide.css +4 -4
  37. package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +6 -6
  38. package/templates/.claude/skills/deck-review/SKILL.md +63 -58
  39. package/templates/.claude/skills/deck-review/assets/slide-annotator.js +58 -49
  40. package/templates/.claude/skills/deck-summarize/SKILL.md +55 -45
  41. package/templates/.claude/skills/deck-summarize/references/brief-template.md +5 -4
  42. package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +8 -7
  43. package/templates/.claude/skills/doc-collab/SKILL.md +39 -34
  44. package/templates/.claude/skills/doc-create/SKILL.md +27 -23
  45. package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +6 -6
  46. package/templates/.claude/skills/draft-emails/SKILL.md +63 -53
  47. package/templates/.claude/skills/draft-emails/references/template.md +2 -2
  48. package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +15 -8
  49. package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +19 -11
  50. package/templates/.claude/skills/extract-entities/SKILL.md +76 -75
  51. package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +6 -5
  52. package/templates/.claude/skills/extract-entities/references/conditions.md +20 -18
  53. package/templates/.claude/skills/extract-entities/references/content.md +10 -9
  54. package/templates/.claude/skills/extract-entities/references/links.md +37 -13
  55. package/templates/.claude/skills/extract-entities/references/recruitment.md +30 -24
  56. package/templates/.claude/skills/extract-entities/references/resolution.md +14 -14
  57. package/templates/.claude/skills/extract-entities/references/sources.md +9 -9
  58. package/templates/.claude/skills/extract-entities/references/templates-conditions.md +12 -6
  59. package/templates/.claude/skills/extract-entities/references/templates-people-orgs.md +29 -10
  60. package/templates/.claude/skills/extract-entities/references/templates-priorities.md +10 -4
  61. package/templates/.claude/skills/extract-entities/references/templates-projects-topics.md +19 -7
  62. package/templates/.claude/skills/extract-entities/scripts/state.mjs +9 -8
  63. package/templates/.claude/skills/meeting-prep/SKILL.md +42 -36
  64. package/templates/.claude/skills/organize-files/SKILL.md +27 -22
  65. package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +5 -4
  66. package/templates/.claude/skills/organize-files/scripts/summarize.mjs +4 -4
  67. package/templates/.claude/skills/person-identify/SKILL.md +77 -27
  68. package/templates/.claude/skills/person-identify/scripts/identify.sh +125 -26
  69. package/templates/.claude/skills/person-lookup/SKILL.md +34 -30
  70. package/templates/.claude/skills/person-lookup/scripts/lookup.sh +46 -17
  71. package/templates/.claude/skills/req-assess/SKILL.md +44 -36
  72. package/templates/.claude/skills/req-assess/references/interview-template.md +7 -2
  73. package/templates/.claude/skills/req-assess/references/panel-template.md +7 -2
  74. package/templates/.claude/skills/req-assess/references/rubric.md +13 -13
  75. package/templates/.claude/skills/req-bundle/SKILL.md +164 -0
  76. package/templates/.claude/skills/req-bundle/references/matching.md +70 -0
  77. package/templates/.claude/skills/req-bundle/references/pdf-structure.md +74 -0
  78. package/templates/.claude/skills/req-bundle/scripts/split-bundle.mjs +377 -0
  79. package/templates/.claude/skills/req-decide/SKILL.md +52 -41
  80. package/templates/.claude/skills/req-decide/references/rubric.md +11 -10
  81. package/templates/.claude/skills/req-decide/references/template.md +2 -2
  82. package/templates/.claude/skills/req-forget/SKILL.md +58 -39
  83. package/templates/.claude/skills/req-forget/references/classify.md +15 -13
  84. package/templates/.claude/skills/req-forget/references/locations.md +27 -22
  85. package/templates/.claude/skills/req-forget/references/report-template.md +24 -16
  86. package/templates/.claude/skills/req-scan/SKILL.md +27 -25
  87. package/templates/.claude/skills/req-scan/references/fallbacks.md +9 -9
  88. package/templates/.claude/skills/req-scan/references/filters.md +13 -13
  89. package/templates/.claude/skills/req-scan/references/sources.md +7 -7
  90. package/templates/.claude/skills/req-scan/references/state.md +1 -1
  91. package/templates/.claude/skills/req-scan/references/template.md +11 -1
  92. package/templates/.claude/skills/req-scan/scripts/state.mjs +3 -2
  93. package/templates/.claude/skills/req-screen/SKILL.md +96 -50
  94. package/templates/.claude/skills/req-screen/references/rubric.md +46 -50
  95. package/templates/.claude/skills/req-screen/references/scoring.md +79 -0
  96. package/templates/.claude/skills/req-screen/references/template.md +29 -17
  97. package/templates/.claude/skills/req-track/SKILL.md +76 -76
  98. package/templates/.claude/skills/req-track/references/fields.md +31 -27
  99. package/templates/.claude/skills/req-track/references/overlays.md +42 -0
  100. package/templates/.claude/skills/req-track/references/signals.md +12 -12
  101. package/templates/.claude/skills/req-track/references/statuses.md +3 -2
  102. package/templates/.claude/skills/req-track/references/templates.md +28 -27
  103. package/templates/.claude/skills/req-workday/SKILL.md +69 -40
  104. package/templates/.claude/skills/req-workday/references/brief.md +81 -0
  105. package/templates/.claude/skills/req-workday/references/status-mapping.md +5 -5
  106. package/templates/.claude/skills/req-workday/references/templates.md +11 -78
  107. package/templates/.claude/skills/req-workday/references/xlsx-format.md +26 -7
  108. package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +113 -35
  109. package/templates/.claude/skills/send-chat/SKILL.md +58 -54
  110. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +23 -19
  111. package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +7 -7
  112. package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +6 -6
  113. package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +9 -8
  114. package/templates/.claude/skills/sync-apple-mail/SKILL.md +20 -14
  115. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +10 -10
  116. package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +9 -9
  117. package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +16 -13
  118. package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +3 -3
  119. package/templates/.claude/skills/sync-teams/SKILL.md +46 -43
  120. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +22 -21
  121. package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +4 -4
  122. package/templates/.claude/skills/sync-teams/scripts/sync.mjs +4 -4
  123. package/templates/.claude/skills/upstream-instructions/SKILL.md +43 -41
  124. package/templates/.claude/skills/upstream-instructions/references/examples.md +5 -5
  125. package/templates/CLAUDE.md +97 -85
  126. package/templates/MIGRATION.md +359 -0
  127. package/templates/registry.yaml +25 -0
  128. package/templates/.claude/skills/anarlog-follow/scripts/follow.mjs +0 -243
  129. 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 using Playwright to render HTML to 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.
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
- Generate multi-page A4 PDF documents from user requests. Uses Playwright to
10
- render self-contained HTML to PDF. Can pull context from the knowledge base for
11
- company info, project details, and people.
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 will be installed on first use
24
+ - Playwright installs on first use
22
25
 
23
26
  ## Inputs
24
27
 
25
28
  - User's description of the document
26
- - `Knowledge/` — optional context about company, product, team, projects
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: `Knowledge/Projects/`)
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 `Knowledge/` for relevant context about the company, product, team,
40
+ 1. Check `3-Team/` for relevant context about the company, product, team,
38
41
  projects, or people mentioned.
39
- 2. Ensure Playwright is installed:
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 see **HTML Document Rules** below.
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 is omitted, the PDF is written alongside the HTML file with the
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. Fix and re-render if
53
- needed.
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`, not with PDF margin settings
64
- - The PDF is rendered with zero margins — the HTML owns all spacing
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 no external font loading
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 fitting:**
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 if it does, reduce spacing
82
- or font sizes and re-render
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 Playwright can fail silently on
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
- - Tables should be compact and readable right-align monetary values
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 using Playwright.
3
+ * Convert an HTML document to an A4 PDF with Playwright.
4
4
  *
5
- * Renders an HTML file into an A4 PDF with background colours preserved and
6
- * zero margins (the HTML is expected to handle its own page layout via CSS
7
- * @page rules and page-break-after). Defaults to writing the PDF alongside
8
- * the input file when no output path is given.
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 via Playwright
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 using the knowledge base and calendar for context. Use when the user asks to draft, reply to, respond to, or send an email.
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
- Draft and send email responses using the knowledge base and calendar for full
9
- context on every person and conversation. Every draft requires explicit user
10
- approval before sending.
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 | `Knowledge/People/*.md` |
26
- | Organizations | `Knowledge/Organizations/*.md` |
27
- | Email threads | `~/.cache/fit/outpost/apple_mail/*.md` |
28
- | Calendar events | `~/.cache/fit/outpost/apple_calendar/*.json` |
29
- | Handled IDs | `Drafts/handled` (one ID per line) |
30
- | Ignored IDs | `Drafts/ignored` (one ID per line) |
31
- | Draft files | `Drafts/{email_id}_draft.md` |
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 via DM);
35
- `ignored` for threads that need no response (newsletters, spam, outbound with no
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
- - [ ] Sender and organization were looked up in `Knowledge/` before drafting.
41
- - [ ] Draft is a single email (not multiple variants) and matches the incoming
42
- tone.
43
- - [ ] Body has no sign-off / name / "Best" — Apple Mail signature handles it.
44
- - [ ] Recruitment thread: candidate excluded from internal recipients; any
45
- direct-to-candidate draft is flagged `⚠️ RECRUITER ONLY`.
46
- - [ ] No sensitive personal data (health, politics, etc.) was included.
47
- - [ ] User has explicitly approved the draft before any send.
48
- - [ ] Send used `--draft <path>` so cleanup and `Drafts/handled` happen
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
- Outputs `email_id<TAB>subject` for unprocessed emails (those not in
62
- `Drafts/handled` or `Drafts/ignored`).
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 `Drafts/ignored`): newsletters, marketing, automated
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 when in doubt, draft.
79
+ Be conservative with ignore. When in doubt, draft.
73
80
 
74
81
  ### 3. Gather context
75
82
 
76
- Before drafting, look up the sender and organization in `Knowledge/`:
83
+ Before you draft, look up the sender and the organization in the graph:
77
84
 
78
85
  ```bash
79
- rg -l "sender_name" Knowledge/
80
- cat "Knowledge/People/Sender Name.md"
81
- cat "Knowledge/Organizations/Company Name.md"
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 scheduling emails, also read the relevant calendar event:
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 is
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 `Drafts/{email_id}_draft.md` using the template in
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; for scheduling, propose specific times from calendar availability.
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
- Candidates **must never** be copied on internal threads about them.
111
+ You **must never** copy candidates on internal threads about them.
103
112
 
104
- - Identify the candidate from the thread and `Knowledge/Candidates/`.
105
- - Strip the candidate from To/CC; draft to internal stakeholders only.
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 and exclude the candidate.
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 sending. Apply
115
- edits and present again as needed.
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 approval, send via Apple Mail:
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 "Drafts/12345_draft.md"
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 draft file is deleted and the email ID is
132
- appended to `Drafts/handled` automatically.
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 without sending
143
+ ### 8. Mark handled and do not send
135
144
 
136
- When a thread is resolved through other channels:
145
+ When other channels resolve a thread:
137
146
 
138
147
  ```bash
139
- echo "$EMAIL_ID" >> Drafts/handled
140
- rm -f "Drafts/${EMAIL_ID}_draft.md"
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 `Drafts/{email_id}_draft.md` using this layout:
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, so do **not** add a sign-off, "Best",
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
- * Checks ~/.cache/fit/outpost/apple_mail/ for email thread markdown files not
6
- * yet listed in Drafts/handled or Drafts/ignored. Outputs one tab-separated
7
- * line per unprocessed thread: email_id<TAB>subject. Used by the draft-emails
8
- * skill to identify threads that need a reply.
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 Drafts/handled or Drafts/ignored. Outputs one line per
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
- const handled = loadIdSet("Drafts/handled");
53
- const ignored = loadIdSet("Drafts/ignored");
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 via Apple Mail using AppleScript.
3
+ * Send an email through Apple Mail with AppleScript.
4
4
  *
5
- * Builds an AppleScript command to create and send an outgoing message through
6
- * Apple Mail. The script writes a temporary .scpt file, executes it with
7
- * osascript, and cleans up afterwards. Mail.app must be running.
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
- * The body should be plain text no HTML. Do NOT include an email signature
10
- * or sign-off; Apple Mail appends the user's configured signature automatically.
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 via Apple Mail
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 deleted after send, ID appended to Drafts/handled
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 be running. No signature or sign-off needed — Apple Mail appends it.`;
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
- appendFileSync("Drafts/handled", emailId + "\n");
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
  }