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.
- package/README.md +20 -2
- package/bin/digital-brain.js +136 -20
- package/docs/INTEGRATIONS.md +72 -0
- package/docs/PRIVACY.md +3 -1
- package/docs/SETUP.md +33 -1
- package/examples/sample-vault/{04 People/Interpreted Relationships/Close Friend.md → 06 AI Memory/Generated Relationship Drafts/Close Friend (WhatsApp).md } +4 -3
- package/examples/sample-vault/{04 People/Interpreted Relationships/Mom.md → 06 AI Memory/Generated Relationship Drafts/Mom (WhatsApp).md } +4 -3
- package/examples/sample-vault/{08 Sources/WhatsApp/Analysis/Interpreted/Project Team.md → 06 AI Memory/Generated Relationship Drafts/Project Team (WhatsApp).md } +4 -3
- package/examples/sample-vault/06 AI Memory/Interpreted Relationship Memory.md +3 -3
- package/examples/sample-vault/06 AI Memory/Person Context Index.md +26 -0
- package/examples/sample-vault/06 AI Memory/Person Reply Context.md +26 -0
- package/examples/sample-vault/08 Sources/{WhatsApp/Analysis/Interpreted/Close Friend.md → Analysis/Interpreted/Close Friend (WhatsApp).md } +4 -3
- package/examples/sample-vault/08 Sources/{WhatsApp/Analysis/Interpreted/Mom.md → Analysis/Interpreted/Mom (WhatsApp).md } +4 -3
- package/examples/sample-vault/{04 People/Interpreted Relationships/Project Team.md → 08 Sources/Analysis/Interpreted/Project Team (WhatsApp).md } +4 -3
- package/examples/sample-vault/08 Sources/Analysis/Relationship Map.md +38 -0
- package/examples/sample-vault/08 Sources/Analysis/interpreted_relationship_models.json +175 -0
- package/examples/sample-vault/08 Sources/Analysis/person_identity_map.json +78 -0
- package/examples/sample-vault/08 Sources/Analysis/relationship_profiles.json +122 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Close Friend (WhatsApp).md +44 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Mom (WhatsApp).md +45 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Interpreted/Project Team (WhatsApp).md +45 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/Relationship Map.md +9 -3
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/interpreted_relationship_models.json +18 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/person_identity_map.json +78 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Analysis/relationship_profiles.json +18 -0
- package/examples/sample-vault/08 Sources/WhatsApp/Raw/2026-01-01.jsonl +6 -6
- package/lib/fs.js +7 -1
- package/package.json +2 -1
- package/scripts/digital_brain_imessage_sync.py +175 -0
- package/scripts/digital_brain_linkedin_export_import.py +214 -0
- package/scripts/digital_brain_relationship_extractor.py +189 -12
- package/scripts/digital_brain_relationship_interpreter.py +104 -15
- package/scripts/digital_brain_slack_export_import.py +181 -0
- package/scripts/digital_brain_whatsapp_mac_sync.py +37 -8
- package/templates/vault/00 Home/How AI Should Use This Vault.md +1 -1
- package/templates/vault/00 Home/Start Here.md +2 -1
- package/templates/vault/04 People/Relationship Overrides.md +2 -1
- package/templates/vault/06 AI Memory/Generated Relationship Drafts/README.md +5 -0
- package/templates/vault/06 AI Memory/Interpreted Relationship Memory.md +1 -2
- package/templates/vault/06 AI Memory/Person Context Index.md +4 -0
- package/templates/vault/06 AI Memory/Person Reply Context.md +4 -0
- package/templates/vault/08 Sources/README.md +5 -0
- package/templates/vault/08 Sources/WhatsApp/Outbound/README.md +2 -2
- package/templates/vault/AGENTS.md +5 -1
- package/templates/vault/CLAUDE.md +3 -0
- package/templates/vault/GEMINI.md +4 -0
- 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
|
|
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
|
|
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.
|
package/bin/digital-brain.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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).
|