digital-brain 0.1.7 → 1.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 (47) hide show
  1. package/README.md +20 -2
  2. package/bin/digital-brain.js +136 -20
  3. package/docs/INTEGRATIONS.md +72 -0
  4. package/docs/PRIVACY.md +3 -1
  5. package/docs/SETUP.md +33 -1
  6. package/examples/sample-vault/{04 People/Interpreted Relationships/Close Friend.md → 06 AI Memory/Generated Relationship Drafts/Close Friend (WhatsApp).md } +4 -3
  7. package/examples/sample-vault/{04 People/Interpreted Relationships/Mom.md → 06 AI Memory/Generated Relationship Drafts/Mom (WhatsApp).md } +4 -3
  8. package/examples/sample-vault/{08 Sources/WhatsApp/Analysis/Interpreted/Project Team.md → 06 AI Memory/Generated Relationship Drafts/Project Team (WhatsApp).md } +4 -3
  9. package/examples/sample-vault/06 AI Memory/Interpreted Relationship Memory.md +3 -3
  10. package/examples/sample-vault/06 AI Memory/Person Context Index.md +26 -0
  11. package/examples/sample-vault/06 AI Memory/Person Reply Context.md +26 -0
  12. package/examples/sample-vault/08 Sources/{WhatsApp/Analysis/Interpreted/Close Friend.md → Analysis/Interpreted/Close Friend (WhatsApp).md } +4 -3
  13. package/examples/sample-vault/08 Sources/{WhatsApp/Analysis/Interpreted/Mom.md → Analysis/Interpreted/Mom (WhatsApp).md } +4 -3
  14. package/examples/sample-vault/{04 People/Interpreted Relationships/Project Team.md → 08 Sources/Analysis/Interpreted/Project Team (WhatsApp).md } +4 -3
  15. package/examples/sample-vault/08 Sources/Analysis/Relationship Map.md +38 -0
  16. package/examples/sample-vault/08 Sources/Analysis/interpreted_relationship_models.json +175 -0
  17. package/examples/sample-vault/08 Sources/Analysis/person_identity_map.json +78 -0
  18. package/examples/sample-vault/08 Sources/Analysis/relationship_profiles.json +122 -0
  19. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Close Friend (WhatsApp).md +44 -0
  20. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Mom (WhatsApp).md +45 -0
  21. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Project Team (WhatsApp).md +45 -0
  22. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Relationship Map.md +9 -3
  23. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/interpreted_relationship_models.json +18 -0
  24. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/person_identity_map.json +78 -0
  25. package/examples/sample-vault/08 Sources/WhatsApp/Analysis/relationship_profiles.json +18 -0
  26. package/examples/sample-vault/08 Sources/WhatsApp/Raw/2026-01-01.jsonl +6 -6
  27. package/lib/fs.js +7 -1
  28. package/package.json +2 -1
  29. package/scripts/digital_brain_imessage_sync.py +175 -0
  30. package/scripts/digital_brain_linkedin_export_import.py +214 -0
  31. package/scripts/digital_brain_relationship_extractor.py +189 -12
  32. package/scripts/digital_brain_relationship_interpreter.py +104 -15
  33. package/scripts/digital_brain_slack_export_import.py +181 -0
  34. package/scripts/digital_brain_whatsapp_mac_sync.py +37 -8
  35. package/templates/vault/00 Home/How AI Should Use This Vault.md +1 -1
  36. package/templates/vault/00 Home/Start Here.md +2 -1
  37. package/templates/vault/04 People/Relationship Overrides.md +2 -1
  38. package/templates/vault/06 AI Memory/Generated Relationship Drafts/README.md +5 -0
  39. package/templates/vault/06 AI Memory/Interpreted Relationship Memory.md +1 -2
  40. package/templates/vault/06 AI Memory/Person Context Index.md +4 -0
  41. package/templates/vault/06 AI Memory/Person Reply Context.md +4 -0
  42. package/templates/vault/08 Sources/README.md +5 -0
  43. package/templates/vault/08 Sources/WhatsApp/Outbound/README.md +2 -2
  44. package/templates/vault/AGENTS.md +5 -1
  45. package/templates/vault/CLAUDE.md +3 -0
  46. package/templates/vault/GEMINI.md +4 -0
  47. package/whatsapp-web/send.mjs +32 -5
package/README.md CHANGED
@@ -46,7 +46,7 @@ npx digital-brain init --full-auto
46
46
 
47
47
  Full-auto means local repeated refreshes. It does not mean blind auto-send. WhatsApp sending still defaults to drafts or explicit confirmation, and the AI-disclosure guard stays enabled.
48
48
 
49
- `init` also runs a setup check. npm installs the package dependencies automatically, and Digital Brain does not require pip packages. If Python, WhatsApp for Mac, or optional Ollama setup is missing, the check prints the exact next step. See [docs/SETUP.md](docs/SETUP.md).
49
+ `init` also runs a setup check. npm installs the package dependencies automatically, and Digital Brain does not require pip packages. If Python or a selected source is missing, the check prints the exact next step and setup link. See [docs/SETUP.md](docs/SETUP.md).
50
50
 
51
51
  For local development:
52
52
 
@@ -60,6 +60,9 @@ node ./bin/digital-brain.js init ./Digital Brain\ Vault
60
60
  - An Obsidian-friendly Markdown vault.
61
61
  - AI adapter files for Codex, Claude, and Gemini.
62
62
  - WhatsApp Mac import tools.
63
+ - Apple iMessage import tools.
64
+ - Slack export import tools.
65
+ - LinkedIn data archive import tools.
63
66
  - Relationship extraction and interpretation models.
64
67
  - Optional WhatsApp Web outbound sender.
65
68
  - A refresh script based on your install-time answers.
@@ -68,10 +71,15 @@ node ./bin/digital-brain.js init ./Digital Brain\ Vault
68
71
  ## What It Can Do
69
72
 
70
73
  - Import recent WhatsApp history from the local macOS WhatsApp database.
74
+ - Import recent iMessage history from the local macOS Messages database.
75
+ - Import Slack workspace exports.
76
+ - Import LinkedIn data archives for connections and messages when available.
71
77
  - Build relationship profiles from message patterns.
78
+ - Merge confirmed-looking same-person profiles across sources into a person context index.
72
79
  - Infer provisional roles like parent, family group, work collaborator, close personal contact, or unlabeled contact.
73
80
  - Extract relationship-specific typing style: casing, message length, punctuation, emoji, and slang.
74
81
  - Generate "how to continue this relationship" notes.
82
+ - Generate reply-ready person context that keeps WhatsApp, iMessage, Slack, and LinkedIn evidence separate under the same person.
75
83
  - Create AI-readable memory files for future prompts.
76
84
  - Draft WhatsApp sends by default, and only send with explicit `--yes`.
77
85
  - Enforce an AI-disclosure guard after repeated AI-assisted sends.
@@ -82,10 +90,15 @@ node ./bin/digital-brain.js init ./Digital Brain\ Vault
82
90
  digital-brain init
83
91
  digital-brain run
84
92
  digital-brain doctor
93
+ digital-brain sync-imessage --days 30
94
+ digital-brain import-slack --input ./slack-export.zip
95
+ digital-brain import-linkedin --input ./linkedin-archive.zip
85
96
  digital-brain send-whatsapp --to "Name" --message "text"
86
97
  ```
87
98
 
88
- `init` remembers your vault globally, so `run` works from anywhere. `run` syncs WhatsApp, extracts relationships, and writes interpreted memory in one command.
99
+ `init` remembers your vault globally, so `run` works from anywhere. `run` syncs the live local sources you selected, extracts relationships, and writes interpreted memory in one command.
100
+
101
+ Slack and LinkedIn are import-based. Digital Brain reads official export archives; it does not scrape LinkedIn or automate private app UIs. See [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md).
89
102
 
90
103
  Use `digital-brain doctor` or `digital-brain tutorial` anytime to see dependency status and next steps.
91
104
 
@@ -93,6 +106,7 @@ The lower-level commands still exist for debugging:
93
106
 
94
107
  ```bash
95
108
  digital-brain sync-whatsapp
109
+ digital-brain sync-imessage
96
110
  digital-brain extract
97
111
  digital-brain interpret
98
112
  ```
@@ -145,6 +159,10 @@ Digital Brain is local-first. It does not upload messages or notes.
145
159
 
146
160
  WhatsApp support reads the local macOS WhatsApp database when available. This is experimental and unofficial. Outbound messaging uses WhatsApp Web through `whatsapp-web.js`.
147
161
 
162
+ Apple iMessage support reads the local macOS Messages `chat.db` when available. If the database is missing or inaccessible and iMessage was selected, `digital-brain run` fails with a clear setup error instead of silently skipping it.
163
+
164
+ Slack support reads Slack workspace export JSON. LinkedIn support reads LinkedIn data archive CSV files when LinkedIn includes the relevant files in your archive.
165
+
148
166
  Relationship labels are working notes, not truth. You can edit them with `relationship_overrides.json`.
149
167
 
150
168
  Always-on and outbound modes depend on local app databases, WhatsApp Web, and third-party behavior that can change. You are responsible for consent, privacy, message content, and anything sent from your machine.
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
8
8
  import { copyDir, ensureDir, packageRoot, resolveVault, writeDefaultVault } from "../lib/fs.js";
9
9
 
10
10
  const root = packageRoot(import.meta.url);
11
+ const CONFIG_SCHEMA_VERSION = 1;
11
12
 
12
13
  main().catch((error) => {
13
14
  console.error(error.message);
@@ -23,6 +24,9 @@ async function main() {
23
24
  else if (command === "doctor") doctor();
24
25
  else if (command === "tutorial" || command === "setup-check") doctor({ tutorial: true });
25
26
  else if (command === "sync-whatsapp") runPython("digital_brain_whatsapp_mac_sync.py", argv);
27
+ else if (command === "sync-imessage") runPython("digital_brain_imessage_sync.py", argv);
28
+ else if (command === "import-slack") runPython("digital_brain_slack_export_import.py", argv);
29
+ else if (command === "import-linkedin") runPython("digital_brain_linkedin_export_import.py", argv);
26
30
  else if (command === "extract") runPython("digital_brain_relationship_extractor.py", argv);
27
31
  else if (command === "interpret") runPython("digital_brain_relationship_interpreter.py", argv);
28
32
  else if (command === "send-whatsapp") runNode("whatsapp-web/send.mjs", argv);
@@ -44,6 +48,9 @@ async function init(argv, args) {
44
48
  let activeWindow = args["active-window"] || "08:00-12:00";
45
49
  let timezone = args.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
46
50
  let outboundMode = args["outbound-mode"] || "draft";
51
+ let privacyMode = args["privacy-mode"] || "standard";
52
+ let sourceMarkdownMode = args["source-markdown-mode"] || "none";
53
+ let selectedSources = parseList(args.sources || "whatsapp");
47
54
  let responsibilityAccepted = fullAuto || schedule === "always-on";
48
55
 
49
56
  if (!args.yes) {
@@ -81,6 +88,16 @@ async function init(argv, args) {
81
88
  });
82
89
  }
83
90
  activeWindow = await ask(rl, "🪟 Active window for frequent refreshes", activeWindow);
91
+ selectedSources = await multiSelect(rl, "Sources to set up", [
92
+ ["whatsapp", "WhatsApp Mac", "Live local sync from WhatsApp for Mac database.", "💚"],
93
+ ["imessage", "Apple iMessage", "Live local sync from macOS Messages database.", "💬"],
94
+ ["slack", "Slack export", "Import official Slack workspace export ZIP/folder.", "🧵"],
95
+ ["linkedin", "LinkedIn archive", "Import official LinkedIn data archive ZIP/folder.", "💼"],
96
+ ], selectedSources);
97
+ privacyMode = await select(rl, "Privacy mode", [
98
+ ["standard", "Standard", "Keep raw JSONL locally for analysis, but do not generate raw chat Markdown.", "🔐"],
99
+ ["metadata-only", "Metadata only", "Store timestamps and participants, but omit message bodies.", "🧼"],
100
+ ], privacyMode);
84
101
  outboundMode = await select(rl, "WhatsApp outbound mode", [
85
102
  ["disabled", "Disabled", "Never prepares WhatsApp sends.", "🔒"],
86
103
  ["draft", "Draft only", "Prepares text and requires you to send it.", "✍️"],
@@ -100,6 +117,7 @@ async function init(argv, args) {
100
117
  ensureDir(vault);
101
118
  copyDir(path.join(root, "templates", "vault"), vault);
102
119
  const config = {
120
+ schemaVersion: CONFIG_SCHEMA_VERSION,
103
121
  selfName: selfName || "Me",
104
122
  dataWindowDays,
105
123
  focus: focus || "relationship-memory",
@@ -108,6 +126,10 @@ async function init(argv, args) {
108
126
  activeWindow,
109
127
  timezone,
110
128
  outboundMode,
129
+ privacyMode,
130
+ sourceMarkdownMode,
131
+ selectedSources,
132
+ outboundLogMode: args["outbound-log-mode"] || "metadata",
111
133
  setupMode,
112
134
  responsibilityAccepted,
113
135
  defaults: {
@@ -157,17 +179,34 @@ function runRefresh(argv, args) {
157
179
  const vault = getVaultFromArgs(argv);
158
180
  const config = readVaultConfig(vault);
159
181
  const days = String(args.days || args["data-window-days"] || config.dataWindowDays || 30);
160
- const syncArgs = ["--vault", vault, "--days", days, "--markdown-mode", args["markdown-mode"] || "month"];
182
+ const markdownMode = args["markdown-mode"] || config.sourceMarkdownMode || "none";
183
+ const privacyMode = args["privacy-mode"] || config.privacyMode || "standard";
184
+ const selectedSources = parseList(args.sources || "").length ? parseList(args.sources) : config.selectedSources || ["whatsapp"];
185
+ const syncArgs = ["--vault", vault, "--days", days, "--markdown-mode", markdownMode, "--privacy-mode", privacyMode];
161
186
  const extractArgs = ["--vault", vault, "--days", days];
162
187
  const interpretArgs = ["--vault", vault, "--days", days];
163
188
  if (args["min-messages"]) extractArgs.push("--min-messages", String(args["min-messages"]));
164
189
  console.log(`Digital Brain refresh: ${vault}`);
165
- for (const [label, script, stepArgs] of [
166
- ["sync", "digital_brain_whatsapp_mac_sync.py", syncArgs],
167
- ["extract", "digital_brain_relationship_extractor.py", extractArgs],
168
- ["interpret", "digital_brain_relationship_interpreter.py", interpretArgs],
169
- ]) {
170
- if (toBoolean(args[`skip-${label}`])) {
190
+ if (toBoolean(args["dry-run"])) {
191
+ console.log("Dry run. Planned steps:");
192
+ if (selectedSources.includes("whatsapp")) console.log(` sync WhatsApp: days=${days}, markdown=${markdownMode}, privacy=${privacyMode}`);
193
+ if (selectedSources.includes("imessage")) console.log(` sync iMessage: days=${days}, markdown=${markdownMode}, privacy=${privacyMode}`);
194
+ if (selectedSources.includes("slack")) console.log(" Slack: import-only; run digital-brain import-slack --input <export.zip>");
195
+ if (selectedSources.includes("linkedin")) console.log(" LinkedIn: import-only; run digital-brain import-linkedin --input <archive.zip>");
196
+ console.log(` extract relationships: days=${days}`);
197
+ console.log(` interpret relationship drafts: days=${days}`);
198
+ return;
199
+ }
200
+ const steps = [];
201
+ if (selectedSources.includes("whatsapp")) steps.push(["sync WhatsApp", "sync-whatsapp", "digital_brain_whatsapp_mac_sync.py", syncArgs]);
202
+ if (selectedSources.includes("imessage")) steps.push(["sync iMessage", "sync-imessage", "digital_brain_imessage_sync.py", syncArgs]);
203
+ steps.push(
204
+ ["extract", "extract", "digital_brain_relationship_extractor.py", extractArgs],
205
+ ["interpret", "interpret", "digital_brain_relationship_interpreter.py", interpretArgs],
206
+ );
207
+ for (const [label, skipKey, script, stepArgs] of steps) {
208
+ const skipRequested = toBoolean(args[`skip-${skipKey}`]) || (skipKey.startsWith("sync-") && toBoolean(args["skip-sync"]));
209
+ if (skipRequested) {
171
210
  console.log(`\n→ ${label} skipped`);
172
211
  continue;
173
212
  }
@@ -208,10 +247,13 @@ function readVaultConfig(vault) {
208
247
  }
209
248
 
210
249
  function printSetupCheck(vault, options = {}) {
250
+ const config = fs.existsSync(path.join(vault, "digital-brain.config.json")) ? readVaultConfig(vault) : {};
251
+ const selectedSources = config.selectedSources || ["whatsapp"];
211
252
  const pythonVersion = shell("python3", ["--version"], true);
212
253
  const pythonSqlite = shell("python3", ["-c", "import sqlite3; print('ok')"], true);
213
254
  const ollamaVersion = shell("ollama", ["--version"], true);
214
255
  const macDb = path.join(os.homedir(), "Library/Group Containers/group.net.whatsapp.WhatsApp.shared/ChatStorage.sqlite");
256
+ const messagesDb = path.join(os.homedir(), "Library/Messages/chat.db");
215
257
  const checks = [
216
258
  {
217
259
  label: "Node",
@@ -236,12 +278,6 @@ function printSetupCheck(vault, options = {}) {
236
278
  value: pythonSqlite === "ok" ? "available" : "not found",
237
279
  hint: "Use a Python 3 build with sqlite3 support.",
238
280
  },
239
- {
240
- label: "WhatsApp Mac database",
241
- ok: fs.existsSync(macDb),
242
- value: fs.existsSync(macDb) ? "found" : "not found yet",
243
- hint: "Open WhatsApp for Mac and log in. If macOS blocks access, grant Terminal Full Disk Access.",
244
- },
245
281
  {
246
282
  label: "Ollama",
247
283
  ok: Boolean(ollamaVersion),
@@ -250,6 +286,38 @@ function printSetupCheck(vault, options = {}) {
250
286
  optional: true,
251
287
  },
252
288
  ];
289
+ if (selectedSources.includes("whatsapp")) {
290
+ checks.push({
291
+ label: "WhatsApp Mac database",
292
+ ok: fs.existsSync(macDb),
293
+ value: fs.existsSync(macDb) ? "found" : "not found yet",
294
+ hint: "Install/open WhatsApp for Mac, log in, then grant Terminal Full Disk Access if needed: https://faq.whatsapp.com/686469079565350",
295
+ });
296
+ }
297
+ if (selectedSources.includes("imessage")) {
298
+ checks.push({
299
+ label: "Apple Messages database",
300
+ ok: fs.existsSync(messagesDb),
301
+ value: fs.existsSync(messagesDb) ? "found" : "not found yet",
302
+ hint: "Open Messages on macOS and grant Terminal Full Disk Access if needed: https://support.apple.com/guide/messages/welcome/mac",
303
+ });
304
+ }
305
+ if (selectedSources.includes("slack")) {
306
+ checks.push({
307
+ label: "Slack export",
308
+ ok: true,
309
+ value: "import manually",
310
+ hint: "Export guide: https://slack.com/help/articles/201658943-Export-your-workspace-data",
311
+ });
312
+ }
313
+ if (selectedSources.includes("linkedin")) {
314
+ checks.push({
315
+ label: "LinkedIn archive",
316
+ ok: true,
317
+ value: "import manually",
318
+ hint: "Export guide: https://www.linkedin.com/help/linkedin/answer/a566336",
319
+ });
320
+ }
253
321
 
254
322
  console.log("");
255
323
  console.log("Setup check");
@@ -268,16 +336,28 @@ function printSetupCheck(vault, options = {}) {
268
336
  if (options.tutorial) {
269
337
  console.log("");
270
338
  console.log("How to use it");
271
- console.log(" 1. Open WhatsApp for Mac once and keep it logged in.");
272
- console.log(" 2. Run: digital-brain run");
273
- console.log(" 3. Ask your AI to use the generated vault notes for personal context.");
339
+ let step = 1;
340
+ if (selectedSources.includes("whatsapp")) {
341
+ console.log(` ${step++}. Open WhatsApp for Mac once and keep it logged in.`);
342
+ }
343
+ if (selectedSources.includes("imessage")) {
344
+ console.log(` ${step++}. Open Messages on macOS once and grant Terminal Full Disk Access if needed.`);
345
+ }
346
+ if (selectedSources.includes("slack")) {
347
+ console.log(` ${step++}. Download a Slack export, then run: digital-brain import-slack --input <export.zip>`);
348
+ }
349
+ if (selectedSources.includes("linkedin")) {
350
+ console.log(` ${step++}. Download a LinkedIn archive, then run: digital-brain import-linkedin --input <archive.zip>`);
351
+ }
352
+ console.log(` ${step++}. Run: digital-brain run`);
353
+ console.log(` ${step}. Ask your AI to use the generated vault notes for personal context.`);
274
354
  console.log("");
275
355
  console.log("No pip install is needed. npm installs the package dependencies.");
276
356
  }
277
357
  }
278
358
 
279
359
  function writeConfig(vault, config) {
280
- fs.writeFileSync(path.join(vault, "digital-brain.config.json"), `${JSON.stringify(config, null, 2)}\n`);
360
+ writeFileAtomic(path.join(vault, "digital-brain.config.json"), `${JSON.stringify(config, null, 2)}\n`);
281
361
  }
282
362
 
283
363
  function writeRefreshScript(vault, config) {
@@ -290,12 +370,12 @@ set -euo pipefail
290
370
  VAULT="${vault.replace(/"/g, '\\"')}"
291
371
  DAYS="${days}"
292
372
 
293
- digital-brain run --vault "$VAULT" --days "$DAYS" --markdown-mode month
373
+ digital-brain run --vault "$VAULT" --days "$DAYS"
294
374
 
295
375
  echo "Digital Brain refresh complete for $VAULT"
296
376
  `;
297
377
  const scriptPath = path.join(toolsDir, "digital-brain-refresh.sh");
298
- fs.writeFileSync(scriptPath, content);
378
+ writeFileAtomic(scriptPath, content);
299
379
  fs.chmodSync(scriptPath, 0o755);
300
380
  }
301
381
 
@@ -318,7 +398,7 @@ while true; do
318
398
  done
319
399
  `;
320
400
  const scriptPath = path.join(toolsDir, "digital-brain-watch.sh");
321
- fs.writeFileSync(scriptPath, content);
401
+ writeFileAtomic(scriptPath, content);
322
402
  fs.chmodSync(scriptPath, 0o755);
323
403
  }
324
404
 
@@ -339,6 +419,12 @@ Use it as local personal context when the user asks about preferences, relations
339
419
  console.log(`${label} pointer: ${file}`);
340
420
  }
341
421
 
422
+ function writeFileAtomic(file, content) {
423
+ const temp = `${file}.${process.pid}.tmp`;
424
+ fs.writeFileSync(temp, content);
425
+ fs.renameSync(temp, file);
426
+ }
427
+
342
428
  function printSetupHeader(defaultVault) {
343
429
  console.log("");
344
430
  console.log("╭────────────────────────────────────────╮");
@@ -386,6 +472,28 @@ async function select(rl, label, options, fallback) {
386
472
  return exact ? exact[0] : options[defaultIndex][0];
387
473
  }
388
474
 
475
+ async function multiSelect(rl, label, options, fallbackValues) {
476
+ const fallback = fallbackValues.length ? fallbackValues : [options[0][0]];
477
+ console.log("");
478
+ console.log(`◇ ${label}`);
479
+ options.forEach(([, title, description, icon = "•"], index) => {
480
+ const selected = fallback.includes(options[index][0]) ? " ← default" : "";
481
+ const letter = letterFor(index);
482
+ console.log(` ${letter}) ${icon} ${title}${selected}`);
483
+ console.log(` ${description}`);
484
+ });
485
+ const answer = await rl.question(`Choose one or more, comma-separated [${fallback.join(",")}]: `);
486
+ if (!answer.trim()) return fallback;
487
+ const values = [];
488
+ for (const token of answer.split(",").map((value) => value.trim()).filter(Boolean)) {
489
+ const letterIndex = indexFromLetter(token);
490
+ const numericIndex = Number(token) - 1;
491
+ const option = options[letterIndex] || options[numericIndex] || options.find(([value, title]) => value === token || title.toLowerCase() === token.toLowerCase());
492
+ if (option && !values.includes(option[0])) values.push(option[0]);
493
+ }
494
+ return values.length ? values : fallback;
495
+ }
496
+
389
497
  async function confirm(rl, label, fallback) {
390
498
  const hint = fallback ? "Y/n" : "y/N";
391
499
  const answer = (await rl.question(`${label} [${hint}]: `)).trim().toLowerCase();
@@ -443,6 +551,11 @@ function parseArgs(argv) {
443
551
  return out;
444
552
  }
445
553
 
554
+ function parseList(value) {
555
+ if (!value) return [];
556
+ return String(value).split(",").map((item) => item.trim()).filter(Boolean);
557
+ }
558
+
446
559
  function toBoolean(value) {
447
560
  if (value === undefined) return false;
448
561
  if (value === true) return true;
@@ -464,6 +577,9 @@ Usage:
464
577
  digital-brain doctor
465
578
  digital-brain tutorial
466
579
  digital-brain sync-whatsapp --days 30
580
+ digital-brain sync-imessage --days 30
581
+ digital-brain import-slack --input slack-export.zip
582
+ digital-brain import-linkedin --input linkedin-archive.zip
467
583
  digital-brain extract --days 30
468
584
  digital-brain interpret --days 30
469
585
  digital-brain send-whatsapp --to "Name" --message "Text" [--yes]
@@ -0,0 +1,72 @@
1
+ # Integrations
2
+
3
+ Digital Brain prefers official exports and local files over scraping.
4
+
5
+ ## WhatsApp
6
+
7
+ ```bash
8
+ digital-brain run
9
+ ```
10
+
11
+ WhatsApp support reads the local macOS WhatsApp database when available.
12
+
13
+ Dependency selected in setup:
14
+
15
+ - Install/open WhatsApp for Mac and log in: https://faq.whatsapp.com/686469079565350
16
+ - If macOS blocks access, grant Full Disk Access to the terminal app running Digital Brain.
17
+ - If WhatsApp is selected and the database is missing, `digital-brain run` exits with an error.
18
+
19
+ ## Apple iMessage
20
+
21
+ ```bash
22
+ digital-brain sync-imessage --days 30
23
+ digital-brain extract
24
+ digital-brain interpret
25
+ ```
26
+
27
+ iMessage support reads the local macOS Messages database at `~/Library/Messages/chat.db`.
28
+
29
+ Dependency selected in setup:
30
+
31
+ - Open Messages on macOS at least once: https://support.apple.com/guide/messages/welcome/mac
32
+ - If macOS blocks access, grant Full Disk Access to the terminal app running Digital Brain.
33
+ - If iMessage is selected and the database is missing, `digital-brain run` exits with an error.
34
+
35
+ ## Slack
36
+
37
+ Use Slack's official workspace export ZIP or extracted export folder.
38
+
39
+ ```bash
40
+ digital-brain import-slack --input ./slack-export.zip
41
+ digital-brain extract
42
+ digital-brain interpret
43
+ ```
44
+
45
+ Slack export access depends on workspace permissions and plan. Public-channel exports are common; private channels and DMs may require elevated workspace export permissions.
46
+
47
+ Export guide: https://slack.com/help/articles/201658943-Export-your-workspace-data
48
+
49
+ ## LinkedIn
50
+
51
+ Use LinkedIn's official data archive ZIP or extracted archive folder.
52
+
53
+ ```bash
54
+ digital-brain import-linkedin --input ./linkedin-archive.zip
55
+ digital-brain extract
56
+ digital-brain interpret
57
+ ```
58
+
59
+ Digital Brain does not scrape LinkedIn or automate the LinkedIn app. LinkedIn controls what appears in the archive, so connections/messages may vary by account and export type.
60
+
61
+ Data archive guide: https://www.linkedin.com/help/linkedin/answer/a566336
62
+
63
+ ## Useful Next Sources
64
+
65
+ - Calendar: recurring people, meetings, and relationship cadence.
66
+ - Email export: long-form relationship and work context, but privacy risk is high.
67
+ - GitHub: collaborators, repos, review style, and work graph.
68
+ - Linear/Jira: active projects and operating context.
69
+ - Contacts: canonical names, phones, companies, and relationship labels.
70
+ - Browser bookmarks/read-later: interests and research areas.
71
+
72
+ The right rule: add sources that improve memory without turning Digital Brain into spyware. Prefer explicit exports, clear consent, and source-specific privacy controls.
package/docs/PRIVACY.md CHANGED
@@ -7,13 +7,15 @@ Digital Brain is designed for local use.
7
7
  - No cloud API is called by default.
8
8
  - Ollama interpretation is local when enabled.
9
9
  - WhatsApp sending uses a local WhatsApp Web session.
10
+ - Raw source data stays under `08 Sources/`; normal AI context should use `06 AI Memory/` and human notes under `04 People/`.
11
+ - Same-person matching across sources is provisional and file-based; keep source evidence visible when using merged person context.
10
12
 
11
13
  Things to be careful about:
12
14
 
13
15
  - Do not commit a generated vault.
14
16
  - Do not paste private message exports into GitHub issues.
15
17
  - Do not enable outbound sending without understanding the risk.
16
- - Treat WhatsApp database access and WhatsApp Web automation as unofficial, changeable integrations.
18
+ - Treat WhatsApp database access, iMessage database access, and WhatsApp Web automation as local, permission-sensitive integrations that can change.
17
19
  - Always-on mode runs on your machine and inherits your local permissions.
18
20
  - You are responsible for consent, privacy, message content, and sends made from your machine.
19
21
  - Treat relationship labels as editable working notes, not truth.
package/docs/SETUP.md CHANGED
@@ -16,7 +16,12 @@ After the quiz, `init` runs a setup check for:
16
16
  - npm package dependencies
17
17
  - Python 3
18
18
  - Python sqlite3 support
19
- - WhatsApp for Mac local database access
19
+ - selected live source access:
20
+ - WhatsApp for Mac local database access
21
+ - Apple Messages local database access
22
+ - selected import source instructions:
23
+ - Slack export link
24
+ - LinkedIn archive link
20
25
  - optional Ollama
21
26
 
22
27
  Run the check again anytime:
@@ -36,11 +41,38 @@ digital-brain tutorial
36
41
  Digital Brain does not silently install or configure system apps.
37
42
 
38
43
  - WhatsApp for Mac must be installed and logged in by the user.
44
+ - Apple Messages must be opened by the user before iMessage sync can read `~/Library/Messages/chat.db`.
39
45
  - macOS may require Full Disk Access for the terminal app.
40
46
  - Ollama is optional and only needed for local LLM workflows.
41
47
 
48
+ Useful links:
49
+
50
+ - Node: https://nodejs.org
51
+ - WhatsApp for Mac: https://faq.whatsapp.com/686469079565350
52
+ - Apple Messages: https://support.apple.com/guide/messages/welcome/mac
53
+ - Slack exports: https://slack.com/help/articles/201658943-Export-your-workspace-data
54
+ - LinkedIn data archive: https://www.linkedin.com/help/linkedin/answer/a566336
55
+
42
56
  Once setup passes, normal use is:
43
57
 
44
58
  ```bash
45
59
  digital-brain run
46
60
  ```
61
+
62
+ `run` executes the live local sources selected during setup. If a selected live source does not exist or cannot be opened, the command fails with a setup error.
63
+
64
+ Optional imports:
65
+
66
+ ```bash
67
+ digital-brain import-slack --input ./slack-export.zip
68
+ digital-brain import-linkedin --input ./linkedin-archive.zip
69
+ digital-brain extract
70
+ digital-brain interpret
71
+ ```
72
+
73
+ Useful direct sync commands:
74
+
75
+ ```bash
76
+ digital-brain sync-whatsapp --days 30
77
+ digital-brain sync-imessage --days 30
78
+ ```
@@ -1,13 +1,14 @@
1
- # Close Friend
1
+ # Close Friend (WhatsApp)
2
2
 
3
- Generated: 2026-06-09T22:26:26.131803+00:00
3
+ Generated: 2026-06-09T23:15:09.589124+00:00
4
+ Source: WhatsApp
4
5
  Role: operational contact
5
6
  Role confidence: low
6
7
  Closeness: medium
7
8
  Conversation difficulty: low
8
9
  Typing style: short
9
10
 
10
- These are private working notes. Edit them where wrong.
11
+ Generated draft, not truth. These are private working notes. Edit them where wrong.
11
12
 
12
13
  ## Role / Relationship Label
13
14
  - operational contact (low confidence).
@@ -1,13 +1,14 @@
1
- # Mom
1
+ # Mom (WhatsApp)
2
2
 
3
- Generated: 2026-06-09T22:26:26.131148+00:00
3
+ Generated: 2026-06-09T23:15:09.587707+00:00
4
+ Source: WhatsApp
4
5
  Role: mother
5
6
  Role confidence: high
6
7
  Closeness: medium
7
8
  Conversation difficulty: low
8
9
  Typing style: very short
9
10
 
10
- These are private working notes. Edit them where wrong.
11
+ Generated draft, not truth. These are private working notes. Edit them where wrong.
11
12
 
12
13
  ## Role / Relationship Label
13
14
  - mother (high confidence).
@@ -1,13 +1,14 @@
1
- # Project Team
1
+ # Project Team (WhatsApp)
2
2
 
3
- Generated: 2026-06-09T22:26:26.131626+00:00
3
+ Generated: 2026-06-09T23:15:09.588601+00:00
4
+ Source: WhatsApp
4
5
  Role: work collaborator
5
6
  Role confidence: medium
6
7
  Closeness: low/unclear
7
8
  Conversation difficulty: practical/low-emotional
8
9
  Typing style: short
9
10
 
10
- These are private working notes. Edit them where wrong.
11
+ Generated draft, not truth. These are private working notes. Edit them where wrong.
11
12
 
12
13
  ## Role / Relationship Label
13
14
  - work collaborator (medium confidence).
@@ -2,6 +2,6 @@
2
2
 
3
3
  Generated working notes. Treat as editable, not truth.
4
4
 
5
- - [[Mom]]: mother (high), closeness medium, difficulty low, style very short
6
- - [[Project Team]]: work collaborator (medium), closeness low/unclear, difficulty practical/low-emotional, style short
7
- - [[Close Friend]]: operational contact (low), closeness medium, difficulty low, style short
5
+ - [[Mom (WhatsApp)]]: mother (high), closeness medium, difficulty low, style very short
6
+ - [[Project Team (WhatsApp)]]: work collaborator (medium), closeness low/unclear, difficulty practical/low-emotional, style short
7
+ - [[Close Friend (WhatsApp)]]: operational contact (low), closeness medium, difficulty low, style short
@@ -0,0 +1,26 @@
1
+ # Person Context Index
2
+
3
+ Window: last 365 days
4
+
5
+ Canonical people matched across sources. Treat matches as provisional unless manually confirmed.
6
+
7
+ ## Mom
8
+
9
+ - Canonical key: `person::mom`
10
+ - Aliases: Mom
11
+ - Sources: WhatsApp
12
+ - Messages: 2
13
+ - Dates: 2026-01-01 to 2026-01-01
14
+ - Source-specific context:
15
+ - WhatsApp / Mom: warm personal relationship; 2 messages; style very short; avg 4.0 words; lowercase 0.0; emoji 0.0; slang none
16
+
17
+ ## Close Friend
18
+
19
+ - Canonical key: `person::close friend`
20
+ - Aliases: Close Friend
21
+ - Sources: WhatsApp
22
+ - Messages: 2
23
+ - Dates: 2026-01-01 to 2026-01-01
24
+ - Source-specific context:
25
+ - WhatsApp / Close Friend: warm personal relationship; 2 messages; style short; avg 5.0 words; lowercase 0.0; emoji 0.0; slang none
26
+
@@ -0,0 +1,26 @@
1
+ # Person Reply Context
2
+
3
+ Use this first when responding to a specific person. It merges confirmed-looking matches across sources while keeping each source visible.
4
+
5
+ ## Mom
6
+
7
+ - Canonical key: `person::mom`
8
+ - Aliases: Mom
9
+ - Sources: WhatsApp
10
+ - Total messages: 2
11
+ - Source-specific guidance:
12
+ - WhatsApp / Mom: mother (high), closeness medium, difficulty low.
13
+ Style: very short.
14
+ Reply: Keep replies very short unless context demands detail. Match style without exaggerating or parodying the person.
15
+
16
+ ## Close Friend
17
+
18
+ - Canonical key: `person::close friend`
19
+ - Aliases: Close Friend
20
+ - Sources: WhatsApp
21
+ - Total messages: 2
22
+ - Source-specific guidance:
23
+ - WhatsApp / Close Friend: operational contact (low), closeness medium, difficulty low.
24
+ Style: short.
25
+ Reply: Keep replies very short unless context demands detail. Match style without exaggerating or parodying the person.
26
+
@@ -1,13 +1,14 @@
1
- # Close Friend
1
+ # Close Friend (WhatsApp)
2
2
 
3
- Generated: 2026-06-09T22:26:26.131803+00:00
3
+ Generated: 2026-06-09T23:15:09.589124+00:00
4
+ Source: WhatsApp
4
5
  Role: operational contact
5
6
  Role confidence: low
6
7
  Closeness: medium
7
8
  Conversation difficulty: low
8
9
  Typing style: short
9
10
 
10
- These are private working notes. Edit them where wrong.
11
+ Generated draft, not truth. These are private working notes. Edit them where wrong.
11
12
 
12
13
  ## Role / Relationship Label
13
14
  - operational contact (low confidence).
@@ -1,13 +1,14 @@
1
- # Mom
1
+ # Mom (WhatsApp)
2
2
 
3
- Generated: 2026-06-09T22:26:26.131148+00:00
3
+ Generated: 2026-06-09T23:15:09.587707+00:00
4
+ Source: WhatsApp
4
5
  Role: mother
5
6
  Role confidence: high
6
7
  Closeness: medium
7
8
  Conversation difficulty: low
8
9
  Typing style: very short
9
10
 
10
- These are private working notes. Edit them where wrong.
11
+ Generated draft, not truth. These are private working notes. Edit them where wrong.
11
12
 
12
13
  ## Role / Relationship Label
13
14
  - mother (high confidence).