mindlink 1.2.0 → 2.0.1

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.1";
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,9 +370,20 @@ 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
+ writeFileSync2(hookDest, JSON.stringify(settings, null, 2));
275
375
  restored.push(".claude/settings.json");
276
376
  }
377
+ const mcpJsonDest = join3(projectPath, ".mcp.json");
378
+ if (!existsSync2(mcpJsonDest)) {
379
+ const mcpJson = {
380
+ mcpServers: {
381
+ mindlink: { type: "stdio", command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
382
+ }
383
+ };
384
+ writeFileSync2(mcpJsonDest, JSON.stringify(mcpJson, null, 2));
385
+ restored.push(".mcp.json");
386
+ }
277
387
  }
278
388
  const configPath = join3(brainDir, "config.json");
279
389
  if (!existsSync2(configPath)) {
@@ -452,6 +562,18 @@ Examples:
452
562
  writeFileSync2(dest, content);
453
563
  created.push(`${file.label.padEnd(32)} ${chalk2.dim(file.desc)}`);
454
564
  }
565
+ const memoryDest = join3(brainDir, "MEMORY.md");
566
+ if (existsSync2(GLOBAL_USER_PROFILE_PATH)) {
567
+ const profileContent = readFileSync2(GLOBAL_USER_PROFILE_PATH, "utf8");
568
+ if (sectionHasRealContent(profileContent, "MindLink \u2014 Global User Profile") || profileContent.split("\n").some((l) => {
569
+ const t = l.trim();
570
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith(">") && !t.startsWith("<!--");
571
+ })) {
572
+ const injected = injectUserProfile(readFileSync2(memoryDest, "utf8"), profileContent);
573
+ writeFileSync2(memoryDest, injected);
574
+ created.push(`User Profile imported from ~/.mindlink/USER.md`);
575
+ }
576
+ }
455
577
  for (const agentValue of selectedAgents) {
456
578
  const agent = AGENTS.find((a) => a.value === agentValue);
457
579
  if (!agent) continue;
@@ -464,8 +586,77 @@ Examples:
464
586
  const hookDest = join3(projectPath, ".claude", "settings.json");
465
587
  if (!existsSync2(hookDest)) {
466
588
  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")}`);
589
+ const settings = JSON.parse(readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
590
+ writeFileSync2(hookDest, JSON.stringify(settings, null, 2));
591
+ created.push(`.claude/settings.json${" ".repeat(14)} ${chalk2.dim("Claude Code hooks")}`);
592
+ }
593
+ const mcpJsonDest = join3(projectPath, ".mcp.json");
594
+ if (!existsSync2(mcpJsonDest)) {
595
+ const mcpJson = {
596
+ mcpServers: {
597
+ mindlink: { type: "stdio", command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
598
+ }
599
+ };
600
+ writeFileSync2(mcpJsonDest, JSON.stringify(mcpJson, null, 2));
601
+ created.push(`.mcp.json${" ".repeat(24)} ${chalk2.dim("Claude Code MCP server")}`);
602
+ }
603
+ }
604
+ if (selectedAgents.includes("cursor")) {
605
+ const cursorMcpDest = join3(projectPath, ".cursor", "mcp.json");
606
+ if (!existsSync2(cursorMcpDest)) {
607
+ mkdirSync2(join3(projectPath, ".cursor"), { recursive: true });
608
+ const cursorMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
609
+ writeFileSync2(cursorMcpDest, JSON.stringify(cursorMcp, null, 2));
610
+ created.push(`.cursor/mcp.json${" ".repeat(20)} ${chalk2.dim("Cursor MCP server")}`);
611
+ }
612
+ }
613
+ if (selectedAgents.includes("continue")) {
614
+ const continueMcpDest = join3(projectPath, ".continue", "mcpServers", "mindlink.json");
615
+ if (!existsSync2(continueMcpDest)) {
616
+ mkdirSync2(join3(projectPath, ".continue", "mcpServers"), { recursive: true });
617
+ const continueMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
618
+ writeFileSync2(continueMcpDest, JSON.stringify(continueMcp, null, 2));
619
+ created.push(`.continue/mcpServers/mindlink.json ${chalk2.dim("Continue MCP server")}`);
620
+ }
621
+ }
622
+ if (selectedAgents.includes("copilot")) {
623
+ const copilotMcpDest = join3(projectPath, ".vscode", "mcp.json");
624
+ if (!existsSync2(copilotMcpDest)) {
625
+ mkdirSync2(join3(projectPath, ".vscode"), { recursive: true });
626
+ const copilotMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
627
+ writeFileSync2(copilotMcpDest, JSON.stringify(copilotMcp, null, 2));
628
+ created.push(`.vscode/mcp.json${" ".repeat(19)} ${chalk2.dim("GitHub Copilot MCP server")}`);
629
+ }
630
+ }
631
+ if (selectedAgents.includes("kiro")) {
632
+ const kiroMcpDest = join3(projectPath, ".kiro", "settings", "mcp.json");
633
+ if (!existsSync2(kiroMcpDest)) {
634
+ mkdirSync2(join3(projectPath, ".kiro", "settings"), { recursive: true });
635
+ const kiroMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
636
+ writeFileSync2(kiroMcpDest, JSON.stringify(kiroMcp, null, 2));
637
+ created.push(`.kiro/settings/mcp.json${" ".repeat(13)} ${chalk2.dim("Kiro MCP server")}`);
638
+ }
639
+ }
640
+ if (selectedAgents.includes("windsurf")) {
641
+ try {
642
+ mkdirSync2(dirname2(GLOBAL_WINDSURF_MCP_PATH), { recursive: true });
643
+ let existingWindsurf = {};
644
+ if (existsSync2(GLOBAL_WINDSURF_MCP_PATH)) {
645
+ try {
646
+ existingWindsurf = JSON.parse(readFileSync2(GLOBAL_WINDSURF_MCP_PATH, "utf8"));
647
+ } catch {
648
+ }
649
+ }
650
+ const mergedWindsurf = {
651
+ ...existingWindsurf,
652
+ mcpServers: {
653
+ ...typeof existingWindsurf.mcpServers === "object" && existingWindsurf.mcpServers !== null ? existingWindsurf.mcpServers : {},
654
+ mindlink: { command: "mindlink", args: ["mcp"] }
655
+ }
656
+ };
657
+ writeFileSync2(GLOBAL_WINDSURF_MCP_PATH, JSON.stringify(mergedWindsurf, null, 2));
658
+ created.push(`~/.codeium/windsurf/mcp_config.json ${chalk2.dim("Windsurf MCP server (global)")}`);
659
+ } catch {
469
660
  }
470
661
  }
471
662
  if (!gitTracking) {
@@ -498,7 +689,15 @@ Examples:
498
689
  console.log("");
499
690
  for (const err of errors) console.log(` ${chalk2.red("\u2717")} ${err}`);
500
691
  }
501
- console.log("");
692
+ if (selectedAgents.includes("cline")) {
693
+ console.log(` ${chalk2.yellow("\u2192")} Cline: add the MCP server manually in Cline's settings UI (MCP Servers tab)`);
694
+ console.log(` ${chalk2.dim("Command: mindlink Args: mcp Env: MINDLINK_PROJECT_PATH=" + projectPath)}`);
695
+ console.log("");
696
+ }
697
+ if (!existsSync2(GLOBAL_USER_PROFILE_PATH)) {
698
+ console.log(` ${chalk2.dim("\u2192")} Run ${chalk2.cyan("mindlink profile")} to set up a global user profile \u2014 imported into every new project automatically.`);
699
+ console.log("");
700
+ }
502
701
  note(
503
702
  `Your AI finally has a brain.
504
703
 
@@ -1200,7 +1399,7 @@ var REQUIRED_BRAIN_FILES = ["MEMORY.md", "SESSION.md", "SHARED.md", "LOG.md"];
1200
1399
  async function latestVersion() {
1201
1400
  try {
1202
1401
  const { default: https } = await import("https");
1203
- return new Promise((resolve14) => {
1402
+ return new Promise((resolve17) => {
1204
1403
  const req = https.get(
1205
1404
  "https://registry.npmjs.org/mindlink/latest",
1206
1405
  { headers: { "User-Agent": "mindlink-cli" } },
@@ -1212,17 +1411,17 @@ async function latestVersion() {
1212
1411
  res.on("end", () => {
1213
1412
  try {
1214
1413
  const parsed = JSON.parse(data);
1215
- resolve14(parsed.version ?? null);
1414
+ resolve17(parsed.version ?? null);
1216
1415
  } catch {
1217
- resolve14(null);
1416
+ resolve17(null);
1218
1417
  }
1219
1418
  });
1220
1419
  }
1221
1420
  );
1222
- req.on("error", () => resolve14(null));
1421
+ req.on("error", () => resolve17(null));
1223
1422
  req.setTimeout(8e3, () => {
1224
1423
  req.destroy();
1225
- resolve14(null);
1424
+ resolve17(null);
1226
1425
  });
1227
1426
  });
1228
1427
  } catch {
@@ -1243,71 +1442,73 @@ Examples:
1243
1442
  mindlink update
1244
1443
  `).action(async () => {
1245
1444
  const current = VERSION;
1445
+ let nonTtyExitCode = null;
1246
1446
  if (!process.stdin.isTTY) {
1247
- const latest2 = await latestVersion();
1248
- if (!latest2) {
1447
+ const latest = await latestVersion();
1448
+ if (!latest) {
1249
1449
  console.log(JSON.stringify({ current, latest: null, upToDate: null }));
1250
- process.exit(1);
1450
+ nonTtyExitCode = 1;
1451
+ } else {
1452
+ const upToDate = !semverGt(latest, current);
1453
+ console.log(JSON.stringify({ current, latest, upToDate }));
1454
+ if (!upToDate) nonTtyExitCode = 2;
1251
1455
  }
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
1456
  } 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
- }
1457
+ const s = spinner2();
1458
+ s.start("Checking for updates...");
1459
+ const latest = await latestVersion();
1460
+ if (!latest) {
1461
+ s.stop("Could not reach npm registry.");
1291
1462
  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.");
1463
+ console.log(` ${chalk9.red("\u2717")} Could not check for updates. Check your internet connection.`);
1464
+ console.log(` ${chalk9.dim("Latest releases: github.com/404-not-found/mindlink/releases")}`);
1299
1465
  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.");
1466
+ process.exit(1);
1467
+ }
1468
+ s.stop("Done.");
1469
+ console.log("");
1470
+ console.log(` Current version : ${chalk9.dim(current)}`);
1471
+ console.log(` Latest version : ${semverGt(latest, current) ? chalk9.green(latest) : chalk9.dim(latest)}`);
1472
+ console.log("");
1473
+ if (!semverGt(latest, current)) {
1474
+ console.log(` ${chalk9.green("\u2713")} You're on the latest version (${current}).`);
1304
1475
  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)}`);
1476
+ } else {
1477
+ const action = await select3({
1478
+ message: `Update to ${latest}?`,
1479
+ options: [
1480
+ { value: "update", label: `Update to ${latest}` },
1481
+ { value: "skip", label: "Skip this version" },
1482
+ { value: "cancel", label: "Cancel" }
1483
+ ]
1484
+ });
1485
+ if (isCancel4(action) || action === "cancel" || action === "skip") {
1486
+ if (action === "skip") {
1487
+ console.log(` ${chalk9.dim("Skipped. Run mindlink update again to install later.")}`);
1488
+ } else {
1489
+ cancel4("Cancelled.");
1490
+ }
1491
+ console.log("");
1492
+ return;
1493
+ }
1494
+ const s2 = spinner2();
1495
+ s2.start(`Installing mindlink@${latest}...`);
1496
+ try {
1497
+ execSync2(`npm install -g mindlink@${latest}`, { stdio: "pipe" });
1498
+ s2.stop("Done.");
1499
+ console.log("");
1500
+ console.log(` ${chalk9.green("\u2713")} Updated to ${latest}.`);
1501
+ console.log(` ${chalk9.dim("See what's new: github.com/404-not-found/mindlink/releases")}`);
1502
+ } catch (err) {
1503
+ s2.stop("Failed.");
1504
+ console.log("");
1505
+ console.log(` ${chalk9.red("\u2717")} Update failed.`);
1506
+ console.log(` ${chalk9.dim("Try: npm install -g mindlink@" + latest)}`);
1507
+ if (err instanceof Error && err.message.includes("EACCES")) {
1508
+ console.log(` ${chalk9.dim("Permission error \u2014 try: sudo npm install -g mindlink@" + latest)}`);
1509
+ }
1510
+ process.exit(1);
1309
1511
  }
1310
- process.exit(1);
1311
1512
  }
1312
1513
  }
1313
1514
  const cwd = process.cwd();
@@ -1356,10 +1557,146 @@ Examples:
1356
1557
  const hookDest = join10(projectPath, ".claude", "settings.json");
1357
1558
  try {
1358
1559
  mkdirSync4(join10(projectPath, ".claude"), { recursive: true });
1359
- writeFileSync6(hookDest, readFileSync9(join10(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
1560
+ const template = JSON.parse(readFileSync9(join10(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
1561
+ let existing = {};
1562
+ if (existsSync9(hookDest)) {
1563
+ try {
1564
+ existing = JSON.parse(readFileSync9(hookDest, "utf8"));
1565
+ } catch {
1566
+ }
1567
+ }
1568
+ const { mcpServers: _removed, ...existingWithoutMcp } = existing;
1569
+ void _removed;
1570
+ const merged = {
1571
+ ...existingWithoutMcp,
1572
+ hooks: template.hooks,
1573
+ permissions: template.permissions
1574
+ };
1575
+ writeFileSync6(hookDest, JSON.stringify(merged, null, 2));
1360
1576
  refreshed.push(".claude/settings.json");
1361
1577
  } catch {
1362
1578
  }
1579
+ const mcpJsonDest = join10(projectPath, ".mcp.json");
1580
+ try {
1581
+ let existing = {};
1582
+ if (existsSync9(mcpJsonDest)) {
1583
+ try {
1584
+ existing = JSON.parse(readFileSync9(mcpJsonDest, "utf8"));
1585
+ } catch {
1586
+ }
1587
+ }
1588
+ const merged = {
1589
+ ...existing,
1590
+ mcpServers: {
1591
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1592
+ mindlink: { type: "stdio", command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1593
+ }
1594
+ };
1595
+ writeFileSync6(mcpJsonDest, JSON.stringify(merged, null, 2));
1596
+ refreshed.push(".mcp.json");
1597
+ } catch {
1598
+ }
1599
+ }
1600
+ if (agentValues.includes("cursor")) {
1601
+ const cursorMcpDest = join10(projectPath, ".cursor", "mcp.json");
1602
+ try {
1603
+ mkdirSync4(join10(projectPath, ".cursor"), { recursive: true });
1604
+ let existing = {};
1605
+ if (existsSync9(cursorMcpDest)) {
1606
+ try {
1607
+ existing = JSON.parse(readFileSync9(cursorMcpDest, "utf8"));
1608
+ } catch {
1609
+ }
1610
+ }
1611
+ const merged = {
1612
+ ...existing,
1613
+ mcpServers: {
1614
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1615
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1616
+ }
1617
+ };
1618
+ writeFileSync6(cursorMcpDest, JSON.stringify(merged, null, 2));
1619
+ refreshed.push(".cursor/mcp.json");
1620
+ } catch {
1621
+ }
1622
+ }
1623
+ if (agentValues.includes("continue")) {
1624
+ const continueMcpDest = join10(projectPath, ".continue", "mcpServers", "mindlink.json");
1625
+ try {
1626
+ mkdirSync4(join10(projectPath, ".continue", "mcpServers"), { recursive: true });
1627
+ const continueMcp = { mcpServers: { mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } } } };
1628
+ writeFileSync6(continueMcpDest, JSON.stringify(continueMcp, null, 2));
1629
+ refreshed.push(".continue/mcpServers/mindlink.json");
1630
+ } catch {
1631
+ }
1632
+ }
1633
+ if (agentValues.includes("copilot")) {
1634
+ const copilotMcpDest = join10(projectPath, ".vscode", "mcp.json");
1635
+ try {
1636
+ mkdirSync4(join10(projectPath, ".vscode"), { recursive: true });
1637
+ let existing = {};
1638
+ if (existsSync9(copilotMcpDest)) {
1639
+ try {
1640
+ existing = JSON.parse(readFileSync9(copilotMcpDest, "utf8"));
1641
+ } catch {
1642
+ }
1643
+ }
1644
+ const merged = {
1645
+ ...existing,
1646
+ mcpServers: {
1647
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1648
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1649
+ }
1650
+ };
1651
+ writeFileSync6(copilotMcpDest, JSON.stringify(merged, null, 2));
1652
+ refreshed.push(".vscode/mcp.json");
1653
+ } catch {
1654
+ }
1655
+ }
1656
+ if (agentValues.includes("kiro")) {
1657
+ const kiroMcpDest = join10(projectPath, ".kiro", "settings", "mcp.json");
1658
+ try {
1659
+ mkdirSync4(join10(projectPath, ".kiro", "settings"), { recursive: true });
1660
+ let existing = {};
1661
+ if (existsSync9(kiroMcpDest)) {
1662
+ try {
1663
+ existing = JSON.parse(readFileSync9(kiroMcpDest, "utf8"));
1664
+ } catch {
1665
+ }
1666
+ }
1667
+ const merged = {
1668
+ ...existing,
1669
+ mcpServers: {
1670
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1671
+ mindlink: { command: "mindlink", args: ["mcp"], env: { MINDLINK_PROJECT_PATH: projectPath } }
1672
+ }
1673
+ };
1674
+ writeFileSync6(kiroMcpDest, JSON.stringify(merged, null, 2));
1675
+ refreshed.push(".kiro/settings/mcp.json");
1676
+ } catch {
1677
+ }
1678
+ }
1679
+ if (agentValues.includes("windsurf")) {
1680
+ try {
1681
+ mkdirSync4(dirname4(GLOBAL_WINDSURF_MCP_PATH), { recursive: true });
1682
+ let existing = {};
1683
+ if (existsSync9(GLOBAL_WINDSURF_MCP_PATH)) {
1684
+ try {
1685
+ existing = JSON.parse(readFileSync9(GLOBAL_WINDSURF_MCP_PATH, "utf8"));
1686
+ } catch {
1687
+ }
1688
+ }
1689
+ const merged = {
1690
+ ...existing,
1691
+ mcpServers: {
1692
+ ...typeof existing.mcpServers === "object" && existing.mcpServers !== null ? existing.mcpServers : {},
1693
+ mindlink: { command: "mindlink", args: ["mcp"] }
1694
+ }
1695
+ };
1696
+ writeFileSync6(GLOBAL_WINDSURF_MCP_PATH, JSON.stringify(merged, null, 2));
1697
+ refreshed.push("~/.codeium/windsurf/mcp_config.json");
1698
+ } catch {
1699
+ }
1363
1700
  }
1364
1701
  const memoryPath = join10(projectPath, BRAIN_DIR, "MEMORY.md");
1365
1702
  if (existsSync9(memoryPath)) {
@@ -1401,6 +1738,26 @@ Examples:
1401
1738
  } catch {
1402
1739
  }
1403
1740
  }
1741
+ if (existsSync9(GLOBAL_USER_PROFILE_PATH) && existsSync9(memoryPath)) {
1742
+ try {
1743
+ const profileRaw = readFileSync9(GLOBAL_USER_PROFILE_PATH, "utf8");
1744
+ const profileLines = profileRaw.split("\n");
1745
+ const bodyStart = profileLines.findIndex((l) => {
1746
+ const t = l.trim();
1747
+ return t.length > 0 && !t.startsWith("#") && !t.startsWith(">") && !t.startsWith("<!--");
1748
+ });
1749
+ const profileBody = bodyStart >= 0 ? profileLines.slice(bodyStart).join("\n").trim() : "";
1750
+ if (profileBody) {
1751
+ const memContent = readFileSync9(memoryPath, "utf8");
1752
+ const updated = replaceSection(memContent, "User Profile", profileBody);
1753
+ if (updated !== memContent) {
1754
+ writeFileSync6(memoryPath, updated);
1755
+ if (!refreshed.includes(".brain/MEMORY.md")) refreshed.push(".brain/MEMORY.md (profile synced)");
1756
+ }
1757
+ }
1758
+ } catch {
1759
+ }
1760
+ }
1404
1761
  console.log(` ${chalk9.bold(projectPath)}`);
1405
1762
  for (const f of refreshed) {
1406
1763
  console.log(` ${chalk9.green("\u2713")} ${f}`);
@@ -1410,6 +1767,7 @@ Examples:
1410
1767
  console.log(` ${chalk9.dim("All agent files are up to date.")}`);
1411
1768
  }
1412
1769
  console.log("");
1770
+ if (nonTtyExitCode !== null) process.exit(nonTtyExitCode);
1413
1771
  });
1414
1772
 
1415
1773
  // src/commands/summary.ts
@@ -2238,8 +2596,760 @@ Examples:
2238
2596
  console.log("");
2239
2597
  });
2240
2598
 
2599
+ // src/commands/verify.ts
2600
+ import { Command as Command16 } from "commander";
2601
+ import chalk17 from "chalk";
2602
+ import { existsSync as existsSync16, readFileSync as readFileSync14, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
2603
+ import { dirname as dirname5 } from "path";
2604
+ import { join as join17, resolve as resolve14 } from "path";
2605
+ var SESSION_WARN_DAYS = 3;
2606
+ var SESSION_FAIL_DAYS = 7;
2607
+ var MEMORY_WARN_LINES = 100;
2608
+ var MEMORY_FAIL_LINES = 200;
2609
+ function pass(id, label, message) {
2610
+ return { id, label, status: "pass", message, fixable: false };
2611
+ }
2612
+ function warn2(id, label, message, fixable = false) {
2613
+ return { id, label, status: "warn", message, fixable };
2614
+ }
2615
+ function fail2(id, label, message, fixable = false) {
2616
+ return { id, label, status: "fail", message, fixable };
2617
+ }
2618
+ function icon2(status) {
2619
+ switch (status) {
2620
+ case "pass":
2621
+ return chalk17.green("\u2713");
2622
+ case "warn":
2623
+ return chalk17.yellow("\u26A0");
2624
+ case "fail":
2625
+ return chalk17.red("\u2717");
2626
+ }
2627
+ }
2628
+ function runChecks(projectPath) {
2629
+ const brainDir = join17(projectPath, BRAIN_DIR);
2630
+ const results = [];
2631
+ if (!existsSync16(brainDir)) {
2632
+ results.push(fail2("brain_missing", ".brain/ missing", `Run ${chalk17.cyan("mindlink init")} to set up memory.`));
2633
+ return results;
2634
+ }
2635
+ const configPath = join17(brainDir, "config.json");
2636
+ let config = {};
2637
+ if (existsSync16(configPath)) {
2638
+ try {
2639
+ config = JSON.parse(readFileSync14(configPath, "utf8"));
2640
+ } catch {
2641
+ }
2642
+ }
2643
+ const memoryPath = join17(brainDir, "MEMORY.md");
2644
+ if (!existsSync16(memoryPath)) {
2645
+ results.push(fail2("core", "Core section", `MEMORY.md is missing \u2014 run ${chalk17.cyan("mindlink init")}.`));
2646
+ } else {
2647
+ const memMd = readFileSync14(memoryPath, "utf8");
2648
+ if (!sectionHasRealContent(memMd, "Core")) {
2649
+ results.push(fail2("core", "Core section \u2014 empty", "Start a session and tell your AI to fill in the Core section."));
2650
+ } else {
2651
+ results.push(pass("core", "Core section \u2014 filled", ""));
2652
+ }
2653
+ }
2654
+ if (existsSync16(memoryPath)) {
2655
+ const memMd = readFileSync14(memoryPath, "utf8");
2656
+ if (!sectionHasRealContent(memMd, "User Profile")) {
2657
+ results.push(fail2("user_profile", "User Profile \u2014 empty", `Run ${chalk17.cyan("mindlink profile")} to set up your global profile and import it here.`));
2658
+ } else {
2659
+ results.push(pass("user_profile", "User Profile \u2014 filled", ""));
2660
+ }
2661
+ }
2662
+ const sessionPath = join17(brainDir, "SESSION.md");
2663
+ if (!existsSync16(sessionPath)) {
2664
+ results.push(fail2("session_fresh", "SESSION.md \u2014 missing", `Run ${chalk17.cyan("mindlink init")} to recreate it.`));
2665
+ } else {
2666
+ const ageDays = (Date.now() - statSync5(sessionPath).mtime.getTime()) / 864e5;
2667
+ const hasContent = readFileSync14(sessionPath, "utf8").split("\n").some((l) => l.trim().length > 0 && !l.startsWith("#") && !l.startsWith("<!--"));
2668
+ if (!hasContent) {
2669
+ results.push(warn2("session_fresh", "SESSION.md \u2014 no content yet", "Start a session \u2014 your AI will fill this in automatically."));
2670
+ } else if (ageDays > SESSION_FAIL_DAYS) {
2671
+ const days = Math.floor(ageDays);
2672
+ 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."));
2673
+ } else if (ageDays > SESSION_WARN_DAYS) {
2674
+ const days = Math.floor(ageDays);
2675
+ 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."));
2676
+ } else {
2677
+ const mins = Math.floor((Date.now() - statSync5(sessionPath).mtime.getTime()) / 6e4);
2678
+ const age = mins < 60 ? `${mins}m ago` : mins < 1440 ? `${Math.floor(mins / 60)}h ago` : `${Math.floor(mins / 1440)}d ago`;
2679
+ results.push(pass("session_fresh", `SESSION.md \u2014 updated ${age}`, ""));
2680
+ }
2681
+ }
2682
+ const logPath = join17(brainDir, "LOG.md");
2683
+ if (!existsSync16(logPath)) {
2684
+ results.push(fail2("log_present", "LOG.md \u2014 missing", `Run ${chalk17.cyan("mindlink init")} to recreate it.`));
2685
+ } else {
2686
+ const entries = (readFileSync14(logPath, "utf8").match(/^##\s+/gm) ?? []).length;
2687
+ if (entries === 0) {
2688
+ results.push(pass("log_present", "LOG.md \u2014 no sessions yet", ""));
2689
+ } else {
2690
+ results.push(pass("log_present", `LOG.md \u2014 ${entries} session${entries !== 1 ? "s" : ""} logged`, ""));
2691
+ }
2692
+ }
2693
+ if (existsSync16(memoryPath)) {
2694
+ const memMd = readFileSync14(memoryPath, "utf8");
2695
+ const lines = countRealLines(memMd);
2696
+ if (lines > MEMORY_FAIL_LINES) {
2697
+ results.push(fail2("memory_size", `MEMORY.md \u2014 ${lines} lines (target: under ${MEMORY_FAIL_LINES})`, `Run ${chalk17.cyan("mindlink prune")} to consolidate stale entries.`));
2698
+ } else if (lines > MEMORY_WARN_LINES) {
2699
+ results.push(warn2("memory_size", `MEMORY.md \u2014 ${lines} lines (getting long)`, `Consider running ${chalk17.cyan("mindlink prune")} to retire old entries.`));
2700
+ } else {
2701
+ results.push(pass("memory_size", `MEMORY.md \u2014 ${lines} line${lines !== 1 ? "s" : ""} (healthy)`, ""));
2702
+ }
2703
+ }
2704
+ const configuredAgents = config.agents ?? [];
2705
+ if (configuredAgents.length === 0) {
2706
+ results.push(warn2("agent_files", "No agents configured", `Run ${chalk17.cyan("mindlink config")} \u2192 Agent instruction files.`, false));
2707
+ } else {
2708
+ const missing = [];
2709
+ for (const agentValue of configuredAgents) {
2710
+ const agent = AGENTS.find((a) => a.value === agentValue);
2711
+ if (!agent) continue;
2712
+ if (!existsSync16(join17(projectPath, agent.destFile))) missing.push(agent.destFile);
2713
+ }
2714
+ if (missing.length === configuredAgents.length) {
2715
+ 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));
2716
+ } else if (missing.length > 0) {
2717
+ results.push(warn2("agent_files", `Agent files \u2014 ${missing.length} missing: ${missing.join(", ")}`, `Run ${chalk17.cyan("mindlink verify --fix")} to regenerate.`, true));
2718
+ } else {
2719
+ results.push(pass("agent_files", `Agent files \u2014 all ${configuredAgents.length} present`, ""));
2720
+ }
2721
+ }
2722
+ return results;
2723
+ }
2724
+ function applyFix(projectPath, results) {
2725
+ const brainDir = join17(projectPath, BRAIN_DIR);
2726
+ const configPath = join17(brainDir, "config.json");
2727
+ let config = {};
2728
+ if (existsSync16(configPath)) {
2729
+ try {
2730
+ config = JSON.parse(readFileSync14(configPath, "utf8"));
2731
+ } catch {
2732
+ }
2733
+ }
2734
+ const fixable = results.filter((r) => r.fixable && r.status !== "pass");
2735
+ if (fixable.length === 0) {
2736
+ console.log(` ${chalk17.dim("Nothing to auto-fix. Address the issues above manually.")}`);
2737
+ return;
2738
+ }
2739
+ let fixed = 0;
2740
+ for (const r of fixable) {
2741
+ if (r.id === "agent_files") {
2742
+ const configuredAgents = config.agents ?? [];
2743
+ for (const agentValue of configuredAgents) {
2744
+ const agent = AGENTS.find((a) => a.value === agentValue);
2745
+ if (!agent) continue;
2746
+ const destPath = join17(projectPath, agent.destFile);
2747
+ if (!existsSync16(destPath)) {
2748
+ try {
2749
+ mkdirSync6(dirname5(destPath), { recursive: true });
2750
+ writeFileSync7(destPath, readFileSync14(join17(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
2751
+ console.log(` ${chalk17.green("\u2713")} Regenerated ${agent.destFile}`);
2752
+ fixed++;
2753
+ } catch (err) {
2754
+ console.log(` ${chalk17.red("\u2717")} Failed to regenerate ${agent.destFile}: ${err instanceof Error ? err.message : err}`);
2755
+ }
2756
+ }
2757
+ }
2758
+ if (configuredAgents.includes("claude")) {
2759
+ const hookPath = join17(projectPath, ".claude", "settings.json");
2760
+ if (!existsSync16(hookPath)) {
2761
+ try {
2762
+ mkdirSync6(dirname5(hookPath), { recursive: true });
2763
+ writeFileSync7(hookPath, readFileSync14(join17(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
2764
+ console.log(` ${chalk17.green("\u2713")} Regenerated .claude/settings.json`);
2765
+ fixed++;
2766
+ } catch {
2767
+ }
2768
+ }
2769
+ }
2770
+ }
2771
+ }
2772
+ for (const r of results) {
2773
+ if (r.status === "fail" || r.status === "warn") {
2774
+ if (r.id === "core" || r.id === "user_profile") {
2775
+ console.log(` ${chalk17.dim("\u2192")} ${r.id === "core" ? "Core" : "User Profile"}: start a session and tell your AI to fill this in.`);
2776
+ } else if (r.id === "memory_size") {
2777
+ console.log(` ${chalk17.dim("\u2192")} MEMORY.md too large: run ${chalk17.cyan("mindlink prune")} to consolidate.`);
2778
+ }
2779
+ }
2780
+ }
2781
+ if (fixed > 0) console.log("");
2782
+ }
2783
+ 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", `
2784
+ What is checked:
2785
+ Core \u2014 MEMORY.md Core section has real content
2786
+ User Profile \u2014 MEMORY.md User Profile has real content
2787
+ SESSION.md \u2014 was updated recently (warn >3 days, fail >7 days)
2788
+ LOG.md \u2014 present and readable
2789
+ MEMORY.md size \u2014 line count (warn >100, fail >200)
2790
+ Agent files \u2014 all files from config.json are present on disk
2791
+
2792
+ Examples:
2793
+ mindlink verify
2794
+ mindlink verify --json
2795
+ mindlink verify --fix
2796
+ `).action((opts) => {
2797
+ const projectPath = resolve14(process.cwd());
2798
+ const results = runChecks(projectPath);
2799
+ if (opts.json) {
2800
+ console.log(JSON.stringify({ ok: results.every((r) => r.status === "pass"), checks: results }, null, 2));
2801
+ const hasFailure = results.some((r) => r.status === "fail");
2802
+ process.exit(hasFailure ? 1 : 0);
2803
+ }
2804
+ console.log("");
2805
+ console.log(` ${chalk17.bold("\u25C9 MindLink Verify")}`);
2806
+ console.log(` ${chalk17.dim(projectPath)}`);
2807
+ console.log("");
2808
+ for (const r of results) {
2809
+ console.log(` ${icon2(r.status)} ${r.label}`);
2810
+ if (r.message) console.log(` ${chalk17.dim(r.message)}`);
2811
+ }
2812
+ console.log("");
2813
+ const failCount = results.filter((r) => r.status === "fail").length;
2814
+ const warnCount = results.filter((r) => r.status === "warn").length;
2815
+ const fixableCount = results.filter((r) => r.fixable && r.status !== "pass").length;
2816
+ if (opts.fix) {
2817
+ applyFix(projectPath, results);
2818
+ }
2819
+ if (failCount > 0) {
2820
+ console.log(` ${chalk17.red.bold(`${failCount} error${failCount !== 1 ? "s" : ""}`)}, ${warnCount} warning${warnCount !== 1 ? "s" : ""}.`);
2821
+ if (!opts.fix && fixableCount > 0) {
2822
+ console.log(` ${chalk17.dim(`Run ${chalk17.cyan("mindlink verify --fix")} to auto-repair ${fixableCount} issue${fixableCount !== 1 ? "s" : ""}.`)}`);
2823
+ }
2824
+ } else if (warnCount > 0) {
2825
+ console.log(` ${chalk17.yellow.bold(`${warnCount} warning${warnCount !== 1 ? "s" : ""}`)}, no critical errors.`);
2826
+ } else {
2827
+ console.log(` ${chalk17.green.bold("All good.")} Your AI's memory is healthy.`);
2828
+ }
2829
+ console.log("");
2830
+ if (failCount > 0) process.exit(1);
2831
+ });
2832
+
2833
+ // src/commands/profile.ts
2834
+ import { Command as Command17 } from "commander";
2835
+ import chalk18 from "chalk";
2836
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
2837
+ import { execSync as execSync4 } from "child_process";
2838
+ var PROFILE_TEMPLATE = `# MindLink \u2014 Global User Profile
2839
+
2840
+ > This file is imported into every new project's MEMORY.md on \`mindlink init\`.
2841
+ > Edit it with \`mindlink profile\`. Run \`mindlink update\` to sync changes to all projects.
2842
+
2843
+ <!-- Role, company, title, level, years of experience -->
2844
+ <!-- Primary languages and tools -->
2845
+ <!-- Communication style and preferences -->
2846
+ <!-- Editor, OS, shell setup -->
2847
+ <!-- Added: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)} -->
2848
+ `;
2849
+ function ensureProfileExists() {
2850
+ if (!existsSync17(GLOBAL_MINDLINK_DIR)) {
2851
+ mkdirSync7(GLOBAL_MINDLINK_DIR, { recursive: true });
2852
+ }
2853
+ if (!existsSync17(GLOBAL_USER_PROFILE_PATH)) {
2854
+ writeFileSync8(GLOBAL_USER_PROFILE_PATH, PROFILE_TEMPLATE);
2855
+ }
2856
+ }
2857
+ 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", `
2858
+ Your profile is stored at ~/.mindlink/USER.md and auto-imported into
2859
+ MEMORY.md when you run \`mindlink init\` on a new project.
2860
+
2861
+ Run \`mindlink update\` to sync profile changes to all registered projects.
2862
+
2863
+ Examples:
2864
+ mindlink profile # open profile in $EDITOR
2865
+ mindlink profile --show # print profile to stdout
2866
+ mindlink profile --path # print file path
2867
+ `).action((opts) => {
2868
+ if (opts.path) {
2869
+ console.log(GLOBAL_USER_PROFILE_PATH);
2870
+ return;
2871
+ }
2872
+ if (opts.show) {
2873
+ if (!existsSync17(GLOBAL_USER_PROFILE_PATH)) {
2874
+ console.log("");
2875
+ console.log(` ${chalk18.yellow("\u26A0")} No global profile yet.`);
2876
+ console.log(` Run ${chalk18.cyan("mindlink profile")} to create one.`);
2877
+ console.log("");
2878
+ return;
2879
+ }
2880
+ console.log("");
2881
+ console.log(readFileSync15(GLOBAL_USER_PROFILE_PATH, "utf8"));
2882
+ return;
2883
+ }
2884
+ ensureProfileExists();
2885
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
2886
+ console.log("");
2887
+ console.log(` ${chalk18.dim(`Opening ${GLOBAL_USER_PROFILE_PATH} in ${editor}...`)}`);
2888
+ console.log(` ${chalk18.dim("Run mindlink update after saving to sync to all registered projects.")}`);
2889
+ console.log("");
2890
+ try {
2891
+ execSync4(`${editor} "${GLOBAL_USER_PROFILE_PATH}"`, { stdio: "inherit" });
2892
+ console.log("");
2893
+ console.log(` ${chalk18.green("\u2713")} Profile saved.`);
2894
+ console.log(` ${chalk18.dim("\u2192 Run mindlink update to sync to all registered projects.")}`);
2895
+ console.log("");
2896
+ } catch {
2897
+ console.log("");
2898
+ }
2899
+ });
2900
+
2901
+ // src/commands/prune.ts
2902
+ import { Command as Command18 } from "commander";
2903
+ import { select as select6, isCancel as isCancel8 } from "@clack/prompts";
2904
+ import chalk19 from "chalk";
2905
+ import { existsSync as existsSync18, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
2906
+ import { join as join18, resolve as resolve15 } from "path";
2907
+ var STALENESS_THRESHOLDS = {
2908
+ "current focus": 14,
2909
+ "decisions": 180,
2910
+ "conventions": 180,
2911
+ "architecture": 365
2912
+ // 'user profile' and 'important context' have no expiry
2913
+ };
2914
+ var TIMESTAMP_RE = /<!--\s*added:\s*(\d{4}-\d{2}-\d{2})\s*-->/;
2915
+ var SECTION_RE = /^(#{1,6})\s+(.+)/;
2916
+ function parseTimestampedEntries(content) {
2917
+ const lines = content.split("\n");
2918
+ const entries = [];
2919
+ let currentSection = "";
2920
+ let currentHeadingLevel = 0;
2921
+ let entryLines = [];
2922
+ let entryStart = -1;
2923
+ function flushEntry(lineEnd) {
2924
+ if (entryStart < 0 || entryLines.length === 0) return;
2925
+ const fullText = entryLines.join("\n");
2926
+ const timestampMatch = fullText.match(TIMESTAMP_RE);
2927
+ if (!timestampMatch) {
2928
+ entryLines = [];
2929
+ entryStart = -1;
2930
+ return;
2931
+ }
2932
+ const addedDate = /* @__PURE__ */ new Date(timestampMatch[1] + "T00:00:00Z");
2933
+ const ageInDays = (Date.now() - addedDate.getTime()) / 864e5;
2934
+ const sectionKey = currentSection.toLowerCase().replace(/\s*<!--.*?-->\s*/g, "").trim();
2935
+ const threshold = STALENESS_THRESHOLDS[sectionKey] ?? Infinity;
2936
+ const isStale = isFinite(threshold) && ageInDays > threshold;
2937
+ const trimmedLines = entryLines.filter((l) => l.trim().length > 0);
2938
+ const displayText = trimmedLines.join("\n");
2939
+ entries.push({
2940
+ section: currentSection.replace(/<!--.*?-->/g, "").trim(),
2941
+ text: displayText,
2942
+ addedDate,
2943
+ lineStart: entryStart,
2944
+ lineEnd,
2945
+ ageInDays,
2946
+ threshold,
2947
+ isStale
2948
+ });
2949
+ entryLines = [];
2950
+ entryStart = -1;
2951
+ }
2952
+ for (let i = 0; i < lines.length; i++) {
2953
+ const line = lines[i];
2954
+ const sectionMatch = line.match(SECTION_RE);
2955
+ if (sectionMatch) {
2956
+ const level = sectionMatch[1].length;
2957
+ const title = sectionMatch[2];
2958
+ flushEntry(i);
2959
+ if (level <= 2) {
2960
+ currentSection = title;
2961
+ currentHeadingLevel = level;
2962
+ }
2963
+ continue;
2964
+ }
2965
+ if (line.trim() === "---") {
2966
+ flushEntry(i);
2967
+ continue;
2968
+ }
2969
+ if (line.trim().startsWith("<!--") && line.trim().endsWith("-->")) {
2970
+ if (entryStart < 0) continue;
2971
+ }
2972
+ if (TIMESTAMP_RE.test(line)) {
2973
+ if (entryStart < 0) entryStart = i;
2974
+ entryLines.push(line);
2975
+ flushEntry(i + 1);
2976
+ continue;
2977
+ }
2978
+ if (line.trim().length > 0 && !line.trim().startsWith(">")) {
2979
+ if (entryStart < 0) entryStart = i;
2980
+ entryLines.push(line);
2981
+ } else if (entryStart >= 0) {
2982
+ entryLines.push(line);
2983
+ }
2984
+ }
2985
+ flushEntry(lines.length);
2986
+ return entries;
2987
+ }
2988
+ function removeLines(content, lineStart, lineEnd) {
2989
+ const lines = content.split("\n");
2990
+ lines.splice(lineStart, lineEnd - lineStart);
2991
+ return lines.join("\n");
2992
+ }
2993
+ function appendToArchive(content, entryText, pruneDate) {
2994
+ const archiveHeading = "## Archive";
2995
+ const archiveEntry = `${entryText} <!-- archived: ${pruneDate} -->`;
2996
+ if (content.includes(archiveHeading)) {
2997
+ return content.replace(
2998
+ /(## Archive\n(?:<!--[^>]*-->\n)*)/,
2999
+ `$1
3000
+ ${archiveEntry}
3001
+ `
3002
+ );
3003
+ } else {
3004
+ return content.trimEnd() + `
3005
+
3006
+ ${archiveHeading}
3007
+
3008
+ <!-- Entries moved here by mindlink prune \u2014 kept for reference -->
3009
+
3010
+ ${archiveEntry}
3011
+ `;
3012
+ }
3013
+ }
3014
+ function formatAge(days) {
3015
+ if (days < 30) return `${Math.round(days)} days`;
3016
+ if (days < 365) return `${Math.round(days / 30)} months`;
3017
+ return `${(days / 365).toFixed(1)} years`;
3018
+ }
3019
+ 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", `
3020
+ Scans MEMORY.md for entries with <!-- added: YYYY-MM-DD --> timestamps.
3021
+ Entries older than their section's staleness threshold are flagged for review.
3022
+
3023
+ Staleness thresholds:
3024
+ Current Focus \u2014 14 days
3025
+ Decisions \u2014 180 days
3026
+ Conventions \u2014 180 days
3027
+ Architecture \u2014 365 days
3028
+ User Profile \u2014 never expires
3029
+
3030
+ Archived entries are moved to ## Archive at the bottom of MEMORY.md.
3031
+ They are never permanently deleted unless you choose "Delete".
3032
+
3033
+ Examples:
3034
+ mindlink prune
3035
+ mindlink prune --dry-run
3036
+ mindlink prune --all
3037
+ `).action(async (opts) => {
3038
+ const projectPath = resolve15(process.cwd());
3039
+ const brainDir = join18(projectPath, BRAIN_DIR);
3040
+ const memoryPath = join18(brainDir, "MEMORY.md");
3041
+ console.log("");
3042
+ console.log(` ${chalk19.bold("\u25C9 MindLink Prune")}`);
3043
+ console.log(` ${chalk19.dim(memoryPath)}`);
3044
+ console.log("");
3045
+ if (!existsSync18(memoryPath)) {
3046
+ console.log(` ${chalk19.red("\u2717")} MEMORY.md not found. Run ${chalk19.cyan("mindlink init")} first.`);
3047
+ console.log("");
3048
+ process.exit(1);
3049
+ }
3050
+ const allEntries = parseTimestampedEntries(readFileSync16(memoryPath, "utf8"));
3051
+ const toReview = opts.all ? allEntries.filter((e) => e.addedDate !== null) : allEntries.filter((e) => e.isStale);
3052
+ if (toReview.length === 0) {
3053
+ if (allEntries.length === 0) {
3054
+ console.log(` ${chalk19.dim("No timestamped entries found in MEMORY.md.")}`);
3055
+ console.log(` ${chalk19.dim("Entries are timestamped when your AI writes them (<!-- added: YYYY-MM-DD -->).")}`);
3056
+ } else {
3057
+ console.log(` ${chalk19.green("\u2713")} No stale entries found. ${allEntries.length} entry${allEntries.length !== 1 ? "ies" : "y"} all within threshold.`);
3058
+ console.log(` ${chalk19.dim("Run mindlink prune --all to review all timestamped entries.")}`);
3059
+ }
3060
+ console.log("");
3061
+ return;
3062
+ }
3063
+ console.log(` Scanning MEMORY.md for ${opts.all ? "timestamped" : "stale"} entries...`);
3064
+ console.log(` Found ${toReview.length} entr${toReview.length !== 1 ? "ies" : "y"} to review.`);
3065
+ console.log("");
3066
+ if (opts.dryRun) {
3067
+ for (const entry of toReview) {
3068
+ const age = entry.ageInDays !== null ? formatAge(entry.ageInDays) : "undated";
3069
+ const thresholdStr = isFinite(entry.threshold) ? ` \u2014 threshold: ${entry.threshold} days` : "";
3070
+ console.log(` ${chalk19.yellow("\u26A0")} [${entry.section}] ${entry.text.split("\n")[0].slice(0, 80)}`);
3071
+ console.log(` ${chalk19.dim(`Added: ${entry.addedDate?.toISOString().slice(0, 10) ?? "unknown"} (${age} ago)${thresholdStr}`)}`);
3072
+ console.log("");
3073
+ }
3074
+ console.log(` ${chalk19.dim("Dry run \u2014 no changes made.")}`);
3075
+ console.log("");
3076
+ return;
3077
+ }
3078
+ const pruneDate = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3079
+ let content = readFileSync16(memoryPath, "utf8");
3080
+ let archived = 0;
3081
+ let deleted = 0;
3082
+ let kept = 0;
3083
+ let skippedAll = false;
3084
+ const sorted = [...toReview].sort((a, b) => b.lineStart - a.lineStart);
3085
+ for (const entry of sorted) {
3086
+ if (skippedAll) {
3087
+ kept++;
3088
+ continue;
3089
+ }
3090
+ const age = entry.ageInDays !== null ? formatAge(entry.ageInDays) : "undated";
3091
+ const thresholdStr = isFinite(entry.threshold) ? ` \u2014 threshold: ${entry.threshold} days` : "";
3092
+ const displayLines = entry.text.split("\n").slice(0, 3).join("\n");
3093
+ console.log(` ${chalk19.bold("\u2500".repeat(55))}`);
3094
+ console.log(` ${chalk19.bold("Section:")} ${entry.section}`);
3095
+ console.log(` ${chalk19.bold("Entry:")}`);
3096
+ for (const l of displayLines.split("\n")) console.log(` ${chalk19.dim(l)}`);
3097
+ console.log(` ${chalk19.bold("Added:")} ${entry.addedDate?.toISOString().slice(0, 10) ?? "unknown"} (${age} ago)${thresholdStr}`);
3098
+ console.log("");
3099
+ const action = await select6({
3100
+ message: "What would you like to do?",
3101
+ options: [
3102
+ { value: "keep", label: "Keep", hint: "leave as-is" },
3103
+ { value: "archive", label: "Archive", hint: "move to ## Archive section" },
3104
+ { value: "delete", label: "Delete", hint: "remove permanently" },
3105
+ { value: "skip_all", label: "Skip remaining", hint: "keep all remaining entries unchanged" }
3106
+ ]
3107
+ });
3108
+ if (isCancel8(action)) {
3109
+ console.log("");
3110
+ break;
3111
+ }
3112
+ if (action === "skip_all") {
3113
+ skippedAll = true;
3114
+ kept++;
3115
+ continue;
3116
+ }
3117
+ if (action === "archive") {
3118
+ const entryText = content.split("\n").slice(entry.lineStart, entry.lineEnd).join("\n").trim();
3119
+ content = removeLines(content, entry.lineStart, entry.lineEnd);
3120
+ content = appendToArchive(content, entryText, pruneDate);
3121
+ archived++;
3122
+ } else if (action === "delete") {
3123
+ content = removeLines(content, entry.lineStart, entry.lineEnd);
3124
+ deleted++;
3125
+ } else {
3126
+ kept++;
3127
+ }
3128
+ console.log("");
3129
+ }
3130
+ if (archived > 0 || deleted > 0) {
3131
+ writeFileSync9(memoryPath, content);
3132
+ }
3133
+ console.log(` ${chalk19.bold("\u2500".repeat(55))}`);
3134
+ if (archived > 0) console.log(` ${chalk19.green("\u2713")} ${archived} entry${archived !== 1 ? "ies" : ""} archived`);
3135
+ if (deleted > 0) console.log(` ${chalk19.green("\u2713")} ${deleted} entry${deleted !== 1 ? "ies" : ""} deleted`);
3136
+ if (kept > 0) console.log(` ${chalk19.dim("\xB7")} ${kept} entry${kept !== 1 ? "ies" : ""} kept`);
3137
+ if (archived > 0 || deleted > 0) {
3138
+ console.log(` ${chalk19.green("\u2713")} MEMORY.md updated.`);
3139
+ }
3140
+ console.log("");
3141
+ });
3142
+
3143
+ // src/commands/mcp.ts
3144
+ import { Command as Command19 } from "commander";
3145
+ import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "fs";
3146
+ import { join as join19, resolve as resolve16, dirname as dirname6 } from "path";
3147
+ function resolveProjectPath() {
3148
+ const envPath = process.env.MINDLINK_PROJECT_PATH;
3149
+ if (envPath && existsSync19(join19(envPath, BRAIN_DIR))) return envPath;
3150
+ let current = resolve16(process.cwd());
3151
+ for (let i = 0; i < 10; i++) {
3152
+ if (existsSync19(join19(current, BRAIN_DIR))) return current;
3153
+ const parent = dirname6(current);
3154
+ if (parent === current) break;
3155
+ current = parent;
3156
+ }
3157
+ return null;
3158
+ }
3159
+ function readMemorySection(memoryPath, section) {
3160
+ const content = readFileSync17(memoryPath, "utf8");
3161
+ if (!section) {
3162
+ const core = extractSection(content, "Core");
3163
+ const profile = extractSection(content, "User Profile");
3164
+ return `## Core
3165
+
3166
+ ${core}
3167
+
3168
+ ## User Profile
3169
+
3170
+ ${profile}`.trim();
3171
+ }
3172
+ return extractSection(content, section);
3173
+ }
3174
+ function appendToSection(memoryPath, section, newContent) {
3175
+ let content = readFileSync17(memoryPath, "utf8");
3176
+ const sectionBody = extractSection(content, section);
3177
+ const lines = content.split("\n");
3178
+ let headingIdx = -1;
3179
+ let nextSectionIdx = lines.length;
3180
+ let headingLevel = 0;
3181
+ for (let i = 0; i < lines.length; i++) {
3182
+ const match = lines[i].match(/^(#{1,6})\s+(.+)/);
3183
+ if (match) {
3184
+ const level = match[1].length;
3185
+ const title = match[2].replace(/<!--.*?-->/g, "").trim();
3186
+ if (headingIdx < 0 && title.toLowerCase() === section.toLowerCase()) {
3187
+ headingIdx = i;
3188
+ headingLevel = level;
3189
+ } else if (headingIdx >= 0 && level <= headingLevel) {
3190
+ nextSectionIdx = i;
3191
+ break;
3192
+ }
3193
+ }
3194
+ }
3195
+ if (headingIdx < 0) {
3196
+ content = content.trimEnd() + `
3197
+
3198
+ ## ${section}
3199
+
3200
+ ${newContent.trim()}
3201
+ `;
3202
+ } else {
3203
+ const insertAt = nextSectionIdx > 0 && lines[nextSectionIdx - 1].trim() === "---" ? nextSectionIdx - 1 : nextSectionIdx;
3204
+ lines.splice(insertAt, 0, "", newContent.trim(), "");
3205
+ content = lines.join("\n");
3206
+ }
3207
+ writeFileSync10(memoryPath, content);
3208
+ void sectionBody;
3209
+ }
3210
+ var mcpCommand = new Command19("mcp").description("Start the MindLink MCP server (stdio transport for AI tool integration)").addHelpText("after", `
3211
+ The MCP server runs as a local process launched by Claude Code.
3212
+ It exposes 4 tools for auditable, schema-validated memory reads and writes.
3213
+
3214
+ Tools:
3215
+ mindlink_read_memory(section?) \u2014 read a section of MEMORY.md
3216
+ mindlink_write_memory(section, content) \u2014 append to a MEMORY.md section
3217
+ mindlink_session_update(summary) \u2014 overwrite SESSION.md
3218
+ mindlink_verify() \u2014 run health check, return JSON
3219
+
3220
+ Configure in .claude/settings.json (done automatically by mindlink init):
3221
+ { "mcpServers": { "mindlink": { "command": "mindlink", "args": ["mcp"] } } }
3222
+
3223
+ Examples:
3224
+ mindlink mcp # (launched by Claude Code, not by hand)
3225
+ `).action(async () => {
3226
+ const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
3227
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
3228
+ const { z } = await import("./zod-FDOV7Y2P.js");
3229
+ const server = new McpServer({ name: "mindlink", version: VERSION });
3230
+ server.tool(
3231
+ "mindlink_read_memory",
3232
+ "Read a section of this project's MEMORY.md. If section is omitted, returns Core + User Profile only.",
3233
+ {
3234
+ section: z.enum(["Core", "Architecture", "Decisions", "Conventions", "User Profile", "Important Context"]).optional().describe("Section to read. Omit for Core + User Profile (recommended default).")
3235
+ },
3236
+ async ({ section }) => {
3237
+ const projectPath = resolveProjectPath();
3238
+ if (!projectPath) {
3239
+ return {
3240
+ content: [{ type: "text", text: "Error: No MindLink project found at this path. Run mindlink init first." }],
3241
+ isError: true
3242
+ };
3243
+ }
3244
+ const memoryPath = join19(projectPath, BRAIN_DIR, "MEMORY.md");
3245
+ if (!existsSync19(memoryPath)) {
3246
+ return {
3247
+ content: [{ type: "text", text: "Error: MEMORY.md not found. Run mindlink init to create it." }],
3248
+ isError: true
3249
+ };
3250
+ }
3251
+ try {
3252
+ const content = readMemorySection(memoryPath, section);
3253
+ return { content: [{ type: "text", text: content || "(section is empty)" }] };
3254
+ } catch (err) {
3255
+ return {
3256
+ content: [{ type: "text", text: `Error reading MEMORY.md: ${err instanceof Error ? err.message : err}` }],
3257
+ isError: true
3258
+ };
3259
+ }
3260
+ }
3261
+ );
3262
+ server.tool(
3263
+ "mindlink_write_memory",
3264
+ "Append a fact or decision to a section of MEMORY.md. Never overwrites existing content. Always include a <!-- added: YYYY-MM-DD --> timestamp.",
3265
+ {
3266
+ section: z.enum(["Core", "Architecture", "Decisions", "Conventions", "User Profile", "Important Context"]).describe("Section to append to."),
3267
+ content: z.string().describe("The markdown content to append. Include <!-- added: YYYY-MM-DD --> timestamp.")
3268
+ },
3269
+ async ({ section, content }) => {
3270
+ const projectPath = resolveProjectPath();
3271
+ if (!projectPath) {
3272
+ return {
3273
+ content: [{ type: "text", text: "Error: No MindLink project found. Run mindlink init first." }],
3274
+ isError: true
3275
+ };
3276
+ }
3277
+ const memoryPath = join19(projectPath, BRAIN_DIR, "MEMORY.md");
3278
+ if (!existsSync19(memoryPath)) {
3279
+ return {
3280
+ content: [{ type: "text", text: "Error: MEMORY.md not found. Run mindlink init to create it." }],
3281
+ isError: true
3282
+ };
3283
+ }
3284
+ try {
3285
+ appendToSection(memoryPath, section, content);
3286
+ return { content: [{ type: "text", text: `\u2713 Appended to ## ${section} in MEMORY.md.` }] };
3287
+ } catch (err) {
3288
+ return {
3289
+ content: [{ type: "text", text: `Error writing MEMORY.md: ${err instanceof Error ? err.message : err}` }],
3290
+ isError: true
3291
+ };
3292
+ }
3293
+ }
3294
+ );
3295
+ server.tool(
3296
+ "mindlink_session_update",
3297
+ "Update SESSION.md with the current session summary. Call this as the last action of every response.",
3298
+ {
3299
+ summary: z.string().describe("Current task state \u2014 what was asked, what you answered, any decisions made, what's next.")
3300
+ },
3301
+ async ({ summary }) => {
3302
+ const projectPath = resolveProjectPath();
3303
+ if (!projectPath) {
3304
+ return {
3305
+ content: [{ type: "text", text: "Error: No MindLink project found. Run mindlink init first." }],
3306
+ isError: true
3307
+ };
3308
+ }
3309
+ const sessionPath = join19(projectPath, BRAIN_DIR, "SESSION.md");
3310
+ try {
3311
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3312
+ const content = `# Session State
3313
+
3314
+ <!-- Last updated: ${date} -->
3315
+
3316
+ ${summary.trim()}
3317
+ `;
3318
+ writeFileSync10(sessionPath, content);
3319
+ return { content: [{ type: "text", text: "\u2713 SESSION.md updated." }] };
3320
+ } catch (err) {
3321
+ return {
3322
+ content: [{ type: "text", text: `Error writing SESSION.md: ${err instanceof Error ? err.message : err}` }],
3323
+ isError: true
3324
+ };
3325
+ }
3326
+ }
3327
+ );
3328
+ server.tool(
3329
+ "mindlink_verify",
3330
+ "Run a health check on .brain/. Returns pass/warn/fail status for each check. Call this to verify your memory writes succeeded.",
3331
+ {},
3332
+ async () => {
3333
+ const projectPath = resolveProjectPath();
3334
+ if (!projectPath) {
3335
+ return {
3336
+ content: [{ type: "text", text: JSON.stringify({ ok: false, error: "No MindLink project found. Run mindlink init first." }) }],
3337
+ isError: true
3338
+ };
3339
+ }
3340
+ const checks = runChecks(projectPath);
3341
+ const ok2 = checks.every((c) => c.status === "pass");
3342
+ return {
3343
+ content: [{ type: "text", text: JSON.stringify({ ok: ok2, checks }, null, 2) }]
3344
+ };
3345
+ }
3346
+ );
3347
+ const transport = new StdioServerTransport();
3348
+ await server.connect(transport);
3349
+ });
3350
+
2241
3351
  // src/cli.ts
2242
- var program = new Command16();
3352
+ var program = new Command20();
2243
3353
  program.name("mindlink").description("Give your AI a brain.").version(VERSION, "-v, --version");
2244
3354
  program.addCommand(initCommand);
2245
3355
  program.addCommand(statusCommand);
@@ -2256,9 +3366,13 @@ program.addCommand(importCommand);
2256
3366
  program.addCommand(doctorCommand);
2257
3367
  program.addCommand(versionCommand);
2258
3368
  program.addCommand(diffCommand);
3369
+ program.addCommand(verifyCommand);
3370
+ program.addCommand(profileCommand);
3371
+ program.addCommand(pruneCommand);
3372
+ program.addCommand(mcpCommand);
2259
3373
  program.on("command:*", (operands) => {
2260
3374
  const unknown = operands[0];
2261
- const known = ["init", "status", "log", "clear", "reset", "config", "sync", "update", "summary", "uninstall", "export", "import", "doctor", "version", "diff"];
3375
+ const known = ["init", "status", "log", "clear", "reset", "config", "sync", "update", "summary", "uninstall", "export", "import", "doctor", "version", "diff", "verify", "profile", "prune", "mcp"];
2262
3376
  function levenshtein(a, b) {
2263
3377
  const m = a.length, n = b.length;
2264
3378
  const dp = Array.from(
@@ -2274,11 +3388,11 @@ program.on("command:*", (operands) => {
2274
3388
  }
2275
3389
  const closest = known.map((cmd) => ({ cmd, dist: levenshtein(unknown, cmd) })).sort((a, b) => a.dist - b.dist)[0];
2276
3390
  console.log("");
2277
- console.log(` ${chalk17.red("\u2717")} Unknown command: ${chalk17.bold(unknown)}`);
3391
+ console.log(` ${chalk20.red("\u2717")} Unknown command: ${chalk20.bold(unknown)}`);
2278
3392
  if (closest && closest.dist <= 3) {
2279
- console.log(` Did you mean ${chalk17.cyan("mindlink " + closest.cmd)}?`);
3393
+ console.log(` Did you mean ${chalk20.cyan("mindlink " + closest.cmd)}?`);
2280
3394
  }
2281
- console.log(` Run ${chalk17.cyan("mindlink --help")} to see all commands.`);
3395
+ console.log(` Run ${chalk20.cyan("mindlink --help")} to see all commands.`);
2282
3396
  console.log("");
2283
3397
  process.exit(1);
2284
3398
  });