mindlink 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.js +236 -204
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Command as Command15 } from "commander";
|
|
|
5
5
|
import chalk16 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/utils/version.ts
|
|
8
|
-
var VERSION = "1.0.
|
|
8
|
+
var VERSION = "1.0.10";
|
|
9
9
|
|
|
10
10
|
// src/commands/init.ts
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -20,13 +20,13 @@ import {
|
|
|
20
20
|
} from "@clack/prompts";
|
|
21
21
|
import chalk2 from "chalk";
|
|
22
22
|
import {
|
|
23
|
-
existsSync,
|
|
24
|
-
mkdirSync,
|
|
25
|
-
readFileSync,
|
|
26
|
-
writeFileSync,
|
|
23
|
+
existsSync as existsSync2,
|
|
24
|
+
mkdirSync as mkdirSync2,
|
|
25
|
+
readFileSync as readFileSync2,
|
|
26
|
+
writeFileSync as writeFileSync2,
|
|
27
27
|
appendFileSync
|
|
28
28
|
} from "fs";
|
|
29
|
-
import { join as
|
|
29
|
+
import { join as join3, resolve, dirname as dirname2, basename } from "path";
|
|
30
30
|
|
|
31
31
|
// src/utils/paths.ts
|
|
32
32
|
import { fileURLToPath } from "url";
|
|
@@ -61,16 +61,43 @@ var AGENTS = [
|
|
|
61
61
|
{ value: "aider", label: "Aider", hint: "CONVENTIONS.md", templateFile: "CONVENTIONS.md", destFile: "CONVENTIONS.md", selected: false }
|
|
62
62
|
];
|
|
63
63
|
|
|
64
|
+
// src/utils/registry.ts
|
|
65
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
66
|
+
import { join as join2, homedir } from "path";
|
|
67
|
+
var REGISTRY_DIR = join2(homedir(), ".mindlink");
|
|
68
|
+
var REGISTRY_PATH = join2(REGISTRY_DIR, "projects.json");
|
|
69
|
+
function load() {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(readFileSync(REGISTRY_PATH, "utf8"));
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function save(paths) {
|
|
77
|
+
mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
78
|
+
writeFileSync(REGISTRY_PATH, JSON.stringify(paths, null, 2));
|
|
79
|
+
}
|
|
80
|
+
function registerProject(projectPath) {
|
|
81
|
+
const paths = load();
|
|
82
|
+
if (!paths.includes(projectPath)) {
|
|
83
|
+
paths.push(projectPath);
|
|
84
|
+
save(paths);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function getRegisteredProjects() {
|
|
88
|
+
return load();
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
// src/commands/init.ts
|
|
65
92
|
function detectProjectInfo(projectPath) {
|
|
66
93
|
const date = (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
|
|
67
94
|
let name = basename(projectPath);
|
|
68
95
|
let description = "";
|
|
69
96
|
let stack = "";
|
|
70
|
-
const pkgPath =
|
|
71
|
-
if (
|
|
97
|
+
const pkgPath = join3(projectPath, "package.json");
|
|
98
|
+
if (existsSync2(pkgPath)) {
|
|
72
99
|
try {
|
|
73
|
-
const pkg = JSON.parse(
|
|
100
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
74
101
|
if (pkg.name) name = pkg.name;
|
|
75
102
|
if (pkg.description) description = pkg.description;
|
|
76
103
|
stack = "Node.js";
|
|
@@ -78,13 +105,13 @@ function detectProjectInfo(projectPath) {
|
|
|
78
105
|
}
|
|
79
106
|
}
|
|
80
107
|
if (!stack) {
|
|
81
|
-
if (
|
|
82
|
-
else if (
|
|
83
|
-
else if (
|
|
84
|
-
else if (
|
|
85
|
-
else if (
|
|
86
|
-
else if (
|
|
87
|
-
else if (
|
|
108
|
+
if (existsSync2(join3(projectPath, "Cargo.toml"))) stack = "Rust";
|
|
109
|
+
else if (existsSync2(join3(projectPath, "go.mod"))) stack = "Go";
|
|
110
|
+
else if (existsSync2(join3(projectPath, "pyproject.toml")) || existsSync2(join3(projectPath, "requirements.txt"))) stack = "Python";
|
|
111
|
+
else if (existsSync2(join3(projectPath, "pom.xml"))) stack = "Java (Maven)";
|
|
112
|
+
else if (existsSync2(join3(projectPath, "build.gradle")) || existsSync2(join3(projectPath, "build.gradle.kts"))) stack = "Kotlin/Java (Gradle)";
|
|
113
|
+
else if (existsSync2(join3(projectPath, "composer.json"))) stack = "PHP";
|
|
114
|
+
else if (existsSync2(join3(projectPath, "Gemfile"))) stack = "Ruby";
|
|
88
115
|
}
|
|
89
116
|
return { name, description, stack, date };
|
|
90
117
|
}
|
|
@@ -125,9 +152,9 @@ Examples:
|
|
|
125
152
|
mindlink init --yes
|
|
126
153
|
`).action(async (opts) => {
|
|
127
154
|
const projectPath = resolve(process.cwd());
|
|
128
|
-
const brainDir =
|
|
155
|
+
const brainDir = join3(projectPath, BRAIN_DIR);
|
|
129
156
|
printBanner();
|
|
130
|
-
if (
|
|
157
|
+
if (existsSync2(brainDir)) {
|
|
131
158
|
if (opts.yes) {
|
|
132
159
|
console.log(` ${chalk2.red("\u2717")} Already initialized at this path.`);
|
|
133
160
|
console.log(` Run ${chalk2.cyan("mindlink config")} to change settings.`);
|
|
@@ -232,39 +259,40 @@ Examples:
|
|
|
232
259
|
const created = [];
|
|
233
260
|
const errors = [];
|
|
234
261
|
try {
|
|
235
|
-
|
|
262
|
+
mkdirSync2(brainDir, { recursive: true });
|
|
263
|
+
registerProject(projectPath);
|
|
236
264
|
const projectInfo = detectProjectInfo(projectPath);
|
|
237
265
|
for (const file of BRAIN_FILES) {
|
|
238
|
-
const dest =
|
|
239
|
-
const templateContent =
|
|
266
|
+
const dest = join3(brainDir, file.templateFile);
|
|
267
|
+
const templateContent = readFileSync2(join3(BRAIN_TEMPLATES_DIR, file.templateFile), "utf8");
|
|
240
268
|
const content = file.templateFile === "MEMORY.md" ? buildMemoryMd(templateContent, projectInfo) : templateContent;
|
|
241
|
-
|
|
269
|
+
writeFileSync2(dest, content);
|
|
242
270
|
created.push(`${file.label.padEnd(32)} ${chalk2.dim(file.desc)}`);
|
|
243
271
|
}
|
|
244
272
|
for (const agentValue of selectedAgents) {
|
|
245
273
|
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
246
274
|
if (!agent) continue;
|
|
247
|
-
const destPath =
|
|
248
|
-
|
|
249
|
-
|
|
275
|
+
const destPath = join3(projectPath, agent.destFile);
|
|
276
|
+
mkdirSync2(dirname2(destPath), { recursive: true });
|
|
277
|
+
writeFileSync2(destPath, readFileSync2(join3(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
|
|
250
278
|
created.push(`${agent.destFile.padEnd(32)} ${chalk2.dim(agent.label)}`);
|
|
251
279
|
}
|
|
252
280
|
if (selectedAgents.includes("claude")) {
|
|
253
|
-
const hookDest =
|
|
254
|
-
if (!
|
|
255
|
-
|
|
256
|
-
|
|
281
|
+
const hookDest = join3(projectPath, ".claude", "settings.json");
|
|
282
|
+
if (!existsSync2(hookDest)) {
|
|
283
|
+
mkdirSync2(dirname2(hookDest), { recursive: true });
|
|
284
|
+
writeFileSync2(hookDest, readFileSync2(join3(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
|
|
257
285
|
created.push(`.claude/settings.json${" ".repeat(14)} ${chalk2.dim("Claude Code compact hook")}`);
|
|
258
286
|
}
|
|
259
287
|
}
|
|
260
288
|
if (!gitTracking) {
|
|
261
|
-
const gitignorePath =
|
|
289
|
+
const gitignorePath = join3(projectPath, ".gitignore");
|
|
262
290
|
const entry = "\n# MindLink memory (personal \u2014 not shared with team)\n.brain/\n";
|
|
263
|
-
if (
|
|
264
|
-
const current =
|
|
291
|
+
if (existsSync2(gitignorePath)) {
|
|
292
|
+
const current = readFileSync2(gitignorePath, "utf8");
|
|
265
293
|
if (!current.includes(".brain/")) appendFileSync(gitignorePath, entry);
|
|
266
294
|
} else {
|
|
267
|
-
|
|
295
|
+
writeFileSync2(gitignorePath, entry.trim() + "\n");
|
|
268
296
|
}
|
|
269
297
|
created.push(`.gitignore${" ".repeat(23)} ${chalk2.dim(".brain/ excluded")}`);
|
|
270
298
|
}
|
|
@@ -274,7 +302,7 @@ Examples:
|
|
|
274
302
|
agents: selectedAgents,
|
|
275
303
|
maxLogEntries: DEFAULT_MAX_LOG_ENTRIES
|
|
276
304
|
};
|
|
277
|
-
|
|
305
|
+
writeFileSync2(join3(brainDir, "config.json"), JSON.stringify(config, null, 2));
|
|
278
306
|
} catch (err) {
|
|
279
307
|
errors.push(err instanceof Error ? err.message : String(err));
|
|
280
308
|
}
|
|
@@ -310,8 +338,8 @@ Run ${chalk2.cyan("mindlink help")} to see all commands.`,
|
|
|
310
338
|
// src/commands/status.ts
|
|
311
339
|
import { Command as Command2 } from "commander";
|
|
312
340
|
import chalk3 from "chalk";
|
|
313
|
-
import { existsSync as
|
|
314
|
-
import { join as
|
|
341
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, statSync } from "fs";
|
|
342
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
315
343
|
|
|
316
344
|
// src/utils/parser.ts
|
|
317
345
|
function extractSection(markdown, heading) {
|
|
@@ -383,19 +411,19 @@ Examples:
|
|
|
383
411
|
mindlink status --json
|
|
384
412
|
`).action((opts) => {
|
|
385
413
|
const projectPath = resolve2(process.cwd());
|
|
386
|
-
const brainDir =
|
|
387
|
-
if (!
|
|
414
|
+
const brainDir = join4(projectPath, BRAIN_DIR);
|
|
415
|
+
if (!existsSync3(brainDir)) {
|
|
388
416
|
console.log(` ${chalk3.red("\u2717")} No .brain/ found in this directory.`);
|
|
389
417
|
console.log(` Run ${chalk3.cyan("mindlink init")} to get started.`);
|
|
390
418
|
console.log("");
|
|
391
419
|
process.exit(1);
|
|
392
420
|
}
|
|
393
|
-
const sessionPath =
|
|
394
|
-
const logPath =
|
|
395
|
-
const memoryPath =
|
|
396
|
-
const sessionMd =
|
|
397
|
-
const logMd =
|
|
398
|
-
const memoryMd =
|
|
421
|
+
const sessionPath = join4(brainDir, "SESSION.md");
|
|
422
|
+
const logPath = join4(brainDir, "LOG.md");
|
|
423
|
+
const memoryPath = join4(brainDir, "MEMORY.md");
|
|
424
|
+
const sessionMd = existsSync3(sessionPath) ? readFileSync3(sessionPath, "utf8") : "";
|
|
425
|
+
const logMd = existsSync3(logPath) ? readFileSync3(logPath, "utf8") : "";
|
|
426
|
+
const memoryMd = existsSync3(memoryPath) ? readFileSync3(memoryPath, "utf8") : "";
|
|
399
427
|
const rawTask = extractSection(sessionMd, "Current Task");
|
|
400
428
|
const currentTask = rawTask.startsWith("<!--") ? "" : rawTask;
|
|
401
429
|
const inProgress = extractBullets(extractSection(sessionMd, "In Progress"));
|
|
@@ -405,7 +433,7 @@ Examples:
|
|
|
405
433
|
const sessionCount = countLogEntries(logMd);
|
|
406
434
|
const lastSession = lastLogDate(logMd);
|
|
407
435
|
const decisionCount = countDecisions(memoryMd);
|
|
408
|
-
const lastUpdated =
|
|
436
|
+
const lastUpdated = existsSync3(sessionPath) ? relativeTime(statSync(sessionPath).mtime) : "never";
|
|
409
437
|
const isEmpty = !currentTask && inProgress.length === 0 && decisions.length === 0 && blockers.length === 0 && upNext.length === 0 && sessionCount === 0;
|
|
410
438
|
if (opts.json) {
|
|
411
439
|
console.log(JSON.stringify({
|
|
@@ -474,8 +502,8 @@ Examples:
|
|
|
474
502
|
// src/commands/log.ts
|
|
475
503
|
import { Command as Command3 } from "commander";
|
|
476
504
|
import chalk4 from "chalk";
|
|
477
|
-
import { existsSync as
|
|
478
|
-
import { join as
|
|
505
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
506
|
+
import { join as join5, resolve as resolve3 } from "path";
|
|
479
507
|
var logCommand = new Command3("log").description("Print session history").option("--all", "Show full history").option("--limit <n>", "Show last N sessions", "10").option("--since <date>", "Show sessions from a date (matched against heading text)").option("--json", "Output as JSON").addHelpText("after", `
|
|
480
508
|
Examples:
|
|
481
509
|
mindlink log
|
|
@@ -485,15 +513,15 @@ Examples:
|
|
|
485
513
|
mindlink log --json
|
|
486
514
|
`).action((opts) => {
|
|
487
515
|
const projectPath = resolve3(process.cwd());
|
|
488
|
-
const brainDir =
|
|
489
|
-
if (!
|
|
516
|
+
const brainDir = join5(projectPath, BRAIN_DIR);
|
|
517
|
+
if (!existsSync4(brainDir)) {
|
|
490
518
|
console.log(` ${chalk4.red("\u2717")} No .brain/ found in this directory.`);
|
|
491
519
|
console.log(` Run ${chalk4.cyan("mindlink init")} to get started.`);
|
|
492
520
|
console.log("");
|
|
493
521
|
process.exit(1);
|
|
494
522
|
}
|
|
495
|
-
const logPath =
|
|
496
|
-
const logMd =
|
|
523
|
+
const logPath = join5(brainDir, "LOG.md");
|
|
524
|
+
const logMd = existsSync4(logPath) ? readFileSync4(logPath, "utf8") : "";
|
|
497
525
|
let entries = parseLogEntries(logMd);
|
|
498
526
|
if (entries.length === 0) {
|
|
499
527
|
console.log("");
|
|
@@ -545,8 +573,8 @@ Examples:
|
|
|
545
573
|
// src/commands/clear.ts
|
|
546
574
|
import { Command as Command4 } from "commander";
|
|
547
575
|
import chalk5 from "chalk";
|
|
548
|
-
import { existsSync as
|
|
549
|
-
import { join as
|
|
576
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
577
|
+
import { join as join6, resolve as resolve4 } from "path";
|
|
550
578
|
var clearCommand = new Command4("clear").description("Reset SESSION.md for a fresh session start").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
551
579
|
What it does:
|
|
552
580
|
Resets SESSION.md to a blank template \u2014 wipes current task, in-progress items,
|
|
@@ -561,8 +589,8 @@ Examples:
|
|
|
561
589
|
mindlink clear --yes
|
|
562
590
|
`).action(async (_opts) => {
|
|
563
591
|
const projectPath = resolve4(process.cwd());
|
|
564
|
-
const brainDir =
|
|
565
|
-
if (!
|
|
592
|
+
const brainDir = join6(projectPath, BRAIN_DIR);
|
|
593
|
+
if (!existsSync5(brainDir)) {
|
|
566
594
|
console.log("");
|
|
567
595
|
console.log(` ${chalk5.red("\u2717")} No .brain/ found in this directory.`);
|
|
568
596
|
console.log(` Run ${chalk5.cyan("mindlink init")} to get started.`);
|
|
@@ -571,9 +599,9 @@ Examples:
|
|
|
571
599
|
}
|
|
572
600
|
console.log("");
|
|
573
601
|
try {
|
|
574
|
-
const templatePath =
|
|
575
|
-
const destPath =
|
|
576
|
-
|
|
602
|
+
const templatePath = join6(BRAIN_TEMPLATES_DIR, "SESSION.md");
|
|
603
|
+
const destPath = join6(brainDir, "SESSION.md");
|
|
604
|
+
writeFileSync3(destPath, readFileSync5(templatePath, "utf8"));
|
|
577
605
|
} catch (err) {
|
|
578
606
|
console.log(` ${chalk5.red("\u2717")} ${err instanceof Error ? err.message : String(err)}`);
|
|
579
607
|
console.log("");
|
|
@@ -588,8 +616,8 @@ Examples:
|
|
|
588
616
|
import { Command as Command5 } from "commander";
|
|
589
617
|
import { confirm, isCancel as isCancel2, cancel as cancel2 } from "@clack/prompts";
|
|
590
618
|
import chalk6 from "chalk";
|
|
591
|
-
import { existsSync as
|
|
592
|
-
import { join as
|
|
619
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
620
|
+
import { join as join7, resolve as resolve5 } from "path";
|
|
593
621
|
var BRAIN_FILES2 = ["MEMORY.md", "SESSION.md", "SHARED.md", "LOG.md"];
|
|
594
622
|
var resetCommand = new Command5("reset").description("Wipe all .brain/ memory files and start completely fresh").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
595
623
|
What it does:
|
|
@@ -605,8 +633,8 @@ Examples:
|
|
|
605
633
|
mindlink reset --yes
|
|
606
634
|
`).action(async (opts) => {
|
|
607
635
|
const projectPath = resolve5(process.cwd());
|
|
608
|
-
const brainDir =
|
|
609
|
-
if (!
|
|
636
|
+
const brainDir = join7(projectPath, BRAIN_DIR);
|
|
637
|
+
if (!existsSync6(brainDir)) {
|
|
610
638
|
console.log(` ${chalk6.red("\u2717")} No .brain/ found in this directory.`);
|
|
611
639
|
console.log(` Run ${chalk6.cyan("mindlink init")} to get started.`);
|
|
612
640
|
console.log("");
|
|
@@ -633,10 +661,10 @@ Examples:
|
|
|
633
661
|
const errors = [];
|
|
634
662
|
for (const file of BRAIN_FILES2) {
|
|
635
663
|
try {
|
|
636
|
-
const templatePath =
|
|
637
|
-
const destPath =
|
|
638
|
-
if (
|
|
639
|
-
|
|
664
|
+
const templatePath = join7(BRAIN_TEMPLATES_DIR, file);
|
|
665
|
+
const destPath = join7(brainDir, file);
|
|
666
|
+
if (existsSync6(templatePath)) {
|
|
667
|
+
writeFileSync4(destPath, readFileSync6(templatePath, "utf8"));
|
|
640
668
|
}
|
|
641
669
|
} catch (err) {
|
|
642
670
|
errors.push(`${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -657,59 +685,59 @@ import { Command as Command6 } from "commander";
|
|
|
657
685
|
import { select as select2, multiselect as multiselect2, text, isCancel as isCancel3 } from "@clack/prompts";
|
|
658
686
|
import chalk7 from "chalk";
|
|
659
687
|
import {
|
|
660
|
-
existsSync as
|
|
661
|
-
readFileSync as
|
|
662
|
-
writeFileSync as
|
|
688
|
+
existsSync as existsSync7,
|
|
689
|
+
readFileSync as readFileSync7,
|
|
690
|
+
writeFileSync as writeFileSync5,
|
|
663
691
|
appendFileSync as appendFileSync2,
|
|
664
|
-
mkdirSync as
|
|
692
|
+
mkdirSync as mkdirSync3,
|
|
665
693
|
unlinkSync
|
|
666
694
|
} from "fs";
|
|
667
|
-
import { join as
|
|
695
|
+
import { join as join8, resolve as resolve6, dirname as dirname3 } from "path";
|
|
668
696
|
function readConfig(brainDir) {
|
|
669
|
-
return JSON.parse(
|
|
697
|
+
return JSON.parse(readFileSync7(join8(brainDir, "config.json"), "utf8"));
|
|
670
698
|
}
|
|
671
699
|
function saveConfig(brainDir, config) {
|
|
672
|
-
|
|
700
|
+
writeFileSync5(join8(brainDir, "config.json"), JSON.stringify(config, null, 2));
|
|
673
701
|
}
|
|
674
702
|
function enableGitTracking(projectPath) {
|
|
675
|
-
const gitignorePath =
|
|
676
|
-
if (!
|
|
677
|
-
const content =
|
|
703
|
+
const gitignorePath = join8(projectPath, ".gitignore");
|
|
704
|
+
if (!existsSync7(gitignorePath)) return;
|
|
705
|
+
const content = readFileSync7(gitignorePath, "utf8");
|
|
678
706
|
const cleaned = content.replace(/\n# MindLink memory[^\n]*\n\.brain\/\n?/g, "").replace(/\n?\.brain\/\n?/g, "\n");
|
|
679
|
-
|
|
707
|
+
writeFileSync5(gitignorePath, cleaned.trimEnd() + "\n");
|
|
680
708
|
}
|
|
681
709
|
function disableGitTracking(projectPath) {
|
|
682
|
-
const gitignorePath =
|
|
710
|
+
const gitignorePath = join8(projectPath, ".gitignore");
|
|
683
711
|
const entry = "\n# MindLink memory (personal \u2014 not shared with team)\n.brain/\n";
|
|
684
|
-
if (
|
|
685
|
-
const content =
|
|
712
|
+
if (existsSync7(gitignorePath)) {
|
|
713
|
+
const content = readFileSync7(gitignorePath, "utf8");
|
|
686
714
|
if (!content.includes(".brain/")) appendFileSync2(gitignorePath, entry);
|
|
687
715
|
} else {
|
|
688
|
-
|
|
716
|
+
writeFileSync5(gitignorePath, entry.trim() + "\n");
|
|
689
717
|
}
|
|
690
718
|
}
|
|
691
719
|
function addAgentFile(projectPath, agentValue) {
|
|
692
720
|
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
693
721
|
if (!agent) return null;
|
|
694
|
-
const destPath =
|
|
695
|
-
if (
|
|
696
|
-
|
|
697
|
-
|
|
722
|
+
const destPath = join8(projectPath, agent.destFile);
|
|
723
|
+
if (existsSync7(destPath)) return null;
|
|
724
|
+
mkdirSync3(dirname3(destPath), { recursive: true });
|
|
725
|
+
writeFileSync5(destPath, readFileSync7(join8(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
|
|
698
726
|
return agent.destFile;
|
|
699
727
|
}
|
|
700
728
|
function removeAgentFile(projectPath, agentValue) {
|
|
701
729
|
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
702
730
|
if (!agent) return null;
|
|
703
|
-
const destPath =
|
|
704
|
-
if (!
|
|
731
|
+
const destPath = join8(projectPath, agent.destFile);
|
|
732
|
+
if (!existsSync7(destPath)) return null;
|
|
705
733
|
unlinkSync(destPath);
|
|
706
734
|
return agent.destFile;
|
|
707
735
|
}
|
|
708
736
|
function addClaudeHook(projectPath) {
|
|
709
|
-
const hookDest =
|
|
710
|
-
if (
|
|
711
|
-
|
|
712
|
-
|
|
737
|
+
const hookDest = join8(projectPath, ".claude", "settings.json");
|
|
738
|
+
if (existsSync7(hookDest)) return false;
|
|
739
|
+
mkdirSync3(dirname3(hookDest), { recursive: true });
|
|
740
|
+
writeFileSync5(hookDest, readFileSync7(join8(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
|
|
713
741
|
return true;
|
|
714
742
|
}
|
|
715
743
|
var configCommand = new Command6("config").description("Change settings for the current project").addHelpText("after", `
|
|
@@ -717,8 +745,8 @@ Examples:
|
|
|
717
745
|
mindlink config
|
|
718
746
|
`).action(async () => {
|
|
719
747
|
const projectPath = resolve6(process.cwd());
|
|
720
|
-
const brainDir =
|
|
721
|
-
if (!
|
|
748
|
+
const brainDir = join8(projectPath, BRAIN_DIR);
|
|
749
|
+
if (!existsSync7(brainDir)) {
|
|
722
750
|
console.log(` ${chalk7.red("\u2717")} No .brain/ found in this directory.`);
|
|
723
751
|
console.log(` Run ${chalk7.cyan("mindlink init")} to get started.`);
|
|
724
752
|
console.log("");
|
|
@@ -871,8 +899,8 @@ Examples:
|
|
|
871
899
|
// src/commands/sync.ts
|
|
872
900
|
import { Command as Command7 } from "commander";
|
|
873
901
|
import chalk8 from "chalk";
|
|
874
|
-
import { existsSync as
|
|
875
|
-
import { join as
|
|
902
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
|
|
903
|
+
import { join as join9, resolve as resolve7, basename as basename2 } from "path";
|
|
876
904
|
function timestamp() {
|
|
877
905
|
const d = /* @__PURE__ */ new Date();
|
|
878
906
|
const month = d.toLocaleString("default", { month: "short" });
|
|
@@ -881,10 +909,10 @@ function timestamp() {
|
|
|
881
909
|
return `${month} ${day} ${time}`;
|
|
882
910
|
}
|
|
883
911
|
function describeFile(filePath) {
|
|
884
|
-
if (!
|
|
912
|
+
if (!existsSync8(filePath)) return chalk8.dim("(missing)");
|
|
885
913
|
const stat = statSync2(filePath);
|
|
886
914
|
const kb = (stat.size / 1024).toFixed(1);
|
|
887
|
-
const content =
|
|
915
|
+
const content = readFileSync8(filePath, "utf8");
|
|
888
916
|
const entries = (content.match(/^## /gm) ?? []).length;
|
|
889
917
|
const name = basename2(filePath);
|
|
890
918
|
if (name === "LOG.md") return entries > 0 ? `${entries} session${entries !== 1 ? "s" : ""}` : chalk8.dim("empty");
|
|
@@ -904,17 +932,17 @@ Examples:
|
|
|
904
932
|
mindlink sync --once
|
|
905
933
|
`).action(async (opts) => {
|
|
906
934
|
const projectPath = resolve7(process.cwd());
|
|
907
|
-
const brainDir =
|
|
908
|
-
if (!
|
|
935
|
+
const brainDir = join9(projectPath, BRAIN_DIR);
|
|
936
|
+
if (!existsSync8(brainDir)) {
|
|
909
937
|
console.log(` ${chalk8.red("\u2717")} No .brain/ found in this directory.`);
|
|
910
938
|
console.log(` Run ${chalk8.cyan("mindlink init")} to get started.`);
|
|
911
939
|
console.log("");
|
|
912
940
|
process.exit(1);
|
|
913
941
|
}
|
|
914
|
-
const sharedPath =
|
|
915
|
-
const sessionPath =
|
|
916
|
-
const logPath =
|
|
917
|
-
const memoryPath =
|
|
942
|
+
const sharedPath = join9(brainDir, "SHARED.md");
|
|
943
|
+
const sessionPath = join9(brainDir, "SESSION.md");
|
|
944
|
+
const logPath = join9(brainDir, "LOG.md");
|
|
945
|
+
const memoryPath = join9(brainDir, "MEMORY.md");
|
|
918
946
|
if (opts.once) {
|
|
919
947
|
console.log("");
|
|
920
948
|
console.log(` ${chalk8.dim("Checking shared context...")}`);
|
|
@@ -927,7 +955,7 @@ Examples:
|
|
|
927
955
|
];
|
|
928
956
|
for (const { label, path } of files) {
|
|
929
957
|
const desc = describeFile(path);
|
|
930
|
-
const mtime =
|
|
958
|
+
const mtime = existsSync8(path) ? statSync2(path).mtime : null;
|
|
931
959
|
const age = mtime ? (() => {
|
|
932
960
|
const diff = Date.now() - mtime.getTime();
|
|
933
961
|
const min = Math.floor(diff / 6e4);
|
|
@@ -983,14 +1011,14 @@ import chalk9 from "chalk";
|
|
|
983
1011
|
import { execSync } from "child_process";
|
|
984
1012
|
import { createRequire } from "module";
|
|
985
1013
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
986
|
-
import { dirname as dirname4, join as
|
|
987
|
-
import { existsSync as
|
|
1014
|
+
import { dirname as dirname4, join as join10 } from "path";
|
|
1015
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
988
1016
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
989
1017
|
var __dirname2 = dirname4(__filename2);
|
|
990
1018
|
var require2 = createRequire(import.meta.url);
|
|
991
1019
|
function currentVersion() {
|
|
992
1020
|
try {
|
|
993
|
-
const pkg = require2(
|
|
1021
|
+
const pkg = require2(join10(__dirname2, "..", "..", "package.json"));
|
|
994
1022
|
return pkg.version;
|
|
995
1023
|
} catch {
|
|
996
1024
|
return "0.0.0";
|
|
@@ -1109,50 +1137,54 @@ Examples:
|
|
|
1109
1137
|
}
|
|
1110
1138
|
process.exit(1);
|
|
1111
1139
|
}
|
|
1112
|
-
const
|
|
1113
|
-
|
|
1114
|
-
if (existsSync8(configPath)) {
|
|
1140
|
+
const projects = getRegisteredProjects().filter((p) => existsSync9(join10(p, BRAIN_DIR, "config.json")));
|
|
1141
|
+
if (projects.length > 0) {
|
|
1115
1142
|
console.log("");
|
|
1116
1143
|
const refresh = await select3({
|
|
1117
|
-
message:
|
|
1144
|
+
message: `Refresh agent instruction files in ${projects.length} project${projects.length > 1 ? "s" : ""}?`,
|
|
1118
1145
|
options: [
|
|
1119
|
-
{ value: "yes", label: "Yes \u2014 update CLAUDE.md, CURSOR.md, etc.", hint: "safe, no memory data is touched" },
|
|
1146
|
+
{ value: "yes", label: "Yes \u2014 update CLAUDE.md, CURSOR.md, etc. in all projects", hint: "safe, no memory data is touched" },
|
|
1120
1147
|
{ value: "no", label: "No \u2014 keep existing files" }
|
|
1121
1148
|
]
|
|
1122
1149
|
});
|
|
1123
1150
|
if (!isCancel4(refresh) && refresh === "yes") {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
const agentValues = config.agents ?? AGENTS.filter((a) => a.selected).map((a) => a.value);
|
|
1130
|
-
const refreshed = [];
|
|
1131
|
-
for (const agentValue of agentValues) {
|
|
1132
|
-
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
1133
|
-
if (!agent) continue;
|
|
1134
|
-
const destPath = join9(projectPath, agent.destFile);
|
|
1151
|
+
console.log("");
|
|
1152
|
+
for (const projectPath of projects) {
|
|
1153
|
+
const configPath = join10(projectPath, BRAIN_DIR, "config.json");
|
|
1154
|
+
let config = {};
|
|
1135
1155
|
try {
|
|
1136
|
-
|
|
1137
|
-
writeFileSync5(destPath, readFileSync8(join9(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
|
|
1138
|
-
refreshed.push(agent.destFile);
|
|
1156
|
+
config = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
1139
1157
|
} catch {
|
|
1140
1158
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1159
|
+
const agentValues = config.agents ?? AGENTS.filter((a) => a.selected).map((a) => a.value);
|
|
1160
|
+
const refreshed = [];
|
|
1161
|
+
for (const agentValue of agentValues) {
|
|
1162
|
+
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
1163
|
+
if (!agent) continue;
|
|
1164
|
+
const destPath = join10(projectPath, agent.destFile);
|
|
1165
|
+
try {
|
|
1166
|
+
mkdirSync4(dirname4(destPath), { recursive: true });
|
|
1167
|
+
writeFileSync6(destPath, readFileSync9(join10(AGENT_TEMPLATES_DIR, agent.templateFile), "utf8"));
|
|
1168
|
+
refreshed.push(agent.destFile);
|
|
1169
|
+
} catch {
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (agentValues.includes("claude")) {
|
|
1173
|
+
const hookDest = join10(projectPath, ".claude", "settings.json");
|
|
1174
|
+
try {
|
|
1175
|
+
mkdirSync4(join10(projectPath, ".claude"), { recursive: true });
|
|
1176
|
+
writeFileSync6(hookDest, readFileSync9(join10(HOOKS_TEMPLATES_DIR, "claude-settings.json"), "utf8"));
|
|
1177
|
+
refreshed.push(".claude/settings.json");
|
|
1178
|
+
} catch {
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
console.log(` ${chalk9.bold(projectPath)}`);
|
|
1182
|
+
for (const f of refreshed) {
|
|
1183
|
+
console.log(` ${chalk9.green("\u2713")} ${f}`);
|
|
1149
1184
|
}
|
|
1150
1185
|
}
|
|
1151
1186
|
console.log("");
|
|
1152
|
-
|
|
1153
|
-
console.log(` ${chalk9.green("\u2713")} ${f}`);
|
|
1154
|
-
}
|
|
1155
|
-
console.log(` ${chalk9.dim("Agent files are up to date.")}`);
|
|
1187
|
+
console.log(` ${chalk9.dim("All agent files are up to date.")}`);
|
|
1156
1188
|
}
|
|
1157
1189
|
}
|
|
1158
1190
|
console.log("");
|
|
@@ -1161,8 +1193,8 @@ Examples:
|
|
|
1161
1193
|
// src/commands/summary.ts
|
|
1162
1194
|
import { Command as Command9 } from "commander";
|
|
1163
1195
|
import chalk10 from "chalk";
|
|
1164
|
-
import { existsSync as
|
|
1165
|
-
import { join as
|
|
1196
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
1197
|
+
import { join as join11, resolve as resolve8 } from "path";
|
|
1166
1198
|
var summaryCommand = new Command9("summary").description("Print a full briefing of what your AI knows \u2014 great for sharing or reviewing context").option("--json", "Output as JSON").addHelpText("after", `
|
|
1167
1199
|
Examples:
|
|
1168
1200
|
mindlink summary
|
|
@@ -1172,21 +1204,21 @@ Tip: your AI agent can run this itself \u2014 ask it to run "mindlink summary"
|
|
|
1172
1204
|
to get a full briefing on the current project state.
|
|
1173
1205
|
`).action((opts) => {
|
|
1174
1206
|
const projectPath = resolve8(process.cwd());
|
|
1175
|
-
const brainDir =
|
|
1176
|
-
if (!
|
|
1207
|
+
const brainDir = join11(projectPath, BRAIN_DIR);
|
|
1208
|
+
if (!existsSync10(brainDir)) {
|
|
1177
1209
|
console.log(` ${chalk10.red("\u2717")} No .brain/ found in this directory.`);
|
|
1178
1210
|
console.log(` Run ${chalk10.cyan("mindlink init")} to get started.`);
|
|
1179
1211
|
console.log("");
|
|
1180
1212
|
process.exit(1);
|
|
1181
1213
|
}
|
|
1182
|
-
const memoryPath =
|
|
1183
|
-
const sessionPath =
|
|
1184
|
-
const logPath =
|
|
1185
|
-
const sharedPath =
|
|
1186
|
-
const memoryMd =
|
|
1187
|
-
const sessionMd =
|
|
1188
|
-
const logMd =
|
|
1189
|
-
const sharedMd =
|
|
1214
|
+
const memoryPath = join11(brainDir, "MEMORY.md");
|
|
1215
|
+
const sessionPath = join11(brainDir, "SESSION.md");
|
|
1216
|
+
const logPath = join11(brainDir, "LOG.md");
|
|
1217
|
+
const sharedPath = join11(brainDir, "SHARED.md");
|
|
1218
|
+
const memoryMd = existsSync10(memoryPath) ? readFileSync10(memoryPath, "utf8") : "";
|
|
1219
|
+
const sessionMd = existsSync10(sessionPath) ? readFileSync10(sessionPath, "utf8") : "";
|
|
1220
|
+
const logMd = existsSync10(logPath) ? readFileSync10(logPath, "utf8") : "";
|
|
1221
|
+
const sharedMd = existsSync10(sharedPath) ? readFileSync10(sharedPath, "utf8") : "";
|
|
1190
1222
|
const projectOverview = extractSection(memoryMd, "Project Overview") || extractSection(memoryMd, "Project Identity") || extractSection(memoryMd, "What Is This Project");
|
|
1191
1223
|
const techStack = extractSection(memoryMd, "Tech Stack") || extractSection(memoryMd, "Stack");
|
|
1192
1224
|
const decisions = extractBullets(
|
|
@@ -1277,8 +1309,8 @@ to get a full briefing on the current project state.
|
|
|
1277
1309
|
import { Command as Command10 } from "commander";
|
|
1278
1310
|
import { select as select4, isCancel as isCancel5, cancel as cancel5 } from "@clack/prompts";
|
|
1279
1311
|
import chalk11 from "chalk";
|
|
1280
|
-
import { existsSync as
|
|
1281
|
-
import { join as
|
|
1312
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1313
|
+
import { join as join12, resolve as resolve9 } from "path";
|
|
1282
1314
|
var uninstallCommand = new Command10("uninstall").description("Remove MindLink from the current project").option("-y, --yes", "Skip confirmation and remove all project files").addHelpText("after", `
|
|
1283
1315
|
What gets removed:
|
|
1284
1316
|
- .brain/ folder (all memory files)
|
|
@@ -1298,8 +1330,8 @@ Examples:
|
|
|
1298
1330
|
mindlink uninstall --yes
|
|
1299
1331
|
`).action(async (opts) => {
|
|
1300
1332
|
const projectPath = resolve9(process.cwd());
|
|
1301
|
-
const brainDir =
|
|
1302
|
-
if (!
|
|
1333
|
+
const brainDir = join12(projectPath, BRAIN_DIR);
|
|
1334
|
+
if (!existsSync11(brainDir)) {
|
|
1303
1335
|
console.log(` ${chalk11.red("\u2717")} No .brain/ found in this directory.`);
|
|
1304
1336
|
console.log(` Nothing to uninstall here.`);
|
|
1305
1337
|
console.log("");
|
|
@@ -1307,7 +1339,7 @@ Examples:
|
|
|
1307
1339
|
}
|
|
1308
1340
|
let agents = [];
|
|
1309
1341
|
try {
|
|
1310
|
-
const cfg = JSON.parse(
|
|
1342
|
+
const cfg = JSON.parse(readFileSync11(join12(brainDir, "config.json"), "utf8"));
|
|
1311
1343
|
agents = cfg.agents ?? [];
|
|
1312
1344
|
} catch {
|
|
1313
1345
|
agents = AGENTS.map((a) => a.value);
|
|
@@ -1356,8 +1388,8 @@ Examples:
|
|
|
1356
1388
|
for (const v of agents) {
|
|
1357
1389
|
const agent = AGENTS.find((a) => a.value === v);
|
|
1358
1390
|
if (!agent) continue;
|
|
1359
|
-
const destPath =
|
|
1360
|
-
if (
|
|
1391
|
+
const destPath = join12(projectPath, agent.destFile);
|
|
1392
|
+
if (existsSync11(destPath)) {
|
|
1361
1393
|
try {
|
|
1362
1394
|
unlinkSync2(destPath);
|
|
1363
1395
|
removed.push(agent.destFile);
|
|
@@ -1367,8 +1399,8 @@ Examples:
|
|
|
1367
1399
|
}
|
|
1368
1400
|
}
|
|
1369
1401
|
if (agents.includes("claude")) {
|
|
1370
|
-
const hookPath =
|
|
1371
|
-
if (
|
|
1402
|
+
const hookPath = join12(projectPath, ".claude", "settings.json");
|
|
1403
|
+
if (existsSync11(hookPath)) {
|
|
1372
1404
|
try {
|
|
1373
1405
|
unlinkSync2(hookPath);
|
|
1374
1406
|
removed.push(".claude/settings.json");
|
|
@@ -1388,8 +1420,8 @@ Examples:
|
|
|
1388
1420
|
import { Command as Command11 } from "commander";
|
|
1389
1421
|
import { text as text2, isCancel as isCancel6, cancel as cancel6 } from "@clack/prompts";
|
|
1390
1422
|
import chalk12 from "chalk";
|
|
1391
|
-
import { existsSync as
|
|
1392
|
-
import { join as
|
|
1423
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2 } from "fs";
|
|
1424
|
+
import { join as join13, resolve as resolve10, basename as basename3 } from "path";
|
|
1393
1425
|
import AdmZip from "adm-zip";
|
|
1394
1426
|
var exportCommand = new Command11("export").description("Export .brain/ memory to a shareable zip file").option("--output <path>", "Directory or full path to save the zip file").option("--include-agents", "Also include agent instruction files (CLAUDE.md, CURSOR.md, etc.)").addHelpText("after", `
|
|
1395
1427
|
What gets exported:
|
|
@@ -1411,8 +1443,8 @@ Examples:
|
|
|
1411
1443
|
mindlink export --include-agents
|
|
1412
1444
|
`).action(async (opts) => {
|
|
1413
1445
|
const projectPath = resolve10(process.cwd());
|
|
1414
|
-
const brainDir =
|
|
1415
|
-
if (!
|
|
1446
|
+
const brainDir = join13(projectPath, BRAIN_DIR);
|
|
1447
|
+
if (!existsSync12(brainDir)) {
|
|
1416
1448
|
console.log("");
|
|
1417
1449
|
console.log(` ${chalk12.red("\u2717")} No .brain/ found in this directory.`);
|
|
1418
1450
|
console.log(` Run ${chalk12.cyan("mindlink init")} to get started.`);
|
|
@@ -1426,8 +1458,8 @@ Examples:
|
|
|
1426
1458
|
let outputPath;
|
|
1427
1459
|
if (opts.output) {
|
|
1428
1460
|
const given = resolve10(opts.output);
|
|
1429
|
-
if (
|
|
1430
|
-
outputPath =
|
|
1461
|
+
if (existsSync12(given) && !given.endsWith(".zip")) {
|
|
1462
|
+
outputPath = join13(given, defaultFilename);
|
|
1431
1463
|
} else {
|
|
1432
1464
|
outputPath = given;
|
|
1433
1465
|
}
|
|
@@ -1444,20 +1476,20 @@ Examples:
|
|
|
1444
1476
|
}
|
|
1445
1477
|
const dest = resolve10(answer);
|
|
1446
1478
|
if (!dest.endsWith(".zip")) {
|
|
1447
|
-
outputPath =
|
|
1479
|
+
outputPath = join13(dest, defaultFilename);
|
|
1448
1480
|
} else {
|
|
1449
1481
|
outputPath = dest;
|
|
1450
1482
|
}
|
|
1451
1483
|
} else {
|
|
1452
|
-
outputPath =
|
|
1484
|
+
outputPath = join13(projectPath, defaultFilename);
|
|
1453
1485
|
}
|
|
1454
1486
|
const zip = new AdmZip();
|
|
1455
1487
|
const brainFiles = ["MEMORY.md", "SESSION.md", "SHARED.md", "LOG.md"];
|
|
1456
1488
|
const included = [];
|
|
1457
1489
|
const skipped = [];
|
|
1458
1490
|
for (const file of brainFiles) {
|
|
1459
|
-
const filePath =
|
|
1460
|
-
if (
|
|
1491
|
+
const filePath = join13(brainDir, file);
|
|
1492
|
+
if (existsSync12(filePath)) {
|
|
1461
1493
|
zip.addLocalFile(filePath, ".brain");
|
|
1462
1494
|
included.push(`.brain/${file}`);
|
|
1463
1495
|
} else {
|
|
@@ -1466,13 +1498,13 @@ Examples:
|
|
|
1466
1498
|
}
|
|
1467
1499
|
const archiveFiles = readdirSync2(brainDir).filter((f) => /^LOG-\d{4}-\d{2}-\d{2}\.md$/.test(f));
|
|
1468
1500
|
for (const file of archiveFiles) {
|
|
1469
|
-
zip.addLocalFile(
|
|
1501
|
+
zip.addLocalFile(join13(brainDir, file), ".brain");
|
|
1470
1502
|
included.push(`.brain/${file}`);
|
|
1471
1503
|
}
|
|
1472
1504
|
if (opts.includeAgents) {
|
|
1473
1505
|
for (const agent of AGENTS) {
|
|
1474
|
-
const agentPath =
|
|
1475
|
-
if (
|
|
1506
|
+
const agentPath = join13(projectPath, agent.destFile);
|
|
1507
|
+
if (existsSync12(agentPath)) {
|
|
1476
1508
|
const dir = agent.destFile.includes("/") ? agent.destFile.split("/").slice(0, -1).join("/") : "";
|
|
1477
1509
|
zip.addLocalFile(agentPath, dir);
|
|
1478
1510
|
included.push(agent.destFile);
|
|
@@ -1502,8 +1534,8 @@ Examples:
|
|
|
1502
1534
|
import { Command as Command12 } from "commander";
|
|
1503
1535
|
import { select as select5, isCancel as isCancel7, cancel as cancel7 } from "@clack/prompts";
|
|
1504
1536
|
import chalk13 from "chalk";
|
|
1505
|
-
import { existsSync as
|
|
1506
|
-
import { join as
|
|
1537
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
|
|
1538
|
+
import { join as join14, resolve as resolve11, extname } from "path";
|
|
1507
1539
|
import AdmZip2 from "adm-zip";
|
|
1508
1540
|
var importCommand = new Command12("import").description("Import a MindLink memory zip into the current project").argument("<file>", "Path to the .zip file exported by mindlink export").option("-y, --yes", "Skip confirmation and overwrite existing memory").addHelpText("after", `
|
|
1509
1541
|
What gets imported:
|
|
@@ -1526,10 +1558,10 @@ Examples:
|
|
|
1526
1558
|
mindlink import ~/Desktop/my-app-brain-2026-04-10.zip --yes
|
|
1527
1559
|
`).action(async (file, opts) => {
|
|
1528
1560
|
const projectPath = resolve11(process.cwd());
|
|
1529
|
-
const brainDir =
|
|
1561
|
+
const brainDir = join14(projectPath, BRAIN_DIR);
|
|
1530
1562
|
const zipPath = resolve11(file);
|
|
1531
1563
|
console.log("");
|
|
1532
|
-
if (!
|
|
1564
|
+
if (!existsSync13(zipPath)) {
|
|
1533
1565
|
console.log(` ${chalk13.red("\u2717")} File not found: ${zipPath}`);
|
|
1534
1566
|
console.log("");
|
|
1535
1567
|
process.exit(1);
|
|
@@ -1556,7 +1588,7 @@ Examples:
|
|
|
1556
1588
|
process.exit(1);
|
|
1557
1589
|
}
|
|
1558
1590
|
let mode = "overwrite";
|
|
1559
|
-
if (
|
|
1591
|
+
if (existsSync13(brainDir)) {
|
|
1560
1592
|
if (opts.yes) {
|
|
1561
1593
|
mode = "overwrite";
|
|
1562
1594
|
} else {
|
|
@@ -1579,18 +1611,18 @@ Examples:
|
|
|
1579
1611
|
console.log("");
|
|
1580
1612
|
}
|
|
1581
1613
|
}
|
|
1582
|
-
|
|
1614
|
+
mkdirSync5(brainDir, { recursive: true });
|
|
1583
1615
|
const written = [];
|
|
1584
1616
|
const skipped = [];
|
|
1585
1617
|
for (const entry of entries) {
|
|
1586
1618
|
if (entry.isDirectory) continue;
|
|
1587
|
-
const destPath =
|
|
1588
|
-
const destDir =
|
|
1589
|
-
if (mode === "merge" &&
|
|
1619
|
+
const destPath = join14(projectPath, entry.entryName);
|
|
1620
|
+
const destDir = join14(projectPath, entry.entryName.split("/").slice(0, -1).join("/"));
|
|
1621
|
+
if (mode === "merge" && existsSync13(destPath)) {
|
|
1590
1622
|
skipped.push(entry.entryName);
|
|
1591
1623
|
continue;
|
|
1592
1624
|
}
|
|
1593
|
-
|
|
1625
|
+
mkdirSync5(destDir, { recursive: true });
|
|
1594
1626
|
zip.extractEntryTo(entry, destDir, false, true);
|
|
1595
1627
|
written.push(entry.entryName);
|
|
1596
1628
|
}
|
|
@@ -1601,7 +1633,7 @@ Examples:
|
|
|
1601
1633
|
console.log(` ${chalk13.dim("Nothing imported \u2014 all files already exist (merge mode).")}`);
|
|
1602
1634
|
} else {
|
|
1603
1635
|
console.log(` ${chalk13.green("\u2713")} Brain transplant complete. Your AI wakes up knowing everything.`);
|
|
1604
|
-
if (!
|
|
1636
|
+
if (!existsSync13(join14(brainDir, "../CLAUDE.md")) && !existsSync13(join14(brainDir, "../CURSOR.md"))) {
|
|
1605
1637
|
console.log(` ${chalk13.dim("No agent instruction files found \u2014 run mindlink init to wire them up.")}`);
|
|
1606
1638
|
}
|
|
1607
1639
|
}
|
|
@@ -1611,8 +1643,8 @@ Examples:
|
|
|
1611
1643
|
// src/commands/doctor.ts
|
|
1612
1644
|
import { Command as Command13 } from "commander";
|
|
1613
1645
|
import chalk14 from "chalk";
|
|
1614
|
-
import { existsSync as
|
|
1615
|
-
import { join as
|
|
1646
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12, statSync as statSync3, readdirSync as readdirSync3 } from "fs";
|
|
1647
|
+
import { join as join15, resolve as resolve12 } from "path";
|
|
1616
1648
|
var CORE_LINE_LIMIT = 50;
|
|
1617
1649
|
var CORE_WARN_THRESHOLD = 40;
|
|
1618
1650
|
function ok(label, detail) {
|
|
@@ -1652,7 +1684,7 @@ Examples:
|
|
|
1652
1684
|
mindlink doctor
|
|
1653
1685
|
`).action(() => {
|
|
1654
1686
|
const projectPath = resolve12(process.cwd());
|
|
1655
|
-
const brainDir =
|
|
1687
|
+
const brainDir = join15(projectPath, BRAIN_DIR);
|
|
1656
1688
|
console.log("");
|
|
1657
1689
|
console.log(` ${chalk14.bold("\u25C9 MindLink Doctor")}`);
|
|
1658
1690
|
console.log(` ${chalk14.dim(projectPath)}`);
|
|
@@ -1660,29 +1692,29 @@ Examples:
|
|
|
1660
1692
|
const checks = [];
|
|
1661
1693
|
let failCount = 0;
|
|
1662
1694
|
let warnCount = 0;
|
|
1663
|
-
if (!
|
|
1695
|
+
if (!existsSync14(brainDir)) {
|
|
1664
1696
|
checks.push(fail(".brain/ missing", `Run ${chalk14.cyan("mindlink init")} to get started.`));
|
|
1665
1697
|
printChecks(checks);
|
|
1666
1698
|
process.exit(1);
|
|
1667
1699
|
}
|
|
1668
1700
|
checks.push(ok(".brain/ found"));
|
|
1669
|
-
const configPath =
|
|
1701
|
+
const configPath = join15(brainDir, "config.json");
|
|
1670
1702
|
let config = {};
|
|
1671
|
-
if (!
|
|
1703
|
+
if (!existsSync14(configPath)) {
|
|
1672
1704
|
checks.push(warn("config.json missing", `Run ${chalk14.cyan("mindlink config")} to repair.`));
|
|
1673
1705
|
} else {
|
|
1674
1706
|
try {
|
|
1675
|
-
config = JSON.parse(
|
|
1707
|
+
config = JSON.parse(readFileSync12(configPath, "utf8"));
|
|
1676
1708
|
checks.push(ok("config.json valid"));
|
|
1677
1709
|
} catch {
|
|
1678
1710
|
checks.push(warn("config.json unreadable", "File may be corrupted \u2014 delete and re-run mindlink init."));
|
|
1679
1711
|
}
|
|
1680
1712
|
}
|
|
1681
|
-
const memoryPath =
|
|
1682
|
-
if (!
|
|
1713
|
+
const memoryPath = join15(brainDir, "MEMORY.md");
|
|
1714
|
+
if (!existsSync14(memoryPath)) {
|
|
1683
1715
|
checks.push(fail("MEMORY.md missing", `Run ${chalk14.cyan("mindlink init")} to recreate it.`));
|
|
1684
1716
|
} else {
|
|
1685
|
-
const memoryMd =
|
|
1717
|
+
const memoryMd = readFileSync12(memoryPath, "utf8");
|
|
1686
1718
|
const coreSection = extractSection(memoryMd, "Core");
|
|
1687
1719
|
const coreLines = coreSection.split("\n").filter((l) => l.trim().length > 0 && !l.startsWith("<!--")).length;
|
|
1688
1720
|
if (coreLines === 0) {
|
|
@@ -1701,11 +1733,11 @@ Examples:
|
|
|
1701
1733
|
checks.push(ok("MEMORY.md Core has content"));
|
|
1702
1734
|
}
|
|
1703
1735
|
}
|
|
1704
|
-
const sessionPath =
|
|
1705
|
-
if (!
|
|
1736
|
+
const sessionPath = join15(brainDir, "SESSION.md");
|
|
1737
|
+
if (!existsSync14(sessionPath)) {
|
|
1706
1738
|
checks.push(fail("SESSION.md missing", `Run ${chalk14.cyan("mindlink init")} to recreate it.`));
|
|
1707
1739
|
} else {
|
|
1708
|
-
const sessionMd =
|
|
1740
|
+
const sessionMd = readFileSync12(sessionPath, "utf8");
|
|
1709
1741
|
const hasContent = sessionMd.split("\n").some((l) => l.trim().length > 0 && !l.startsWith("<!--") && !l.startsWith("#"));
|
|
1710
1742
|
const mtime = statSync3(sessionPath).mtime;
|
|
1711
1743
|
const age = relativeTime(mtime);
|
|
@@ -1715,11 +1747,11 @@ Examples:
|
|
|
1715
1747
|
checks.push(ok(`SESSION.md \u2014 updated ${age}`));
|
|
1716
1748
|
}
|
|
1717
1749
|
}
|
|
1718
|
-
const logPath =
|
|
1719
|
-
if (!
|
|
1750
|
+
const logPath = join15(brainDir, "LOG.md");
|
|
1751
|
+
if (!existsSync14(logPath)) {
|
|
1720
1752
|
checks.push(fail("LOG.md missing", `Run ${chalk14.cyan("mindlink init")} to recreate it.`));
|
|
1721
1753
|
} else {
|
|
1722
|
-
const logMd =
|
|
1754
|
+
const logMd = readFileSync12(logPath, "utf8");
|
|
1723
1755
|
const entries = parseLogEntries(logMd);
|
|
1724
1756
|
const entryCount = entries.length;
|
|
1725
1757
|
const maxEntries = config.maxLogEntries ?? 50;
|
|
@@ -1755,8 +1787,8 @@ Examples:
|
|
|
1755
1787
|
for (const agentValue of configuredAgents) {
|
|
1756
1788
|
const agent = AGENTS.find((a) => a.value === agentValue);
|
|
1757
1789
|
if (!agent) continue;
|
|
1758
|
-
const destPath =
|
|
1759
|
-
if (!
|
|
1790
|
+
const destPath = join15(projectPath, agent.destFile);
|
|
1791
|
+
if (!existsSync14(destPath)) {
|
|
1760
1792
|
checks.push(fail(`${agent.destFile} missing`, `Run ${chalk14.cyan("mindlink config")} \u2192 Agent instruction files to recreate.`));
|
|
1761
1793
|
} else {
|
|
1762
1794
|
checks.push(ok(`${agent.destFile} \u2014 ${agent.label}`));
|
|
@@ -1764,15 +1796,15 @@ Examples:
|
|
|
1764
1796
|
}
|
|
1765
1797
|
}
|
|
1766
1798
|
if (configuredAgents.includes("claude")) {
|
|
1767
|
-
const hookPath =
|
|
1768
|
-
if (!
|
|
1799
|
+
const hookPath = join15(projectPath, ".claude", "settings.json");
|
|
1800
|
+
if (!existsSync14(hookPath)) {
|
|
1769
1801
|
checks.push(warn(
|
|
1770
1802
|
".claude/settings.json missing",
|
|
1771
1803
|
`Claude Code won't auto-reload after context compaction. Run ${chalk14.cyan("mindlink config")} \u2192 Agent instruction files to restore.`
|
|
1772
1804
|
));
|
|
1773
1805
|
} else {
|
|
1774
1806
|
try {
|
|
1775
|
-
const settings = JSON.parse(
|
|
1807
|
+
const settings = JSON.parse(readFileSync12(hookPath, "utf8"));
|
|
1776
1808
|
const hasHook = settings?.hooks?.UserPromptSubmit != null;
|
|
1777
1809
|
if (!hasHook) {
|
|
1778
1810
|
checks.push(warn(".claude/settings.json exists but MindLink hook not found", "Hook may have been removed \u2014 check the file manually."));
|