@forwardimpact/outpost 3.9.0 → 3.10.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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/kb-manager.js +27 -1
  3. package/templates/.claude/agents/chief-of-staff.md +9 -7
  4. package/templates/.claude/agents/concierge.md +6 -4
  5. package/templates/.claude/agents/head-hunter.md +6 -4
  6. package/templates/.claude/agents/librarian.md +14 -10
  7. package/templates/.claude/agents/postman.md +6 -4
  8. package/templates/.claude/agents/recruiter.md +8 -5
  9. package/templates/.claude/skills/anarlog-follow/references/coaching.md +3 -3
  10. package/templates/.claude/skills/anarlog-trim/SKILL.md +9 -3
  11. package/templates/.claude/skills/candidate-report/SKILL.md +4 -3
  12. package/templates/.claude/skills/changelog/SKILL.md +10 -10
  13. package/templates/.claude/skills/deck-create/SKILL.md +23 -22
  14. package/templates/.claude/skills/deck-review/SKILL.md +29 -25
  15. package/templates/.claude/skills/doc-create/SKILL.md +17 -16
  16. package/templates/.claude/skills/extract-entities/SKILL.md +4 -2
  17. package/templates/.claude/skills/meeting-prep/SKILL.md +2 -1
  18. package/templates/.claude/skills/organize-files/SKILL.md +9 -5
  19. package/templates/.claude/skills/person-identify/SKILL.md +2 -2
  20. package/templates/.claude/skills/person-lookup/SKILL.md +3 -3
  21. package/templates/.claude/skills/req-forget/references/report-template.md +1 -1
  22. package/templates/.claude/skills/req-scan/references/fallbacks.md +3 -3
  23. package/templates/.claude/skills/req-scan/references/sources.md +5 -5
  24. package/templates/.claude/skills/req-track/SKILL.md +2 -2
  25. package/templates/.claude/skills/req-workday/references/status-mapping.md +1 -1
  26. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +6 -2
  27. package/templates/.claude/skills/sync-apple-mail/SKILL.md +3 -1
  28. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +1 -1
  29. package/templates/.claude/skills/sync-teams/SKILL.md +13 -1
  30. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +67 -34
  31. package/templates/.claude/skills/upstream-instructions/SKILL.md +2 -1
  32. package/templates/CLAUDE.md +27 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/outpost",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "description": "Personal operations center — context from email, calendar, and knowledge assembled so preparation is continuous, not a morning scramble.",
5
5
  "homepage": "https://www.forwardimpact.team",
6
6
  "repository": {
package/src/kb-manager.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * KBManager — knowledge base init/update operations.
3
3
  */
4
4
 
5
- import { join, dirname, resolve } from "node:path";
5
+ import { join, dirname, resolve, basename } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { createLogger } from "@forwardimpact/libtelemetry";
8
8
 
@@ -214,6 +214,7 @@ export class KBManager {
214
214
  await this.#ensureDir(join(dest, d));
215
215
 
216
216
  await this.copyBundledFiles(templateDir, dest);
217
+ await this.#linkIntoDocuments(dest);
217
218
 
218
219
  this.#logger.info(
219
220
  `Knowledge base initialized at ${dest}\n\nNext steps:\n 1. cd ${dest} && npx apm install\n 2. claude\n 3. Run the person-identify skill to populate your identity`,
@@ -221,6 +222,31 @@ export class KBManager {
221
222
  return { ok: true, value: { dest } };
222
223
  }
223
224
 
225
+ /**
226
+ * Create a navigation symlink at `~/Documents/<name>` pointing to the KB.
227
+ * The KB data itself stays under the XDG data home, outside TCC-protected
228
+ * folders — this is only a convenience pointer so the KB is easy to find and
229
+ * open from Finder. Best-effort: a pre-existing entry is left untouched, and
230
+ * any failure (e.g. macOS denying write access to `~/Documents`) is logged,
231
+ * never fatal, because the KB is already provisioned at `dest`.
232
+ * @param {string} dest - Absolute path to the provisioned KB.
233
+ * @returns {Promise<void>}
234
+ */
235
+ async #linkIntoDocuments(dest) {
236
+ const link = join(homedir(), "Documents", basename(dest));
237
+ if (await this.#exists(link)) {
238
+ this.#logger.info(` Skipped ${link}: already exists`);
239
+ return;
240
+ }
241
+ try {
242
+ await this.#ensureDir(dirname(link));
243
+ await this.#fs.symlink(dest, link, "dir");
244
+ this.#logger.info(` Linked ${link} -> ${dest}`);
245
+ } catch (err) {
246
+ this.#logger.info(` Could not link into ~/Documents: ${err.message}`);
247
+ }
248
+ }
249
+
224
250
  /**
225
251
  * Update an existing knowledge base with the latest bundled files.
226
252
  * @param {string} targetPath
@@ -14,16 +14,18 @@ single briefing.
14
14
 
15
15
  ## Priorities
16
16
 
17
- `Knowledge/Priorities/` is the backbone of every briefing. Read it each wake (it
18
- is also listed under Inputs) and frame the whole briefing around what advances or
19
- threatens the user's priorities.
17
+ `Knowledge/Priorities/` is the backbone of every briefing. Read it and
18
+ `Knowledge/Conditions/` (the live constraints that shape how priorities are
19
+ pursued see Operating Context in CLAUDE.md) each wake (both are also listed
20
+ under Inputs) and frame the whole briefing around what advances or threatens the
21
+ user's priorities.
20
22
 
21
23
  - **Always consider them.** Tie the schedule, the top actions, and the pipeline
22
24
  back to the priority each one serves.
23
25
  - **Always escalate risks.** Consolidate every `## Priority Watch` flag from the
24
26
  sibling triage files — plus anything you find in your own reads — into a
25
- `## Priority Watch` section in the briefing, each item naming the priority, the
26
- evidence, and the risk. A signal that could contradict, block, or slow a
27
+ `## Priority Watch` section in the briefing, each item naming the priority,
28
+ the evidence, and the risk. A signal that could contradict, block, or slow a
27
29
  priority is the most important thing the briefing surfaces.
28
30
 
29
31
  ## Inputs
@@ -37,7 +39,7 @@ authoritative current-state summaries:
37
39
  - `~/.cache/fit/outpost/state/recruiter_triage.md`
38
40
  - `~/.cache/fit/outpost/state/head_hunter_triage.md`
39
41
 
40
- Plus directly: `Knowledge/Priorities/`, `Drafts/`,
42
+ Plus directly: `Knowledge/Priorities/`, `Knowledge/Conditions/`, `Drafts/`,
41
43
  `~/.cache/fit/outpost/apple_calendar/`, and unchecked `- [ ]` items in
42
44
  `Knowledge/`.
43
45
 
@@ -64,7 +66,7 @@ and "Still Outstanding".
64
66
 
65
67
  ## Output
66
68
 
67
- ```
69
+ ```text
68
70
  Decision: {morning/evening} briefing — {key insight about today}
69
71
  Action: Created Briefings/{YYYY-MM-DD}-{morning|evening}.md
70
72
  ```
@@ -18,11 +18,13 @@ recordings.
18
18
 
19
19
  ## Priorities
20
20
 
21
- At the start of every wake, before acting, read `Knowledge/Priorities/`. The
22
- user's priorities are the lens for all your work this wake.
21
+ At the start of every wake, before acting, read `Knowledge/Priorities/` and
22
+ `Knowledge/Conditions/` (which constrains them see Operating Context in
23
+ CLAUDE.md). The user's priorities are the lens for all your work this wake.
23
24
 
24
25
  - **Always consider them.** Weigh each action against whether it advances a
25
- priority, and favour work that does.
26
+ priority, and favour work that does. Let the active conditions shape how you
27
+ act on it.
26
28
  - **Always flag risks.** When you encounter a chat, email, transcript, or any
27
29
  other signal that could **contradict, block, or slow** a priority, record it
28
30
  under a `## Priority Watch` heading in your triage report — name the priority,
@@ -53,7 +55,7 @@ over **anarlog-process** (catch-up work).
53
55
 
54
56
  After acting, emit exactly:
55
57
 
56
- ```
58
+ ```text
57
59
  Decision: {what you observed and why you chose this action}
58
60
  Action: {what you did, e.g. "meeting-prep for 2pm with Sarah Chen"}
59
61
  Priority Watch: {priority at risk + one-line why, or "none"}
@@ -21,11 +21,13 @@ benchmark promising matches, and write prospect notes for the user to review.
21
21
 
22
22
  ## Priorities
23
23
 
24
- At the start of every wake, before acting, read `Knowledge/Priorities/`. The
25
- user's priorities are the lens for all your work this wake.
24
+ At the start of every wake, before acting, read `Knowledge/Priorities/` and
25
+ `Knowledge/Conditions/` (which constrains them see Operating Context in
26
+ CLAUDE.md). The user's priorities are the lens for all your work this wake.
26
27
 
27
28
  - **Always consider them.** Weigh each action against whether it advances a
28
- priority, and favour work that does.
29
+ priority, and favour work that does. Let the active conditions shape how you
30
+ act on it.
29
31
  - **Always flag risks.** When you encounter a chat, email, transcript, or any
30
32
  other signal that could **contradict, block, or slow** a priority, record it
31
33
  under a `## Priority Watch` heading in your triage report — name the priority,
@@ -63,7 +65,7 @@ wake — the chief-of-staff reads it.
63
65
 
64
66
  ## Output
65
67
 
66
- ```
68
+ ```text
67
69
  Decision: {source chosen and why}
68
70
  Action: {what was scanned, e.g. "scanned HN Who Wants to Be Hired March 2026, 47 posts"}
69
71
  Prospects: {N} new ({strong} strong, {moderate} moderate), {total} total
@@ -16,11 +16,13 @@ you process new data into the knowledge graph and keep everything organized.
16
16
 
17
17
  ## Priorities
18
18
 
19
- At the start of every wake, before acting, read `Knowledge/Priorities/`. The
20
- user's priorities are the lens for all your work this wake.
19
+ At the start of every wake, before acting, read `Knowledge/Priorities/` and
20
+ `Knowledge/Conditions/` (which constrains them see Operating Context in
21
+ CLAUDE.md). The user's priorities are the lens for all your work this wake.
21
22
 
22
23
  - **Always consider them.** Weigh each action against whether it advances a
23
- priority, and favour work that does.
24
+ priority, and favour work that does. Let the active conditions shape how you
25
+ act on it.
24
26
  - **Always flag risks.** When you encounter a chat, email, transcript, or any
25
27
  other signal that could **contradict, block, or slow** a priority, record it
26
28
  under a `## Priority Watch` heading in your triage report — name the priority,
@@ -31,18 +33,20 @@ user's priorities are the lens for all your work this wake.
31
33
 
32
34
  Assess what needs processing:
33
35
 
34
- 1. Check for unprocessed synced files (mail and calendar data):
36
+ 1. Check for unprocessed synced files (mail and calendar data):
35
37
 
36
- node .claude/skills/extract-entities/scripts/state.mjs check
38
+ ```text
39
+ node .claude/skills/extract-entities/scripts/state.mjs check
40
+ ```
37
41
 
38
- 2. Count existing knowledge graph entities:
42
+ 2. Count existing knowledge graph entities:
39
43
 
40
- ls Knowledge/People/ Knowledge/Organizations/ Knowledge/Projects/
41
- Knowledge/Topics/ Knowledge/Priorities/ 2>/dev/null | wc -l
44
+ ls Knowledge/People/ Knowledge/Organizations/ Knowledge/Projects/
45
+ Knowledge/Topics/ Knowledge/Priorities/ 2>/dev/null | wc -l
42
46
 
43
47
  Write triage results to `~/.cache/fit/outpost/state/librarian_triage.md`:
44
48
 
45
- ```
49
+ ```text
46
50
  # Knowledge Triage — {YYYY-MM-DD HH:MM}
47
51
  ## Pending Processing
48
52
  - {count} unprocessed synced files
@@ -64,7 +68,7 @@ Choose the most valuable action:
64
68
 
65
69
  After acting, output exactly:
66
70
 
67
- ```
71
+ ```text
68
72
  Decision: {what you observed and why you chose this action}
69
73
  Action: {what you did, e.g. "extract-entities on 7 files"}
70
74
  Priority Watch: {priority at risk + one-line why, or "none"}
@@ -17,11 +17,13 @@ and Teams, triage what's new, take the most valuable action.
17
17
 
18
18
  ## Priorities
19
19
 
20
- At the start of every wake, before acting, read `Knowledge/Priorities/`. The
21
- user's priorities are the lens for all your work this wake.
20
+ At the start of every wake, before acting, read `Knowledge/Priorities/` and
21
+ `Knowledge/Conditions/` (which constrains them see Operating Context in
22
+ CLAUDE.md). The user's priorities are the lens for all your work this wake.
22
23
 
23
24
  - **Always consider them.** Weigh each action against whether it advances a
24
- priority, and favour work that does.
25
+ priority, and favour work that does. Let the active conditions shape how you
26
+ act on it.
25
27
  - **Always flag risks.** When you encounter a chat, email, transcript, or any
26
28
  other signal that could **contradict, block, or slow** a priority, record it
27
29
  under a `## Priority Watch` heading in your triage report — name the priority,
@@ -54,7 +56,7 @@ email only.
54
56
 
55
57
  After acting, emit exactly:
56
58
 
57
- ```
59
+ ```text
58
60
  Decision: {what you observed and why you chose this action}
59
61
  Action: {what you did, e.g. "draft-emails for thread 123"}
60
62
  Priority Watch: {priority at risk + one-line why, or "none"}
@@ -22,11 +22,13 @@ assessment and recommendation references the standard.
22
22
 
23
23
  ## Priorities
24
24
 
25
- At the start of every wake, before acting, read `Knowledge/Priorities/`. The
26
- user's priorities are the lens for all your work this wake.
25
+ At the start of every wake, before acting, read `Knowledge/Priorities/` and
26
+ `Knowledge/Conditions/` (which constrains them see Operating Context in
27
+ CLAUDE.md). The user's priorities are the lens for all your work this wake.
27
28
 
28
29
  - **Always consider them.** Weigh each action against whether it advances a
29
- priority, and favour work that does.
30
+ priority, and favour work that does. Let the active conditions shape how you
31
+ act on it.
30
32
  - **Always flag risks.** When you encounter a chat, email, transcript, or any
31
33
  other signal that could **contradict, block, or slow** a priority, record it
32
34
  under a `## Priority Watch` heading in your triage report — name the priority,
@@ -60,11 +62,12 @@ screen > sync. Stage 3 **never** triggers automatically — only on user request
60
62
 
61
63
  Triage state goes to `~/.cache/fit/outpost/state/recruiter_triage.md` every wake
62
64
  (the chief-of-staff reads it): needs-action by stage, recently processed
63
- candidates, pipeline totals by stage/track, aggregate diversity, retention flags.
65
+ candidates, pipeline totals by stage/track, aggregate diversity, retention
66
+ flags.
64
67
 
65
68
  ## Output
66
69
 
67
- ```
70
+ ```text
68
71
  Decision: {observation and chosen action}
69
72
  Action: {e.g. "req-screen for John Smith against J060 forward-deployed"}
70
73
  Stage: {1 | 2 | sync | erasure}
@@ -6,7 +6,7 @@ Reference for `anarlog-follow` Phases 2 and 3.
6
6
 
7
7
  ### For interviews
8
8
 
9
- ```
9
+ ```text
10
10
  Following: {Title}
11
11
  Type: {Interview type}
12
12
  Candidate: {Name} — {current role} at {employer}
@@ -27,7 +27,7 @@ Watching for: {specific signals at this interview stage}
27
27
 
28
28
  ### For general meetings
29
29
 
30
- ```
30
+ ```text
31
31
  Following: {Title}
32
32
  Attendees: {names with roles}
33
33
 
@@ -66,7 +66,7 @@ Suggested topics:
66
66
 
67
67
  Output **only when actionable**. Each nudge: 1–3 lines max.
68
68
 
69
- ```
69
+ ```text
70
70
  Probe deeper: {Name} mentioned {topic} — ask for a specific example
71
71
  Gap: screening flagged {skill} as uncertain. Try: "{question}"
72
72
  Confirmed: {Name} demonstrated {skill} at {level} — "{brief quote}"
@@ -46,9 +46,11 @@ Run this skill:
46
46
  ### Step 0 — Validate the session
47
47
 
48
48
  1. Confirm the session directory exists:
49
- ```
49
+
50
+ ```text
50
51
  ~/Library/Application Support/anarlog/sessions/{uuid}/
51
52
  ```
53
+
52
54
  2. Confirm `transcript.json` exists and has at least one transcript with words.
53
55
  3. Read `_meta.json` to get the session title for context.
54
56
 
@@ -132,7 +134,8 @@ json.dump(data, open(path, 'w'), indent=2)
132
134
  ```
133
135
 
134
136
  4. Print a summary:
135
- ```
137
+
138
+ ```text
136
139
  Trimmed: {title}
137
140
  Before: {original_words} words, {original_duration}
138
141
  After: {new_words} words, {new_duration}
@@ -147,11 +150,14 @@ people who did not consent to being recorded. The full audio file must be
147
150
  deleted to respect participant privacy.
148
151
 
149
152
  1. Delete the audio file:
153
+
150
154
  ```bash
151
155
  rm "~/Library/Application Support/anarlog/sessions/{uuid}/audio.mp3"
152
156
  ```
157
+
153
158
  2. Confirm deletion and inform the user:
154
- ```
159
+
160
+ ```text
155
161
  Audio deleted: audio.mp3 removed (recording contained unconsented content beyond the meeting)
156
162
  ```
157
163
 
@@ -50,7 +50,8 @@ before deciding whether to invest interview time.
50
50
  - [ ] Verdict class matches the overall assessment.
51
51
  - [ ] Report fits on a single A4 page (browser print preview).
52
52
  - [ ] CSS is inlined in the `<style>` block.
53
- - [ ] Footer shows the author name and role from `~/.cache/fit/outpost/state/identity.md`.
53
+ - [ ] Footer shows the author name and role from
54
+ `~/.cache/fit/outpost/state/identity.md`.
54
55
  - [ ] Written as if the candidate will read it; no special-category data.
55
56
 
56
57
  </do_confirm_checklist>
@@ -61,7 +62,7 @@ before deciding whether to invest interview time.
61
62
 
62
63
  Read whatever exists for the candidate:
63
64
 
64
- ```
65
+ ```text
65
66
  Knowledge/Candidates/{Name}/brief.md # required
66
67
  Knowledge/Candidates/{Name}/screening.md # if produced by req-screen
67
68
  Knowledge/Candidates/{Name}/interview-*.md # if produced by req-assess
@@ -118,7 +119,7 @@ preview overflows, cut content.
118
119
 
119
120
  Save the completed HTML to:
120
121
 
121
- ```
122
+ ```text
122
123
  Drafts/{Recipient}-{CandidateSurname}-Report.html
123
124
  ```
124
125
 
@@ -11,8 +11,8 @@ teammates syncing the same filesystem can see what changed and why.
11
11
 
12
12
  This tracks **graph content** — notes under `Knowledge/People/`,
13
13
  `Organizations/`, `Projects/`, `Topics/`, `Candidates/`, `Priorities/`, and the
14
- other subdirectories. It does **not** track changes to instructions (`CLAUDE.md`,
15
- agents, skills) — that is the `upstream-instructions` skill's job.
14
+ other subdirectories. It does **not** track changes to instructions
15
+ (`CLAUDE.md`, agents, skills) — that is the `upstream-instructions` skill's job.
16
16
 
17
17
  ## Trigger
18
18
 
@@ -40,16 +40,16 @@ agents, skills) — that is the `upstream-instructions` skill's job.
40
40
 
41
41
  ## Ethics
42
42
 
43
- `Knowledge/` is shared with the team. Every entry obeys the KB's integrity rules:
44
- objective and factual, work-relevant, no personal judgments. Assume the person a
45
- note is about will read its changelog entry. Describe **what changed in the
46
- graph**, not opinions about the people in it.
43
+ `Knowledge/` is shared with the team. Every entry obeys the KB's integrity
44
+ rules: objective and factual, work-relevant, no personal judgments. Assume the
45
+ person a note is about will read its changelog entry. Describe
46
+ **what changed in the graph**, not opinions about the people in it.
47
47
 
48
48
  <do_confirm_checklist goal="Verify the changelog is accurate and shareable">
49
49
 
50
50
  - [ ] Exactly one `Knowledge/CHANGELOG.md`; no stray per-folder changelogs.
51
- - [ ] Every entry names its **Scope** — the specific note(s) or folder(s) touched,
52
- by full path.
51
+ - [ ] Every entry names its **Scope** — the specific note(s) or folder(s)
52
+ touched, by full path.
53
53
  - [ ] Each entry has **Who** (author, from identity), **What**, and **Why**.
54
54
  - [ ] Descriptions are specific enough to be useful (not "updated some notes").
55
55
  - [ ] Dates are the date the change was actually made, not guessed.
@@ -104,8 +104,8 @@ Scope lists every note touched.
104
104
 
105
105
  ### 4. Write the changelog
106
106
 
107
- Create or update `Knowledge/CHANGELOG.md` (newest first). Group entries under one
108
- heading per day; one bullet per logical change:
107
+ Create or update `Knowledge/CHANGELOG.md` (newest first). Group entries under
108
+ one heading per day; one bullet per logical change:
109
109
 
110
110
  ```markdown
111
111
  # Knowledge Changelog
@@ -32,18 +32,18 @@ Run when the user asks to create a presentation, slide deck, or pitch deck.
32
32
 
33
33
  ## Workflow
34
34
 
35
- 1. Check `Knowledge/` for relevant context about the company, product, team,
36
- etc.
37
- 2. Ensure Playwright is installed:
38
- `bun install playwright && bunx playwright install chromium`
39
- 3. Create an HTML file at `/tmp/outpost-presentation.html` with slides
40
- (1280x720px each)
41
- 4. Include the required CSS from [references/slide.css](references/slide.css)
42
- 5. Run the conversion script:
35
+ 1. Check `Knowledge/` for relevant context about the company, product, team,
36
+ etc.
37
+ 2. Ensure Playwright is installed:
38
+ `bun install playwright && bunx playwright install chromium`
39
+ 3. Create an HTML file at `/tmp/outpost-presentation.html` with slides
40
+ (1280x720px each)
41
+ 4. Include the required CSS from [references/slide.css](references/slide.css)
42
+ 5. Run the conversion script:
43
43
 
44
44
  node scripts/convert-to-pdf.mjs
45
45
 
46
- 6. Tell the user: "Your presentation is ready at ~/Desktop/presentation.pdf"
46
+ 6. Tell the user: "Your presentation is ready at ~/Desktop/presentation.pdf"
47
47
 
48
48
  **Do NOT show HTML code to the user. Just create the PDF and deliver it.**
49
49
 
@@ -82,28 +82,29 @@ selecting/copying text on a slide, and typing into overlay tools (e.g. the
82
82
  2. **No click-to-advance.** Do NOT add click regions on the slide/stage that
83
83
  navigate (e.g. "click left/right third"). They fire on the mouse-up that ends
84
84
  a text-selection drag and jump the slide unexpectedly.
85
- 3. **No spacebar, PageUp/PageDown, or other global key bindings.** Space conflicts
86
- with typing in overlay inputs; the rest are redundant and surprising.
87
- 4. **A progress indicator may be clickable**, but it must live in the footer/chrome
88
- and never overlap slide content.
85
+ 3. **No spacebar, PageUp/PageDown, or other global key bindings.** Space
86
+ conflicts with typing in overlay inputs; the rest are redundant and
87
+ surprising.
88
+ 4. **A progress indicator may be clickable**, but it must live in the
89
+ footer/chrome and never overlap slide content.
89
90
  5. **Expose `window.deckGoto(index)`** (0-based) right after the slide-show
90
- function, so review/overlay tools can jump to a slide without simulating clicks
91
- or keys:
91
+ function, so review/overlay tools can jump to a slide without simulating
92
+ clicks or keys:
92
93
 
93
94
  function go(n) { /* ...show slide n... */ }
94
95
  window.deckGoto = go;
95
96
 
96
- 6. **Keep the hint honest** — the on-screen nav hint should read `← → to navigate`
97
- (don't advertise click/space).
97
+ 6. **Keep the hint honest** — the on-screen nav hint should read
98
+ `← → to navigate` (don't advertise click/space).
98
99
  7. **Use stable structural hooks.** Make each slide one element with class
99
100
  `.slide`, and put the slide-number label (if any) in a `.slide-num` element.
100
101
  The review overlay defaults to these selectors to detect and index slides.
101
102
 
102
- These rules keep decks compatible with the **`deck-review`** skill, which installs
103
- the `slide-annotator.js` review overlay (highlight text on a slide → sidecar JSON
104
- of feedback that an agent acts on). After producing an interactive HTML deck, you
105
- can offer to run `deck-review` to make it reviewable; see that skill for the
106
- install steps and the sidecar JSON schema.
103
+ These rules keep decks compatible with the **`deck-review`** skill, which
104
+ installs the `slide-annotator.js` review overlay (highlight text on a slide →
105
+ sidecar JSON of feedback that an agent acts on). After producing an interactive
106
+ HTML deck, you can offer to run `deck-review` to make it reviewable; see that
107
+ skill for the install steps and the sidecar JSON schema.
107
108
 
108
109
  ## Constraints
109
110
 
@@ -8,9 +8,10 @@ compatibility: Standalone HTML deck opened in a Chromium-based browser (Chrome/E
8
8
 
9
9
  Install the self-contained `slide-annotator.js` overlay onto an HTML deck so the
10
10
  user can **highlight text on a slide and save the feedback as a sidecar JSON**.
11
- Each annotation carries a robust anchor (exact text + surrounding context + slide)
12
- and, once the folder is connected, the resolved **source line, column and context
13
- lines** — so an agent can locate and edit the exact text in small iterations.
11
+ Each annotation carries a robust anchor (exact text + surrounding context +
12
+ slide) and, once the folder is connected, the resolved
13
+ **source line, column and context lines** — so an agent can locate and edit the
14
+ exact text in small iterations.
14
15
 
15
16
  This is the companion to **`deck-create`**: decks produced by `deck-create`
16
17
  already follow the navigation/structure standards this overlay needs, and this
@@ -19,8 +20,8 @@ overlay is designed to drop onto them with one script tag.
19
20
  ## Trigger
20
21
 
21
22
  Run when the user asks to add review / annotation / highlight / comment / markup
22
- capability to a deck, "make this deck reviewable", or to set up a feedback loop on
23
- slides.
23
+ capability to a deck, "make this deck reviewable", or to set up a feedback loop
24
+ on slides.
24
25
 
25
26
  ## Inputs
26
27
 
@@ -46,10 +47,10 @@ slides.
46
47
  - **Slide selector** — each slide is one element with a stable class
47
48
  (default `.slide`). If the deck uses a different class, note it for step 4.
48
49
  - **Navigation hook** — the deck exposes `window.deckGoto(index)` (0-based).
49
- If it has a slideshow function (e.g. `go(n)`) but no hook, add one line right
50
- after it: `window.deckGoto = go;`. Without it the overlay still works (the
51
- panel's *Go* button falls back to `scrollIntoView`), but it can't jump to a
52
- hidden slide precisely.
50
+ If it has a slideshow function (e.g. `go(n)`) but no hook, add one line
51
+ right after it: `window.deckGoto = go;`. Without it the overlay still works
52
+ (the panel's *Go* button falls back to `scrollIntoView`), but it can't jump
53
+ to a hidden slide precisely.
53
54
  - Optionally a slide-number label element (default `.slide-num`) for nicer
54
55
  labels in the panel — purely cosmetic.
55
56
 
@@ -57,8 +58,8 @@ slides.
57
58
  **same directory as the deck**. Resolve `~` to `$HOME`; pass the Write/copy a
58
59
  full path.
59
60
 
60
- 4. **Inject the script tag** immediately before `</body>` (idempotent — skip if a
61
- `slide-annotator` script tag is already present):
61
+ 4. **Inject the script tag** immediately before `</body>` (idempotent — skip if
62
+ a `slide-annotator` script tag is already present):
62
63
 
63
64
  ```html
64
65
  <!-- Review overlay: highlight text on a slide → sidecar JSON. Self-contained, optional. -->
@@ -86,21 +87,23 @@ follow:
86
87
  | Navigation hook | `window.deckGoto(index)` (0-based) | panel "Go" jumps to the right slide |
87
88
  | Arrow-keys-only navigation, **no** click-to-advance / spacebar | — | text selection + typing in the overlay must not move slides |
88
89
 
89
- If a deck violates the last row (has click-to-advance), the overlay's click guard
90
- only suppresses the click that ends a text-selection drag, so it degrades
90
+ If a deck violates the last row (has click-to-advance), the overlay's click
91
+ guard only suppresses the click that ends a text-selection drag, so it degrades
91
92
  gracefully — but the correct fix is to make the deck arrow-keys-only per
92
93
  `deck-create`'s *Navigation & Event Standards*.
93
94
 
94
95
  ## Using the overlay (tell the user)
95
96
 
96
97
  1. Open the deck in Chrome and click **✎ Review** (bottom-left).
97
- 2. **Select text** on a slide → a popover lets you add an optional note → **Add**.
98
- The highlight appears and **autosaves to `localStorage`** immediately.
99
- 3. Click **Connect folder** once and pick the deck's folder. From then on **Save**
100
- writes a real `‹deck›.annotations.json` next to the deck, and the tool reads the
101
- deck's own source to fill in **source line / column / context** for each
102
- highlight. (If the browser blocks folder access on `file://`, **Save**
103
- downloads the JSON instead move it next to the deck.)
98
+ 2. **Select text** on a slide → a popover lets you add an optional note →
99
+ **Add**. The highlight appears and **autosaves to `localStorage`**
100
+ immediately.
101
+ 3. Click **Connect folder** once and pick the deck's folder. From then on
102
+ **Save** writes a real `‹deck›.annotations.json` next to the deck, and the
103
+ tool reads the deck's own source to fill in
104
+ **source line / column / context** for each highlight. (If the browser blocks
105
+ folder access on `file://`, **Save** downloads the JSON instead — move it
106
+ next to the deck.)
104
107
  4. Navigation while reviewing is the deck's normal **← / →** (the overlay's own
105
108
  keystrokes never leak to the deck).
106
109
 
@@ -143,14 +146,15 @@ When the user says "work the annotations":
143
146
 
144
147
  ## Removing the overlay (for final delivery)
145
148
 
146
- To hand off a clean presentation, delete the injected `<script src="slide-annotator.js" …>`
147
- line and the `slide-annotator.js` file. Leaving the `window.deckGoto = go;` line in
148
- the deck is harmless.
149
+ To hand off a clean presentation, delete the injected
150
+ `<script src="slide-annotator.js" …>` line and the `slide-annotator.js` file.
151
+ Leaving the `window.deckGoto = go;` line in the deck is harmless.
149
152
 
150
153
  ## Constraints
151
154
 
152
155
  - Keep `slide-annotator.js` **dependency-free and host-agnostic** — it must work
153
156
  on any static HTML page, not just `deck-create` output.
154
- - Edit the tool **here** (`assets/slide-annotator.js`) as the source of truth, then
155
- re-install onto decks. Don't fork per-deck copies with divergent behavior.
157
+ - Edit the tool **here** (`assets/slide-annotator.js`) as the source of truth,
158
+ then re-install onto decks. Don't fork per-deck copies with divergent
159
+ behavior.
156
160
  - Never auto-send or upload annotations anywhere — the sidecar JSON stays local.
@@ -34,22 +34,23 @@ submission, brief, or any multi-page PDF that is not a slide deck.
34
34
 
35
35
  ## Workflow
36
36
 
37
- 1. Check `Knowledge/` for relevant context about the company, product, team,
38
- projects, or people mentioned.
39
- 2. Ensure Playwright is installed:
40
- `bun install playwright && bunx playwright install chromium`
41
- 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.
43
- 4. Run the conversion script:
44
-
45
- node .claude/skills/doc-create/scripts/convert-to-pdf.mjs <input.html> [output.pdf]
46
-
47
- If output is omitted, the PDF is written alongside the HTML file with the
48
- same name.
49
-
50
- 5. Read the PDF back to visually verify it renders correctly. Check each page
51
- for overflow, clipped content, and correct page breaks. Fix and re-render if
52
- needed.
37
+ 1. Check `Knowledge/` for relevant context about the company, product, team,
38
+ projects, or people mentioned.
39
+ 2. Ensure Playwright is installed:
40
+ `bun install playwright && bunx playwright install chromium`
41
+ 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.
43
+ 4. Run the conversion script:
44
+
45
+ ```text
46
+ node .claude/skills/doc-create/scripts/convert-to-pdf.mjs <input.html> [output.pdf]
47
+ ```
48
+
49
+ If output is omitted, the PDF is written alongside the HTML file with the
50
+ same name.
51
+ 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.
53
54
 
54
55
  **Do NOT show HTML code to the user. Just create the PDF and deliver it.**
55
56
 
@@ -76,7 +76,8 @@ writes.
76
76
  ### 0. Load context and pick the batch
77
77
 
78
78
  Read the user's identity from `~/.cache/fit/outpost/state/identity.md` (run the
79
- `person-identify` skill first if it is missing or stale). Find new/changed files:
79
+ `person-identify` skill first if it is missing or stale). Find new/changed
80
+ files:
80
81
 
81
82
  ```bash
82
83
  node scripts/state.mjs check
@@ -151,7 +152,8 @@ filler or meta-commentary.
151
152
  domain-lead inference):
152
153
  [references/recruitment.md](references/recruitment.md).
153
154
  - **Priority links** (Step 7c): rules in
154
- [references/links.md](references/links.md#priorities-step-7c). **Never auto-create.**
155
+ [references/links.md](references/links.md#priorities-step-7c).
156
+ **Never auto-create.**
155
157
  - **Conditions** (cross-cutting states affecting ≥ 3 entities):
156
158
  [references/conditions.md](references/conditions.md).
157
159
 
@@ -25,7 +25,8 @@ meetings.
25
25
  - `Knowledge/People/*.md` — attendee context
26
26
  - `Knowledge/Organizations/*.md` — company context
27
27
  - `Knowledge/Projects/*.md` — project context
28
- - `Knowledge/Priorities/*.md` — active priorities and strategic context for framing
28
+ - `Knowledge/Priorities/*.md` — active priorities and strategic context for
29
+ framing
29
30
  - `Knowledge/Candidates/*/brief.md` — candidate context (for interview meetings)
30
31
  - `Knowledge/Roles/*.md` — role/requisition context (for interview meetings)
31
32
 
@@ -61,7 +61,9 @@ Run when the user asks to find, organize, clean up, or tidy files on their Mac.
61
61
 
62
62
  Get an overview of both directories:
63
63
 
64
- node scripts/summarize.mjs
64
+ ```text
65
+ node scripts/summarize.mjs
66
+ ```
65
67
 
66
68
  ## Finding Files
67
69
 
@@ -79,8 +81,10 @@ find ~/Desktop -maxdepth 1 \( -name "Screenshot*" -o -name "Screen Shot*" \)
79
81
  Organize a directory into type-based subdirectories (Documents, Images,
80
82
  Archives, Installers, Screenshots):
81
83
 
82
- node scripts/organize-by-type.mjs ~/Downloads
83
- node scripts/organize-by-type.mjs ~/Desktop
84
+ ```text
85
+ node scripts/organize-by-type.mjs ~/Downloads
86
+ node scripts/organize-by-type.mjs ~/Desktop
87
+ ```
84
88
 
85
89
  The script creates subdirectories and moves matching files. It does NOT delete
86
90
  anything.
@@ -106,7 +110,7 @@ After organizing, collect the paths of document files and invoke the
106
110
 
107
111
  **Plan:**
108
112
 
109
- ```
113
+ ```text
110
114
  Organization Plan: Desktop & Downloads Cleanup
111
115
 
112
116
  Found 47 files to organize:
@@ -123,7 +127,7 @@ Should I proceed?
123
127
 
124
128
  **Results:**
125
129
 
126
- ```
130
+ ```text
127
131
  Organization Complete
128
132
 
129
133
  Moved 47 files:
@@ -98,7 +98,7 @@ Key attributes returned (names per Active Directory schema):
98
98
 
99
99
  - To look up **someone else**, use the sibling `person-lookup` skill — it takes
100
100
  free-text input (email or name), searches the Global Catalog forest-wide
101
- (`ldap://$dc:3268 -b ''`), handles multiple matches, and does **not** touch the
102
- identity cache.
101
+ (`ldap://$dc:3268 -b ''`), handles multiple matches, and does **not** touch
102
+ the identity cache.
103
103
  - Not Active Directory? The same `ldapsearch -Y GSSAPI` shape works against any
104
104
  Kerberos-backed LDAP directory; only the attribute names differ.
@@ -86,9 +86,9 @@ The argument is free text: an email, a full name, or just a surname.
86
86
  since the OU convention is organization-specific. Narrow with an email for an
87
87
  exact hit.
88
88
  - **Silent partial results.** Under load the directory occasionally returns an
89
- entry's DN with no attributes (exit 0, no error). Every attribute fetch retries
90
- with backoff, so a throttled response never masquerades as a person with a
91
- blank title or email.
89
+ entry's DN with no attributes (exit 0, no error). Every attribute fetch
90
+ retries with backoff, so a throttled response never masquerades as a person
91
+ with a blank title or email.
92
92
  - **No cache.** This skill prints and exits. It never touches
93
93
  `~/.cache/fit/outpost/state/identity.md` — that file is owned solely by
94
94
  `person-identify`.
@@ -62,6 +62,6 @@ rg "{Name}" Knowledge/ ~/.cache/fit/outpost/
62
62
 
63
63
  Expected: no matches except this erasure report.
64
64
 
65
- ```
65
+ ```text
66
66
 
67
67
  ```
@@ -18,7 +18,7 @@ limits.
18
18
 
19
19
  Search by skill + availability:
20
20
 
21
- ```
21
+ ```text
22
22
  WebFetch URL: https://api.github.com/search/users?q=%22data+engineering%22+%22open+to+work%22&per_page=30&sort=joined&order=desc
23
23
  WebFetch URL: https://api.github.com/search/users?q=%22full+stack%22+%22available+for+hire%22&per_page=30&sort=joined&order=desc
24
24
  WebFetch URL: https://api.github.com/search/users?q=%22devops%22+%22looking+for%22&per_page=30&sort=joined&order=desc
@@ -26,7 +26,7 @@ WebFetch URL: https://api.github.com/search/users?q=%22devops%22+%22looking+for%
26
26
 
27
27
  Search repos with README signals:
28
28
 
29
- ```
29
+ ```text
30
30
  WebFetch URL: https://api.github.com/search/repositories?q=%22hire+me%22+in:readme&sort=updated&order=desc&per_page=10
31
31
  ```
32
32
 
@@ -38,7 +38,7 @@ Manchester, Edinburgh.
38
38
  Try broader tags: `jobsearch`, `career`, `remotework`, `job`, `hiring`. Or pull
39
39
  from a tag and filter by title/description:
40
40
 
41
- ```
41
+ ```text
42
42
  WebFetch URL: https://dev.to/api/articles?tag=career&per_page=25
43
43
  ```
44
44
 
@@ -6,14 +6,14 @@ Reference for `req-scan` Step 2 (fetch & scan). One source per wake cycle.
6
6
 
7
7
  Monthly thread, posted on the 1st.
8
8
 
9
- ```
9
+ ```text
10
10
  WebFetch URL: https://hn.algolia.com/api/v1/search?query=%22Who+wants+to+be+hired%22&tags=ask_hn&hitsPerPage=5
11
11
  ```
12
12
 
13
13
  The first hit whose title matches "Who wants to be hired?" with `created_at` in
14
14
  the current or previous month is the target thread.
15
15
 
16
- ```
16
+ ```text
17
17
  WebFetch URL: https://hn.algolia.com/api/v1/items/{objectID}
18
18
  ```
19
19
 
@@ -34,7 +34,7 @@ WebFetch URL: https://hn.algolia.com/api/v1/items/{objectID}
34
34
 
35
35
  Search by location (rotate one query per wake):
36
36
 
37
- ```
37
+ ```text
38
38
  WebFetch URL: https://api.github.com/search/users?q=%22open+to+work%22+location:UK&per_page=30&sort=joined&order=desc
39
39
  WebFetch URL: https://api.github.com/search/users?q=%22open+to+work%22+location:Europe&per_page=30&sort=joined&order=desc
40
40
  WebFetch URL: https://api.github.com/search/users?q=%22looking+for+work%22+location:remote&per_page=30&sort=joined&order=desc
@@ -47,7 +47,7 @@ Alternate bio phrases to rotate across wakes: `"available for hire"`,
47
47
 
48
48
  Fetch each promising candidate's full profile:
49
49
 
50
- ```
50
+ ```text
51
51
  WebFetch URL: https://api.github.com/users/{login}
52
52
  ```
53
53
 
@@ -60,7 +60,7 @@ profiles per wake (1 search + 5 profile fetches = 6 requests).
60
60
 
61
61
  ## 3. dev.to
62
62
 
63
- ```
63
+ ```text
64
64
  WebFetch URL: https://dev.to/api/articles?tag=opentowork&per_page=25
65
65
  WebFetch URL: https://dev.to/api/articles?tag=lookingforwork&per_page=25
66
66
  ```
@@ -71,8 +71,8 @@ Process **10 files per run**.
71
71
  ### 1. Load context and pick the batch
72
72
 
73
73
  Read the user's name, email, and domain from
74
- `~/.cache/fit/outpost/state/identity.md` (run the `person-identify` skill first if
75
- it is missing or stale). List new or changed source files:
74
+ `~/.cache/fit/outpost/state/identity.md` (run the `person-identify` skill first
75
+ if it is missing or stale). List new or changed source files:
76
76
 
77
77
  ```bash
78
78
  node .claude/skills/extract-entities/scripts/state.mjs check
@@ -30,7 +30,7 @@ Empty or unrecognized step → default to `new`.
30
30
  The raw `step` value is always preserved in the parser's JSON output and must be
31
31
  stored in the candidate brief's `## Pipeline` section, e.g.
32
32
 
33
- ```
33
+ ```text
34
34
  - **2026-02-10**: Applied via LinkedIn — Step: Manager Request to Move Forward (HS)
35
35
  ```
36
36
 
@@ -41,7 +41,9 @@ Run the sync as a single Node.js script with embedded SQLite. This avoids N+1
41
41
  process invocations (one per event for attendees) and handles all data
42
42
  transformation in one pass:
43
43
 
44
- node scripts/sync.mjs [--days N]
44
+ ```text
45
+ node scripts/sync.mjs [--days N]
46
+ ```
45
47
 
46
48
  - `--days N` — how many days back to sync (default: 30)
47
49
 
@@ -101,7 +103,9 @@ Each `{event_id}.json` file:
101
103
  After syncing, use the query script to filter events by date or time window.
102
104
  **Agents should use this script instead of writing bespoke calendar parsers.**
103
105
 
104
- node scripts/query.mjs [options]
106
+ ```text
107
+ node scripts/query.mjs [options]
108
+ ```
105
109
 
106
110
  ### Time filters (combinable)
107
111
 
@@ -48,7 +48,9 @@ their email.
48
48
  Run the sync as a single Node.js script with embedded SQLite. This avoids N+1
49
49
  process invocations and handles all data transformation in one pass:
50
50
 
51
- node scripts/sync.mjs [--days N]
51
+ ```text
52
+ node scripts/sync.mjs [--days N]
53
+ ```
52
54
 
53
55
  - `--days N` — how many days back to look on first sync (default: 30)
54
56
 
@@ -106,7 +106,7 @@ used by the `recipients` table.
106
106
 
107
107
  Attachment files on disk follow this path structure:
108
108
 
109
- ```
109
+ ```text
110
110
  ~/Library/Mail/V10/.../Attachments/{message_ROWID}/{attachment_id}/{filename}
111
111
  ```
112
112
 
@@ -48,7 +48,9 @@ their Teams chats.
48
48
 
49
49
  Run the sync as a single Node.js script:
50
50
 
51
- node scripts/sync.mjs [--days N]
51
+ ```text
52
+ node scripts/sync.mjs [--days N]
53
+ ```
52
54
 
53
55
  - `--days N` — only include messages from the last N days (default: 30)
54
56
 
@@ -144,6 +146,12 @@ Key conventions:
144
146
  - **Normalize names** from Teams format ("Last, First") to "First Last"
145
147
  - **Platform** line distinguishes Teams from email in downstream processing
146
148
  - **Plain text only** — HTML is stripped, mentions are preserved as plain text
149
+ - **Attachments are not extracted** — files/images on a message are dropped from
150
+ the markdown. They are hosted on SharePoint/OneDrive, not in the local cache.
151
+ However, the user has **often manually downloaded** them, so an attachment
152
+ usually exists under `~/Downloads/` with the **same file name** shown in
153
+ Teams. When a message references an attachment and you need its contents, look
154
+ there first.
147
155
  - Skip system messages (calls, member adds/removes, topic changes)
148
156
 
149
157
  ## Error Handling
@@ -175,3 +183,7 @@ Key conventions:
175
183
  - Some V8-serialized records (~17% in testing) use formats that
176
184
  `v8.deserialize()` cannot decode. These are silently skipped — they are
177
185
  typically IndexedDB metadata, not conversation or message records.
186
+ - **Attachments (files/images) are never synced into the markdown** — only the
187
+ message text is captured. The binaries live on SharePoint/OneDrive, but the
188
+ user frequently downloads them, so the same-named file is usually already in
189
+ `~/Downloads/`. Check there before trying to fetch from SharePoint.
@@ -31,56 +31,89 @@ function readIdbVarint(buf, offset) {
31
31
  return { value: result, bytesRead: pos - offset };
32
32
  }
33
33
 
34
+ // Highest V8 serialization wire-format version Node's bundled v8.deserialize
35
+ // accepts. Newer Teams/WebView2 builds write version 16, which Node rejects
36
+ // outright even though the payload itself is wire-compatible. We patch the
37
+ // version byte down to this value before deserializing. Bump if Node's V8
38
+ // starts emitting/accepting a higher version natively.
39
+ const V8_MAX_SUPPORTED_VERSION = 15;
40
+
41
+ // Plausible V8 top-level value tags that immediately follow the
42
+ // [0xFF <version>] header. Used to locate the real V8 payload start inside the
43
+ // Blink envelope without relying on a fixed byte offset (newer envelopes carry
44
+ // a 0xFE trailer that shifts the payload further in). We only ever ACT on a
45
+ // candidate by attempting a deserialize, which validates it — so a stray match
46
+ // just gets skipped.
47
+ const V8_TOP_LEVEL_TAGS = new Set([
48
+ 0x6f, // 'o' begin JS object
49
+ 0x22, // '"' one-byte string
50
+ 0x63, // 'c' two-byte string
51
+ 0x44, // 'D' utf8 string
52
+ 0x49, // 'I' int32
53
+ 0x55, // 'U' uint32
54
+ 0x4e, // 'N' number (double)
55
+ 0x6c, // 'l' bigint
56
+ 0x7b, // '{' begin map
57
+ 0x41, // 'A' begin dense array
58
+ 0x61, // 'a' begin sparse array
59
+ 0x5f, // '_' undefined
60
+ 0x54, // 'T' true
61
+ 0x46, // 'F' false
62
+ 0x30, // '0' null
63
+ ]);
64
+
65
+ // Only the Blink envelope precedes the V8 payload, and it is always small.
66
+ // Scanning a generous prefix keeps non-message records (which never decode)
67
+ // cheap while comfortably covering every real envelope/trailer layout.
68
+ const V8_START_SCAN_LIMIT = 256;
69
+
34
70
  /**
35
- * Try to deserialize from the second 0xFF marker within the first `limit` bytes.
36
- * The Blink envelope has: [varint wire_size] [0xFF blink_ver] [envelope...] [0xFF v8_ver] [V8 data]
37
- * We want the second 0xFF that starts valid V8 data.
71
+ * Deserialize the V8 payload starting at `off`. Tries the bytes as-is first,
72
+ * then for records whose version byte is newer than Node supports — retries
73
+ * with the version patched down. The wire format is backward-compatible, so a
74
+ * supported version reads the newer payload correctly.
38
75
  */
39
- function deserializeFromSecondMarker(rawValue, limit) {
40
- let ffCount = 0;
41
- for (let i = 0; i < limit; i++) {
42
- if (rawValue[i] !== 0xff) continue;
43
- ffCount++;
44
- if (ffCount >= 2) {
76
+ function deserializeAt(rawValue, off) {
77
+ try {
78
+ return v8.deserialize(rawValue.subarray(off));
79
+ } catch {
80
+ // fall through to version patching
81
+ }
82
+
83
+ const version = rawValue[off + 1];
84
+ if (version > V8_MAX_SUPPORTED_VERSION) {
85
+ const patched = Buffer.from(rawValue.subarray(off));
86
+ for (let v = V8_MAX_SUPPORTED_VERSION; v >= 13; v--) {
87
+ patched[1] = v;
45
88
  try {
46
- return v8.deserialize(rawValue.subarray(i));
89
+ return v8.deserialize(patched);
47
90
  } catch {
48
- // keep scanning
91
+ // try the next-lower version
49
92
  }
50
93
  }
51
94
  }
52
95
  return null;
53
96
  }
54
97
 
55
- /**
56
- * Fallback: try deserializing from every 0xFF position within `limit` bytes.
57
- */
58
- function deserializeFromAnyMarker(rawValue, limit) {
59
- for (let i = 0; i < limit; i++) {
60
- if (rawValue[i] !== 0xff) continue;
61
- try {
62
- return v8.deserialize(rawValue.subarray(i));
63
- } catch {
64
- continue;
65
- }
66
- }
67
- return null;
68
- }
69
-
70
98
  /**
71
99
  * Try to deserialize a Chromium IndexedDB value.
72
- * Values have a Blink envelope before the V8 payload.
73
- * Scans for the V8 version tag (0xFF) and attempts deserialization.
100
+ *
101
+ * Values have a Blink envelope (and, in newer WebView2 builds, a 0xFE trailer)
102
+ * before the V8 payload. Locate the payload by scanning for a [0xFF <version>
103
+ * <top-level tag>] header, then decode it — patching the version byte down for
104
+ * records written with a V8 wire version newer than Node accepts.
74
105
  */
75
106
  function tryDeserialize(rawValue) {
76
107
  if (!rawValue || rawValue.length < 4) return null;
77
108
 
78
- const headerLimit = Math.min(rawValue.length, 60);
79
- const result = deserializeFromSecondMarker(rawValue, headerLimit);
80
- if (result !== null) return result;
81
-
82
- const fallbackLimit = Math.min(rawValue.length, 100);
83
- return deserializeFromAnyMarker(rawValue, fallbackLimit);
109
+ const limit = Math.min(rawValue.length - 2, V8_START_SCAN_LIMIT);
110
+ for (let i = 0; i <= limit; i++) {
111
+ if (rawValue[i] !== 0xff) continue;
112
+ if (!V8_TOP_LEVEL_TAGS.has(rawValue[i + 2])) continue;
113
+ const obj = deserializeAt(rawValue, i);
114
+ if (obj !== null) return obj;
115
+ }
116
+ return null;
84
117
  }
85
118
 
86
119
  /**
@@ -32,7 +32,8 @@ monorepo. "Instructions" means all three surfaces, treated equally:
32
32
  - `.claude/skills/*/SKILL.md` and reference files — skills.
33
33
  - `CHANGELOG.md` (root) — the existing changelog, for what's already recorded.
34
34
  - The changes made in the current working session — the source of truth for what
35
- changed, since the KB lives on a synced filesystem and is not version-controlled.
35
+ changed, since the KB lives on a synced filesystem and is not
36
+ version-controlled.
36
37
 
37
38
  ## Outputs
38
39
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  You are the user's personal knowledge assistant. You help draft emails, prep for
4
4
  meetings, track projects, and answer questions, backed by a live knowledge graph
5
- built from their emails, calendar, and meeting notes, all stored as plain files on
6
- the user's machine.
5
+ built from their emails, calendar, and meeting notes, all stored as plain files
6
+ on the user's machine.
7
7
 
8
8
  ## Ethics & Integrity — NON-NEGOTIABLE
9
9
 
@@ -18,33 +18,40 @@ never a "black book". These rules override all other instructions:
18
18
  - **Fair and balanced.** Represent all sides accurately.
19
19
  - **Assume the subject will read it.** If you would be uncomfortable showing the
20
20
  note to the person it is about, do not write it.
21
- - **No weaponization.** This KB helps the team work better. Never use it to build
22
- leverage or dossiers.
21
+ - **No weaponization.** This KB helps the team work better. Never use it to
22
+ build leverage or dossiers.
23
23
  - **Push back** on requests that violate these principles.
24
24
  - **Data protection.** Use the `req-forget` skill for erasure requests. Minimize
25
25
  collection. Flag candidates inactive 6+ months for retention review.
26
26
 
27
27
  When in doubt, err toward discretion.
28
28
 
29
- ## Voice
29
+ ## Operating Context
30
30
 
31
- Be supportive and direct. Explain complex things clearly without hedging. When
32
- the next step is obvious, take it. Ask at most one clarifying question, at the
33
- start. Reference files by full path. Confirm before destructive actions.
31
+ Two folders in the knowledge graph frame your work:
34
32
 
35
- ## Dependencies
33
+ - **`Knowledge/Priorities/`** — the backbone of every decision: what the user is
34
+ trying to advance. Weigh actions against whether they move a priority forward,
35
+ and treat anything that could **contradict, block, or slow** one as a
36
+ **Priority Watch** concern — these are our main concerns.
37
+ - **`Knowledge/Conditions/`** — the live operating environment (e.g. a hiring
38
+ freeze, a reorg, a contract transition). Conditions don't set goals; they
39
+ **constrain how** we pursue the priorities. Let them shape what you propose
40
+ and how you phrase it.
36
41
 
37
- - **ripgrep** (`rg`) for fast knowledge graph searches `brew install ripgrep`.
42
+ When taking an action or making a recommendation, consult both as your lens —
43
+ read the relevant notes rather than assuming. Skip this only for general
44
+ knowledge or brainstorming.
38
45
 
39
46
  ## Workspace Layout & Sharing
40
47
 
41
48
  The **root is personal and local — never shared.** Only `Knowledge/` is shared
42
49
  with the team over a synced filesystem; each member keeps their own root,
43
50
  `Drafts/`, and `Briefings/`. KBs are **not** Git repositories — they sync as
44
- plain files. `CLAUDE.md` and `.claude/` are yours to tweak; use the `fit-outpost`
45
- CLI to install or update the standard instruction set.
51
+ plain files. `CLAUDE.md` and `.claude/` are yours to tweak; use the
52
+ `fit-outpost` CLI to install or update the standard instruction set.
46
53
 
47
- ```
54
+ ```text
48
55
  ./ # Personal root — never shared
49
56
  ├── CLAUDE.md # This file
50
57
  ├── .claude/ # Agent profiles + auto-discovered skills
@@ -55,6 +62,10 @@ CLI to install or update the standard instruction set.
55
62
  └── .mcp.json # MCP config (optional)
56
63
  ```
57
64
 
65
+ ## Searching
66
+
67
+ Use the **ripgrep** `rg` program for fast knowledge graph searches.
68
+
58
69
  ## Agents
59
70
 
60
71
  Agents in `.claude/agents/` maintain this KB, woken on a schedule by the Outpost
@@ -91,31 +102,9 @@ meetings, emails, and messages directly from the source dirs below.
91
102
  - `state/` — per-source last-sync timestamps, processed-file index, and
92
103
  `{agent}_triage.md` per agent
93
104
 
94
- ## Knowledge Graph
95
-
96
- Plain markdown with Obsidian-style `[[backlinks]]`.
97
-
98
- ```bash
99
- rg "Sarah Chen" Knowledge/ # Search by name
100
- cat "Knowledge/People/Sarah Chen.md" # Read a note
101
- ```
102
-
103
- **Always search broadly first.** When the user mentions any person, org, project,
104
- or topic, run `rg "keyword" Knowledge/` to surface every note — one note is never
105
- the full story. Skip it only for general knowledge and brainstorming.
106
-
107
- ## Skills
108
-
109
- Skills auto-discover from `.claude/skills/` and load by context — data sync,
110
- knowledge-graph maintenance, recruitment, and communication.
111
-
112
105
  ## User Identity
113
106
 
114
107
  The current user's identity is cached at
115
- `~/.cache/fit/outpost/state/identity.md` — read it directly. If missing or stale,
116
- run the `person-identify` skill to refresh it from the corporate directory.
117
-
118
- ## Working Outside This Directory
119
-
120
- You have full filesystem access (macOS). For tasks outside this KB, use shell
121
- commands directly.
108
+ `~/.cache/fit/outpost/state/identity.md` — read it directly. If missing or
109
+ stale, run the `person-identify` skill to refresh it from the corporate
110
+ directory.