@forwardimpact/outpost 3.9.0 → 3.11.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/package.json +1 -1
- package/src/kb-manager.js +27 -1
- 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/SKILL.md +2 -2
- 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 +36 -26
- package/templates/.claude/skills/deck-review/assets/slide-annotator.js +100 -15
- package/templates/.claude/skills/doc-create/SKILL.md +17 -16
- package/templates/.claude/skills/extract-entities/SKILL.md +7 -4
- package/templates/.claude/skills/extract-entities/references/recruitment.md +13 -3
- package/templates/.claude/skills/meeting-prep/SKILL.md +5 -3
- 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-decide/SKILL.md +4 -2
- 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-screen/SKILL.md +4 -2
- package/templates/.claude/skills/req-track/SKILL.md +19 -15
- package/templates/.claude/skills/req-track/references/fields.md +4 -2
- package/templates/.claude/skills/req-workday/SKILL.md +4 -2
- package/templates/.claude/skills/req-workday/references/status-mapping.md +1 -1
- package/templates/.claude/skills/req-workday/references/templates.md +3 -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
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
* (b) resolve each highlight to its SOURCE line/column + context lines by
|
|
14
14
|
* reading the page’s own source file. No build step, no server, no deps.
|
|
15
15
|
*
|
|
16
|
+
* The connected folder is remembered across reloads (the directory handle is
|
|
17
|
+
* stored in IndexedDB). On reload it reconnects silently if the browser still
|
|
18
|
+
* grants permission; otherwise the button reads “Reconnect folder” and one
|
|
19
|
+
* click re-grants access without re-picking the folder.
|
|
20
|
+
*
|
|
16
21
|
* Optional host hook: if the page defines `window.deckGoto(index)` the panel’s
|
|
17
22
|
* “Go” buttons will navigate to the right slide. Without it, the tool still
|
|
18
23
|
* works (it falls back to scrollIntoView).
|
|
@@ -60,7 +65,8 @@
|
|
|
60
65
|
/* ----------------------------- state ------------------------------------ */
|
|
61
66
|
var state = loadLocal(); // { version, tool, target, annotations:[] }
|
|
62
67
|
var review = false;
|
|
63
|
-
var dirHandle = null; // File System Access directory handle
|
|
68
|
+
var dirHandle = null; // File System Access directory handle (active)
|
|
69
|
+
var rememberedHandle = null; // handle from a prior session, awaiting re-grant
|
|
64
70
|
var sourceText = null; // page source (for line/column)
|
|
65
71
|
var pending = null; // annotation awaiting note + confirm
|
|
66
72
|
var ui = {}; // DOM refs
|
|
@@ -150,6 +156,7 @@
|
|
|
150
156
|
ui.count = ui.panel.querySelector(".sa-count");
|
|
151
157
|
ui.status = ui.panel.querySelector(".sa-status");
|
|
152
158
|
ui.list = ui.panel.querySelector(".sa-list");
|
|
159
|
+
ui.connectBtn = ui.panel.querySelector('[data-act="connect"]');
|
|
153
160
|
ui.note = ui.pop.querySelector("textarea");
|
|
154
161
|
var onlyThisSlide = false;
|
|
155
162
|
|
|
@@ -435,28 +442,105 @@
|
|
|
435
442
|
if (ui.count) ui.count.textContent = state.annotations.length;
|
|
436
443
|
}
|
|
437
444
|
|
|
445
|
+
/* Persist the connected directory handle across reloads. FileSystemHandles are
|
|
446
|
+
* structured-cloneable, so they live in IndexedDB (localStorage is strings
|
|
447
|
+
* only). Re-granting read/write permission after a reload needs a user
|
|
448
|
+
* gesture, so a remembered folder reconnects with one click via the same
|
|
449
|
+
* "Connect folder" button rather than the full directory picker. Keyed by
|
|
450
|
+
* cfg.target so distinct decks in a folder don't clobber each other. */
|
|
451
|
+
var IDB_NAME = "slide-annotator", IDB_STORE = "handles";
|
|
452
|
+
function idbOpen() {
|
|
453
|
+
return new Promise(function (resolve, reject) {
|
|
454
|
+
if (!window.indexedDB) { reject(new Error("no-idb")); return; }
|
|
455
|
+
var req = indexedDB.open(IDB_NAME, 1);
|
|
456
|
+
req.onupgradeneeded = function () { req.result.createObjectStore(IDB_STORE); };
|
|
457
|
+
req.onsuccess = function () { resolve(req.result); };
|
|
458
|
+
req.onerror = function () { reject(req.error); };
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
function idbGet(key) {
|
|
462
|
+
return idbOpen().then(function (db) {
|
|
463
|
+
return new Promise(function (resolve, reject) {
|
|
464
|
+
var r = db.transaction(IDB_STORE, "readonly").objectStore(IDB_STORE).get(key);
|
|
465
|
+
r.onsuccess = function () { resolve(r.result || null); };
|
|
466
|
+
r.onerror = function () { reject(r.error); };
|
|
467
|
+
});
|
|
468
|
+
}).catch(function () { return null; });
|
|
469
|
+
}
|
|
470
|
+
function idbSet(key, val) {
|
|
471
|
+
return idbOpen().then(function (db) {
|
|
472
|
+
return new Promise(function (resolve, reject) {
|
|
473
|
+
var tx = db.transaction(IDB_STORE, "readwrite");
|
|
474
|
+
tx.objectStore(IDB_STORE).put(val, key);
|
|
475
|
+
tx.oncomplete = function () { resolve(true); };
|
|
476
|
+
tx.onerror = function () { reject(tx.error); };
|
|
477
|
+
});
|
|
478
|
+
}).catch(function () { return false; });
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function updateConnectLabel() {
|
|
482
|
+
if (!ui.connectBtn) return;
|
|
483
|
+
ui.connectBtn.textContent = dirHandle ? "Folder connected"
|
|
484
|
+
: rememberedHandle ? "Reconnect folder" : "Connect folder";
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Adopt a (freshly picked or re-granted) handle: link source, load any
|
|
488
|
+
// sidecar, and remember it for next time. Shared by connect and restore.
|
|
489
|
+
async function adoptFolder(handle) {
|
|
490
|
+
dirHandle = handle;
|
|
491
|
+
// read page source → enables source line/column resolution
|
|
492
|
+
try {
|
|
493
|
+
var fh = await dirHandle.getFileHandle(cfg.target);
|
|
494
|
+
sourceText = await (await fh.getFile()).text();
|
|
495
|
+
state.annotations.forEach(function (a) { a.source = locateInSource(a); });
|
|
496
|
+
} catch (e) { sourceText = null; setStatus("Connected, but '" + cfg.target + "' not found in folder — line numbers unavailable."); }
|
|
497
|
+
// load an existing sidecar if present
|
|
498
|
+
try {
|
|
499
|
+
var sh = await dirHandle.getFileHandle(SIDECAR);
|
|
500
|
+
mergeLoaded(JSON.parse(await (await sh.getFile()).text()));
|
|
501
|
+
} catch (e) { /* none yet */ }
|
|
502
|
+
await idbSet(cfg.target, dirHandle);
|
|
503
|
+
rememberedHandle = null;
|
|
504
|
+
updateConnectLabel();
|
|
505
|
+
persistLocal(); renderPanel(); renderHighlights();
|
|
506
|
+
setStatus("Connected. Saving to " + SIDECAR + (sourceText ? " · source linked" : ""));
|
|
507
|
+
}
|
|
508
|
+
|
|
438
509
|
async function connectFolder() {
|
|
510
|
+
// Prefer a folder remembered from a prior session: re-granting permission is
|
|
511
|
+
// a single click (this click IS the required user gesture), no picker.
|
|
512
|
+
if (rememberedHandle) {
|
|
513
|
+
try {
|
|
514
|
+
var perm = await rememberedHandle.requestPermission({ mode: "readwrite" });
|
|
515
|
+
if (perm === "granted") { await adoptFolder(rememberedHandle); return; }
|
|
516
|
+
} catch (e) { /* fall through to the picker */ }
|
|
517
|
+
rememberedHandle = null; updateConnectLabel();
|
|
518
|
+
}
|
|
439
519
|
if (!window.showDirectoryPicker) { setStatus("Folder access unsupported in this browser. Use Save (downloads the JSON)."); return; }
|
|
440
520
|
try {
|
|
441
|
-
|
|
442
|
-
// read page source → enables source line/column resolution
|
|
443
|
-
try {
|
|
444
|
-
var fh = await dirHandle.getFileHandle(cfg.target);
|
|
445
|
-
sourceText = await (await fh.getFile()).text();
|
|
446
|
-
state.annotations.forEach(function (a) { a.source = locateInSource(a); });
|
|
447
|
-
} catch (e) { sourceText = null; setStatus("Connected, but '" + cfg.target + "' not found in folder — line numbers unavailable."); }
|
|
448
|
-
// load an existing sidecar if present
|
|
449
|
-
try {
|
|
450
|
-
var sh = await dirHandle.getFileHandle(SIDECAR);
|
|
451
|
-
mergeLoaded(JSON.parse(await (await sh.getFile()).text()));
|
|
452
|
-
} catch (e) { /* none yet */ }
|
|
453
|
-
persistLocal(); renderPanel(); renderHighlights();
|
|
454
|
-
setStatus("Connected. Saving to " + SIDECAR + (sourceText ? " · source linked" : ""));
|
|
521
|
+
await adoptFolder(await window.showDirectoryPicker({ mode: "readwrite" }));
|
|
455
522
|
} catch (e) {
|
|
456
523
|
if (e && e.name !== "AbortError") setStatus("Folder access blocked (" + e.name + "). Use Save to download the JSON instead.");
|
|
457
524
|
}
|
|
458
525
|
}
|
|
459
526
|
|
|
527
|
+
// On load, try to restore a previously connected folder. queryPermission needs
|
|
528
|
+
// no gesture; if it's still "granted" we reconnect silently. If it's "prompt",
|
|
529
|
+
// we keep the handle so the next "Connect folder" click reconnects with one
|
|
530
|
+
// grant dialog (no picker). Handles cleared/denied storage are ignored.
|
|
531
|
+
function restoreFolder() {
|
|
532
|
+
idbGet(cfg.target).then(function (handle) {
|
|
533
|
+
if (!handle || typeof handle.queryPermission !== "function") return;
|
|
534
|
+
handle.queryPermission({ mode: "readwrite" }).then(function (perm) {
|
|
535
|
+
if (perm === "granted") { adoptFolder(handle); }
|
|
536
|
+
else {
|
|
537
|
+
rememberedHandle = handle; updateConnectLabel();
|
|
538
|
+
if (review) setStatus("Folder '" + cfg.target + "' remembered — click Reconnect folder.");
|
|
539
|
+
}
|
|
540
|
+
}).catch(function () {});
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
460
544
|
async function saveDisk() {
|
|
461
545
|
if (sourceText) state.annotations.forEach(function (a) { if (!a.source) a.source = locateInSource(a); });
|
|
462
546
|
var json = JSON.stringify(serialize(), null, 2);
|
|
@@ -591,4 +675,5 @@
|
|
|
591
675
|
};
|
|
592
676
|
|
|
593
677
|
updateCount();
|
|
678
|
+
restoreFolder();
|
|
594
679
|
})();
|
|
@@ -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
|
|
|
@@ -41,8 +41,9 @@ under `Knowledge/`. The core knowledge-graph builder.
|
|
|
41
41
|
auto-created.
|
|
42
42
|
- `Knowledge/Conditions/` — created when cross-cutting patterns are detected, or
|
|
43
43
|
updated.
|
|
44
|
-
- `Knowledge/Roles
|
|
45
|
-
metadata.
|
|
44
|
+
- `Knowledge/Roles/*.md`, `Knowledge/Candidates/*/brief.md` — enriched with
|
|
45
|
+
inferred metadata. New role stubs created with `**Status:** open`; update to
|
|
46
|
+
`**Status:** closed` when a role closes.
|
|
46
47
|
- `~/.cache/fit/outpost/state/graph_processed` — updated.
|
|
47
48
|
|
|
48
49
|
<do_confirm_checklist goal="Verify the batch produced clean, linked,
|
|
@@ -76,7 +77,8 @@ writes.
|
|
|
76
77
|
### 0. Load context and pick the batch
|
|
77
78
|
|
|
78
79
|
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
|
|
80
|
+
`person-identify` skill first if it is missing or stale). Find new/changed
|
|
81
|
+
files:
|
|
80
82
|
|
|
81
83
|
```bash
|
|
82
84
|
node scripts/state.mjs check
|
|
@@ -151,7 +153,8 @@ filler or meta-commentary.
|
|
|
151
153
|
domain-lead inference):
|
|
152
154
|
[references/recruitment.md](references/recruitment.md).
|
|
153
155
|
- **Priority links** (Step 7c): rules in
|
|
154
|
-
[references/links.md](references/links.md#priorities-step-7c).
|
|
156
|
+
[references/links.md](references/links.md#priorities-step-7c).
|
|
157
|
+
**Never auto-create.**
|
|
155
158
|
- **Conditions** (cross-cutting states affecting ≥ 3 entities):
|
|
156
159
|
[references/conditions.md](references/conditions.md).
|
|
157
160
|
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
Reference for `extract-entities` Step 7b. Enrich `Knowledge/Roles/` and
|
|
4
4
|
`Knowledge/Candidates/` with metadata that no single source carries.
|
|
5
5
|
|
|
6
|
+
All Role files are flat in `Knowledge/Roles/`. The `**Status:**` field
|
|
7
|
+
distinguishes:
|
|
8
|
+
|
|
9
|
+
- **`Status: open`** — active openings (use for new candidates and table
|
|
10
|
+
rebuilds).
|
|
11
|
+
- **`Status: closed`** — completed/withdrawn roles (read-only).
|
|
12
|
+
|
|
6
13
|
## Requisition number detection
|
|
7
14
|
|
|
8
15
|
Scan email subjects and bodies for requisition numbers (e.g. 7-digit Workday
|
|
@@ -10,9 +17,12 @@ IDs).
|
|
|
10
17
|
|
|
11
18
|
1. `ls Knowledge/Roles/ | grep "{req_number}"` — does a Role file exist?
|
|
12
19
|
2. **No file:** create a stub using the Role-stub template in `req-track` Step
|
|
13
|
-
0b
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
0b, with `**Status:** open`. Search `rg "{req_number}" Knowledge/` for
|
|
21
|
+
context to enrich it.
|
|
22
|
+
3. **File exists:** check the `**Status:**` field. If `open`, check whether the
|
|
23
|
+
email provides new metadata (hiring manager, recruiter, locations) and update
|
|
24
|
+
the Role file. If `closed`, link for historical reference only; do not add
|
|
25
|
+
new candidates or rebuild tables.
|
|
16
26
|
|
|
17
27
|
## Hiring manager — calendar inference
|
|
18
28
|
|
|
@@ -25,9 +25,11 @@ 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
|
-
- `Knowledge/Roles/*.md` — role/requisition context (for interview meetings)
|
|
31
|
+
- `Knowledge/Roles/*.md` — role/requisition context (for interview meetings);
|
|
32
|
+
check the `**Status:**` field to distinguish active from historical reqs
|
|
31
33
|
|
|
32
34
|
## Outputs
|
|
33
35
|
|
|
@@ -144,7 +146,7 @@ When preparing for interview meetings (title contains "Interview", "Screening",
|
|
|
144
146
|
|
|
145
147
|
1. **Read the candidate brief:** `Knowledge/Candidates/{Name}/brief.md`
|
|
146
148
|
2. **Read the Role file:** Look up the `Req` field and read the corresponding
|
|
147
|
-
`Knowledge/Roles/*.md` file.
|
|
149
|
+
`Knowledge/Roles/*.md` file; check the `**Status:**` field for context.
|
|
148
150
|
3. **Include in the briefing:**
|
|
149
151
|
- Candidate's current status, skills, and screening recommendation
|
|
150
152
|
- Role context: hiring manager, domain lead, remaining positions
|
|
@@ -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`.
|
|
@@ -39,7 +39,8 @@ This is **Stage 3** of the three-stage pipeline:
|
|
|
39
39
|
- Standard data via `fit-pathway`.
|
|
40
40
|
- `Knowledge/Candidates/Insights.md` — cross-candidate context.
|
|
41
41
|
- `Knowledge/Roles/*.md` — the candidate's requisition (positions, hiring
|
|
42
|
-
manager, domain lead).
|
|
42
|
+
manager, domain lead). Check the `**Status:**` field to distinguish active
|
|
43
|
+
from historical reqs.
|
|
43
44
|
- `Knowledge/Priorities/*.md` — strategic context.
|
|
44
45
|
- Other active candidates at the same level — relative positioning.
|
|
45
46
|
|
|
@@ -118,7 +119,8 @@ ls Knowledge/Roles/ | grep "{req_number}"
|
|
|
118
119
|
cat "Knowledge/Roles/{matching file}"
|
|
119
120
|
```
|
|
120
121
|
|
|
121
|
-
Capture remaining positions, hiring manager,
|
|
122
|
+
Check the `**Status:**` field. Capture remaining positions, hiring manager,
|
|
123
|
+
domain lead, goal alignment, other
|
|
122
124
|
candidates on the same req, and channel (hr / vendor). Frame the recommendation
|
|
123
125
|
in terms of strategic impact.
|
|
124
126
|
|
|
@@ -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
|
```
|
|
@@ -39,7 +39,8 @@ This is **Stage 1** of a three-stage hiring pipeline:
|
|
|
39
39
|
- Target role (optional).
|
|
40
40
|
- Existing `Knowledge/Candidates/{Name}/brief.md`, if any.
|
|
41
41
|
- `Knowledge/Roles/*.md` matching the candidate's `Req` (provides `Level`,
|
|
42
|
-
`Discipline`, `Hiring manager`, `Domain lead
|
|
42
|
+
`Discipline`, `Hiring manager`, `Domain lead`, and the `**Status:**` field).
|
|
43
|
+
Look up by Req number or filename substring.
|
|
43
44
|
|
|
44
45
|
## Outputs
|
|
45
46
|
|
|
@@ -82,7 +83,8 @@ cat "Knowledge/Roles/{matching file}"
|
|
|
82
83
|
|
|
83
84
|
Use the Role's `Level` and `Discipline` as the target unless the user specified
|
|
84
85
|
a different target. Capture `Hiring manager` and `Domain lead` for the screening
|
|
85
|
-
header.
|
|
86
|
+
header. Note the `**Status:**` field, but don't let historical (`closed`) roles
|
|
87
|
+
block screening of still-active candidates.
|
|
86
88
|
|
|
87
89
|
If no target is available, estimate one using the level heuristics in
|
|
88
90
|
[references/rubric.md](references/rubric.md#level-estimation-heuristics).
|
|
@@ -29,7 +29,8 @@ pipeline from scattered email threads.
|
|
|
29
29
|
- `~/.cache/fit/outpost/apple_mail/attachments/` — CV/resume attachments.
|
|
30
30
|
- `~/.cache/fit/outpost/apple_calendar/*.json` — calendar events (for
|
|
31
31
|
cross-source inference).
|
|
32
|
-
- `Knowledge/Roles/*.md` —
|
|
32
|
+
- `Knowledge/Roles/*.md` — role/requisition files (check the `**Status:**`
|
|
33
|
+
field: `open` for active roles, `closed` for historical).
|
|
33
34
|
- `~/.cache/fit/outpost/state/graph_processed` — processed-file index (shared
|
|
34
35
|
with `extract-entities`).
|
|
35
36
|
- `~/.cache/fit/outpost/state/identity.md` — user identity for self-exclusion
|
|
@@ -40,8 +41,8 @@ pipeline from scattered email threads.
|
|
|
40
41
|
- `Knowledge/Candidates/{Full Name}/brief.md` — candidate profile note.
|
|
41
42
|
- `Knowledge/Candidates/{Full Name}/CV.pdf` (or `CV.docx`) — local CV copy.
|
|
42
43
|
- `Knowledge/Candidates/{Full Name}/headshot.jpeg` — candidate photo.
|
|
43
|
-
- `Knowledge/Roles/*.md` — created/updated
|
|
44
|
-
|
|
44
|
+
- `Knowledge/Roles/*.md` — role files created/updated; the `**Status:**` field
|
|
45
|
+
determines visibility (open/closed).
|
|
45
46
|
- `~/.cache/fit/outpost/state/graph_processed` — updated with processed threads.
|
|
46
47
|
|
|
47
48
|
<do_confirm_checklist goal="Verify candidate processing batch is complete and
|
|
@@ -71,8 +72,8 @@ Process **10 files per run**.
|
|
|
71
72
|
### 1. Load context and pick the batch
|
|
72
73
|
|
|
73
74
|
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:
|
|
75
|
+
`~/.cache/fit/outpost/state/identity.md` (run the `person-identify` skill first
|
|
76
|
+
if it is missing or stale). List new or changed source files:
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
79
|
node .claude/skills/extract-entities/scripts/state.mjs check
|
|
@@ -94,21 +95,24 @@ links.
|
|
|
94
95
|
|
|
95
96
|
### 3. Sync `Knowledge/Roles/`
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
Role files are flat here; the `**Status:**` field is `open` or `closed`.
|
|
98
99
|
|
|
99
100
|
1. Read each Role file's Info block to map Req → Role file path, Hiring manager,
|
|
100
|
-
Domain lead, recruiter, Channel.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
the
|
|
106
|
-
|
|
101
|
+
Domain lead, recruiter, Channel. Look up by filename substring so a req-less
|
|
102
|
+
role stays findable: `ls Knowledge/Roles/ | grep "{partial_name_or_req}"`.
|
|
103
|
+
2. Find Reqs in candidate briefs missing a Role file:
|
|
104
|
+
`rg "^\*\*Req:\*\*" Knowledge/Candidates/*/brief.md`. Check all Role files
|
|
105
|
+
first; for a genuinely missing open role, create a stub with `**Status:**
|
|
106
|
+
open` using the **Role file stub** in
|
|
107
|
+
[references/templates.md](references/templates.md), then enrich:
|
|
108
|
+
`rg "{req_number}" Knowledge/`.
|
|
109
|
+
3. Rebuild each **open** Role file's `## Candidates` table by scanning briefs:
|
|
107
110
|
`rg -l "Req:.*{req_number}" Knowledge/Candidates/*/brief.md`. Use the **Role
|
|
108
|
-
Candidates table** format from `references/templates.md
|
|
109
|
-
newest first.
|
|
111
|
+
Candidates table** format from `references/templates.md`, newest first.
|
|
110
112
|
4. If a Role file has a hiring manager but no domain lead, walk the
|
|
111
113
|
`**Reports to:**` chain in `Knowledge/People/` to a VP or senior leader.
|
|
114
|
+
5. When a role closes (filled, cancelled, or frozen 6+ months), set its
|
|
115
|
+
`**Status:**` field to `closed` — only on a clear signal.
|
|
112
116
|
|
|
113
117
|
### 4. Identify recruitment threads
|
|
114
118
|
|
|
@@ -43,8 +43,10 @@ Field map and resolution rules for Step 2 of `req-track`.
|
|
|
43
43
|
|
|
44
44
|
Stop at the first match:
|
|
45
45
|
|
|
46
|
-
1. **Req-first inheritance** —
|
|
47
|
-
Req
|
|
46
|
+
1. **Req-first inheritance** — search `Knowledge/Roles/` for a file matching the
|
|
47
|
+
Req number or role description (use filename substring lookup for req-less
|
|
48
|
+
roles); check the `**Status:**` field (open/closed), then inherit Hiring
|
|
49
|
+
manager and Domain lead from the Role file.
|
|
48
50
|
2. **Calendar inference** —
|
|
49
51
|
`rg -l "{Candidate Name}" ~/.cache/fit/outpost/apple_calendar/`. The non-user
|
|
50
52
|
organizer of an interview event is likely the hiring manager.
|
|
@@ -38,7 +38,8 @@ integrate with the `req-track` pipeline format.
|
|
|
38
38
|
|
|
39
39
|
- `Knowledge/Candidates/{Clean Name}/brief.md` — candidate profile.
|
|
40
40
|
- `Knowledge/Candidates/{Clean Name}/CV.md` — resume text as markdown.
|
|
41
|
-
- `Knowledge/Roles/{Req ID} — {Title}.md` — created or updated
|
|
41
|
+
- `Knowledge/Roles/{Req ID} — {Title}.md` — created or updated with
|
|
42
|
+
`**Status:** open`. Update to `**Status:** closed` when the role closes.
|
|
42
43
|
- Updated existing briefs when a candidate already exists.
|
|
43
44
|
|
|
44
45
|
<do_confirm_checklist goal="Verify the Workday import is consistent with
|
|
@@ -96,7 +97,8 @@ header indices, name annotations) are in
|
|
|
96
97
|
ls Knowledge/Roles/ | grep "{Req ID}"
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
New role files go in `Knowledge/Roles/` with `**Status:** open`. Use the
|
|
101
|
+
**Role file stub** in
|
|
100
102
|
[references/templates.md](references/templates.md). Resolve the domain lead by:
|
|
101
103
|
|
|
102
104
|
1. `rg "{Req ID}" Knowledge/` — look in project timelines, People notes, Topics
|
|
@@ -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
|
|
|
@@ -5,13 +5,15 @@ Reference templates for `req-workday` Steps 1b, 4, and 5.
|
|
|
5
5
|
## Role file stub
|
|
6
6
|
|
|
7
7
|
Use when no Role file exists for the requisition. Filename:
|
|
8
|
-
`Knowledge/Roles/{Req ID} — {Short Title}.md`.
|
|
8
|
+
`Knowledge/Roles/{Req ID} — {Short Title}.md`. The `**Status:**` field
|
|
9
|
+
(`open`/`closed`) is updated in place — never move files.
|
|
9
10
|
|
|
10
11
|
```markdown
|
|
11
12
|
# {Requisition Title}
|
|
12
13
|
|
|
13
14
|
## Info
|
|
14
15
|
**Req:** {Req ID}
|
|
16
|
+
**Status:** open
|
|
15
17
|
**Title:** {Full title from export}
|
|
16
18
|
**Level:** {Infer from title: "Principal" → J100, "Staff" → J090, "Director" → J100 M-track, "Senior" → J070}
|
|
17
19
|
**Track:** {P-track for IC roles, M-track for Director/Manager roles}
|
|
@@ -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
|
|