@forwardimpact/outpost 3.0.3

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 (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +15 -0
  3. package/bin/fit-outpost.js +3 -0
  4. package/config/scheduler.json +34 -0
  5. package/package.json +63 -0
  6. package/src/agent-runner.js +189 -0
  7. package/src/index.js +5 -0
  8. package/src/kb-manager.js +228 -0
  9. package/src/outpost.js +417 -0
  10. package/src/scheduler.js +195 -0
  11. package/src/socket-server.js +336 -0
  12. package/src/state-manager.js +126 -0
  13. package/templates/.claude/agents/chief-of-staff.md +58 -0
  14. package/templates/.claude/agents/concierge.md +46 -0
  15. package/templates/.claude/agents/head-hunter.md +57 -0
  16. package/templates/.claude/agents/librarian.md +60 -0
  17. package/templates/.claude/agents/postman.md +47 -0
  18. package/templates/.claude/agents/recruiter.md +63 -0
  19. package/templates/.claude/settings.json +88 -0
  20. package/templates/.claude/skills/candidate-report/SKILL.md +139 -0
  21. package/templates/.claude/skills/candidate-report/references/report-template.html +203 -0
  22. package/templates/.claude/skills/candidate-report/references/report.css +438 -0
  23. package/templates/.claude/skills/candidate-report/references/rubric.md +76 -0
  24. package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +48 -0
  25. package/templates/.claude/skills/deck-create/SKILL.md +75 -0
  26. package/templates/.claude/skills/deck-create/references/slide.css +35 -0
  27. package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +47 -0
  28. package/templates/.claude/skills/deck-summarize/SKILL.md +139 -0
  29. package/templates/.claude/skills/deck-summarize/references/brief-template.md +85 -0
  30. package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +210 -0
  31. package/templates/.claude/skills/doc-collab/SKILL.md +112 -0
  32. package/templates/.claude/skills/doc-create/SKILL.md +105 -0
  33. package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +50 -0
  34. package/templates/.claude/skills/draft-emails/SKILL.md +141 -0
  35. package/templates/.claude/skills/draft-emails/references/template.md +26 -0
  36. package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +66 -0
  37. package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +139 -0
  38. package/templates/.claude/skills/extract-entities/SKILL.md +187 -0
  39. package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +14 -0
  40. package/templates/.claude/skills/extract-entities/references/conditions.md +55 -0
  41. package/templates/.claude/skills/extract-entities/references/content.md +104 -0
  42. package/templates/.claude/skills/extract-entities/references/links.md +48 -0
  43. package/templates/.claude/skills/extract-entities/references/recruitment.md +57 -0
  44. package/templates/.claude/skills/extract-entities/references/resolution.md +71 -0
  45. package/templates/.claude/skills/extract-entities/references/sources.md +70 -0
  46. package/templates/.claude/skills/extract-entities/references/templates-conditions.md +36 -0
  47. package/templates/.claude/skills/extract-entities/references/templates-goals-priorities.md +63 -0
  48. package/templates/.claude/skills/extract-entities/references/templates-people-orgs.md +67 -0
  49. package/templates/.claude/skills/extract-entities/references/templates-projects-topics.md +68 -0
  50. package/templates/.claude/skills/extract-entities/scripts/state.mjs +131 -0
  51. package/templates/.claude/skills/hyprnote-follow/SKILL.md +174 -0
  52. package/templates/.claude/skills/hyprnote-follow/references/coaching.md +118 -0
  53. package/templates/.claude/skills/hyprnote-follow/references/debrief.md +59 -0
  54. package/templates/.claude/skills/hyprnote-follow/references/meeting-types.md +14 -0
  55. package/templates/.claude/skills/hyprnote-follow/scripts/follow.mjs +243 -0
  56. package/templates/.claude/skills/hyprnote-process/SKILL.md +147 -0
  57. package/templates/.claude/skills/hyprnote-process/references/extraction.md +84 -0
  58. package/templates/.claude/skills/hyprnote-process/references/sessions.md +81 -0
  59. package/templates/.claude/skills/hyprnote-process/scripts/scan.mjs +246 -0
  60. package/templates/.claude/skills/hyprnote-trim/SKILL.md +177 -0
  61. package/templates/.claude/skills/manage-tasks/SKILL.md +152 -0
  62. package/templates/.claude/skills/manage-tasks/references/format.md +73 -0
  63. package/templates/.claude/skills/meeting-prep/SKILL.md +180 -0
  64. package/templates/.claude/skills/organize-files/SKILL.md +146 -0
  65. package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +105 -0
  66. package/templates/.claude/skills/organize-files/scripts/summarize.mjs +91 -0
  67. package/templates/.claude/skills/req-assess/SKILL.md +137 -0
  68. package/templates/.claude/skills/req-assess/references/interview-template.md +62 -0
  69. package/templates/.claude/skills/req-assess/references/panel-template.md +117 -0
  70. package/templates/.claude/skills/req-assess/references/rubric.md +65 -0
  71. package/templates/.claude/skills/req-decide/SKILL.md +163 -0
  72. package/templates/.claude/skills/req-decide/references/rubric.md +63 -0
  73. package/templates/.claude/skills/req-decide/references/template.md +118 -0
  74. package/templates/.claude/skills/req-forget/SKILL.md +154 -0
  75. package/templates/.claude/skills/req-forget/references/classify.md +23 -0
  76. package/templates/.claude/skills/req-forget/references/locations.md +59 -0
  77. package/templates/.claude/skills/req-forget/references/report-template.md +67 -0
  78. package/templates/.claude/skills/req-scan/SKILL.md +145 -0
  79. package/templates/.claude/skills/req-scan/references/fallbacks.md +76 -0
  80. package/templates/.claude/skills/req-scan/references/filters.md +83 -0
  81. package/templates/.claude/skills/req-scan/references/sources.md +76 -0
  82. package/templates/.claude/skills/req-scan/references/state.md +54 -0
  83. package/templates/.claude/skills/req-scan/references/template.md +36 -0
  84. package/templates/.claude/skills/req-scan/scripts/state.mjs +396 -0
  85. package/templates/.claude/skills/req-screen/SKILL.md +151 -0
  86. package/templates/.claude/skills/req-screen/references/rubric.md +94 -0
  87. package/templates/.claude/skills/req-screen/references/template.md +91 -0
  88. package/templates/.claude/skills/req-track/SKILL.md +189 -0
  89. package/templates/.claude/skills/req-track/references/fields.md +114 -0
  90. package/templates/.claude/skills/req-track/references/signals.md +42 -0
  91. package/templates/.claude/skills/req-track/references/statuses.md +32 -0
  92. package/templates/.claude/skills/req-track/references/templates.md +127 -0
  93. package/templates/.claude/skills/req-workday/SKILL.md +162 -0
  94. package/templates/.claude/skills/req-workday/references/field-mapping.md +40 -0
  95. package/templates/.claude/skills/req-workday/references/status-mapping.md +38 -0
  96. package/templates/.claude/skills/req-workday/references/templates.md +126 -0
  97. package/templates/.claude/skills/req-workday/references/xlsx-format.md +56 -0
  98. package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +313 -0
  99. package/templates/.claude/skills/send-chat/SKILL.md +170 -0
  100. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +145 -0
  101. package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +80 -0
  102. package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +307 -0
  103. package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +332 -0
  104. package/templates/.claude/skills/sync-apple-mail/SKILL.md +151 -0
  105. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +115 -0
  106. package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +389 -0
  107. package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +372 -0
  108. package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +239 -0
  109. package/templates/.claude/skills/sync-teams/SKILL.md +177 -0
  110. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +147 -0
  111. package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +288 -0
  112. package/templates/.claude/skills/sync-teams/scripts/sync.mjs +381 -0
  113. package/templates/.claude/skills/upstream-skill/SKILL.md +130 -0
  114. package/templates/.claude/skills/upstream-skill/references/examples.md +80 -0
  115. package/templates/.claude/skills/weekly-update/SKILL.md +141 -0
  116. package/templates/.claude/skills/weekly-update/references/template.md +55 -0
  117. package/templates/CLAUDE.md +134 -0
  118. package/templates/USER.md +3 -0
  119. package/templates/apm.yml +5 -0
  120. package/templates/knowledge/Briefings/.gitkeep +0 -0
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * follow.mjs — Read a live Hyprnote transcript and output new content since
5
+ * the last read. Designed to be called repeatedly during a live session.
6
+ *
7
+ * Usage:
8
+ * node follow.mjs <session-id> # First read — outputs everything
9
+ * node follow.mjs <session-id> --after <word-id> # Outputs words after the given word ID
10
+ * node follow.mjs <session-id> --after <word-id> --summary # Condensed summary mode
11
+ * node follow.mjs <session-id> --meta # Output session metadata only
12
+ * node follow.mjs --detect # Detect the most recently active session
13
+ *
14
+ * Output (JSON):
15
+ * {
16
+ * "session_id": "...",
17
+ * "title": "...",
18
+ * "total_words": 1234,
19
+ * "new_words": 56,
20
+ * "last_word_id": "...",
21
+ * "duration_ms": 123456,
22
+ * "channels": {
23
+ * "0": { "label": "user", "word_count": 600 },
24
+ * "1": { "label": "guest", "word_count": 634 }
25
+ * },
26
+ * "text": [
27
+ * { "channel": 0, "start_ms": 1000, "end_ms": 5000, "text": "Hello, how are you?" },
28
+ * ...
29
+ * ]
30
+ * }
31
+ */
32
+
33
+ import { readFileSync, readdirSync, statSync } from "node:fs";
34
+ import { join } from "node:path";
35
+ import { homedir } from "node:os";
36
+
37
+ const SESSIONS_DIR = join(
38
+ homedir(),
39
+ "Library/Application Support/hyprnote/sessions",
40
+ );
41
+
42
+ function parseArgs() {
43
+ const args = process.argv.slice(2);
44
+ const flags = {};
45
+ let positional = null;
46
+
47
+ for (let i = 0; i < args.length; i++) {
48
+ if (args[i] === "--after" && i + 1 < args.length) {
49
+ flags.after = args[++i];
50
+ } else if (args[i] === "--summary") {
51
+ flags.summary = true;
52
+ } else if (args[i] === "--meta") {
53
+ flags.meta = true;
54
+ } else if (args[i] === "--detect") {
55
+ flags.detect = true;
56
+ } else if (!args[i].startsWith("--")) {
57
+ positional = args[i];
58
+ }
59
+ }
60
+
61
+ return { sessionId: positional, ...flags };
62
+ }
63
+
64
+ function detectActiveSession() {
65
+ const entries = readdirSync(SESSIONS_DIR);
66
+ let best = null;
67
+ let bestMtime = 0;
68
+
69
+ for (const entry of entries) {
70
+ const transcriptPath = join(SESSIONS_DIR, entry, "transcript.json");
71
+ try {
72
+ const stat = statSync(transcriptPath);
73
+ if (stat.mtimeMs > bestMtime) {
74
+ bestMtime = stat.mtimeMs;
75
+ best = entry;
76
+ }
77
+ } catch {
78
+ // No transcript — skip
79
+ }
80
+ }
81
+
82
+ if (!best) {
83
+ console.error("No active Hyprnote session found.");
84
+ process.exit(1);
85
+ }
86
+
87
+ // Check if the transcript was modified recently (within last 5 minutes)
88
+ const ageMs = Date.now() - bestMtime;
89
+ const isLive = ageMs < 5 * 60 * 1000;
90
+
91
+ const metaPath = join(SESSIONS_DIR, best, "_meta.json");
92
+ let title = "Unknown";
93
+ try {
94
+ const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
95
+ title = meta.title || "Untitled";
96
+ } catch {
97
+ // No meta
98
+ }
99
+
100
+ return {
101
+ session_id: best,
102
+ title,
103
+ is_live: isLive,
104
+ last_modified: new Date(bestMtime).toISOString(),
105
+ age_seconds: Math.round(ageMs / 1000),
106
+ };
107
+ }
108
+
109
+ function readMeta(sessionId) {
110
+ const metaPath = join(SESSIONS_DIR, sessionId, "_meta.json");
111
+ try {
112
+ return JSON.parse(readFileSync(metaPath, "utf-8"));
113
+ } catch {
114
+ return { title: "Unknown", created_at: null, participants: [] };
115
+ }
116
+ }
117
+
118
+ function readTranscript(sessionId) {
119
+ const path = join(SESSIONS_DIR, sessionId, "transcript.json");
120
+ try {
121
+ const data = JSON.parse(readFileSync(path, "utf-8"));
122
+ return data.transcripts?.[0]?.words || [];
123
+ } catch {
124
+ return [];
125
+ }
126
+ }
127
+
128
+ function groupIntoSegments(words) {
129
+ if (words.length === 0) return [];
130
+
131
+ const segments = [];
132
+ let current = {
133
+ channel: words[0].channel,
134
+ start_ms: words[0].start_ms,
135
+ end_ms: words[0].end_ms,
136
+ texts: [words[0].text],
137
+ };
138
+
139
+ for (let i = 1; i < words.length; i++) {
140
+ const w = words[i];
141
+ const gap = w.start_ms - current.end_ms;
142
+
143
+ // New segment on channel change or >3s gap
144
+ if (w.channel !== current.channel || gap > 3000) {
145
+ segments.push({
146
+ channel: current.channel,
147
+ start_ms: current.start_ms,
148
+ end_ms: current.end_ms,
149
+ text: current.texts.join("").trim(),
150
+ });
151
+ current = {
152
+ channel: w.channel,
153
+ start_ms: w.start_ms,
154
+ end_ms: w.end_ms,
155
+ texts: [w.text],
156
+ };
157
+ } else {
158
+ current.end_ms = w.end_ms;
159
+ current.texts.push(w.text);
160
+ }
161
+ }
162
+
163
+ // Push final segment
164
+ segments.push({
165
+ channel: current.channel,
166
+ start_ms: current.start_ms,
167
+ end_ms: current.end_ms,
168
+ text: current.texts.join("").trim(),
169
+ });
170
+
171
+ return segments.filter((s) => s.text.length > 0);
172
+ }
173
+
174
+ function buildChannelStats(allWords) {
175
+ const channels = {};
176
+ for (const w of allWords) {
177
+ if (!channels[w.channel]) {
178
+ channels[w.channel] = {
179
+ label: w.channel === 0 ? "user" : `guest-${w.channel}`,
180
+ word_count: 0,
181
+ };
182
+ }
183
+ channels[w.channel].word_count++;
184
+ }
185
+ return channels;
186
+ }
187
+
188
+ function filterNewWords(allWords, afterId) {
189
+ if (!afterId) return allWords;
190
+ const idx = allWords.findIndex((w) => w.id === afterId);
191
+ // If word ID not found, return everything (safety fallback)
192
+ return idx >= 0 ? allWords.slice(idx + 1) : allWords;
193
+ }
194
+
195
+ function main() {
196
+ const opts = parseArgs();
197
+
198
+ // Detect mode
199
+ if (opts.detect) {
200
+ console.log(JSON.stringify(detectActiveSession(), null, 2));
201
+ return;
202
+ }
203
+
204
+ if (!opts.sessionId) {
205
+ console.error(
206
+ "Usage: follow.mjs <session-id> [--after <word-id>] [--meta]",
207
+ );
208
+ process.exit(1);
209
+ }
210
+
211
+ const meta = readMeta(opts.sessionId);
212
+
213
+ // Meta-only mode
214
+ if (opts.meta) {
215
+ console.log(JSON.stringify(meta, null, 2));
216
+ return;
217
+ }
218
+
219
+ // Read transcript
220
+ const allWords = readTranscript(opts.sessionId);
221
+ const words = filterNewWords(allWords, opts.after);
222
+ const lastWord = allWords[allWords.length - 1];
223
+
224
+ console.log(
225
+ JSON.stringify(
226
+ {
227
+ session_id: opts.sessionId,
228
+ title: meta.title,
229
+ created_at: meta.created_at,
230
+ total_words: allWords.length,
231
+ new_words: words.length,
232
+ last_word_id: lastWord?.id || null,
233
+ duration_ms: lastWord?.end_ms || 0,
234
+ channels: buildChannelStats(allWords),
235
+ text: groupIntoSegments(words),
236
+ },
237
+ null,
238
+ 2,
239
+ ),
240
+ );
241
+ }
242
+
243
+ main();
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: hyprnote-process
3
+ description: Process Hyprnote meeting sessions (memos, summaries, transcripts) into the knowledge graph. Extracts people, organizations, projects, and topics from AI-generated meeting summaries and user notes, creating or updating Obsidian-compatible notes in knowledge/. Use when the user asks to process meeting notes or after Hyprnote sessions.
4
+ ---
5
+
6
+ # Process Hyprnote
7
+
8
+ Process meeting sessions from Hyprnote (a local AI meeting-notes app) into the
9
+ knowledge graph. Hyprnote records meetings, transcribes them, and generates AI
10
+ summaries; this skill reads that output and feeds it into `knowledge/` — the
11
+ same way `extract-entities` processes emails and calendar events.
12
+
13
+ ## Trigger
14
+
15
+ - The user asks to process meeting notes or Hyprnote sessions.
16
+ - New meetings have been recorded.
17
+ - The user asks to update the knowledge base from recent meetings.
18
+
19
+ ## Prerequisites
20
+
21
+ - Hyprnote installed; sessions at
22
+ `~/Library/Application Support/hyprnote/sessions/`.
23
+ - User identity in `USER.md`.
24
+
25
+ ## Inputs
26
+
27
+ - `~/Library/Application Support/hyprnote/sessions/{uuid}/` — see
28
+ [references/sessions.md](references/sessions.md) for the file shape and skip
29
+ rules.
30
+ - `~/.cache/fit/outpost/state/graph_processed` — processed-file index (TSV,
31
+ shared with `extract-entities`).
32
+ - `USER.md` — user identity for self-exclusion.
33
+
34
+ ## Outputs
35
+
36
+ - `knowledge/People/`, `knowledge/Organizations/`, `knowledge/Projects/`,
37
+ `knowledge/Topics/` — created or updated.
38
+ - `knowledge/Goals/`, `knowledge/Priorities/` — **updated only**, never
39
+ auto-created.
40
+ - `~/.cache/fit/outpost/state/graph_processed` — updated.
41
+
42
+ <do_confirm_checklist goal="Verify each session was processed correctly">
43
+
44
+ - [ ] Empty / test / onboarding sessions skipped (per skip rules).
45
+ - [ ] Both `_memo.md` and `_summary.md` read (when present); transcript
46
+ consulted only for disambiguation.
47
+ - [ ] "Would I prep?" test applied to each person; self excluded.
48
+ - [ ] Interview sessions wrote to `knowledge/Candidates/`, not
49
+ `knowledge/People/`.
50
+ - [ ] All links use absolute paths `[[Folder/Name]]`.
51
+ - [ ] Activity entries describe relationship, not communication method.
52
+ - [ ] No new `Goals/` or `Priorities/` auto-created (user-set only); any
53
+ referenced goal had its progress updated.
54
+ - [ ] `graph_processed` updated for every processed file (memo + summary).
55
+
56
+ </do_confirm_checklist>
57
+
58
+ ## Procedure
59
+
60
+ ### 0. Set up
61
+
62
+ Read `USER.md`. Scan unprocessed sessions:
63
+
64
+ ```bash
65
+ node .claude/skills/hyprnote-process/scripts/scan.mjs
66
+ ```
67
+
68
+ Flags: `--changed` (also detect changed memo/summary hashes), `--json`
69
+ (programmatic output), `--count` (count only), `--limit N` (default 20).
70
+
71
+ A session needs processing when its `_memo.md` is not in `graph_processed`, or
72
+ its hash has changed (`--changed`), or its `_summary.md` exists and is not in
73
+ `graph_processed` (or has changed).
74
+
75
+ Process all unprocessed sessions in one run. **Don't write bespoke scan
76
+ scripts** — this script handles the edge cases (empty memos, missing summaries,
77
+ metadata fallback).
78
+
79
+ ### 1. Build the knowledge index
80
+
81
+ ```bash
82
+ ls knowledge/People/ knowledge/Organizations/ knowledge/Projects/ \
83
+ knowledge/Topics/ knowledge/Goals/ knowledge/Priorities/ \
84
+ knowledge/Conditions/ 2>/dev/null
85
+ ```
86
+
87
+ Read each note's header to build a mental index of known entities (same approach
88
+ as `extract-entities` Step 0).
89
+
90
+ ### 2. Read each session
91
+
92
+ For each unprocessed session, read in this order: `_meta.json`, `_memo.md`,
93
+ `_summary.md` (if present), `transcript.json` (only when disambiguation requires
94
+ it). File shapes and skip rules:
95
+ [references/sessions.md](references/sessions.md).
96
+
97
+ ### 3. Classify the source
98
+
99
+ Hyprnote sessions are **meetings** and follow the meeting rules from
100
+ `extract-entities`:
101
+
102
+ - **Can create** People, Organization, Project, and Topic notes.
103
+ - **Can update** existing notes — including Goals and Priorities, which are
104
+ user-set and never auto-created.
105
+ - **Can detect** state changes.
106
+
107
+ Apply the "Would I prep for this person?" test from `extract-entities` Step 5
108
+ before creating a person note.
109
+
110
+ ### 4. Extract entities and content
111
+
112
+ Combine memo and summary content (prefer summary when both exist). Extraction
113
+ signals — entity types, decisions, commitments, key facts, activity-line format,
114
+ interview-note rules, and linking rules — live in
115
+ [references/extraction.md](references/extraction.md).
116
+
117
+ ### 5. Write updates
118
+
119
+ For **new** entities, use the templates in
120
+ `.claude/skills/extract-entities/references/TEMPLATES.md`. For interview
121
+ sessions, use the candidate brief template from `req-track` (under
122
+ `knowledge/Candidates/`).
123
+
124
+ For **existing** entities, apply targeted edits — never rewrite the file:
125
+
126
+ - Add the new activity entry at the **top** of `## Activity`.
127
+ - Update `Last seen` / `Last activity`.
128
+ - Add new key facts (skip duplicates).
129
+ - Update open items (mark completed, add new).
130
+ - Apply state changes.
131
+
132
+ Verify bidirectional links per `extract-entities` Step 10 (Goal ↔ Project, Goal
133
+ ↔ Priority, Project ↔ Priority).
134
+
135
+ ### 6. Update graph state
136
+
137
+ For each processed session:
138
+
139
+ ```bash
140
+ node .claude/skills/extract-entities/scripts/state.mjs update \
141
+ "$HOME/Library/Application Support/hyprnote/sessions/{uuid}/_memo.md"
142
+
143
+ node .claude/skills/extract-entities/scripts/state.mjs update \
144
+ "$HOME/Library/Application Support/hyprnote/sessions/{uuid}/_summary.md"
145
+ ```
146
+
147
+ (Skip the summary call if `_summary.md` doesn't exist.)
@@ -0,0 +1,84 @@
1
+ # Entity Extraction Signals
2
+
3
+ Reference for `hyprnote-process` Steps 3 and 4. Combine `_memo.md` and
4
+ `_summary.md` (prefer the summary when both exist).
5
+
6
+ ## People
7
+
8
+ Look for names in:
9
+
10
+ - Memo text ("chat with Sarah Chen", "interview with David Kim").
11
+ - Summary bullets ("the user will serve as the senior engineer", "Alex from the
12
+ platform team").
13
+ - `_meta.json` participants.
14
+
15
+ For each: resolve against the knowledge index (Step 0); extract role,
16
+ organization, relationship to the user; note what was discussed.
17
+
18
+ ## Organizations
19
+
20
+ Explicit mentions ("Acme Corp"), or inferred from people's roles or the
21
+ surrounding context.
22
+
23
+ ## Projects
24
+
25
+ Explicit project names ("Customer Portal", "Q2 Migration") or described
26
+ initiatives ("the hiring pipeline", "the product launch").
27
+
28
+ ## Topics
29
+
30
+ Recurring themes ("AI coding agents", "interview process", "architecture
31
+ decisions"). Only create a Topic note when the subject spans multiple meetings
32
+ or is strategically important.
33
+
34
+ ## Self-exclusion
35
+
36
+ Never create or update a note for the user — match against name, email, or
37
+ `@domain` from `USER.md`.
38
+
39
+ ## Interview sessions (special case)
40
+
41
+ If the title or memo indicates "interview with {Name}", the interviewee is a
42
+ **candidate** — create or update their note in `knowledge/Candidates/` (using
43
+ the candidate brief template from `req-track`), **not** in `knowledge/People/`.
44
+
45
+ ## Content signals
46
+
47
+ ### Decisions
48
+
49
+ "decided", "agreed", "plan to", "established", "will serve as".
50
+
51
+ ### Commitments / action items
52
+
53
+ "will share", "plans to", "needs to", "to be created", "will upload". Extract
54
+ owner, action, deadline (if any), status (default `open`).
55
+
56
+ ### Key facts
57
+
58
+ Specific numbers (headcount, budget, timeline), preferences ("non-traditional
59
+ backgrounds"), process details (interview stages, evaluation criteria),
60
+ strategic context (market trends, competitive landscape). Skip filler.
61
+
62
+ ### Activity summary
63
+
64
+ One line per session per entity:
65
+
66
+ ```markdown
67
+ - **2026-02-14** (meeting): Discussed hiring pipeline. 11 internal
68
+ candidates, plan to shortlist to 6-7. [[People/Sarah Chen]] managing
69
+ the team.
70
+ ```
71
+
72
+ ### Interview notes (for candidates)
73
+
74
+ Add to the candidate's `## Notes` section: impressions, technical assessment,
75
+ strengths and concerns, any interview scoring or decisions.
76
+
77
+ ## Linking rules
78
+
79
+ Use absolute paths everywhere: `[[People/Name]]`, `[[Organizations/Name]]`,
80
+ `[[Projects/Name]]`, `[[Goals/Goal Name]]`, `[[Priorities/Priority Name]]`.
81
+
82
+ When meeting content references an existing Goal or Priority, follow the linking
83
+ rules in `extract-entities` Step 7c — update progress and add backlinks, but
84
+ **never** auto-create Goal or Priority notes.
@@ -0,0 +1,81 @@
1
+ # Hyprnote Session Files
2
+
3
+ Reference for `hyprnote-process` Step 1. Each session lives at
4
+ `~/Library/Application Support/hyprnote/sessions/{uuid}/`.
5
+
6
+ ## `_meta.json`
7
+
8
+ ```json
9
+ {
10
+ "created_at": "2026-02-16T13:01:59.187Z",
11
+ "id": "7888363f-4cc6-4987-8470-92f386e5bdfc",
12
+ "participants": [],
13
+ "title": "Director-Level Hiring Pipeline",
14
+ "user_id": "00000000-0000-0000-0000-000000000000"
15
+ }
16
+ ```
17
+
18
+ Use: session date (from `created_at`), title, participants (often empty —
19
+ Hyprnote doesn't reliably populate this).
20
+
21
+ ## `_memo.md`
22
+
23
+ YAML frontmatter (`id`, `session_id`) plus the user's markdown notes:
24
+
25
+ ```markdown
26
+ ---
27
+ id: 213e0f78-a66a-468d-b8e5-bc3fbbe04bf4
28
+ session_id: 213e0f78-a66a-468d-b8e5-bc3fbbe04bf4
29
+ ---
30
+
31
+ Chat with Sarah about the product roadmap.
32
+ ```
33
+
34
+ High-signal — every name and observation is intentional.
35
+
36
+ ## `_summary.md` (optional)
37
+
38
+ YAML frontmatter (`id`, `position`, `session_id`, `title`) plus an AI-generated
39
+ summary — typically the richest source:
40
+
41
+ ```markdown
42
+ ---
43
+ id: 152d9bc9-0cdc-4fb2-9916-cb7670f3a6df
44
+ position: 1
45
+ session_id: 213e0f78-a66a-468d-b8e5-bc3fbbe04bf4
46
+ title: Summary
47
+ ---
48
+
49
+ # Product Roadmap Review
50
+
51
+ - Both speakers reviewed the Q2 roadmap priorities...
52
+ ```
53
+
54
+ ## `transcript.json` (disambiguation only)
55
+
56
+ ```json
57
+ {
58
+ "transcripts": [{
59
+ "words": [
60
+ {"channel": 0, "text": "Hello", "start_ms": 0, "end_ms": 500},
61
+ {"channel": 1, "text": "Hi", "start_ms": 600, "end_ms": 900}
62
+ ]
63
+ }]
64
+ }
65
+ ```
66
+
67
+ **Do not extract entities from the full transcript** — too noisy. Consult it
68
+ only to disambiguate a name from the memo or summary, confirm who said what
69
+ (channel 0 = user, channel 1 = other speaker), or find context around a specific
70
+ topic or decision.
71
+
72
+ ## Skip rules
73
+
74
+ A session is skipped when **all** of:
75
+
76
+ - `_memo.md` body is empty or only `&nbsp;` / whitespace.
77
+ - No `_summary.md` exists.
78
+ - Title is empty or generic ("Hello", "Welcome to Hyprnote", "Test").
79
+
80
+ Process a session if it has **either** a substantive memo **or** a
81
+ `_summary.md`.