digital-brain 0.1.2 → 0.1.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 +1 -1
- package/bin/digital-brain.js +43 -27
- package/docs/AUTOMATIONS.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npx digital-brain init
|
|
|
26
26
|
|
|
27
27
|
The installer asks a short setup quiz: history window, primary focus, refresh cadence, always-on interval, active time window, outbound mode, and AI adapter setup.
|
|
28
28
|
|
|
29
|
-
The quiz is mostly multiple choice.
|
|
29
|
+
The quiz is mostly multiple choice. Pick with `A/B/C`, `1/2/3`, the exact value, or press Enter to accept the default. If you skip the vault path, Digital Brain creates a new folder in the current directory:
|
|
30
30
|
|
|
31
31
|
```text
|
|
32
32
|
./Digital Brain Vault
|
package/bin/digital-brain.js
CHANGED
|
@@ -46,34 +46,34 @@ async function init(argv, args) {
|
|
|
46
46
|
if (!args.yes) {
|
|
47
47
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
48
48
|
printSetupHeader(defaultVault);
|
|
49
|
-
vault ||= path.resolve(await ask(rl, "Vault path", defaultVault, "Enter creates this folder if it does not exist."));
|
|
50
|
-
selfName ||= await ask(rl, "Your name", "Me");
|
|
51
|
-
dataWindowDays = await askNumber(rl, "History to import", dataWindowDays, { suffix: "days", min: 1 });
|
|
49
|
+
vault ||= path.resolve(await ask(rl, "📁 Vault path", defaultVault, "Enter creates this folder if it does not exist."));
|
|
50
|
+
selfName ||= await ask(rl, "👤 Your name", "Me");
|
|
51
|
+
dataWindowDays = await askNumber(rl, "🕰️ History to import", dataWindowDays, { suffix: "days", min: 1 });
|
|
52
52
|
focus ||= await select(rl, "Primary focus", [
|
|
53
|
-
["relationship-memory", "Relationship memory", "Map people, tone, and recurring patterns."],
|
|
54
|
-
["reply-help", "Reply help", "Prioritize drafting guidance and typing-style matching."],
|
|
55
|
-
["work-context", "Work context", "Prioritize collaborators, projects, and operational notes."],
|
|
53
|
+
["relationship-memory", "Relationship memory", "Map people, tone, and recurring patterns.", "🧠"],
|
|
54
|
+
["reply-help", "Reply help", "Prioritize drafting guidance and typing-style matching.", "💬"],
|
|
55
|
+
["work-context", "Work context", "Prioritize collaborators, projects, and operational notes.", "💼"],
|
|
56
56
|
], "relationship-memory");
|
|
57
57
|
schedule = await select(rl, "Refresh cadence", [
|
|
58
|
-
["manual", "Manual", "Only runs when you run a command."],
|
|
59
|
-
["daily", "Daily", "Good for a low-maintenance personal vault."],
|
|
60
|
-
["hourly", "Hourly", "Keeps memory warm without running constantly."],
|
|
61
|
-
["every-30-min", "Every 30 minutes", "Useful for morning or work-window guidance."],
|
|
62
|
-
["always-on", "Always-on local loop", "Runs repeatedly while your computer is awake."],
|
|
58
|
+
["manual", "Manual", "Only runs when you run a command.", "🖐️"],
|
|
59
|
+
["daily", "Daily", "Good for a low-maintenance personal vault.", "🌅"],
|
|
60
|
+
["hourly", "Hourly", "Keeps memory warm without running constantly.", "⏱️"],
|
|
61
|
+
["every-30-min", "Every 30 minutes", "Useful for morning or work-window guidance.", "🔁"],
|
|
62
|
+
["always-on", "Always-on local loop", "Runs repeatedly while your computer is awake.", "⚡"],
|
|
63
63
|
], schedule);
|
|
64
64
|
if (schedule === "always-on") {
|
|
65
|
-
refreshIntervalMinutes = await askNumber(rl, "Always-on pull interval", refreshIntervalMinutes, {
|
|
65
|
+
refreshIntervalMinutes = await askNumber(rl, "⏳ Always-on pull interval", refreshIntervalMinutes, {
|
|
66
66
|
suffix: "minutes",
|
|
67
67
|
min: 1,
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
-
activeWindow = await ask(rl, "Active window for frequent refreshes", activeWindow);
|
|
70
|
+
activeWindow = await ask(rl, "🪟 Active window for frequent refreshes", activeWindow);
|
|
71
71
|
outboundMode = await select(rl, "WhatsApp outbound mode", [
|
|
72
|
-
["disabled", "Disabled", "Never prepares WhatsApp sends."],
|
|
73
|
-
["draft", "Draft only", "Prepares text and requires you to send it."],
|
|
74
|
-
["send-with-confirmation", "Send with confirmation", "Can send only after explicit command confirmation."],
|
|
72
|
+
["disabled", "Disabled", "Never prepares WhatsApp sends.", "🔒"],
|
|
73
|
+
["draft", "Draft only", "Prepares text and requires you to send it.", "✍️"],
|
|
74
|
+
["send-with-confirmation", "Send with confirmation", "Can send only after explicit command confirmation.", "✅"],
|
|
75
75
|
], outboundMode);
|
|
76
|
-
connectAi = await confirm(rl, "Add global AI pointers for Codex/Claude/Gemini?", true);
|
|
76
|
+
connectAi = await confirm(rl, "🔗 Add global AI pointers for Codex/Claude/Gemini?", true);
|
|
77
77
|
responsibilityAccepted = await responsibilityGate(rl, { schedule, outboundMode });
|
|
78
78
|
if (!responsibilityAccepted && (schedule === "always-on" || outboundMode === "send-with-confirmation")) {
|
|
79
79
|
console.log("Full-auto/outbound confirmation was not accepted. Using manual refresh and draft-only outbound.");
|
|
@@ -226,9 +226,11 @@ Use it as local personal context when the user asks about preferences, relations
|
|
|
226
226
|
|
|
227
227
|
function printSetupHeader(defaultVault) {
|
|
228
228
|
console.log("");
|
|
229
|
-
console.log("
|
|
230
|
-
console.log("
|
|
231
|
-
console.log(
|
|
229
|
+
console.log("╭────────────────────────────────────────╮");
|
|
230
|
+
console.log("│ 🧠 Digital Brain setup │");
|
|
231
|
+
console.log("╰────────────────────────────────────────╯");
|
|
232
|
+
console.log("Pick with A/B/C, 1/2/3, exact value, or press Enter for the default.");
|
|
233
|
+
console.log(`Skipping the vault path creates: ${defaultVault}`);
|
|
232
234
|
console.log("");
|
|
233
235
|
}
|
|
234
236
|
|
|
@@ -250,18 +252,22 @@ async function askNumber(rl, label, fallback, options = {}) {
|
|
|
250
252
|
async function select(rl, label, options, fallback) {
|
|
251
253
|
const defaultIndex = Math.max(0, options.findIndex(([value]) => value === fallback));
|
|
252
254
|
console.log("");
|
|
253
|
-
console.log(
|
|
254
|
-
options.forEach(([, title, description], index) => {
|
|
255
|
-
const marker = index === defaultIndex ? "default" : "";
|
|
256
|
-
|
|
255
|
+
console.log(`◇ ${label}`);
|
|
256
|
+
options.forEach(([, title, description, icon = "•"], index) => {
|
|
257
|
+
const marker = index === defaultIndex ? " ← default" : "";
|
|
258
|
+
const letter = letterFor(index);
|
|
259
|
+
console.log(` ${letter}) ${icon} ${title}${marker}`);
|
|
257
260
|
console.log(` ${description}`);
|
|
258
261
|
});
|
|
259
|
-
const answer = await rl.question(`
|
|
262
|
+
const answer = await rl.question(`Choose ${letterFor(defaultIndex)}/${defaultIndex + 1} [${letterFor(defaultIndex)}]: `);
|
|
260
263
|
const trimmed = answer.trim();
|
|
261
264
|
if (!trimmed) return options[defaultIndex][0];
|
|
265
|
+
const letterIndex = indexFromLetter(trimmed);
|
|
266
|
+
if (letterIndex >= 0 && letterIndex < options.length) return options[letterIndex][0];
|
|
262
267
|
const selected = Number(trimmed);
|
|
263
268
|
if (Number.isInteger(selected) && selected >= 1 && selected <= options.length) return options[selected - 1][0];
|
|
264
|
-
const
|
|
269
|
+
const lower = trimmed.toLowerCase();
|
|
270
|
+
const exact = options.find(([value, title]) => value.toLowerCase() === lower || title.toLowerCase() === lower);
|
|
265
271
|
return exact ? exact[0] : options[defaultIndex][0];
|
|
266
272
|
}
|
|
267
273
|
|
|
@@ -276,13 +282,23 @@ async function responsibilityGate(rl, { schedule, outboundMode }) {
|
|
|
276
282
|
const needsGate = schedule === "always-on" || outboundMode === "send-with-confirmation";
|
|
277
283
|
if (!needsGate) return true;
|
|
278
284
|
console.log("");
|
|
279
|
-
console.log("Responsibility check:");
|
|
285
|
+
console.log("⚠️ Responsibility check:");
|
|
280
286
|
console.log(" Digital Brain may use local databases, WhatsApp Web, and black-box third-party app behavior.");
|
|
281
287
|
console.log(" You are responsible for consent, privacy, message content, and any sends triggered from this machine.");
|
|
282
288
|
console.log(" Enter does not approve this mode.");
|
|
283
289
|
return confirm(rl, "I understand and want this mode enabled", false);
|
|
284
290
|
}
|
|
285
291
|
|
|
292
|
+
function letterFor(index) {
|
|
293
|
+
return String.fromCharCode(65 + index);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function indexFromLetter(value) {
|
|
297
|
+
const normalized = value.trim().toUpperCase();
|
|
298
|
+
if (!/^[A-Z]$/.test(normalized)) return -1;
|
|
299
|
+
return normalized.charCodeAt(0) - 65;
|
|
300
|
+
}
|
|
301
|
+
|
|
286
302
|
function shell(command, args, optional = false) {
|
|
287
303
|
const result = spawnSync(command, args, { encoding: "utf8" });
|
|
288
304
|
if (result.error && optional) return "";
|
package/docs/AUTOMATIONS.md
CHANGED
|
@@ -24,7 +24,7 @@ That script runs sync, extract, and interpret using the install-time config.
|
|
|
24
24
|
- WhatsApp outbound mode
|
|
25
25
|
- whether to add AI adapter pointers
|
|
26
26
|
|
|
27
|
-
Most questions are multiple choice.
|
|
27
|
+
Most questions are multiple choice. Pick with `A/B/C`, `1/2/3`, the exact value, or press Enter to use the displayed default.
|
|
28
28
|
|
|
29
29
|
Important defaults:
|
|
30
30
|
|