@forwardimpact/outpost 3.8.1 → 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.
- package/config/scheduler.json +12 -6
- package/package.json +1 -1
- package/src/agent-runner.js +27 -0
- package/src/kb-manager.js +54 -1
- package/src/outpost.js +12 -5
- package/src/privilege.js +45 -0
- package/templates/.claude/agents/chief-of-staff.md +9 -7
- package/templates/.claude/agents/concierge.md +6 -4
- package/templates/.claude/agents/head-hunter.md +6 -4
- package/templates/.claude/agents/librarian.md +14 -10
- package/templates/.claude/agents/postman.md +6 -4
- package/templates/.claude/agents/recruiter.md +8 -5
- package/templates/.claude/skills/anarlog-follow/references/coaching.md +3 -3
- package/templates/.claude/skills/anarlog-trim/SKILL.md +9 -3
- package/templates/.claude/skills/candidate-report/SKILL.md +4 -3
- package/templates/.claude/skills/changelog/SKILL.md +10 -10
- package/templates/.claude/skills/deck-create/SKILL.md +23 -22
- package/templates/.claude/skills/deck-review/SKILL.md +29 -25
- package/templates/.claude/skills/doc-create/SKILL.md +17 -16
- package/templates/.claude/skills/extract-entities/SKILL.md +4 -2
- package/templates/.claude/skills/meeting-prep/SKILL.md +2 -1
- package/templates/.claude/skills/organize-files/SKILL.md +9 -5
- package/templates/.claude/skills/person-identify/SKILL.md +2 -2
- package/templates/.claude/skills/person-lookup/SKILL.md +3 -3
- package/templates/.claude/skills/req-forget/references/report-template.md +1 -1
- package/templates/.claude/skills/req-scan/references/fallbacks.md +3 -3
- package/templates/.claude/skills/req-scan/references/sources.md +5 -5
- package/templates/.claude/skills/req-track/SKILL.md +2 -2
- package/templates/.claude/skills/req-workday/references/status-mapping.md +1 -1
- package/templates/.claude/skills/sync-apple-calendar/SKILL.md +6 -2
- package/templates/.claude/skills/sync-apple-mail/SKILL.md +3 -1
- package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +1 -1
- package/templates/.claude/skills/sync-teams/SKILL.md +13 -1
- package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +67 -34
- package/templates/.claude/skills/upstream-instructions/SKILL.md +2 -1
- package/templates/CLAUDE.md +27 -38
|
@@ -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.
|
|
36
|
-
|
|
37
|
-
2.
|
|
38
|
-
|
|
39
|
-
3.
|
|
40
|
-
|
|
41
|
-
4.
|
|
42
|
-
5.
|
|
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.
|
|
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
|
|
86
|
-
with typing in overlay inputs; the rest are redundant and
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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
|
|
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
|
|
103
|
-
the `slide-annotator.js` review overlay (highlight text on a slide →
|
|
104
|
-
of feedback that an agent acts on). After producing an interactive
|
|
105
|
-
can offer to run `deck-review` to make it reviewable; see that
|
|
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 +
|
|
12
|
-
and, once the folder is connected, the resolved
|
|
13
|
-
lines** — so an agent can locate and edit the
|
|
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
|
|
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
|
|
50
|
-
after it: `window.deckGoto = go;`. Without it the overlay still works
|
|
51
|
-
panel's *Go* button falls back to `scrollIntoView`), but it can't jump
|
|
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
|
|
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
|
|
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 →
|
|
98
|
-
The highlight appears and **autosaves to `localStorage`**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
147
|
-
line and the `slide-annotator.js` file.
|
|
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,
|
|
155
|
-
re-install onto decks. Don't fork per-deck copies with divergent
|
|
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.
|
|
38
|
-
|
|
39
|
-
2.
|
|
40
|
-
|
|
41
|
-
3.
|
|
42
|
-
|
|
43
|
-
4.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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).
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
|
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
|
|
90
|
-
with backoff, so a throttled response never masquerades as a person
|
|
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`.
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -48,7 +48,9 @@ their Teams chats.
|
|
|
48
48
|
|
|
49
49
|
Run the sync as a single Node.js script:
|
|
50
50
|
|
|
51
|
-
|
|
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
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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(
|
|
89
|
+
return v8.deserialize(patched);
|
|
47
90
|
} catch {
|
|
48
|
-
//
|
|
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
|
-
*
|
|
73
|
-
*
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
35
|
+
changed, since the KB lives on a synced filesystem and is not
|
|
36
|
+
version-controlled.
|
|
36
37
|
|
|
37
38
|
## Outputs
|
|
38
39
|
|