mindlink 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunk-2H7UOFLK.js";
2
3
 
3
4
  // src/cli.ts
4
- import { Command as Command16 } from "commander";
5
- import chalk17 from "chalk";
5
+ import { Command as Command20 } from "commander";
6
+ import chalk20 from "chalk";
6
7
 
7
8
  // src/utils/version.ts
8
- var VERSION = "1.1.5";
9
+ var VERSION = "2.0.0";
9
10
 
10
11
  // src/commands/init.ts
11
12
  import { Command } from "commander";
@@ -33,6 +34,7 @@ import { join as join3, resolve, dirname as dirname2, basename } from "path";
33
34
  // src/utils/paths.ts
34
35
  import { fileURLToPath } from "url";
35
36
  import { dirname, join } from "path";
37
+ import { homedir } from "os";
36
38
  var __filename = fileURLToPath(import.meta.url);
37
39
  var __dirname = dirname(__filename);
38
40
  var TEMPLATES_DIR = join(__dirname, "templates");
@@ -40,6 +42,9 @@ var BRAIN_TEMPLATES_DIR = join(TEMPLATES_DIR, "brain");
40
42
  var AGENT_TEMPLATES_DIR = join(TEMPLATES_DIR, "agents");
41
43
  var HOOKS_TEMPLATES_DIR = join(TEMPLATES_DIR, "hooks");
42
44
  var BRAIN_DIR = ".brain";
45
+ var GLOBAL_MINDLINK_DIR = join(homedir(), ".mindlink");
46
+ var GLOBAL_USER_PROFILE_PATH = join(GLOBAL_MINDLINK_DIR, "USER.md");
47
+ var GLOBAL_WINDSURF_MCP_PATH = join(homedir(), ".codeium", "windsurf", "mcp_config.json");
43
48
 
44
49
  // src/utils/banner.ts
45
50
  import chalk from "chalk";
@@ -70,8 +75,8 @@ var AGENTS = [
70
75
  // src/utils/registry.ts
71
76
  import { readFileSync, writeFileSync, mkdirSync } from "fs";
72
77
  import { join as join2 } from "path";
73
- import { homedir } from "os";
74
- var REGISTRY_DIR = join2(homedir(), ".mindlink");
78
+ import { homedir as homedir2 } from "os";
79
+ var REGISTRY_DIR = join2(homedir2(), ".mindlink");
75
80
  var REGISTRY_PATH = join2(REGISTRY_DIR, "projects.json");
76
81
  function load() {
77
82
  try {
@@ -100,6 +105,65 @@ function pruneRegistry(isValid) {
100
105
  if (pruned.length !== paths.length) save(pruned);
101
106
  }
102
107
 
108
+ // src/utils/content.ts
109
+ function sectionHasRealContent(markdown, heading) {
110
+ const lines = markdown.split("\n");
111
+ let inSection = false;
112
+ let headingLevel = 0;
113
+ for (const line of lines) {
114
+ const match = line.match(/^(#{1,6})\s+(.+)/);
115
+ if (match) {
116
+ const level = match[1].length;
117
+ const title = match[2].replace(/<!--.*?-->/g, "").trim();
118
+ if (title.toLowerCase() === heading.toLowerCase()) {
119
+ inSection = true;
120
+ headingLevel = level;
121
+ continue;
122
+ }
123
+ if (inSection && level <= headingLevel) {
124
+ break;
125
+ }
126
+ }
127
+ if (inSection) {
128
+ const t = line.trim();
129
+ if (t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--") && !t.startsWith(">") && !t.startsWith("|") && t !== "---") {
130
+ return true;
131
+ }
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+ function replaceSection(markdown, heading, newBody) {
137
+ const lines = markdown.split("\n");
138
+ let headingIdx = -1;
139
+ let nextSectionIdx = lines.length;
140
+ let headingLevel = 0;
141
+ for (let i = 0; i < lines.length; i++) {
142
+ const match = lines[i].match(/^(#{1,6})\s+(.+)/);
143
+ if (match) {
144
+ const level = match[1].length;
145
+ const title = match[2].replace(/<!--.*?-->/g, "").trim();
146
+ if (headingIdx < 0 && title.toLowerCase() === heading.toLowerCase()) {
147
+ headingIdx = i;
148
+ headingLevel = level;
149
+ } else if (headingIdx >= 0 && level <= headingLevel) {
150
+ nextSectionIdx = i;
151
+ break;
152
+ }
153
+ }
154
+ }
155
+ if (headingIdx < 0) return markdown;
156
+ const before = lines.slice(0, headingIdx + 1);
157
+ const after = lines.slice(nextSectionIdx);
158
+ return [...before, "", newBody.trim(), "", ...after].join("\n");
159
+ }
160
+ function countRealLines(markdown) {
161
+ return markdown.split("\n").filter((line) => {
162
+ const t = line.trim();
163
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--") && !t.startsWith(">") && t !== "---";
164
+ }).length;
165
+ }
166
+
103
167
  // src/commands/init.ts
104
168
  function detectProjectInfo(projectPath) {
105
169
  const date = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
@@ -176,6 +240,41 @@ function memoryHasRealContent(memoryPath) {
176
240
  return false;
177
241
  }
178
242
  }
243
+ function injectUserProfile(memoryContent, profileContent) {
244
+ const profileLines = profileContent.split("\n");
245
+ const contentStart = profileLines.findIndex((l) => {
246
+ const t = l.trim();
247
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith(">") && !t.startsWith("<!--");
248
+ });
249
+ const profileBody = contentStart >= 0 ? profileLines.slice(contentStart).join("\n").trim() : "";
250
+ if (!profileBody) return memoryContent;
251
+ const lines = memoryContent.split("\n");
252
+ let insertAt = -1;
253
+ let inProfile = false;
254
+ let profileHeadingLevel = 0;
255
+ for (let i = 0; i < lines.length; i++) {
256
+ const match = lines[i].match(/^(#{1,6})\s+(.+)/);
257
+ if (match) {
258
+ const level = match[1].length;
259
+ const title = match[2].replace(/<!--.*?-->/g, "").trim();
260
+ if (title.toLowerCase() === "user profile") {
261
+ inProfile = true;
262
+ profileHeadingLevel = level;
263
+ continue;
264
+ }
265
+ if (inProfile && level <= profileHeadingLevel) {
266
+ insertAt = i;
267
+ break;
268
+ }
269
+ }
270
+ if (inProfile && insertAt < 0 && i === lines.length - 1) {
271
+ insertAt = lines.length;
272
+ }
273
+ }
274
+ if (insertAt < 0) return memoryContent;
275
+ lines.splice(insertAt, 0, profileBody, "");
276
+ return lines.join("\n");
277
+ }
179
278
  function buildMemoryMd(templateContent, info2) {
180
279
  let content = templateContent;
181
280
  const whatLine = info2.description ? `**${info2.name}** \u2014 ${info2.description}` : `**${info2.name}**`;
@@ -271,7 +370,11 @@ Examples:
271
370
  const hookDest = join3(projectPath, ".claude", "settings.json");
272
371
  if (!existsSync2(hookDest)) {
273
372
  mkdirSync2(dirname2(hookDest), { recursive: true });
274
- writeFileSync2(hookDest, readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
373
+ const settings = JSON.parse(readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
374
+ settings.mcpServers = {
375
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
376
+ };
377
+ writeFileSync2(hookDest, JSON.stringify(settings, null, 2));
275
378
  restored.push(".claude/settings.json");
276
379
  }
277
380
  }
@@ -452,6 +555,18 @@ Examples:
452
555
  writeFileSync2(dest, content);
453
556
  created.push(`${file.label.padEnd(32)} ${chalk2.dim(file.desc)}`);
454
557
  }
558
+ const memoryDest = join3(brainDir, "MEMORY.md");
559
+ if (existsSync2(GLOBAL_USER_PROFILE_PATH)) {
560
+ const profileContent = readFileSync2(GLOBAL_USER_PROFILE_PATH, "utf8");
561
+ if (sectionHasRealContent(profileContent, "MindLink \u2014 Global User Profile") || profileContent.split("\n").some((l) => {
562
+ const t = l.trim();
563
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith(">") && !t.startsWith("<!--");
564
+ })) {
565
+ const injected = injectUserProfile(readFileSync2(memoryDest, "utf8"), profileContent);
566
+ writeFileSync2(memoryDest, injected);
567
+ created.push(`User Profile imported from ~/.mindlink/USER.md`);
568
+ }
569
+ }
455
570
  for (const agentValue of selectedAgents) {
456
571
  const agent = AGENTS.find((a) => a.value === agentValue);
457
572
  if (!agent) continue;
@@ -464,8 +579,70 @@ Examples:
464
579
  const hookDest = join3(projectPath, ".claude", "settings.json");
465
580
  if (!existsSync2(hookDest)) {
466
581
  mkdirSync2(dirname2(hookDest), { recursive: true });
467
- writeFileSync2(hookDest, readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
468
- created.push(`.claude/settings.json${" ".repeat(14)} ${chalk2.dim("Claude Code compact hook")}`);
582
+ const settings = JSON.parse(readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
583
+ settings.mcpServers = {
584
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
585
+ };
586
+ writeFileSync2(hookDest, JSON.stringify(settings, null, 2));
587
+ created.push(`.claude/settings.json${" ".repeat(14)} ${chalk2.dim("Claude Code hooks + MCP server")}`);
588
+ }
589
+ }
590
+ if (selectedAgents.includes("cursor")) {
591
+ const cursorMcpDest = join3(projectPath, ".cursor", "mcp.json");
592
+ if (!existsSync2(cursorMcpDest)) {
593
+ mkdirSync2(join3(projectPath, ".cursor"), { recursive: true });
594
+ const cursorMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
595
+ writeFileSync2(cursorMcpDest, JSON.stringify(cursorMcp, null, 2));
596
+ created.push(`.cursor/mcp.json${" ".repeat(20)} ${chalk2.dim("Cursor MCP server")}`);
597
+ }
598
+ }
599
+ if (selectedAgents.includes("continue")) {
600
+ const continueMcpDest = join3(projectPath, ".continue", "mcpServers", "mindlink.json");
601
+ if (!existsSync2(continueMcpDest)) {
602
+ mkdirSync2(join3(projectPath, ".continue", "mcpServers"), { recursive: true });
603
+ const continueMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
604
+ writeFileSync2(continueMcpDest, JSON.stringify(continueMcp, null, 2));
605
+ created.push(`.continue/mcpServers/mindlink.json ${chalk2.dim("Continue MCP server")}`);
606
+ }
607
+ }
608
+ if (selectedAgents.includes("copilot")) {
609
+ const copilotMcpDest = join3(projectPath, ".vscode", "mcp.json");
610
+ if (!existsSync2(copilotMcpDest)) {
611
+ mkdirSync2(join3(projectPath, ".vscode"), { recursive: true });
612
+ const copilotMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
613
+ writeFileSync2(copilotMcpDest, JSON.stringify(copilotMcp, null, 2));
614
+ created.push(`.vscode/mcp.json${" ".repeat(19)} ${chalk2.dim("GitHub Copilot MCP server")}`);
615
+ }
616
+ }
617
+ if (selectedAgents.includes("kiro")) {
618
+ const kiroMcpDest = join3(projectPath, ".kiro", "settings", "mcp.json");
619
+ if (!existsSync2(kiroMcpDest)) {
620
+ mkdirSync2(join3(projectPath, ".kiro", "settings"), { recursive: true });
621
+ const kiroMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
622
+ writeFileSync2(kiroMcpDest, JSON.stringify(kiroMcp, null, 2));
623
+ created.push(`.kiro/settings/mcp.json${" ".repeat(13)} ${chalk2.dim("Kiro MCP server")}`);
624
+ }
625
+ }
626
+ if (selectedAgents.includes("windsurf")) {
627
+ try {
628
+ mkdirSync2(dirname2(GLOBAL_WINDSURF_MCP_PATH), { recursive: true });
629
+ let existingWindsurf = {};
630
+ if (existsSync2(GLOBAL_WINDSURF_MCP_PATH)) {
631
+ try {
632
+ existingWindsurf = JSON.parse(readFileSync2(GLOBAL_WINDSURF_MCP_PATH, "utf8"));
633
+ } catch {
634
+ }
635
+ }
636
+ const mergedWindsurf = {
637
+ ...existingWindsurf,
638
+ mcpServers: {
639
+ ...typeof existingWindsurf.mcpServers === "object" && existingWindsurf.mcpServers !== null ? existingWindsurf.mcpServers : {},
640
+ mindlink: { command: "mindlink", args: ["mcp"] }
641
+ }
642
+ };
643
+ writeFileSync2(GLOBAL_WINDSURF_MCP_PATH, JSON.stringify(mergedWindsurf, null, 2));
644
+ created.push(`~/.codeium/windsurf/mcp_config.json ${chalk2.dim("Windsurf MCP server (global)")}`);
645
+ } catch {
469
646
  }
470
647
  }
471
648
  if (!gitTracking) {
@@ -498,7 +675,15 @@ Examples:
498
675
  console.log("");
499
676
  for (const err of errors) console.log(` ${chalk2.red("\u2717")} ${err}`);
500
677
  }
501
- console.log("");
678
+ if (selectedAgents.includes("cline")) {
679
+ console.log(` ${chalk2.yellow("\u2192")} Cline: add the MCP server manually in Cline's settings UI (MCP Servers tab)`);
680
+ console.log(` ${chalk2.dim("Command: mindlink Args: mcp Env: MINDLINK_PROJECT_PATH=" + projectPath)}`);
681
+ console.log("");
682
+ }
683
+ if (!existsSync2(GLOBAL_USER_PROFILE_PATH)) {
684
+ console.log(` ${chalk2.dim("\u2192")} Run ${chalk2.cyan("mindlink profile")} to set up a global user profile \u2014 imported into every new project automatically.`);
685
+ console.log("");
686
+ }
502
687
  note(
503
688
  `Your AI finally has a brain.
504
689
 
@@ -1200,7 +1385,7 @@ var REQUIRED_BRAIN_FILES = ["MEMORY.md", "SESSION.md", "SHARED.md", "LOG.md"];
1200
1385
  async function latestVersion() {
1201
1386
  try {
1202
1387
  const { default: https } = await import("https");
1203
- return new Promise((resolve14) => {
1388
+ return new Promise((resolve17) => {
1204
1389
  const req = https.get(
1205
1390
  "https://registry.npmjs.org/mindlink/latest",
1206
1391
  { headers: { "User-Agent": "mindlink-cli" } },
@@ -1212,17 +1397,17 @@ async function latestVersion() {
1212
1397
  res.on("end", () => {
1213
1398
  try {
1214
1399
  const parsed = JSON.parse(data);
1215
- resolve14(parsed.version ?? null);
1400
+ resolve17(parsed.version ?? null);
1216
1401
  } catch {
1217
- resolve14(null);
1402
+ resolve17(null);
1218
1403
  }
1219
1404
  });
1220
1405
  }
1221
1406
  );
1222
- req.on("error", () => resolve14(null));
1407
+ req.on("error", () => resolve17(null));
1223
1408
  req.setTimeout(8e3, () => {
1224
1409
  req.destroy();
1225
- resolve14(null);
1410
+ resolve17(null);
1226
1411
  });
1227
1412
  });
1228
1413
  } catch {
@@ -1243,71 +1428,73 @@ Examples:
1243
1428
  mindlink update
1244
1429
  `).action(async () => {
1245
1430
  const current = VERSION;
1431
+ let nonTtyExitCode = null;
1246
1432
  if (!process.stdin.isTTY) {
1247
- const latest2 = await latestVersion();
1248
- if (!latest2) {
1433
+ const latest = await latestVersion();
1434
+ if (!latest) {
1249
1435
  console.log(JSON.stringify({ current, latest: null, upToDate: null }));
1250
- process.exit(1);
1436
+ nonTtyExitCode = 1;
1437
+ } else {
1438
+ const upToDate = !semverGt(latest, current);
1439
+ console.log(JSON.stringify({ current, latest, upToDate }));
1440
+ if (!upToDate) nonTtyExitCode = 2;
1251
1441
  }
1252
- const upToDate = !semverGt(latest2, current);
1253
- console.log(JSON.stringify({ current, latest: latest2, upToDate }));
1254
- if (!upToDate) process.exit(2);
1255
- return;
1256
- }
1257
- const s = spinner2();
1258
- s.start("Checking for updates...");
1259
- const latest = await latestVersion();
1260
- if (!latest) {
1261
- s.stop("Could not reach npm registry.");
1262
- console.log("");
1263
- console.log(` ${chalk9.red("\u2717")} Could not check for updates. Check your internet connection.`);
1264
- console.log(` ${chalk9.dim("Latest releases: github.com/404-not-found/mindlink/releases")}`);
1265
- console.log("");
1266
- process.exit(1);
1267
- }
1268
- s.stop("Done.");
1269
- console.log("");
1270
- console.log(` Current version : ${chalk9.dim(current)}`);
1271
- console.log(` Latest version : ${semverGt(latest, current) ? chalk9.green(latest) : chalk9.dim(latest)}`);
1272
- console.log("");
1273
- if (!semverGt(latest, current)) {
1274
- console.log(` ${chalk9.green("\u2713")} You're on the latest version (${current}).`);
1275
- console.log("");
1276
1442
  } else {
1277
- const action = await select3({
1278
- message: `Update to ${latest}?`,
1279
- options: [
1280
- { value: "update", label: `Update to ${latest}` },
1281
- { value: "skip", label: "Skip this version" },
1282
- { value: "cancel", label: "Cancel" }
1283
- ]
1284
- });
1285
- if (isCancel4(action) || action === "cancel" || action === "skip") {
1286
- if (action === "skip") {
1287
- console.log(` ${chalk9.dim("Skipped. Run mindlink update again to install later.")}`);
1288
- } else {
1289
- cancel4("Cancelled.");
1290
- }
1443
+ const s = spinner2();
1444
+ s.start("Checking for updates...");
1445
+ const latest = await latestVersion();
1446
+ if (!latest) {
1447
+ s.stop("Could not reach npm registry.");
1291
1448
  console.log("");
1292
- return;
1293
- }
1294
- const s2 = spinner2();
1295
- s2.start(`Installing mindlink@${latest}...`);
1296
- try {
1297
- execSync2(`npm install -g mindlink@${latest}`, { stdio: "pipe" });
1298
- s2.stop("Done.");
1449
+ console.log(` ${chalk9.red("\u2717")} Could not check for updates. Check your internet connection.`);
1450
+ console.log(` ${chalk9.dim("Latest releases: github.com/404-not-found/mindlink/releases")}`);
1299
1451
  console.log("");
1300
- console.log(` ${chalk9.green("\u2713")} Updated to ${latest}.`);
1301
- console.log(` ${chalk9.dim("See what's new: github.com/404-not-found/mindlink/releases")}`);
1302
- } catch (err) {
1303
- s2.stop("Failed.");
1452
+ process.exit(1);
1453
+ }
1454
+ s.stop("Done.");
1455
+ console.log("");
1456
+ console.log(` Current version : ${chalk9.dim(current)}`);
1457
+ console.log(` Latest version : ${semverGt(latest, current) ? chalk9.green(latest) : chalk9.dim(latest)}`);
1458
+ console.log("");
1459
+ if (!semverGt(latest, current)) {
1460
+ console.log(` ${chalk9.green("\u2713")} You're on the latest version (${current}).`);
1304
1461
  console.log("");
1305
- console.log(` ${chalk9.red("\u2717")} Update failed.`);
1306
- console.log(` ${chalk9.dim("Try: npm install -g mindlink@" + latest)}`);
1307
- if (err instanceof Error && err.message.includes("EACCES")) {
1308
- console.log(` ${chalk9.dim("Permission error \u2014 try: sudo npm install -g mindlink@" + latest)}`);
1462
+ } else {
1463
+ const action = await select3({
1464
+ message: `Update to ${latest}?`,
1465
+ options: [
1466
+ { value: "update", label: `Update to ${latest}` },
1467
+ { value: "skip", label: "Skip this version" },
1468
+ { value: "cancel", label: "Cancel" }
1469
+ ]
1470
+ });
1471
+ if (isCancel4(action) || action === "cancel" || action === "skip") {
1472
+ if (action === "skip") {
1473
+ console.log(` ${chalk9.dim("Skipped. Run mindlink update again to install later.")}`);
1474
+ } else {
1475
+ cancel4("Cancelled.");
1476
+ }
1477
+ console.log("");
1478
+ return;
1479
+ }
1480
+ const s2 = spinner2();
1481
+ s2.start(`Installing mindlink@${latest}...`);
1482
+ try {
1483
+ execSync2(`npm install -g mindlink@${latest}`, { stdio: "pipe" });
1484
+ s2.stop("Done.");
1485
+ console.log("");
1486
+ console.log(` ${chalk9.green("\u2713")} Updated to ${latest}.`);
1487
+ console.log(` ${chalk9.dim("See what's new: github.com/404-not-found/mindlink/releases")}`);
1488
+ } catch (err) {
1489
+ s2.stop("Failed.");
1490
+ console.log("");
1491
+ console.log(` ${chalk9.red("\u2717")} Update failed.`);
1492
+ console.log(` ${chalk9.dim("Try: npm install -g mindlink@" + latest)}`);
1493
+ if (err instanceof Error && err.message.includes("EACCES")) {
1494
+ console.log(` ${chalk9.dim("Permission error \u2014 try: sudo npm install -g mindlink@" + latest)}`);
1495
+ }
1496
+ process.exit(1);
1309
1497
  }
1310
- process.exit(1);
1311
1498
  }
1312
1499
  }
1313
1500
  const cwd = process.cwd();
@@ -1356,11 +1543,132 @@ Examples:
1356
1543
  const hookDest = join10(projectPath, ".claude", "settings.json");
1357
1544
  try {
1358
1545
  mkdirSync4(join10(projectPath, ".claude"), { recursive: true });
1359
- writeFileSync6(hookDest, readFileSync9(join10(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
1546
+ const template = JSON.parse(readFileSync9(join10(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
1547
+ let existing = {};
1548
+ if (existsSync9(hookDest)) {
1549
+ try {
1550
+ existing = JSON.parse(readFileSync9(hookDest, "utf8"));
1551
+ } catch {
1552
+ }
1553
+ }
1554
+ const merged = {
1555
+ ...template,
1556
+ ...existing,
1557
+ // Always refresh hooks and permissions from template
1558
+ hooks: template.hooks,
1559
+ permissions: template.permissions,
1560
+ // Merge mcpServers: keep user's other servers, update mindlink entry
1561
+ mcpServers: {
1562
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1563
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1564
+ }
1565
+ };
1566
+ writeFileSync6(hookDest, JSON.stringify(merged, null, 2));
1360
1567
  refreshed.push(".claude/settings.json");
1361
1568
  } catch {
1362
1569
  }
1363
1570
  }
1571
+ if (agentValues.includes("cursor")) {
1572
+ const cursorMcpDest = join10(projectPath, ".cursor", "mcp.json");
1573
+ try {
1574
+ mkdirSync4(join10(projectPath, ".cursor"), { recursive: true });
1575
+ let existing = {};
1576
+ if (existsSync9(cursorMcpDest)) {
1577
+ try {
1578
+ existing = JSON.parse(readFileSync9(cursorMcpDest, "utf8"));
1579
+ } catch {
1580
+ }
1581
+ }
1582
+ const merged = {
1583
+ ...existing,
1584
+ mcpServers: {
1585
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1586
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1587
+ }
1588
+ };
1589
+ writeFileSync6(cursorMcpDest, JSON.stringify(merged, null, 2));
1590
+ refreshed.push(".cursor/mcp.json");
1591
+ } catch {
1592
+ }
1593
+ }
1594
+ if (agentValues.includes("continue")) {
1595
+ const continueMcpDest = join10(projectPath, ".continue", "mcpServers", "mindlink.json");
1596
+ try {
1597
+ mkdirSync4(join10(projectPath, ".continue", "mcpServers"), { recursive: true });
1598
+ const continueMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
1599
+ writeFileSync6(continueMcpDest, JSON.stringify(continueMcp, null, 2));
1600
+ refreshed.push(".continue/mcpServers/mindlink.json");
1601
+ } catch {
1602
+ }
1603
+ }
1604
+ if (agentValues.includes("copilot")) {
1605
+ const copilotMcpDest = join10(projectPath, ".vscode", "mcp.json");
1606
+ try {
1607
+ mkdirSync4(join10(projectPath, ".vscode"), { recursive: true });
1608
+ let existing = {};
1609
+ if (existsSync9(copilotMcpDest)) {
1610
+ try {
1611
+ existing = JSON.parse(readFileSync9(copilotMcpDest, "utf8"));
1612
+ } catch {
1613
+ }
1614
+ }
1615
+ const merged = {
1616
+ ...existing,
1617
+ mcpServers: {
1618
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1619
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1620
+ }
1621
+ };
1622
+ writeFileSync6(copilotMcpDest, JSON.stringify(merged, null, 2));
1623
+ refreshed.push(".vscode/mcp.json");
1624
+ } catch {
1625
+ }
1626
+ }
1627
+ if (agentValues.includes("kiro")) {
1628
+ const kiroMcpDest = join10(projectPath, ".kiro", "settings", "mcp.json");
1629
+ try {
1630
+ mkdirSync4(join10(projectPath, ".kiro", "settings"), { recursive: true });
1631
+ let existing = {};
1632
+ if (existsSync9(kiroMcpDest)) {
1633
+ try {
1634
+ existing = JSON.parse(readFileSync9(kiroMcpDest, "utf8"));
1635
+ } catch {
1636
+ }
1637
+ }
1638
+ const merged = {
1639
+ ...existing,
1640
+ mcpServers: {
1641
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1642
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1643
+ }
1644
+ };
1645
+ writeFileSync6(kiroMcpDest, JSON.stringify(merged, null, 2));
1646
+ refreshed.push(".kiro/settings/mcp.json");
1647
+ } catch {
1648
+ }
1649
+ }
1650
+ if (agentValues.includes("windsurf")) {
1651
+ try {
1652
+ mkdirSync4(dirname4(GLOBAL_WINDSURF_MCP_PATH), { recursive: true });
1653
+ let existing = {};
1654
+ if (existsSync9(GLOBAL_WINDSURF_MCP_PATH)) {
1655
+ try {
1656
+ existing = JSON.parse(readFileSync9(GLOBAL_WINDSURF_MCP_PATH, "utf8"));
1657
+ } catch {
1658
+ }
1659
+ }
1660
+ const merged = {
1661
+ ...existing,
1662
+ mcpServers: {
1663
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1664
+ mindlink: { command: "mindlink", args: ["mcp"] }
1665
+ }
1666
+ };
1667
+ writeFileSync6(GLOBAL_WINDSURF_MCP_PATH, JSON.stringify(merged, null, 2));
1668
+ refreshed.push("~/.codeium/windsurf/mcp_config.json");
1669
+ } catch {
1670
+ }
1671
+ }
1364
1672
  const memoryPath = join10(projectPath, BRAIN_DIR, "MEMORY.md");
1365
1673
  if (existsSync9(memoryPath)) {
1366
1674
  try {
@@ -1401,6 +1709,26 @@ Examples:
1401
1709
  } catch {
1402
1710
  }
1403
1711
  }
1712
+ if (existsSync9(GLOBAL_USER_PROFILE_PATH) && existsSync9(memoryPath)) {
1713
+ try {
1714
+ const profileRaw = readFileSync9(GLOBAL_USER_PROFILE_PATH, "utf8");
1715
+ const profileLines = profileRaw.split("\n");
1716
+ const bodyStart = profileLines.findIndex((l) => {
1717
+ const t = l.trim();
1718
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith(">") && !t.startsWith("<!--");
1719
+ });
1720
+ const profileBody = bodyStart >= 0 ? profileLines.slice(bodyStart).join("\n").trim() : "";
1721
+ if (profileBody) {
1722
+ const memContent = readFileSync9(memoryPath, "utf8");
1723
+ const updated = replaceSection(memContent, "User Profile", profileBody);
1724
+ if (updated !== memContent) {
1725
+ writeFileSync6(memoryPath, updated);
1726
+ if (!refreshed.includes(".brain/MEMORY.md")) refreshed.push(".brain/MEMORY.md (profile synced)");
1727
+ }
1728
+ }
1729
+ } catch {
1730
+ }
1731
+ }
1404
1732
  console.log(` ${chalk9.bold(projectPath)}`);
1405
1733
  for (const f of refreshed) {
1406
1734
  console.log(` ${chalk9.green("\u2713")} ${f}`);
@@ -1410,6 +1738,7 @@ Examples:
1410
1738
  console.log(` ${chalk9.dim("All agent files are up to date.")}`);
1411
1739
  }
1412
1740
  console.log("");
1741
+ if (nonTtyExitCode !== null) process.exit(nonTtyExitCode);
1413
1742
  });
1414
1743
 
1415
1744
  // src/commands/summary.ts
@@ -2238,8 +2567,760 @@ Examples:
2238
2567
  console.log("");
2239
2568
  });
2240
2569
 
2570
+ // src/commands/verify.ts
2571
+ import { Command as Command16 } from "commander";
2572
+ import chalk17 from "chalk";
2573
+ import { existsSync as existsSync16, readFileSync as readFileSync14, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
2574
+ import { dirname as dirname5 } from "path";
2575
+ import { join as join17, resolve as resolve14 } from "path";
2576
+ var SESSION_WARN_DAYS = 3;
2577
+ var SESSION_FAIL_DAYS = 7;
2578
+ var MEMORY_WARN_LINES = 100;
2579
+ var MEMORY_FAIL_LINES = 200;
2580
+ function pass(id, label, message) {
2581
+ return { id, label, status: "pass", message, fixable: false };
2582
+ }
2583
+ function warn2(id, label, message, fixable = false) {
2584
+ return { id, label, status: "warn", message, fixable };
2585
+ }
2586
+ function fail2(id, label, message, fixable = false) {
2587
+ return { id, label, status: "fail", message, fixable };
2588
+ }
2589
+ function icon2(status) {
2590
+ switch (status) {
2591
+ case "pass":
2592
+ return chalk17.green("\u2713");
2593
+ case "warn":
2594
+ return chalk17.yellow("\u26A0");
2595
+ case "fail":
2596
+ return chalk17.red("\u2717");
2597
+ }
2598
+ }
2599
+ function runChecks(projectPath) {
2600
+ const brainDir = join17(projectPath, BRAIN_DIR);
2601
+ const results = [];
2602
+ if (!existsSync16(brainDir)) {
2603
+ results.push(fail2("brain_missing", ".brain/ missing", `Run ${chalk17.cyan("mindlink init")} to set up memory.`));
2604
+ return results;
2605
+ }
2606
+ const configPath = join17(brainDir, "config.json");
2607
+ let config = {};
2608
+ if (existsSync16(configPath)) {
2609
+ try {
2610
+ config = JSON.parse(readFileSync14(configPath, "utf8"));
2611
+ } catch {
2612
+ }
2613
+ }
2614
+ const memoryPath = join17(brainDir, "MEMORY.md");
2615
+ if (!existsSync16(memoryPath)) {
2616
+ results.push(fail2("core", "Core section", `MEMORY.md is missing \u2014 run ${chalk17.cyan("mindlink init")}.`));
2617
+ } else {
2618
+ const memMd = readFileSync14(memoryPath, "utf8");
2619
+ if (!sectionHasRealContent(memMd, "Core")) {
2620
+ results.push(fail2("core", "Core section \u2014 empty", "Start a session and tell your AI to fill in the Core section."));
2621
+ } else {
2622
+ results.push(pass("core", "Core section \u2014 filled", ""));
2623
+ }
2624
+ }
2625
+ if (existsSync16(memoryPath)) {
2626
+ const memMd = readFileSync14(memoryPath, "utf8");
2627
+ if (!sectionHasRealContent(memMd, "User Profile")) {
2628
+ results.push(fail2("user_profile", "User Profile \u2014 empty", `Run ${chalk17.cyan("mindlink profile")} to set up your global profile and import it here.`));
2629
+ } else {
2630
+ results.push(pass("user_profile", "User Profile \u2014 filled", ""));
2631
+ }
2632
+ }
2633
+ const sessionPath = join17(brainDir, "SESSION.md");
2634
+ if (!existsSync16(sessionPath)) {
2635
+ results.push(fail2("session_fresh", "SESSION.md \u2014 missing", `Run ${chalk17.cyan("mindlink init")} to recreate it.`));
2636
+ } else {
2637
+ const ageDays = (Date.now() - statSync5(sessionPath).mtime.getTime()) / 864e5;
2638
+ const hasContent = readFileSync14(sessionPath, "utf8").split("\n").some((l) => l.trim().length > 0 && !l.startsWith("#") && !l.startsWith("<!--"));
2639
+ if (!hasContent) {
2640
+ results.push(warn2("session_fresh", "SESSION.md \u2014 no content yet", "Start a session \u2014 your AI will fill this in automatically."));
2641
+ } else if (ageDays > SESSION_FAIL_DAYS) {
2642
+ const days = Math.floor(ageDays);
2643
+ results.push(fail2("session_fresh", `SESSION.md \u2014 last updated ${days} days ago`, "SESSION.md has not been updated in a week. Your AI may not be writing session state. Check that it is completing the REQUIRED session-end steps."));
2644
+ } else if (ageDays > SESSION_WARN_DAYS) {
2645
+ const days = Math.floor(ageDays);
2646
+ results.push(warn2("session_fresh", `SESSION.md \u2014 last updated ${days} days ago`, "SESSION.md is getting stale. If you have active sessions, your AI should be updating this after each response."));
2647
+ } else {
2648
+ const mins = Math.floor((Date.now() - statSync5(sessionPath).mtime.getTime()) / 6e4);
2649
+ const age = mins < 60 ? `${mins}m ago` : mins < 1440 ? `${Math.floor(mins / 60)}h ago` : `${Math.floor(mins / 1440)}d ago`;
2650
+ results.push(pass("session_fresh", `SESSION.md \u2014 updated ${age}`, ""));
2651
+ }
2652
+ }
2653
+ const logPath = join17(brainDir, "LOG.md");
2654
+ if (!existsSync16(logPath)) {
2655
+ results.push(fail2("log_present", "LOG.md \u2014 missing", `Run ${chalk17.cyan("mindlink init")} to recreate it.`));
2656
+ } else {
2657
+ const entries = (readFileSync14(logPath, "utf8").match(/^##\s+/gm) ?? []).length;
2658
+ if (entries === 0) {
2659
+ results.push(pass("log_present", "LOG.md \u2014 no sessions yet", ""));
2660
+ } else {
2661
+ results.push(pass("log_present", `LOG.md \u2014 ${entries} session${entries !== 1 ? "s" : ""} logged`, ""));
2662
+ }
2663
+ }
2664
+ if (existsSync16(memoryPath)) {
2665
+ const memMd = readFileSync14(memoryPath, "utf8");
2666
+ const lines = countRealLines(memMd);
2667
+ if (lines > MEMORY_FAIL_LINES) {
2668
+ results.push(fail2("memory_size", `MEMORY.md \u2014 ${lines} lines (target: under ${MEMORY_FAIL_LINES})`, `Run ${chalk17.cyan("mindlink prune")} to consolidate stale entries.`));
2669
+ } else if (lines > MEMORY_WARN_LINES) {
2670
+ results.push(warn2("memory_size", `MEMORY.md \u2014 ${lines} lines (getting long)`, `Consider running ${chalk17.cyan("mindlink prune")} to retire old entries.`));
2671
+ } else {
2672
+ results.push(pass("memory_size", `MEMORY.md \u2014 ${lines} line${lines !== 1 ? "s" : ""} (healthy)`, ""));
2673
+ }
2674
+ }
2675
+ const configuredAgents = config.agents ?? [];
2676
+ if (configuredAgents.length === 0) {
2677
+ results.push(warn2("agent_files", "No agents configured", `Run ${chalk17.cyan("mindlink config")} \u2192 Agent instruction files.`, false));
2678
+ } else {
2679
+ const missing = [];
2680
+ for (const agentValue of configuredAgents) {
2681
+ const agent = AGENTS.find((a) => a.value === agentValue);
2682
+ if (!agent) continue;
2683
+ if (!existsSync16(join17(projectPath, agent.destFile))) missing.push(agent.destFile);
2684
+ }
2685
+ if (missing.length === configuredAgents.length) {
2686
+ results.push(fail2("agent_files", "Agent files \u2014 none present", `All configured agent files are missing. Run ${chalk17.cyan("mindlink verify --fix")} to regenerate.`, true));
2687
+ } else if (missing.length > 0) {
2688
+ results.push(warn2("agent_files", `Agent files \u2014 ${missing.length} missing: ${missing.join(", ")}`, `Run ${chalk17.cyan("mindlink verify --fix")} to regenerate.`, true));
2689
+ } else {
2690
+ results.push(pass("agent_files", `Agent files \u2014 all ${configuredAgents.length} present`, ""));
2691
+ }
2692
+ }
2693
+ return results;
2694
+ }
2695
+ function applyFix(projectPath, results) {
2696
+ const brainDir = join17(projectPath, BRAIN_DIR);
2697
+ const configPath = join17(brainDir, "config.json");
2698
+ let config = {};
2699
+ if (existsSync16(configPath)) {
2700
+ try {
2701
+ config = JSON.parse(readFileSync14(configPath, "utf8"));
2702
+ } catch {
2703
+ }
2704
+ }
2705
+ const fixable = results.filter((r) => r.fixable && r.status !== "pass");
2706
+ if (fixable.length === 0) {
2707
+ console.log(` ${chalk17.dim("Nothing to auto-fix. Address the issues above manually.")}`);
2708
+ return;
2709
+ }
2710
+ let fixed = 0;
2711
+ for (const r of fixable) {
2712
+ if (r.id === "agent_files") {
2713
+ const configuredAgents = config.agents ?? [];
2714
+ for (const agentValue of configuredAgents) {
2715
+ const agent = AGENTS.find((a) => a.value === agentValue);
2716
+ if (!agent) continue;
2717
+ const destPath = join17(projectPath, agent.destFile);
2718
+ if (!existsSync16(destPath)) {
2719
+ try {
2720
+ mkdirSync6(dirname5(destPath), { recursive: true });
2721
+ writeFileSync7(destPath, readFileSync14(join17(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
2722
+ console.log(` ${chalk17.green("\u2713")} Regenerated ${agent.destFile}`);
2723
+ fixed++;
2724
+ } catch (err) {
2725
+ console.log(` ${chalk17.red("\u2717")} Failed to regenerate ${agent.destFile}: ${err instanceof Error ? err.message : err}`);
2726
+ }
2727
+ }
2728
+ }
2729
+ if (configuredAgents.includes("claude")) {
2730
+ const hookPath = join17(projectPath, ".claude", "settings.json");
2731
+ if (!existsSync16(hookPath)) {
2732
+ try {
2733
+ mkdirSync6(dirname5(hookPath), { recursive: true });
2734
+ writeFileSync7(hookPath, readFileSync14(join17(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
2735
+ console.log(` ${chalk17.green("\u2713")} Regenerated .claude/settings.json`);
2736
+ fixed++;
2737
+ } catch {
2738
+ }
2739
+ }
2740
+ }
2741
+ }
2742
+ }
2743
+ for (const r of results) {
2744
+ if (r.status === "fail" || r.status === "warn") {
2745
+ if (r.id === "core" || r.id === "user_profile") {
2746
+ console.log(` ${chalk17.dim("\u2192")} ${r.id === "core" ? "Core" : "User Profile"}: start a session and tell your AI to fill this in.`);
2747
+ } else if (r.id === "memory_size") {
2748
+ console.log(` ${chalk17.dim("\u2192")} MEMORY.md too large: run ${chalk17.cyan("mindlink prune")} to consolidate.`);
2749
+ }
2750
+ }
2751
+ }
2752
+ if (fixed > 0) console.log("");
2753
+ }
2754
+ var verifyCommand = new Command16("verify").description("Check that .brain/ memory is healthy and up-to-date").option("--json", "Output results as JSON").option("--fix", "Auto-fix recoverable issues (regenerate missing agent files)").addHelpText("after", `
2755
+ What is checked:
2756
+ Core \u2014 MEMORY.md Core section has real content
2757
+ User Profile \u2014 MEMORY.md User Profile has real content
2758
+ SESSION.md \u2014 was updated recently (warn >3 days, fail >7 days)
2759
+ LOG.md \u2014 present and readable
2760
+ MEMORY.md size \u2014 line count (warn >100, fail >200)
2761
+ Agent files \u2014 all files from config.json are present on disk
2762
+
2763
+ Examples:
2764
+ mindlink verify
2765
+ mindlink verify --json
2766
+ mindlink verify --fix
2767
+ `).action((opts) => {
2768
+ const projectPath = resolve14(process.cwd());
2769
+ const results = runChecks(projectPath);
2770
+ if (opts.json) {
2771
+ console.log(JSON.stringify({ ok: results.every((r) => r.status === "pass"), checks: results }, null, 2));
2772
+ const hasFailure = results.some((r) => r.status === "fail");
2773
+ process.exit(hasFailure ? 1 : 0);
2774
+ }
2775
+ console.log("");
2776
+ console.log(` ${chalk17.bold("\u25C9 MindLink Verify")}`);
2777
+ console.log(` ${chalk17.dim(projectPath)}`);
2778
+ console.log("");
2779
+ for (const r of results) {
2780
+ console.log(` ${icon2(r.status)} ${r.label}`);
2781
+ if (r.message) console.log(` ${chalk17.dim(r.message)}`);
2782
+ }
2783
+ console.log("");
2784
+ const failCount = results.filter((r) => r.status === "fail").length;
2785
+ const warnCount = results.filter((r) => r.status === "warn").length;
2786
+ const fixableCount = results.filter((r) => r.fixable && r.status !== "pass").length;
2787
+ if (opts.fix) {
2788
+ applyFix(projectPath, results);
2789
+ }
2790
+ if (failCount > 0) {
2791
+ console.log(` ${chalk17.red.bold(`${failCount} error${failCount !== 1 ? "s" : ""}`)}, ${warnCount} warning${warnCount !== 1 ? "s" : ""}.`);
2792
+ if (!opts.fix && fixableCount > 0) {
2793
+ console.log(` ${chalk17.dim(`Run ${chalk17.cyan("mindlink verify --fix")} to auto-repair ${fixableCount} issue${fixableCount !== 1 ? "s" : ""}.`)}`);
2794
+ }
2795
+ } else if (warnCount > 0) {
2796
+ console.log(` ${chalk17.yellow.bold(`${warnCount} warning${warnCount !== 1 ? "s" : ""}`)}, no critical errors.`);
2797
+ } else {
2798
+ console.log(` ${chalk17.green.bold("All good.")} Your AI's memory is healthy.`);
2799
+ }
2800
+ console.log("");
2801
+ if (failCount > 0) process.exit(1);
2802
+ });
2803
+
2804
+ // src/commands/profile.ts
2805
+ import { Command as Command17 } from "commander";
2806
+ import chalk18 from "chalk";
2807
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
2808
+ import { execSync as execSync4 } from "child_process";
2809
+ var PROFILE_TEMPLATE = `# MindLink \u2014 Global User Profile
2810
+
2811
+ > This file is imported into every new project's MEMORY.md on \`mindlink init\`.
2812
+ > Edit it with \`mindlink profile\`. Run \`mindlink update\` to sync changes to all projects.
2813
+
2814
+ <!-- Role, company, title, level, years of experience -->
2815
+ <!-- Primary languages and tools -->
2816
+ <!-- Communication style and preferences -->
2817
+ <!-- Editor, OS, shell setup -->
2818
+ <!-- Added: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)} -->
2819
+ `;
2820
+ function ensureProfileExists() {
2821
+ if (!existsSync17(GLOBAL_MINDLINK_DIR)) {
2822
+ mkdirSync7(GLOBAL_MINDLINK_DIR, { recursive: true });
2823
+ }
2824
+ if (!existsSync17(GLOBAL_USER_PROFILE_PATH)) {
2825
+ writeFileSync8(GLOBAL_USER_PROFILE_PATH, PROFILE_TEMPLATE);
2826
+ }
2827
+ }
2828
+ var profileCommand = new Command17("profile").description("Manage your global user profile (imported into every new project)").option("--show", "Print current profile to stdout").option("--path", "Print profile file path only").addHelpText("after", `
2829
+ Your profile is stored at ~/.mindlink/USER.md and auto-imported into
2830
+ MEMORY.md when you run \`mindlink init\` on a new project.
2831
+
2832
+ Run \`mindlink update\` to sync profile changes to all registered projects.
2833
+
2834
+ Examples:
2835
+ mindlink profile # open profile in $EDITOR
2836
+ mindlink profile --show # print profile to stdout
2837
+ mindlink profile --path # print file path
2838
+ `).action((opts) => {
2839
+ if (opts.path) {
2840
+ console.log(GLOBAL_USER_PROFILE_PATH);
2841
+ return;
2842
+ }
2843
+ if (opts.show) {
2844
+ if (!existsSync17(GLOBAL_USER_PROFILE_PATH)) {
2845
+ console.log("");
2846
+ console.log(` ${chalk18.yellow("\u26A0")} No global profile yet.`);
2847
+ console.log(` Run ${chalk18.cyan("mindlink profile")} to create one.`);
2848
+ console.log("");
2849
+ return;
2850
+ }
2851
+ console.log("");
2852
+ console.log(readFileSync15(GLOBAL_USER_PROFILE_PATH, "utf8"));
2853
+ return;
2854
+ }
2855
+ ensureProfileExists();
2856
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
2857
+ console.log("");
2858
+ console.log(` ${chalk18.dim(`Opening ${GLOBAL_USER_PROFILE_PATH} in ${editor}...`)}`);
2859
+ console.log(` ${chalk18.dim("Run mindlink update after saving to sync to all registered projects.")}`);
2860
+ console.log("");
2861
+ try {
2862
+ execSync4(`${editor} "${GLOBAL_USER_PROFILE_PATH}"`, { stdio: "inherit" });
2863
+ console.log("");
2864
+ console.log(` ${chalk18.green("\u2713")} Profile saved.`);
2865
+ console.log(` ${chalk18.dim("\u2192 Run mindlink update to sync to all registered projects.")}`);
2866
+ console.log("");
2867
+ } catch {
2868
+ console.log("");
2869
+ }
2870
+ });
2871
+
2872
+ // src/commands/prune.ts
2873
+ import { Command as Command18 } from "commander";
2874
+ import { select as select6, isCancel as isCancel8 } from "@clack/prompts";
2875
+ import chalk19 from "chalk";
2876
+ import { existsSync as existsSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
2877
+ import { join as join18, resolve as resolve15 } from "path";
2878
+ var STALENESS_THRESHOLDS = {
2879
+ "current focus": 14,
2880
+ "decisions": 180,
2881
+ "conventions": 180,
2882
+ "architecture": 365
2883
+ // 'user profile' and 'important context' have no expiry
2884
+ };
2885
+ var TIMESTAMP_RE = /<!--\s*added:\s*(\d{4}-\d{2}-\d{2})\s*-->/;
2886
+ var SECTION_RE = /^(#{1,6})\s+(.+)/;
2887
+ function parseTimestampedEntries(content) {
2888
+ const lines = content.split("\n");
2889
+ const entries = [];
2890
+ let currentSection = "";
2891
+ let currentHeadingLevel = 0;
2892
+ let entryLines = [];
2893
+ let entryStart = -1;
2894
+ function flushEntry(lineEnd) {
2895
+ if (entryStart < 0 || entryLines.length === 0) return;
2896
+ const fullText = entryLines.join("\n");
2897
+ const timestampMatch = fullText.match(TIMESTAMP_RE);
2898
+ if (!timestampMatch) {
2899
+ entryLines = [];
2900
+ entryStart = -1;
2901
+ return;
2902
+ }
2903
+ const addedDate = /* @__PURE__ */ new Date(timestampMatch[1] + "T00:00:00Z");
2904
+ const ageInDays = (Date.now() - addedDate.getTime()) / 864e5;
2905
+ const sectionKey = currentSection.toLowerCase().replace(/\s*<!--.*?-->\s*/g, "").trim();
2906
+ const threshold = STALENESS_THRESHOLDS[sectionKey] ?? Infinity;
2907
+ const isStale = isFinite(threshold) && ageInDays > threshold;
2908
+ const trimmedLines = entryLines.filter((l) => l.trim().length > 0);
2909
+ const displayText = trimmedLines.join("\n");
2910
+ entries.push({
2911
+ section: currentSection.replace(/<!--.*?-->/g, "").trim(),
2912
+ text: displayText,
2913
+ addedDate,
2914
+ lineStart: entryStart,
2915
+ lineEnd,
2916
+ ageInDays,
2917
+ threshold,
2918
+ isStale
2919
+ });
2920
+ entryLines = [];
2921
+ entryStart = -1;
2922
+ }
2923
+ for (let i = 0; i < lines.length; i++) {
2924
+ const line = lines[i];
2925
+ const sectionMatch = line.match(SECTION_RE);
2926
+ if (sectionMatch) {
2927
+ const level = sectionMatch[1].length;
2928
+ const title = sectionMatch[2];
2929
+ flushEntry(i);
2930
+ if (level <= 2) {
2931
+ currentSection = title;
2932
+ currentHeadingLevel = level;
2933
+ }
2934
+ continue;
2935
+ }
2936
+ if (line.trim() === "---") {
2937
+ flushEntry(i);
2938
+ continue;
2939
+ }
2940
+ if (line.trim().startsWith("<!--") && line.trim().endsWith("-->")) {
2941
+ if (entryStart < 0) continue;
2942
+ }
2943
+ if (TIMESTAMP_RE.test(line)) {
2944
+ if (entryStart < 0) entryStart = i;
2945
+ entryLines.push(line);
2946
+ flushEntry(i + 1);
2947
+ continue;
2948
+ }
2949
+ if (line.trim().length > 0 && !line.trim().startsWith(">")) {
2950
+ if (entryStart < 0) entryStart = i;
2951
+ entryLines.push(line);
2952
+ } else if (entryStart >= 0) {
2953
+ entryLines.push(line);
2954
+ }
2955
+ }
2956
+ flushEntry(lines.length);
2957
+ return entries;
2958
+ }
2959
+ function removeLines(content, lineStart, lineEnd) {
2960
+ const lines = content.split("\n");
2961
+ lines.splice(lineStart, lineEnd - lineStart);
2962
+ return lines.join("\n");
2963
+ }
2964
+ function appendToArchive(content, entryText, pruneDate) {
2965
+ const archiveHeading = "## Archive";
2966
+ const archiveEntry = `${entryText} <!-- archived: ${pruneDate} -->`;
2967
+ if (content.includes(archiveHeading)) {
2968
+ return content.replace(
2969
+ /(## Archive\n(?:<!--[^>]*-->\n)*)/,
2970
+ `$1
2971
+ ${archiveEntry}
2972
+ `
2973
+ );
2974
+ } else {
2975
+ return content.trimEnd() + `
2976
+
2977
+ ${archiveHeading}
2978
+
2979
+ <!-- Entries moved here by mindlink prune \u2014 kept for reference -->
2980
+
2981
+ ${archiveEntry}
2982
+ `;
2983
+ }
2984
+ }
2985
+ function formatAge(days) {
2986
+ if (days < 30) return `${Math.round(days)} days`;
2987
+ if (days < 365) return `${Math.round(days / 30)} months`;
2988
+ return `${(days / 365).toFixed(1)} years`;
2989
+ }
2990
+ var pruneCommand = new Command18("prune").description("Review and retire stale MEMORY.md entries interactively").option("--dry-run", "Show stale entries without making any changes").option("--all", "Show all timestamped entries regardless of age").addHelpText("after", `
2991
+ Scans MEMORY.md for entries with <!-- added: YYYY-MM-DD --> timestamps.
2992
+ Entries older than their section's staleness threshold are flagged for review.
2993
+
2994
+ Staleness thresholds:
2995
+ Current Focus \u2014 14 days
2996
+ Decisions \u2014 180 days
2997
+ Conventions \u2014 180 days
2998
+ Architecture \u2014 365 days
2999
+ User Profile \u2014 never expires
3000
+
3001
+ Archived entries are moved to ## Archive at the bottom of MEMORY.md.
3002
+ They are never permanently deleted unless you choose "Delete".
3003
+
3004
+ Examples:
3005
+ mindlink prune
3006
+ mindlink prune --dry-run
3007
+ mindlink prune --all
3008
+ `).action(async (opts) => {
3009
+ const projectPath = resolve15(process.cwd());
3010
+ const brainDir = join18(projectPath, BRAIN_DIR);
3011
+ const memoryPath = join18(brainDir, "MEMORY.md");
3012
+ console.log("");
3013
+ console.log(` ${chalk19.bold("\u25C9 MindLink Prune")}`);
3014
+ console.log(` ${chalk19.dim(memoryPath)}`);
3015
+ console.log("");
3016
+ if (!existsSync18(memoryPath)) {
3017
+ console.log(` ${chalk19.red("\u2717")} MEMORY.md not found. Run ${chalk19.cyan("mindlink init")} first.`);
3018
+ console.log("");
3019
+ process.exit(1);
3020
+ }
3021
+ const allEntries = parseTimestampedEntries(readFileSync16(memoryPath, "utf8"));
3022
+ const toReview = opts.all ? allEntries.filter((e) => e.addedDate !== null) : allEntries.filter((e) => e.isStale);
3023
+ if (toReview.length === 0) {
3024
+ if (allEntries.length === 0) {
3025
+ console.log(` ${chalk19.dim("No timestamped entries found in MEMORY.md.")}`);
3026
+ console.log(` ${chalk19.dim("Entries are timestamped when your AI writes them (<!-- added: YYYY-MM-DD -->).")}`);
3027
+ } else {
3028
+ console.log(` ${chalk19.green("\u2713")} No stale entries found. ${allEntries.length} entry${allEntries.length !== 1 ? "ies" : "y"} all within threshold.`);
3029
+ console.log(` ${chalk19.dim("Run mindlink prune --all to review all timestamped entries.")}`);
3030
+ }
3031
+ console.log("");
3032
+ return;
3033
+ }
3034
+ console.log(` Scanning MEMORY.md for ${opts.all ? "timestamped" : "stale"} entries...`);
3035
+ console.log(` Found ${toReview.length} entr${toReview.length !== 1 ? "ies" : "y"} to review.`);
3036
+ console.log("");
3037
+ if (opts.dryRun) {
3038
+ for (const entry of toReview) {
3039
+ const age = entry.ageInDays !== null ? formatAge(entry.ageInDays) : "undated";
3040
+ const thresholdStr = isFinite(entry.threshold) ? ` \u2014 threshold: ${entry.threshold} days` : "";
3041
+ console.log(` ${chalk19.yellow("\u26A0")} [${entry.section}] ${entry.text.split("\n")[0].slice(0, 80)}`);
3042
+ console.log(` ${chalk19.dim(`Added: ${entry.addedDate?.toISOString().slice(0, 10) ?? "unknown"} (${age} ago)${thresholdStr}`)}`);
3043
+ console.log("");
3044
+ }
3045
+ console.log(` ${chalk19.dim("Dry run \u2014 no changes made.")}`);
3046
+ console.log("");
3047
+ return;
3048
+ }
3049
+ const pruneDate = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3050
+ let content = readFileSync16(memoryPath, "utf8");
3051
+ let archived = 0;
3052
+ let deleted = 0;
3053
+ let kept = 0;
3054
+ let skippedAll = false;
3055
+ const sorted = [...toReview].sort((a, b) => b.lineStart - a.lineStart);
3056
+ for (const entry of sorted) {
3057
+ if (skippedAll) {
3058
+ kept++;
3059
+ continue;
3060
+ }
3061
+ const age = entry.ageInDays !== null ? formatAge(entry.ageInDays) : "undated";
3062
+ const thresholdStr = isFinite(entry.threshold) ? ` \u2014 threshold: ${entry.threshold} days` : "";
3063
+ const displayLines = entry.text.split("\n").slice(0, 3).join("\n");
3064
+ console.log(` ${chalk19.bold("\u2500".repeat(55))}`);
3065
+ console.log(` ${chalk19.bold("Section:")} ${entry.section}`);
3066
+ console.log(` ${chalk19.bold("Entry:")}`);
3067
+ for (const l of displayLines.split("\n")) console.log(` ${chalk19.dim(l)}`);
3068
+ console.log(` ${chalk19.bold("Added:")} ${entry.addedDate?.toISOString().slice(0, 10) ?? "unknown"} (${age} ago)${thresholdStr}`);
3069
+ console.log("");
3070
+ const action = await select6({
3071
+ message: "What would you like to do?",
3072
+ options: [
3073
+ { value: "keep", label: "Keep", hint: "leave as-is" },
3074
+ { value: "archive", label: "Archive", hint: "move to ## Archive section" },
3075
+ { value: "delete", label: "Delete", hint: "remove permanently" },
3076
+ { value: "skip_all", label: "Skip remaining", hint: "keep all remaining entries unchanged" }
3077
+ ]
3078
+ });
3079
+ if (isCancel8(action)) {
3080
+ console.log("");
3081
+ break;
3082
+ }
3083
+ if (action === "skip_all") {
3084
+ skippedAll = true;
3085
+ kept++;
3086
+ continue;
3087
+ }
3088
+ if (action === "archive") {
3089
+ const entryText = content.split("\n").slice(entry.lineStart, entry.lineEnd).join("\n").trim();
3090
+ content = removeLines(content, entry.lineStart, entry.lineEnd);
3091
+ content = appendToArchive(content, entryText, pruneDate);
3092
+ archived++;
3093
+ } else if (action === "delete") {
3094
+ content = removeLines(content, entry.lineStart, entry.lineEnd);
3095
+ deleted++;
3096
+ } else {
3097
+ kept++;
3098
+ }
3099
+ console.log("");
3100
+ }
3101
+ if (archived > 0 || deleted > 0) {
3102
+ writeFileSync9(memoryPath, content);
3103
+ }
3104
+ console.log(` ${chalk19.bold("\u2500".repeat(55))}`);
3105
+ if (archived > 0) console.log(` ${chalk19.green("\u2713")} ${archived} entry${archived !== 1 ? "ies" : ""} archived`);
3106
+ if (deleted > 0) console.log(` ${chalk19.green("\u2713")} ${deleted} entry${deleted !== 1 ? "ies" : ""} deleted`);
3107
+ if (kept > 0) console.log(` ${chalk19.dim("\xB7")} ${kept} entry${kept !== 1 ? "ies" : ""} kept`);
3108
+ if (archived > 0 || deleted > 0) {
3109
+ console.log(` ${chalk19.green("\u2713")} MEMORY.md updated.`);
3110
+ }
3111
+ console.log("");
3112
+ });
3113
+
3114
+ // src/commands/mcp.ts
3115
+ import { Command as Command19 } from "commander";
3116
+ import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "fs";
3117
+ import { join as join19, resolve as resolve16, dirname as dirname6 } from "path";
3118
+ function resolveProjectPath() {
3119
+ const envPath = process.env.MINDLINK_PROJECT_PATH;
3120
+ if (envPath && existsSync19(join19(envPath, BRAIN_DIR))) return envPath;
3121
+ let current = resolve16(process.cwd());
3122
+ for (let i = 0; i < 10; i++) {
3123
+ if (existsSync19(join19(current, BRAIN_DIR))) return current;
3124
+ const parent = dirname6(current);
3125
+ if (parent === current) break;
3126
+ current = parent;
3127
+ }
3128
+ return null;
3129
+ }
3130
+ function readMemorySection(memoryPath, section) {
3131
+ const content = readFileSync17(memoryPath, "utf8");
3132
+ if (!section) {
3133
+ const core = extractSection(content, "Core");
3134
+ const profile = extractSection(content, "User Profile");
3135
+ return `## Core
3136
+
3137
+ ${core}
3138
+
3139
+ ## User Profile
3140
+
3141
+ ${profile}`.trim();
3142
+ }
3143
+ return extractSection(content, section);
3144
+ }
3145
+ function appendToSection(memoryPath, section, newContent) {
3146
+ let content = readFileSync17(memoryPath, "utf8");
3147
+ const sectionBody = extractSection(content, section);
3148
+ const lines = content.split("\n");
3149
+ let headingIdx = -1;
3150
+ let nextSectionIdx = lines.length;
3151
+ let headingLevel = 0;
3152
+ for (let i = 0; i < lines.length; i++) {
3153
+ const match = lines[i].match(/^(#{1,6})\s+(.+)/);
3154
+ if (match) {
3155
+ const level = match[1].length;
3156
+ const title = match[2].replace(/<!--.*?-->/g, "").trim();
3157
+ if (headingIdx < 0 && title.toLowerCase() === section.toLowerCase()) {
3158
+ headingIdx = i;
3159
+ headingLevel = level;
3160
+ } else if (headingIdx >= 0 && level <= headingLevel) {
3161
+ nextSectionIdx = i;
3162
+ break;
3163
+ }
3164
+ }
3165
+ }
3166
+ if (headingIdx < 0) {
3167
+ content = content.trimEnd() + `
3168
+
3169
+ ## ${section}
3170
+
3171
+ ${newContent.trim()}
3172
+ `;
3173
+ } else {
3174
+ const insertAt = nextSectionIdx > 0 && lines[nextSectionIdx - 1].trim() === "---" ? nextSectionIdx - 1 : nextSectionIdx;
3175
+ lines.splice(insertAt, 0, "", newContent.trim(), "");
3176
+ content = lines.join("\n");
3177
+ }
3178
+ writeFileSync10(memoryPath, content);
3179
+ void sectionBody;
3180
+ }
3181
+ var mcpCommand = new Command19("mcp").description("Start the MindLink MCP server (stdio transport for AI tool integration)").addHelpText("after", `
3182
+ The MCP server runs as a local process launched by Claude Code.
3183
+ It exposes 4 tools for auditable, schema-validated memory reads and writes.
3184
+
3185
+ Tools:
3186
+ mindlink_read_memory(section?) \u2014 read a section of MEMORY.md
3187
+ mindlink_write_memory(section, content) \u2014 append to a MEMORY.md section
3188
+ mindlink_session_update(summary) \u2014 overwrite SESSION.md
3189
+ mindlink_verify() \u2014 run health check, return JSON
3190
+
3191
+ Configure in .claude/settings.json (done automatically by mindlink init):
3192
+ { "mcpServers": { "mindlink": { "command": "mindlink", "args": ["mcp"] } } }
3193
+
3194
+ Examples:
3195
+ mindlink mcp # (launched by Claude Code, not by hand)
3196
+ `).action(async () => {
3197
+ const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
3198
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
3199
+ const { z } = await import("./zod-FDOV7Y2P.js");
3200
+ const server = new McpServer({ name: "mindlink", version: VERSION });
3201
+ server.tool(
3202
+ "mindlink_read_memory",
3203
+ "Read a section of this project's MEMORY.md. If section is omitted, returns Core + User Profile only.",
3204
+ {
3205
+ section: z.enum(["Core", "Architecture", "Decisions", "Conventions", "User Profile", "Important Context"]).optional().describe("Section to read. Omit for Core + User Profile (recommended default).")
3206
+ },
3207
+ async ({ section }) => {
3208
+ const projectPath = resolveProjectPath();
3209
+ if (!projectPath) {
3210
+ return {
3211
+ content: [{ type: "text", text: "Error: No MindLink project found at this path. Run mindlink init first." }],
3212
+ isError: true
3213
+ };
3214
+ }
3215
+ const memoryPath = join19(projectPath, BRAIN_DIR, "MEMORY.md");
3216
+ if (!existsSync19(memoryPath)) {
3217
+ return {
3218
+ content: [{ type: "text", text: "Error: MEMORY.md not found. Run mindlink init to create it." }],
3219
+ isError: true
3220
+ };
3221
+ }
3222
+ try {
3223
+ const content = readMemorySection(memoryPath, section);
3224
+ return { content: [{ type: "text", text: content || "(section is empty)" }] };
3225
+ } catch (err) {
3226
+ return {
3227
+ content: [{ type: "text", text: `Error reading MEMORY.md: ${err instanceof Error ? err.message : err}` }],
3228
+ isError: true
3229
+ };
3230
+ }
3231
+ }
3232
+ );
3233
+ server.tool(
3234
+ "mindlink_write_memory",
3235
+ "Append a fact or decision to a section of MEMORY.md. Never overwrites existing content. Always include a <!-- added: YYYY-MM-DD --> timestamp.",
3236
+ {
3237
+ section: z.enum(["Core", "Architecture", "Decisions", "Conventions", "User Profile", "Important Context"]).describe("Section to append to."),
3238
+ content: z.string().describe("The markdown content to append. Include <!-- added: YYYY-MM-DD --> timestamp.")
3239
+ },
3240
+ async ({ section, content }) => {
3241
+ const projectPath = resolveProjectPath();
3242
+ if (!projectPath) {
3243
+ return {
3244
+ content: [{ type: "text", text: "Error: No MindLink project found. Run mindlink init first." }],
3245
+ isError: true
3246
+ };
3247
+ }
3248
+ const memoryPath = join19(projectPath, BRAIN_DIR, "MEMORY.md");
3249
+ if (!existsSync19(memoryPath)) {
3250
+ return {
3251
+ content: [{ type: "text", text: "Error: MEMORY.md not found. Run mindlink init to create it." }],
3252
+ isError: true
3253
+ };
3254
+ }
3255
+ try {
3256
+ appendToSection(memoryPath, section, content);
3257
+ return { content: [{ type: "text", text: `\u2713 Appended to ## ${section} in MEMORY.md.` }] };
3258
+ } catch (err) {
3259
+ return {
3260
+ content: [{ type: "text", text: `Error writing MEMORY.md: ${err instanceof Error ? err.message : err}` }],
3261
+ isError: true
3262
+ };
3263
+ }
3264
+ }
3265
+ );
3266
+ server.tool(
3267
+ "mindlink_session_update",
3268
+ "Update SESSION.md with the current session summary. Call this as the last action of every response.",
3269
+ {
3270
+ summary: z.string().describe("Current task state \u2014 what was asked, what you answered, any decisions made, what's next.")
3271
+ },
3272
+ async ({ summary }) => {
3273
+ const projectPath = resolveProjectPath();
3274
+ if (!projectPath) {
3275
+ return {
3276
+ content: [{ type: "text", text: "Error: No MindLink project found. Run mindlink init first." }],
3277
+ isError: true
3278
+ };
3279
+ }
3280
+ const sessionPath = join19(projectPath, BRAIN_DIR, "SESSION.md");
3281
+ try {
3282
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3283
+ const content = `# Session State
3284
+
3285
+ <!-- Last updated: ${date} -->
3286
+
3287
+ ${summary.trim()}
3288
+ `;
3289
+ writeFileSync10(sessionPath, content);
3290
+ return { content: [{ type: "text", text: "\u2713 SESSION.md updated." }] };
3291
+ } catch (err) {
3292
+ return {
3293
+ content: [{ type: "text", text: `Error writing SESSION.md: ${err instanceof Error ? err.message : err}` }],
3294
+ isError: true
3295
+ };
3296
+ }
3297
+ }
3298
+ );
3299
+ server.tool(
3300
+ "mindlink_verify",
3301
+ "Run a health check on .brain/. Returns pass/warn/fail status for each check. Call this to verify your memory writes succeeded.",
3302
+ {},
3303
+ async () => {
3304
+ const projectPath = resolveProjectPath();
3305
+ if (!projectPath) {
3306
+ return {
3307
+ content: [{ type: "text", text: JSON.stringify({ ok: false, error: "No MindLink project found. Run mindlink init first." }) }],
3308
+ isError: true
3309
+ };
3310
+ }
3311
+ const checks = runChecks(projectPath);
3312
+ const ok2 = checks.every((c) => c.status === "pass");
3313
+ return {
3314
+ content: [{ type: "text", text: JSON.stringify({ ok: ok2, checks }, null, 2) }]
3315
+ };
3316
+ }
3317
+ );
3318
+ const transport = new StdioServerTransport();
3319
+ await server.connect(transport);
3320
+ });
3321
+
2241
3322
  // src/cli.ts
2242
- var program = new Command16();
3323
+ var program = new Command20();
2243
3324
  program.name("mindlink").description("Give your AI a brain.").version(VERSION, "-v, --version");
2244
3325
  program.addCommand(initCommand);
2245
3326
  program.addCommand(statusCommand);
@@ -2256,9 +3337,13 @@ program.addCommand(importCommand);
2256
3337
  program.addCommand(doctorCommand);
2257
3338
  program.addCommand(versionCommand);
2258
3339
  program.addCommand(diffCommand);
3340
+ program.addCommand(verifyCommand);
3341
+ program.addCommand(profileCommand);
3342
+ program.addCommand(pruneCommand);
3343
+ program.addCommand(mcpCommand);
2259
3344
  program.on("command:*", (operands) => {
2260
3345
  const unknown = operands[0];
2261
- const known = ["init", "status", "log", "clear", "reset", "config", "sync", "update", "summary", "uninstall", "export", "import", "doctor", "version", "diff"];
3346
+ const known = ["init", "status", "log", "clear", "reset", "config", "sync", "update", "summary", "uninstall", "export", "import", "doctor", "version", "diff", "verify", "profile", "prune", "mcp"];
2262
3347
  function levenshtein(a, b) {
2263
3348
  const m = a.length, n = b.length;
2264
3349
  const dp = Array.from(
@@ -2274,11 +3359,11 @@ program.on("command:*", (operands) => {
2274
3359
  }
2275
3360
  const closest = known.map((cmd) => ({ cmd, dist: levenshtein(unknown, cmd) })).sort((a, b) => a.dist - b.dist)[0];
2276
3361
  console.log("");
2277
- console.log(` ${chalk17.red("\u2717")} Unknown command: ${chalk17.bold(unknown)}`);
3362
+ console.log(` ${chalk20.red("\u2717")} Unknown command: ${chalk20.bold(unknown)}`);
2278
3363
  if (closest && closest.dist <= 3) {
2279
- console.log(` Did you mean ${chalk17.cyan("mindlink " + closest.cmd)}?`);
3364
+ console.log(` Did you mean ${chalk20.cyan("mindlink " + closest.cmd)}?`);
2280
3365
  }
2281
- console.log(` Run ${chalk17.cyan("mindlink --help")} to see all commands.`);
3366
+ console.log(` Run ${chalk20.cyan("mindlink --help")} to see all commands.`);
2282
3367
  console.log("");
2283
3368
  process.exit(1);
2284
3369
  });