@memories.sh/cli 0.1.0 → 0.2.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/index.js +653 -359
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command22 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import
|
|
8
|
+
import chalk2 from "chalk";
|
|
9
9
|
|
|
10
10
|
// src/lib/db.ts
|
|
11
11
|
import { createClient } from "@libsql/client";
|
|
@@ -265,8 +265,8 @@ async function searchMemories(query, opts) {
|
|
|
265
265
|
args: [ftsQuery, ...args]
|
|
266
266
|
});
|
|
267
267
|
return result.rows;
|
|
268
|
-
} catch (
|
|
269
|
-
console.error("FTS search failed, falling back to LIKE:",
|
|
268
|
+
} catch (error2) {
|
|
269
|
+
console.error("FTS search failed, falling back to LIKE:", error2);
|
|
270
270
|
return searchMemoriesLike(query, opts);
|
|
271
271
|
}
|
|
272
272
|
}
|
|
@@ -462,55 +462,181 @@ async function bulkForgetByIds(ids) {
|
|
|
462
462
|
return ids.length;
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
// src/lib/auth.ts
|
|
466
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
467
|
+
import { existsSync as existsSync2 } from "fs";
|
|
468
|
+
import { join as join2 } from "path";
|
|
469
|
+
import { homedir as homedir2 } from "os";
|
|
470
|
+
var AUTH_DIR = join2(homedir2(), ".config", "memories");
|
|
471
|
+
var AUTH_FILE = join2(AUTH_DIR, "auth.json");
|
|
472
|
+
async function readAuth() {
|
|
473
|
+
if (!existsSync2(AUTH_FILE)) return null;
|
|
474
|
+
try {
|
|
475
|
+
const raw = await readFile2(AUTH_FILE, "utf-8");
|
|
476
|
+
return JSON.parse(raw);
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async function saveAuth(data) {
|
|
482
|
+
await mkdir2(AUTH_DIR, { recursive: true });
|
|
483
|
+
await writeFile2(AUTH_FILE, JSON.stringify(data, null, 2), {
|
|
484
|
+
encoding: "utf-8",
|
|
485
|
+
mode: 384
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
async function clearAuth() {
|
|
489
|
+
if (existsSync2(AUTH_FILE)) {
|
|
490
|
+
await unlink(AUTH_FILE);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function getApiClient(auth) {
|
|
494
|
+
return async function apiFetch(path, opts) {
|
|
495
|
+
const url = `${auth.apiUrl}${path}`;
|
|
496
|
+
return fetch(url, {
|
|
497
|
+
...opts,
|
|
498
|
+
headers: {
|
|
499
|
+
...opts?.headers,
|
|
500
|
+
Authorization: `Bearer ${auth.token}`,
|
|
501
|
+
"Content-Type": "application/json"
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/lib/ui.ts
|
|
508
|
+
import chalk from "chalk";
|
|
509
|
+
import figlet from "figlet";
|
|
510
|
+
import gradient from "gradient-string";
|
|
511
|
+
import boxen from "boxen";
|
|
512
|
+
var memoriesGradient = gradient(["#a855f7", "#6366f1"]);
|
|
513
|
+
function banner() {
|
|
514
|
+
const text = figlet.textSync("memories", {
|
|
515
|
+
font: "Small",
|
|
516
|
+
horizontalLayout: "fitted"
|
|
517
|
+
});
|
|
518
|
+
console.log(memoriesGradient(text));
|
|
519
|
+
console.log(chalk.dim(" One memory, every AI tool\n"));
|
|
520
|
+
}
|
|
521
|
+
function success(message) {
|
|
522
|
+
console.log(chalk.green("\u2713") + " " + message);
|
|
523
|
+
}
|
|
524
|
+
function warn(message) {
|
|
525
|
+
console.log(chalk.yellow("\u26A0") + " " + message);
|
|
526
|
+
}
|
|
527
|
+
function error(message) {
|
|
528
|
+
console.log(chalk.red("\u2717") + " " + message);
|
|
529
|
+
}
|
|
530
|
+
function info(message) {
|
|
531
|
+
console.log(chalk.blue("\u2139") + " " + message);
|
|
532
|
+
}
|
|
533
|
+
function step(num, total, message) {
|
|
534
|
+
console.log(chalk.dim(`[${num}/${total}]`) + " " + message);
|
|
535
|
+
}
|
|
536
|
+
function dim(message) {
|
|
537
|
+
console.log(chalk.dim(" " + message));
|
|
538
|
+
}
|
|
539
|
+
function box(content, title) {
|
|
540
|
+
console.log(
|
|
541
|
+
boxen(content, {
|
|
542
|
+
padding: 1,
|
|
543
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
544
|
+
borderStyle: "round",
|
|
545
|
+
borderColor: "gray",
|
|
546
|
+
title,
|
|
547
|
+
titleAlignment: "left"
|
|
548
|
+
})
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
function nextSteps(steps) {
|
|
552
|
+
const content = steps.map((s) => chalk.dim("\u2192 ") + s).join("\n");
|
|
553
|
+
box(content, chalk.bold("Next steps"));
|
|
554
|
+
}
|
|
555
|
+
function proFeature(feature) {
|
|
556
|
+
console.log(
|
|
557
|
+
chalk.yellow("\u2B50") + " " + chalk.dim(`${feature} requires `) + chalk.bold("Pro") + chalk.dim(". Run ") + chalk.cyan("memories login") + chalk.dim(" to upgrade.")
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
465
561
|
// src/commands/init.ts
|
|
466
562
|
var initCommand = new Command("init").description("Initialize memories for the current project or globally").option("-g, --global", "Initialize global memories (user-wide)").option("-r, --rule <rule>", "Add an initial rule", (val, acc) => [...acc, val], []).action(async (opts) => {
|
|
467
563
|
try {
|
|
564
|
+
banner();
|
|
565
|
+
step(1, 3, "Initializing database...");
|
|
468
566
|
await getDb();
|
|
469
567
|
const configDir = getConfigDir();
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
568
|
+
dim(`Database: ${configDir}/local.db`);
|
|
569
|
+
step(2, 3, "Detecting scope...");
|
|
570
|
+
let useGlobal = opts.global;
|
|
571
|
+
if (!useGlobal) {
|
|
474
572
|
const projectId = getProjectId();
|
|
475
573
|
const gitRoot = getGitRoot();
|
|
476
574
|
if (!projectId) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
575
|
+
warn("Not in a git repository - using global scope");
|
|
576
|
+
dim("Global memories apply to all projects");
|
|
577
|
+
useGlobal = true;
|
|
578
|
+
} else {
|
|
579
|
+
success("Using project scope");
|
|
580
|
+
dim(`Project: ${projectId}`);
|
|
581
|
+
dim(`Root: ${gitRoot}`);
|
|
480
582
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
583
|
+
}
|
|
584
|
+
if (useGlobal) {
|
|
585
|
+
success("Using global scope (applies to all projects)");
|
|
586
|
+
}
|
|
587
|
+
step(3, 3, "Checking account...");
|
|
588
|
+
const auth = await readAuth();
|
|
589
|
+
if (auth) {
|
|
590
|
+
success(`Logged in as ${chalk2.bold(auth.email)}`);
|
|
591
|
+
} else {
|
|
592
|
+
dim("Not logged in (local-only mode)");
|
|
593
|
+
dim("Run " + chalk2.cyan("memories login") + " for cloud sync");
|
|
485
594
|
}
|
|
486
595
|
if (opts.rule?.length) {
|
|
487
596
|
console.log("");
|
|
488
|
-
|
|
597
|
+
info("Adding initial rules...");
|
|
489
598
|
for (const rule of opts.rule) {
|
|
490
599
|
const memory = await addMemory(rule, {
|
|
491
600
|
type: "rule",
|
|
492
|
-
global:
|
|
601
|
+
global: useGlobal
|
|
493
602
|
});
|
|
494
|
-
|
|
603
|
+
dim(`${memory.id}: ${rule}`);
|
|
495
604
|
}
|
|
496
605
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
606
|
+
nextSteps([
|
|
607
|
+
`${chalk2.cyan("memories add")} ${chalk2.dim('"Your first memory"')}`,
|
|
608
|
+
`${chalk2.cyan("memories add --rule")} ${chalk2.dim('"Always use TypeScript"')}`,
|
|
609
|
+
`${chalk2.cyan("memories generate")} ${chalk2.dim("to create IDE rule files")}`,
|
|
610
|
+
`${chalk2.cyan("memories serve")} ${chalk2.dim("to start MCP server")}`
|
|
611
|
+
]);
|
|
612
|
+
} catch (error2) {
|
|
613
|
+
error("Failed to initialize: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
504
614
|
process.exit(1);
|
|
505
615
|
}
|
|
506
616
|
});
|
|
507
617
|
|
|
508
618
|
// src/commands/add.ts
|
|
509
619
|
import { Command as Command2 } from "commander";
|
|
510
|
-
import
|
|
620
|
+
import chalk3 from "chalk";
|
|
511
621
|
var VALID_TYPES = ["rule", "decision", "fact", "note"];
|
|
512
622
|
var addCommand = new Command2("add").description("Add a new memory").argument("<content>", "Memory content").option("-t, --tags <tags>", "Comma-separated tags").option("-g, --global", "Store as global memory (default: project-scoped if in git repo)").option("--type <type>", "Memory type: rule, decision, fact, note (default: note)").option("-r, --rule", "Shorthand for --type rule").option("-d, --decision", "Shorthand for --type decision").option("-f, --fact", "Shorthand for --type fact").action(async (content, opts) => {
|
|
513
623
|
try {
|
|
624
|
+
const auth = await readAuth();
|
|
625
|
+
if (auth) {
|
|
626
|
+
try {
|
|
627
|
+
const apiFetch = getApiClient(auth);
|
|
628
|
+
const res = await apiFetch("/api/db/limits");
|
|
629
|
+
if (res.ok) {
|
|
630
|
+
const limits = await res.json();
|
|
631
|
+
if (limits.memoryLimit !== null && limits.memoryCount >= limits.memoryLimit) {
|
|
632
|
+
warn(`You've reached the free plan limit of ${limits.memoryLimit.toLocaleString()} memories.`);
|
|
633
|
+
proFeature("Unlimited memories");
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
} catch {
|
|
638
|
+
}
|
|
639
|
+
}
|
|
514
640
|
const tags = opts.tags?.split(",").map((t) => t.trim());
|
|
515
641
|
let type = "note";
|
|
516
642
|
if (opts.rule) type = "rule";
|
|
@@ -518,24 +644,29 @@ var addCommand = new Command2("add").description("Add a new memory").argument("<
|
|
|
518
644
|
else if (opts.fact) type = "fact";
|
|
519
645
|
else if (opts.type) {
|
|
520
646
|
if (!VALID_TYPES.includes(opts.type)) {
|
|
521
|
-
console.error(
|
|
647
|
+
console.error(chalk3.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES.join(", ")}`);
|
|
522
648
|
process.exit(1);
|
|
523
649
|
}
|
|
524
650
|
type = opts.type;
|
|
525
651
|
}
|
|
526
652
|
const memory = await addMemory(content, { tags, global: opts.global, type });
|
|
527
|
-
const
|
|
528
|
-
const scopeInfo = memory.scope === "global" ?
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
653
|
+
const typeLabel = type === "rule" ? "Rule" : type === "decision" ? "Decision" : type === "fact" ? "Fact" : "Note";
|
|
654
|
+
const scopeInfo = memory.scope === "global" ? "global" : "project";
|
|
655
|
+
success(`Stored ${chalk3.bold(typeLabel.toLowerCase())} ${chalk3.dim(memory.id)}`);
|
|
656
|
+
dim(`Scope: ${scopeInfo}${tags?.length ? ` \u2022 Tags: ${tags.join(", ")}` : ""}`);
|
|
657
|
+
if (type === "rule") {
|
|
658
|
+
console.log("");
|
|
659
|
+
dim(`Run ${chalk3.cyan("memories generate")} to update your IDE rule files`);
|
|
660
|
+
}
|
|
661
|
+
} catch (error2) {
|
|
662
|
+
error("Failed to add memory: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
532
663
|
process.exit(1);
|
|
533
664
|
}
|
|
534
665
|
});
|
|
535
666
|
|
|
536
667
|
// src/commands/recall.ts
|
|
537
668
|
import { Command as Command3 } from "commander";
|
|
538
|
-
import
|
|
669
|
+
import chalk4 from "chalk";
|
|
539
670
|
var TYPE_ICONS = {
|
|
540
671
|
rule: "\u{1F4CC}",
|
|
541
672
|
decision: "\u{1F4A1}",
|
|
@@ -543,18 +674,18 @@ var TYPE_ICONS = {
|
|
|
543
674
|
note: "\u{1F4DD}"
|
|
544
675
|
};
|
|
545
676
|
var TYPE_COLORS = {
|
|
546
|
-
rule:
|
|
547
|
-
decision:
|
|
548
|
-
fact:
|
|
549
|
-
note:
|
|
677
|
+
rule: chalk4.yellow,
|
|
678
|
+
decision: chalk4.cyan,
|
|
679
|
+
fact: chalk4.green,
|
|
680
|
+
note: chalk4.white
|
|
550
681
|
};
|
|
551
682
|
function formatMemory(m, verbose) {
|
|
552
683
|
const icon = TYPE_ICONS[m.type] || "\u{1F4DD}";
|
|
553
|
-
const colorFn = TYPE_COLORS[m.type] ||
|
|
554
|
-
const scope = m.scope === "global" ?
|
|
555
|
-
const tags = m.tags ?
|
|
684
|
+
const colorFn = TYPE_COLORS[m.type] || chalk4.white;
|
|
685
|
+
const scope = m.scope === "global" ? chalk4.dim("G") : chalk4.dim("P");
|
|
686
|
+
const tags = m.tags ? chalk4.dim(` [${m.tags}]`) : "";
|
|
556
687
|
if (verbose) {
|
|
557
|
-
return `${icon} ${scope} ${
|
|
688
|
+
return `${icon} ${scope} ${chalk4.dim(m.id)} ${colorFn(m.content)}${tags}`;
|
|
558
689
|
}
|
|
559
690
|
return `${icon} ${colorFn(m.content)}`;
|
|
560
691
|
}
|
|
@@ -568,11 +699,11 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
568
699
|
return;
|
|
569
700
|
}
|
|
570
701
|
if (rules2.length === 0) {
|
|
571
|
-
console.log(
|
|
572
|
-
console.log(
|
|
702
|
+
console.log(chalk4.dim("No rules defined."));
|
|
703
|
+
console.log(chalk4.dim('Add one with: memories add --rule "Your rule here"'));
|
|
573
704
|
return;
|
|
574
705
|
}
|
|
575
|
-
console.log(
|
|
706
|
+
console.log(chalk4.bold("Rules:"));
|
|
576
707
|
for (const rule of rules2) {
|
|
577
708
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
578
709
|
}
|
|
@@ -587,26 +718,26 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
587
718
|
return;
|
|
588
719
|
}
|
|
589
720
|
if (rules.length > 0) {
|
|
590
|
-
console.log(
|
|
721
|
+
console.log(chalk4.bold("Rules:"));
|
|
591
722
|
for (const rule of rules) {
|
|
592
723
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
593
724
|
}
|
|
594
725
|
console.log("");
|
|
595
726
|
}
|
|
596
727
|
if (memories.length > 0) {
|
|
597
|
-
console.log(
|
|
728
|
+
console.log(chalk4.bold(query ? `Relevant to "${query}":` : "Recent memories:"));
|
|
598
729
|
for (const m of memories) {
|
|
599
730
|
console.log(formatMemory(m, opts.verbose ?? false));
|
|
600
731
|
}
|
|
601
732
|
} else if (query) {
|
|
602
|
-
console.log(
|
|
733
|
+
console.log(chalk4.dim(`No memories found matching "${query}"`));
|
|
603
734
|
}
|
|
604
735
|
if (rules.length === 0 && memories.length === 0) {
|
|
605
|
-
console.log(
|
|
606
|
-
console.log(
|
|
736
|
+
console.log(chalk4.dim("No memories found."));
|
|
737
|
+
console.log(chalk4.dim('Add some with: memories add "Your memory here"'));
|
|
607
738
|
}
|
|
608
|
-
} catch (
|
|
609
|
-
console.error(
|
|
739
|
+
} catch (error2) {
|
|
740
|
+
console.error(chalk4.red("\u2717") + " Failed to recall:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
610
741
|
process.exit(1);
|
|
611
742
|
}
|
|
612
743
|
});
|
|
@@ -614,7 +745,7 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
614
745
|
// src/commands/prompt.ts
|
|
615
746
|
import { Command as Command4 } from "commander";
|
|
616
747
|
import { execFileSync } from "child_process";
|
|
617
|
-
import
|
|
748
|
+
import chalk5 from "chalk";
|
|
618
749
|
var VALID_TYPES2 = ["rule", "decision", "fact", "note"];
|
|
619
750
|
function formatMarkdown(sections) {
|
|
620
751
|
return sections.map(({ title, memories }) => {
|
|
@@ -657,7 +788,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
657
788
|
try {
|
|
658
789
|
const format = opts.format ?? "markdown";
|
|
659
790
|
if (!["markdown", "xml", "plain"].includes(format)) {
|
|
660
|
-
console.error(
|
|
791
|
+
console.error(chalk5.red("\u2717") + ` Invalid format "${opts.format}". Use: markdown, xml, plain`);
|
|
661
792
|
process.exit(1);
|
|
662
793
|
}
|
|
663
794
|
const projectId = getProjectId() ?? void 0;
|
|
@@ -669,7 +800,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
669
800
|
for (const t of opts.include.split(",").map((s) => s.trim())) {
|
|
670
801
|
const normalized = t.replace(/s$/, "");
|
|
671
802
|
if (!VALID_TYPES2.includes(normalized)) {
|
|
672
|
-
console.error(
|
|
803
|
+
console.error(chalk5.red("\u2717") + ` Invalid type "${t}". Valid: decisions, facts, notes`);
|
|
673
804
|
process.exit(1);
|
|
674
805
|
}
|
|
675
806
|
if (normalized !== "rule") extraTypes.push(normalized);
|
|
@@ -698,7 +829,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
698
829
|
}
|
|
699
830
|
if (sections.length === 0) {
|
|
700
831
|
if (!opts.quiet) {
|
|
701
|
-
console.error(
|
|
832
|
+
console.error(chalk5.dim('No memories found. Add rules with: memories add --rule "Your rule"'));
|
|
702
833
|
}
|
|
703
834
|
return;
|
|
704
835
|
}
|
|
@@ -718,26 +849,26 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
718
849
|
if (copied) {
|
|
719
850
|
console.log(output);
|
|
720
851
|
if (!opts.quiet) {
|
|
721
|
-
console.error(
|
|
852
|
+
console.error(chalk5.green("\u2713") + " Copied to clipboard");
|
|
722
853
|
}
|
|
723
854
|
} else {
|
|
724
855
|
console.log(output);
|
|
725
856
|
if (!opts.quiet) {
|
|
726
|
-
console.error(
|
|
857
|
+
console.error(chalk5.yellow("\u26A0") + " Could not copy to clipboard (unsupported platform)");
|
|
727
858
|
}
|
|
728
859
|
}
|
|
729
860
|
} else {
|
|
730
861
|
console.log(output);
|
|
731
862
|
}
|
|
732
|
-
} catch (
|
|
733
|
-
console.error(
|
|
863
|
+
} catch (error2) {
|
|
864
|
+
console.error(chalk5.red("\u2717") + " Failed to generate prompt:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
734
865
|
process.exit(1);
|
|
735
866
|
}
|
|
736
867
|
});
|
|
737
868
|
|
|
738
869
|
// src/commands/search.ts
|
|
739
870
|
import { Command as Command5 } from "commander";
|
|
740
|
-
import
|
|
871
|
+
import chalk6 from "chalk";
|
|
741
872
|
var TYPE_ICONS2 = {
|
|
742
873
|
rule: "\u{1F4CC}",
|
|
743
874
|
decision: "\u{1F4A1}",
|
|
@@ -747,16 +878,16 @@ var TYPE_ICONS2 = {
|
|
|
747
878
|
var VALID_TYPES3 = ["rule", "decision", "fact", "note"];
|
|
748
879
|
function formatMemory2(m) {
|
|
749
880
|
const icon = TYPE_ICONS2[m.type] || "\u{1F4DD}";
|
|
750
|
-
const scope = m.scope === "global" ?
|
|
751
|
-
const tags = m.tags ?
|
|
752
|
-
return `${icon} ${scope} ${
|
|
881
|
+
const scope = m.scope === "global" ? chalk6.dim("G") : chalk6.dim("P");
|
|
882
|
+
const tags = m.tags ? chalk6.dim(` [${m.tags}]`) : "";
|
|
883
|
+
return `${icon} ${scope} ${chalk6.dim(m.id)} ${m.content}${tags}`;
|
|
753
884
|
}
|
|
754
885
|
var searchCommand = new Command5("search").description("Search memories using full-text search").argument("<query>", "Search query").option("-l, --limit <n>", "Max results", "20").option("--type <type>", "Filter by type: rule, decision, fact, note").option("-g, --global", "Search only global memories").option("--project-only", "Search only project memories (exclude global)").option("--json", "Output as JSON").action(async (query, opts) => {
|
|
755
886
|
try {
|
|
756
887
|
let types;
|
|
757
888
|
if (opts.type) {
|
|
758
889
|
if (!VALID_TYPES3.includes(opts.type)) {
|
|
759
|
-
console.error(
|
|
890
|
+
console.error(chalk6.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES3.join(", ")}`);
|
|
760
891
|
process.exit(1);
|
|
761
892
|
}
|
|
762
893
|
types = [opts.type];
|
|
@@ -770,7 +901,7 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
770
901
|
includeGlobal = false;
|
|
771
902
|
projectId = getProjectId() ?? void 0;
|
|
772
903
|
if (!projectId) {
|
|
773
|
-
console.log(
|
|
904
|
+
console.log(chalk6.yellow("\u26A0") + " Not in a git repository. No project memories to search.");
|
|
774
905
|
return;
|
|
775
906
|
}
|
|
776
907
|
}
|
|
@@ -786,30 +917,30 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
786
917
|
return;
|
|
787
918
|
}
|
|
788
919
|
if (memories.length === 0) {
|
|
789
|
-
console.log(
|
|
920
|
+
console.log(chalk6.dim(`No memories found matching "${query}"`));
|
|
790
921
|
return;
|
|
791
922
|
}
|
|
792
|
-
console.log(
|
|
923
|
+
console.log(chalk6.bold(`Results for "${query}":`));
|
|
793
924
|
console.log("");
|
|
794
925
|
for (const m of memories) {
|
|
795
926
|
console.log(formatMemory2(m));
|
|
796
927
|
}
|
|
797
|
-
console.log(
|
|
928
|
+
console.log(chalk6.dim(`
|
|
798
929
|
${memories.length} results`));
|
|
799
|
-
} catch (
|
|
800
|
-
console.error(
|
|
930
|
+
} catch (error2) {
|
|
931
|
+
console.error(chalk6.red("\u2717") + " Failed to search:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
801
932
|
process.exit(1);
|
|
802
933
|
}
|
|
803
934
|
});
|
|
804
935
|
|
|
805
936
|
// src/commands/list.ts
|
|
806
937
|
import { Command as Command6 } from "commander";
|
|
807
|
-
import
|
|
938
|
+
import chalk7 from "chalk";
|
|
808
939
|
var TYPE_COLORS2 = {
|
|
809
|
-
rule:
|
|
810
|
-
decision:
|
|
811
|
-
fact:
|
|
812
|
-
note:
|
|
940
|
+
rule: chalk7.blue,
|
|
941
|
+
decision: chalk7.yellow,
|
|
942
|
+
fact: chalk7.green,
|
|
943
|
+
note: chalk7.dim
|
|
813
944
|
};
|
|
814
945
|
var TYPE_LABELS = {
|
|
815
946
|
rule: "rule",
|
|
@@ -824,12 +955,12 @@ function truncate(str, max) {
|
|
|
824
955
|
return str.slice(0, max - 1) + "\u2026";
|
|
825
956
|
}
|
|
826
957
|
function formatMemory3(m) {
|
|
827
|
-
const typeColor = TYPE_COLORS2[m.type] ??
|
|
958
|
+
const typeColor = TYPE_COLORS2[m.type] ?? chalk7.dim;
|
|
828
959
|
const typeLabel = typeColor(TYPE_LABELS[m.type].padEnd(9));
|
|
829
|
-
const scope = m.scope === "global" ?
|
|
830
|
-
const id =
|
|
960
|
+
const scope = m.scope === "global" ? chalk7.magenta("G") : chalk7.cyan("P");
|
|
961
|
+
const id = chalk7.dim(m.id);
|
|
831
962
|
const content = truncate(m.content, MAX_CONTENT_WIDTH);
|
|
832
|
-
const tags = m.tags ?
|
|
963
|
+
const tags = m.tags ? chalk7.dim(` [${m.tags}]`) : "";
|
|
833
964
|
return ` ${scope} ${typeLabel} ${id} ${content}${tags}`;
|
|
834
965
|
}
|
|
835
966
|
var listCommand = new Command6("list").description("List memories").option("-l, --limit <n>", "Max results", "50").option("-t, --tags <tags>", "Filter by comma-separated tags").option("--type <type>", "Filter by type: rule, decision, fact, note").option("-g, --global", "Show only global memories").option("--project-only", "Show only project memories (exclude global)").option("--json", "Output as JSON").action(async (opts) => {
|
|
@@ -838,7 +969,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
838
969
|
let types;
|
|
839
970
|
if (opts.type) {
|
|
840
971
|
if (!VALID_TYPES4.includes(opts.type)) {
|
|
841
|
-
console.error(
|
|
972
|
+
console.error(chalk7.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES4.join(", ")}`);
|
|
842
973
|
process.exit(1);
|
|
843
974
|
}
|
|
844
975
|
types = [opts.type];
|
|
@@ -852,7 +983,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
852
983
|
includeGlobal = false;
|
|
853
984
|
projectId = getProjectId() ?? void 0;
|
|
854
985
|
if (!projectId) {
|
|
855
|
-
console.log(
|
|
986
|
+
console.log(chalk7.yellow("\u26A0") + " Not in a git repository. No project memories to show.");
|
|
856
987
|
return;
|
|
857
988
|
}
|
|
858
989
|
}
|
|
@@ -869,21 +1000,21 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
869
1000
|
return;
|
|
870
1001
|
}
|
|
871
1002
|
if (memories.length === 0) {
|
|
872
|
-
console.log(
|
|
1003
|
+
console.log(chalk7.dim("No memories found."));
|
|
873
1004
|
return;
|
|
874
1005
|
}
|
|
875
1006
|
const currentProject = getProjectId();
|
|
876
1007
|
if (currentProject && !opts.global) {
|
|
877
|
-
console.log(
|
|
1008
|
+
console.log(chalk7.dim(` Project: ${currentProject}
|
|
878
1009
|
`));
|
|
879
1010
|
}
|
|
880
1011
|
for (const m of memories) {
|
|
881
1012
|
console.log(formatMemory3(m));
|
|
882
1013
|
}
|
|
883
|
-
console.log(
|
|
1014
|
+
console.log(chalk7.dim(`
|
|
884
1015
|
${memories.length} memories`));
|
|
885
|
-
} catch (
|
|
886
|
-
console.error(
|
|
1016
|
+
} catch (error2) {
|
|
1017
|
+
console.error(chalk7.red("\u2717") + " Failed to list memories:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
887
1018
|
process.exit(1);
|
|
888
1019
|
}
|
|
889
1020
|
});
|
|
@@ -891,7 +1022,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
891
1022
|
// src/commands/forget.ts
|
|
892
1023
|
import { Command as Command7 } from "commander";
|
|
893
1024
|
import { createInterface } from "readline";
|
|
894
|
-
import
|
|
1025
|
+
import chalk8 from "chalk";
|
|
895
1026
|
var VALID_TYPES5 = ["rule", "decision", "fact", "note"];
|
|
896
1027
|
var TYPE_ICONS3 = {
|
|
897
1028
|
rule: "\u{1F4CC}",
|
|
@@ -913,28 +1044,28 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
913
1044
|
if (id) {
|
|
914
1045
|
const deleted = await forgetMemory(id);
|
|
915
1046
|
if (deleted) {
|
|
916
|
-
console.log(
|
|
1047
|
+
console.log(chalk8.green("\u2713") + ` Forgot memory ${chalk8.dim(id)}`);
|
|
917
1048
|
} else {
|
|
918
|
-
console.error(
|
|
1049
|
+
console.error(chalk8.red("\u2717") + ` Memory ${id} not found or already forgotten.`);
|
|
919
1050
|
process.exit(1);
|
|
920
1051
|
}
|
|
921
1052
|
return;
|
|
922
1053
|
}
|
|
923
1054
|
const hasBulkFilter = opts.type || opts.tag || opts.olderThan || opts.pattern || opts.all;
|
|
924
1055
|
if (!hasBulkFilter) {
|
|
925
|
-
console.error(
|
|
1056
|
+
console.error(chalk8.red("\u2717") + " Provide a memory ID or a bulk filter (--type, --tag, --older-than, --pattern, --all)");
|
|
926
1057
|
process.exit(1);
|
|
927
1058
|
}
|
|
928
1059
|
if (opts.all && (opts.type || opts.tag || opts.olderThan || opts.pattern)) {
|
|
929
|
-
console.error(
|
|
1060
|
+
console.error(chalk8.red("\u2717") + " --all cannot be combined with other filters. Use --all alone to delete everything.");
|
|
930
1061
|
process.exit(1);
|
|
931
1062
|
}
|
|
932
1063
|
if (opts.type && !VALID_TYPES5.includes(opts.type)) {
|
|
933
|
-
console.error(
|
|
1064
|
+
console.error(chalk8.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES5.join(", ")}`);
|
|
934
1065
|
process.exit(1);
|
|
935
1066
|
}
|
|
936
1067
|
if (opts.olderThan && (isNaN(parseInt(opts.olderThan, 10)) || parseInt(opts.olderThan, 10) <= 0)) {
|
|
937
|
-
console.error(
|
|
1068
|
+
console.error(chalk8.red("\u2717") + " --older-than must be a positive number of days");
|
|
938
1069
|
process.exit(1);
|
|
939
1070
|
}
|
|
940
1071
|
const filter = {
|
|
@@ -947,27 +1078,27 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
947
1078
|
};
|
|
948
1079
|
const matches = await findMemoriesToForget(filter);
|
|
949
1080
|
if (matches.length === 0) {
|
|
950
|
-
console.log(
|
|
1081
|
+
console.log(chalk8.dim("No memories match the filter."));
|
|
951
1082
|
return;
|
|
952
1083
|
}
|
|
953
|
-
console.log(
|
|
1084
|
+
console.log(chalk8.bold(`${matches.length} memories will be forgotten:
|
|
954
1085
|
`));
|
|
955
1086
|
for (const m of matches.slice(0, 30)) {
|
|
956
1087
|
const icon = TYPE_ICONS3[m.type] || "\u{1F4DD}";
|
|
957
|
-
const scope = m.scope === "global" ?
|
|
958
|
-
console.log(` ${icon} ${scope} ${
|
|
1088
|
+
const scope = m.scope === "global" ? chalk8.dim("G") : chalk8.dim("P");
|
|
1089
|
+
console.log(` ${icon} ${scope} ${chalk8.dim(m.id)} ${m.content}`);
|
|
959
1090
|
}
|
|
960
1091
|
if (matches.length > 30) {
|
|
961
|
-
console.log(
|
|
1092
|
+
console.log(chalk8.dim(` ... and ${matches.length - 30} more`));
|
|
962
1093
|
}
|
|
963
1094
|
console.log("");
|
|
964
1095
|
if (opts.dryRun) {
|
|
965
|
-
console.log(
|
|
1096
|
+
console.log(chalk8.yellow("Dry run") + " \u2014 no memories were deleted.");
|
|
966
1097
|
return;
|
|
967
1098
|
}
|
|
968
1099
|
if (!opts.force) {
|
|
969
1100
|
const proceed = await confirm(
|
|
970
|
-
|
|
1101
|
+
chalk8.yellow(`Forget ${matches.length} memories? This is a soft-delete. [y/N] `)
|
|
971
1102
|
);
|
|
972
1103
|
if (!proceed) {
|
|
973
1104
|
console.log("Cancelled.");
|
|
@@ -976,17 +1107,17 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
976
1107
|
}
|
|
977
1108
|
const ids = matches.map((m) => m.id);
|
|
978
1109
|
const count = await bulkForgetByIds(ids);
|
|
979
|
-
console.log(
|
|
980
|
-
} catch (
|
|
981
|
-
console.error(
|
|
1110
|
+
console.log(chalk8.green("\u2713") + ` Forgot ${count} memories.`);
|
|
1111
|
+
} catch (error2) {
|
|
1112
|
+
console.error(chalk8.red("\u2717") + " Failed to forget:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
982
1113
|
process.exit(1);
|
|
983
1114
|
}
|
|
984
1115
|
});
|
|
985
1116
|
|
|
986
1117
|
// src/commands/export.ts
|
|
987
1118
|
import { Command as Command8 } from "commander";
|
|
988
|
-
import
|
|
989
|
-
import { writeFile as
|
|
1119
|
+
import chalk9 from "chalk";
|
|
1120
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
990
1121
|
var exportCommand = new Command8("export").description("Export memories to JSON or YAML file").option("-o, --output <file>", "Output file path (default: stdout)").option("-f, --format <format>", "Output format: json, yaml (default: json)", "json").option("-g, --global", "Export only global memories").option("--project-only", "Export only project memories").option("--type <type>", "Filter by type: rule, decision, fact, note").action(async (opts) => {
|
|
991
1122
|
try {
|
|
992
1123
|
const projectId = getProjectId();
|
|
@@ -999,7 +1130,7 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
999
1130
|
includeGlobal = false;
|
|
1000
1131
|
queryProjectId = projectId ?? void 0;
|
|
1001
1132
|
if (!queryProjectId) {
|
|
1002
|
-
console.error(
|
|
1133
|
+
console.error(chalk9.red("\u2717") + " Not in a git repository. No project memories to export.");
|
|
1003
1134
|
process.exit(1);
|
|
1004
1135
|
}
|
|
1005
1136
|
}
|
|
@@ -1033,24 +1164,24 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
1033
1164
|
output = JSON.stringify(exportData, null, 2);
|
|
1034
1165
|
}
|
|
1035
1166
|
if (opts.output) {
|
|
1036
|
-
await
|
|
1037
|
-
console.log(
|
|
1167
|
+
await writeFile3(opts.output, output, "utf-8");
|
|
1168
|
+
console.log(chalk9.green("\u2713") + ` Exported ${memories.length} memories to ${chalk9.dim(opts.output)}`);
|
|
1038
1169
|
} else {
|
|
1039
1170
|
console.log(output);
|
|
1040
1171
|
}
|
|
1041
|
-
} catch (
|
|
1042
|
-
console.error(
|
|
1172
|
+
} catch (error2) {
|
|
1173
|
+
console.error(chalk9.red("\u2717") + " Failed to export:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1043
1174
|
process.exit(1);
|
|
1044
1175
|
}
|
|
1045
1176
|
});
|
|
1046
1177
|
|
|
1047
1178
|
// src/commands/import.ts
|
|
1048
1179
|
import { Command as Command9 } from "commander";
|
|
1049
|
-
import
|
|
1050
|
-
import { readFile as
|
|
1180
|
+
import chalk10 from "chalk";
|
|
1181
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1051
1182
|
var importCommand = new Command9("import").description("Import memories from JSON or YAML file").argument("<file>", "Input file path").option("-f, --format <format>", "Input format: json, yaml (auto-detected from extension)").option("-g, --global", "Import all as global memories (override file scope)").option("--dry-run", "Show what would be imported without actually importing").action(async (file, opts) => {
|
|
1052
1183
|
try {
|
|
1053
|
-
const content = await
|
|
1184
|
+
const content = await readFile3(file, "utf-8");
|
|
1054
1185
|
let format = opts.format;
|
|
1055
1186
|
if (!format) {
|
|
1056
1187
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
@@ -1067,7 +1198,7 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
1067
1198
|
data = JSON.parse(content);
|
|
1068
1199
|
}
|
|
1069
1200
|
if (!data || typeof data !== "object" || !("memories" in data) || !Array.isArray(data.memories)) {
|
|
1070
|
-
console.error(
|
|
1201
|
+
console.error(chalk10.red("\u2717") + " Invalid import file: missing 'memories' array");
|
|
1071
1202
|
process.exit(1);
|
|
1072
1203
|
}
|
|
1073
1204
|
const VALID_TYPES11 = ["rule", "decision", "fact", "note"];
|
|
@@ -1080,23 +1211,23 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
1080
1211
|
continue;
|
|
1081
1212
|
}
|
|
1082
1213
|
if (m.type && !VALID_TYPES11.includes(m.type)) {
|
|
1083
|
-
console.error(
|
|
1214
|
+
console.error(chalk10.yellow("\u26A0") + ` Skipping memory with invalid type "${m.type}": ${m.content.slice(0, 50)}`);
|
|
1084
1215
|
skipped++;
|
|
1085
1216
|
continue;
|
|
1086
1217
|
}
|
|
1087
1218
|
validMemories.push(m);
|
|
1088
1219
|
}
|
|
1089
1220
|
if (opts.dryRun) {
|
|
1090
|
-
console.log(
|
|
1221
|
+
console.log(chalk10.blue("Dry run - would import:"));
|
|
1091
1222
|
for (const m of validMemories) {
|
|
1092
1223
|
const type = m.type || "note";
|
|
1093
1224
|
const scope = opts.global ? "global" : m.scope || "project";
|
|
1094
1225
|
const tags = m.tags?.length ? ` [${m.tags.join(", ")}]` : "";
|
|
1095
1226
|
console.log(` ${type} (${scope}): ${m.content}${tags}`);
|
|
1096
1227
|
}
|
|
1097
|
-
console.log(
|
|
1228
|
+
console.log(chalk10.dim(`
|
|
1098
1229
|
${validMemories.length} memories would be imported`));
|
|
1099
|
-
if (skipped > 0) console.log(
|
|
1230
|
+
if (skipped > 0) console.log(chalk10.dim(`${skipped} entries skipped (invalid)`));
|
|
1100
1231
|
return;
|
|
1101
1232
|
}
|
|
1102
1233
|
let imported = 0;
|
|
@@ -1109,20 +1240,20 @@ ${validMemories.length} memories would be imported`));
|
|
|
1109
1240
|
global: opts.global || m.scope === "global"
|
|
1110
1241
|
});
|
|
1111
1242
|
imported++;
|
|
1112
|
-
} catch (
|
|
1113
|
-
console.error(
|
|
1243
|
+
} catch (error2) {
|
|
1244
|
+
console.error(chalk10.yellow("\u26A0") + ` Failed to import: ${m.content.slice(0, 50)}...`);
|
|
1114
1245
|
failed++;
|
|
1115
1246
|
}
|
|
1116
1247
|
}
|
|
1117
|
-
console.log(
|
|
1248
|
+
console.log(chalk10.green("\u2713") + ` Imported ${imported} memories`);
|
|
1118
1249
|
if (failed > 0) {
|
|
1119
|
-
console.log(
|
|
1250
|
+
console.log(chalk10.yellow("\u26A0") + ` ${failed} memories failed to import`);
|
|
1120
1251
|
}
|
|
1121
1252
|
if (skipped > 0) {
|
|
1122
|
-
console.log(
|
|
1253
|
+
console.log(chalk10.dim(`${skipped} entries skipped (invalid content or type)`));
|
|
1123
1254
|
}
|
|
1124
|
-
} catch (
|
|
1125
|
-
console.error(
|
|
1255
|
+
} catch (error2) {
|
|
1256
|
+
console.error(chalk10.red("\u2717") + " Failed to import:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1126
1257
|
process.exit(1);
|
|
1127
1258
|
}
|
|
1128
1259
|
});
|
|
@@ -1131,17 +1262,17 @@ ${validMemories.length} memories would be imported`));
|
|
|
1131
1262
|
import { Command as Command10 } from "commander";
|
|
1132
1263
|
|
|
1133
1264
|
// src/lib/config.ts
|
|
1134
|
-
import { readFile as
|
|
1135
|
-
import { join as
|
|
1136
|
-
import { existsSync as
|
|
1265
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1266
|
+
import { join as join3 } from "path";
|
|
1267
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1137
1268
|
import { parse, stringify } from "yaml";
|
|
1138
1269
|
var AGENTS_DIR = ".agents";
|
|
1139
1270
|
var CONFIG_FILE = "config.yaml";
|
|
1140
1271
|
async function initConfig(dir) {
|
|
1141
|
-
const agentsDir =
|
|
1142
|
-
await
|
|
1143
|
-
const configPath =
|
|
1144
|
-
if (!
|
|
1272
|
+
const agentsDir = join3(dir, AGENTS_DIR);
|
|
1273
|
+
await mkdir3(agentsDir, { recursive: true });
|
|
1274
|
+
const configPath = join3(agentsDir, CONFIG_FILE);
|
|
1275
|
+
if (!existsSync3(configPath)) {
|
|
1145
1276
|
const defaultConfig = {
|
|
1146
1277
|
name: "my-project",
|
|
1147
1278
|
description: "Agent memory configuration",
|
|
@@ -1151,14 +1282,14 @@ async function initConfig(dir) {
|
|
|
1151
1282
|
store: "~/.config/memories/local.db"
|
|
1152
1283
|
}
|
|
1153
1284
|
};
|
|
1154
|
-
await
|
|
1285
|
+
await writeFile4(configPath, stringify(defaultConfig), "utf-8");
|
|
1155
1286
|
}
|
|
1156
1287
|
return configPath;
|
|
1157
1288
|
}
|
|
1158
1289
|
async function readConfig(dir) {
|
|
1159
|
-
const configPath =
|
|
1160
|
-
if (!
|
|
1161
|
-
const raw = await
|
|
1290
|
+
const configPath = join3(dir, AGENTS_DIR, CONFIG_FILE);
|
|
1291
|
+
if (!existsSync3(configPath)) return null;
|
|
1292
|
+
const raw = await readFile4(configPath, "utf-8");
|
|
1162
1293
|
return parse(raw);
|
|
1163
1294
|
}
|
|
1164
1295
|
|
|
@@ -1240,9 +1371,9 @@ ${projectRules.map((r) => `- ${r.content}`).join("\n")}`);
|
|
|
1240
1371
|
return {
|
|
1241
1372
|
contents: [{ uri: "memories://rules", mimeType: "text/markdown", text: parts.join("\n\n") }]
|
|
1242
1373
|
};
|
|
1243
|
-
} catch (
|
|
1374
|
+
} catch (error2) {
|
|
1244
1375
|
return {
|
|
1245
|
-
contents: [{ uri: "memories://rules", mimeType: "text/markdown", text: `Error loading rules: ${
|
|
1376
|
+
contents: [{ uri: "memories://rules", mimeType: "text/markdown", text: `Error loading rules: ${error2 instanceof Error ? error2.message : "Unknown error"}` }]
|
|
1246
1377
|
};
|
|
1247
1378
|
}
|
|
1248
1379
|
}
|
|
@@ -1268,9 +1399,9 @@ ${projectRules.map((r) => `- ${r.content}`).join("\n")}`);
|
|
|
1268
1399
|
|
|
1269
1400
|
${text}` }]
|
|
1270
1401
|
};
|
|
1271
|
-
} catch (
|
|
1402
|
+
} catch (error2) {
|
|
1272
1403
|
return {
|
|
1273
|
-
contents: [{ uri: "memories://recent", mimeType: "text/markdown", text: `Error loading memories: ${
|
|
1404
|
+
contents: [{ uri: "memories://recent", mimeType: "text/markdown", text: `Error loading memories: ${error2 instanceof Error ? error2.message : "Unknown error"}` }]
|
|
1274
1405
|
};
|
|
1275
1406
|
}
|
|
1276
1407
|
}
|
|
@@ -1297,9 +1428,9 @@ ${text}` }]
|
|
|
1297
1428
|
|
|
1298
1429
|
${text}` }]
|
|
1299
1430
|
};
|
|
1300
|
-
} catch (
|
|
1431
|
+
} catch (error2) {
|
|
1301
1432
|
return {
|
|
1302
|
-
contents: [{ uri: uri.href, mimeType: "text/markdown", text: `Error loading project memories: ${
|
|
1433
|
+
contents: [{ uri: uri.href, mimeType: "text/markdown", text: `Error loading project memories: ${error2 instanceof Error ? error2.message : "Unknown error"}` }]
|
|
1303
1434
|
};
|
|
1304
1435
|
}
|
|
1305
1436
|
}
|
|
@@ -1337,9 +1468,9 @@ Use this at the start of tasks to understand project conventions and recall past
|
|
|
1337
1468
|
return {
|
|
1338
1469
|
content: [{ type: "text", text: parts.join("\n\n") }]
|
|
1339
1470
|
};
|
|
1340
|
-
} catch (
|
|
1471
|
+
} catch (error2) {
|
|
1341
1472
|
return {
|
|
1342
|
-
content: [{ type: "text", text: `Failed to get context: ${
|
|
1473
|
+
content: [{ type: "text", text: `Failed to get context: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1343
1474
|
isError: true
|
|
1344
1475
|
};
|
|
1345
1476
|
}
|
|
@@ -1376,9 +1507,9 @@ By default, memories are project-scoped when in a git repo. Use global: true for
|
|
|
1376
1507
|
}
|
|
1377
1508
|
]
|
|
1378
1509
|
};
|
|
1379
|
-
} catch (
|
|
1510
|
+
} catch (error2) {
|
|
1380
1511
|
return {
|
|
1381
|
-
content: [{ type: "text", text: `Failed to add memory: ${
|
|
1512
|
+
content: [{ type: "text", text: `Failed to add memory: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1382
1513
|
isError: true
|
|
1383
1514
|
};
|
|
1384
1515
|
}
|
|
@@ -1415,9 +1546,9 @@ ${formatted}`
|
|
|
1415
1546
|
}
|
|
1416
1547
|
]
|
|
1417
1548
|
};
|
|
1418
|
-
} catch (
|
|
1549
|
+
} catch (error2) {
|
|
1419
1550
|
return {
|
|
1420
|
-
content: [{ type: "text", text: `Failed to search memories: ${
|
|
1551
|
+
content: [{ type: "text", text: `Failed to search memories: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1421
1552
|
isError: true
|
|
1422
1553
|
};
|
|
1423
1554
|
}
|
|
@@ -1449,9 +1580,9 @@ ${projectRules.map((r) => `- ${r.content}`).join("\n")}`);
|
|
|
1449
1580
|
return {
|
|
1450
1581
|
content: [{ type: "text", text: parts.join("\n\n") }]
|
|
1451
1582
|
};
|
|
1452
|
-
} catch (
|
|
1583
|
+
} catch (error2) {
|
|
1453
1584
|
return {
|
|
1454
|
-
content: [{ type: "text", text: `Failed to get rules: ${
|
|
1585
|
+
content: [{ type: "text", text: `Failed to get rules: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1455
1586
|
isError: true
|
|
1456
1587
|
};
|
|
1457
1588
|
}
|
|
@@ -1489,9 +1620,9 @@ ${formatted}`
|
|
|
1489
1620
|
}
|
|
1490
1621
|
]
|
|
1491
1622
|
};
|
|
1492
|
-
} catch (
|
|
1623
|
+
} catch (error2) {
|
|
1493
1624
|
return {
|
|
1494
|
-
content: [{ type: "text", text: `Failed to list memories: ${
|
|
1625
|
+
content: [{ type: "text", text: `Failed to list memories: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1495
1626
|
isError: true
|
|
1496
1627
|
};
|
|
1497
1628
|
}
|
|
@@ -1530,9 +1661,9 @@ Find the memory ID first with search_memories or list_memories.`,
|
|
|
1530
1661
|
content: [{ type: "text", text: `Memory ${id} not found or already deleted.` }],
|
|
1531
1662
|
isError: true
|
|
1532
1663
|
};
|
|
1533
|
-
} catch (
|
|
1664
|
+
} catch (error2) {
|
|
1534
1665
|
return {
|
|
1535
|
-
content: [{ type: "text", text: `Failed to edit memory: ${
|
|
1666
|
+
content: [{ type: "text", text: `Failed to edit memory: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1536
1667
|
isError: true
|
|
1537
1668
|
};
|
|
1538
1669
|
}
|
|
@@ -1556,9 +1687,9 @@ Find the memory ID first with search_memories or list_memories.`,
|
|
|
1556
1687
|
content: [{ type: "text", text: `Memory ${id} not found or already forgotten.` }],
|
|
1557
1688
|
isError: true
|
|
1558
1689
|
};
|
|
1559
|
-
} catch (
|
|
1690
|
+
} catch (error2) {
|
|
1560
1691
|
return {
|
|
1561
|
-
content: [{ type: "text", text: `Failed to forget memory: ${
|
|
1692
|
+
content: [{ type: "text", text: `Failed to forget memory: ${error2 instanceof Error ? error2.message : "Unknown error"}` }],
|
|
1562
1693
|
isError: true
|
|
1563
1694
|
};
|
|
1564
1695
|
}
|
|
@@ -1581,6 +1712,7 @@ var serveCommand = new Command11("serve").description("Start the MCP server (std
|
|
|
1581
1712
|
|
|
1582
1713
|
// src/commands/sync.ts
|
|
1583
1714
|
import { Command as Command12 } from "commander";
|
|
1715
|
+
import ora from "ora";
|
|
1584
1716
|
|
|
1585
1717
|
// src/lib/turso.ts
|
|
1586
1718
|
import { nanoid as nanoid2 } from "nanoid";
|
|
@@ -1633,27 +1765,69 @@ async function createDatabaseToken(org, dbName) {
|
|
|
1633
1765
|
}
|
|
1634
1766
|
|
|
1635
1767
|
// src/commands/sync.ts
|
|
1636
|
-
import { unlinkSync, existsSync as
|
|
1637
|
-
import { join as
|
|
1638
|
-
import { homedir as
|
|
1639
|
-
var DB_PATH =
|
|
1768
|
+
import { unlinkSync, existsSync as existsSync4 } from "fs";
|
|
1769
|
+
import { join as join4 } from "path";
|
|
1770
|
+
import { homedir as homedir3 } from "os";
|
|
1771
|
+
var DB_PATH = join4(homedir3(), ".config", "memories", "local.db");
|
|
1640
1772
|
var syncCommand = new Command12("sync").description(
|
|
1641
1773
|
"Manage remote sync"
|
|
1642
1774
|
);
|
|
1643
|
-
syncCommand.command("enable").description("Provision a
|
|
1775
|
+
syncCommand.command("enable").description("Provision a cloud database and enable sync").option("-o, --org <org>", "Turso organization", "webrenew").action(async (opts) => {
|
|
1644
1776
|
const existing = await readSyncConfig();
|
|
1645
1777
|
if (existing) {
|
|
1646
|
-
|
|
1647
|
-
|
|
1778
|
+
info(`Sync already enabled`);
|
|
1779
|
+
dim(`Remote: ${existing.syncUrl}`);
|
|
1780
|
+
dim(`Run ${success} memories sync push to sync now.`);
|
|
1648
1781
|
return;
|
|
1649
1782
|
}
|
|
1650
|
-
|
|
1783
|
+
const auth = await readAuth();
|
|
1784
|
+
if (auth) {
|
|
1785
|
+
const spinner2 = ora("Fetching cloud database credentials...").start();
|
|
1786
|
+
try {
|
|
1787
|
+
const apiFetch = getApiClient(auth);
|
|
1788
|
+
const res = await apiFetch("/api/db/provision", { method: "POST" });
|
|
1789
|
+
if (res.ok) {
|
|
1790
|
+
const data = await res.json();
|
|
1791
|
+
const profileRes = await apiFetch("/api/db/credentials");
|
|
1792
|
+
if (profileRes.ok) {
|
|
1793
|
+
const creds = await profileRes.json();
|
|
1794
|
+
if (existsSync4(DB_PATH)) {
|
|
1795
|
+
resetDb();
|
|
1796
|
+
unlinkSync(DB_PATH);
|
|
1797
|
+
}
|
|
1798
|
+
await saveSyncConfig({
|
|
1799
|
+
syncUrl: creds.url,
|
|
1800
|
+
syncToken: creds.token,
|
|
1801
|
+
org: opts.org,
|
|
1802
|
+
dbName: creds.dbName
|
|
1803
|
+
});
|
|
1804
|
+
spinner2.text = "Waiting for database to be ready...";
|
|
1805
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
1806
|
+
resetDb();
|
|
1807
|
+
await syncDb();
|
|
1808
|
+
spinner2.stop();
|
|
1809
|
+
success("Cloud sync enabled");
|
|
1810
|
+
dim(`Remote: ${data.url}`);
|
|
1811
|
+
dim(`Database: ${creds.dbName}`);
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
spinner2.stop();
|
|
1816
|
+
} catch {
|
|
1817
|
+
spinner2.stop();
|
|
1818
|
+
warn("Could not fetch credentials from web, provisioning directly...");
|
|
1819
|
+
}
|
|
1820
|
+
} else {
|
|
1821
|
+
warn("Not logged in");
|
|
1822
|
+
proFeature("Cloud sync");
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
const spinner = ora(`Creating database in ${opts.org}...`).start();
|
|
1651
1826
|
const db = await createDatabase(opts.org);
|
|
1652
|
-
|
|
1653
|
-
console.log("Generating auth token...");
|
|
1827
|
+
spinner.text = "Generating auth token...";
|
|
1654
1828
|
const token = await createDatabaseToken(opts.org, db.name);
|
|
1655
1829
|
const syncUrl = `libsql://${db.hostname}`;
|
|
1656
|
-
if (
|
|
1830
|
+
if (existsSync4(DB_PATH)) {
|
|
1657
1831
|
resetDb();
|
|
1658
1832
|
unlinkSync(DB_PATH);
|
|
1659
1833
|
}
|
|
@@ -1663,40 +1837,48 @@ syncCommand.command("enable").description("Provision a Turso database and enable
|
|
|
1663
1837
|
org: opts.org,
|
|
1664
1838
|
dbName: db.name
|
|
1665
1839
|
});
|
|
1666
|
-
|
|
1840
|
+
spinner.text = "Waiting for database to be ready...";
|
|
1667
1841
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
1668
1842
|
resetDb();
|
|
1669
1843
|
await syncDb();
|
|
1670
|
-
|
|
1844
|
+
spinner.stop();
|
|
1845
|
+
success("Cloud sync enabled");
|
|
1846
|
+
dim(`Remote: ${syncUrl}`);
|
|
1847
|
+
dim(`Database: ${db.name}`);
|
|
1671
1848
|
});
|
|
1672
1849
|
syncCommand.command("push").description("Push local changes to remote").action(async () => {
|
|
1673
1850
|
const config = await readSyncConfig();
|
|
1674
1851
|
if (!config) {
|
|
1675
|
-
|
|
1852
|
+
error("Sync not enabled");
|
|
1853
|
+
dim(`Run ${"memories sync enable"} first, or ${"memories login"} to set up cloud sync.`);
|
|
1676
1854
|
process.exitCode = 1;
|
|
1677
1855
|
return;
|
|
1678
1856
|
}
|
|
1857
|
+
const spinner = ora("Syncing to remote...").start();
|
|
1679
1858
|
await syncDb();
|
|
1680
|
-
|
|
1859
|
+
spinner.stop();
|
|
1860
|
+
success("Synced to remote");
|
|
1681
1861
|
});
|
|
1682
1862
|
syncCommand.command("status").description("Show sync configuration").action(async () => {
|
|
1683
1863
|
const config = await readSyncConfig();
|
|
1684
1864
|
if (!config) {
|
|
1685
|
-
|
|
1865
|
+
info("Sync not enabled (local-only mode)");
|
|
1866
|
+
dim(`Run ${"memories login"} to enable cloud sync.`);
|
|
1686
1867
|
return;
|
|
1687
1868
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1869
|
+
success("Cloud sync enabled");
|
|
1870
|
+
dim(`Remote: ${config.syncUrl}`);
|
|
1871
|
+
dim(`Org: ${config.org}`);
|
|
1872
|
+
dim(`Database: ${config.dbName}`);
|
|
1691
1873
|
});
|
|
1692
1874
|
|
|
1693
1875
|
// src/commands/generate.ts
|
|
1694
1876
|
import { Command as Command13 } from "commander";
|
|
1695
|
-
import
|
|
1696
|
-
import { writeFile as
|
|
1697
|
-
import { existsSync as
|
|
1698
|
-
import { dirname, resolve, join as
|
|
1699
|
-
import { homedir as
|
|
1877
|
+
import chalk11 from "chalk";
|
|
1878
|
+
import { writeFile as writeFile5, readFile as readFile5, mkdir as mkdir4 } from "fs/promises";
|
|
1879
|
+
import { existsSync as existsSync5, watch as fsWatch } from "fs";
|
|
1880
|
+
import { dirname, resolve, join as join5 } from "path";
|
|
1881
|
+
import { homedir as homedir4 } from "os";
|
|
1700
1882
|
import { checkbox } from "@inquirer/prompts";
|
|
1701
1883
|
var MARKER = "Generated by memories.sh";
|
|
1702
1884
|
var VALID_TYPES6 = ["rule", "decision", "fact", "note"];
|
|
@@ -1709,18 +1891,40 @@ function groupByType(memories) {
|
|
|
1709
1891
|
const order = ["Rules", "Key Decisions", "Project Facts", "Notes"];
|
|
1710
1892
|
return order.filter((t) => groups[t]?.length).map((title) => ({ title, memories: groups[title] }));
|
|
1711
1893
|
}
|
|
1712
|
-
|
|
1894
|
+
var AGENT_INSTRUCTIONS = `## Memory Management
|
|
1895
|
+
|
|
1896
|
+
When you learn something important about this project, save it for future sessions.
|
|
1897
|
+
|
|
1898
|
+
**Via CLI:**
|
|
1899
|
+
- \`memories add "<decision>" --type decision\` \u2014 Architectural decisions
|
|
1900
|
+
- \`memories add "<convention>" --type rule\` \u2014 Coding conventions
|
|
1901
|
+
- \`memories add "<fact>" --type fact\` \u2014 Project facts
|
|
1902
|
+
|
|
1903
|
+
**Via MCP (if connected):**
|
|
1904
|
+
Use the \`add_memory\` tool with content and type parameters.
|
|
1905
|
+
|
|
1906
|
+
**When to save:**
|
|
1907
|
+
- Architectural decisions and their rationale
|
|
1908
|
+
- Project-specific patterns or conventions
|
|
1909
|
+
- Non-obvious setup, configuration, or gotchas
|
|
1910
|
+
- Tricky bugs and how they were resolved`;
|
|
1911
|
+
function formatMemoriesAsMarkdown(memories, includeAgentInstructions = true) {
|
|
1713
1912
|
const sections = groupByType(memories);
|
|
1714
|
-
|
|
1715
|
-
return sections.map(({ title, memories: mems }) => {
|
|
1913
|
+
const memoriesContent = sections.length === 0 ? "" : sections.map(({ title, memories: mems }) => {
|
|
1716
1914
|
const items = mems.map((m) => `- ${m.content}`).join("\n");
|
|
1717
1915
|
return `## ${title}
|
|
1718
1916
|
|
|
1719
1917
|
${items}`;
|
|
1720
1918
|
}).join("\n\n");
|
|
1919
|
+
if (includeAgentInstructions) {
|
|
1920
|
+
return memoriesContent ? `${memoriesContent}
|
|
1921
|
+
|
|
1922
|
+
${AGENT_INSTRUCTIONS}` : AGENT_INSTRUCTIONS;
|
|
1923
|
+
}
|
|
1924
|
+
return memoriesContent;
|
|
1721
1925
|
}
|
|
1722
1926
|
function formatCursorMdc(memories) {
|
|
1723
|
-
const body = formatMemoriesAsMarkdown(memories);
|
|
1927
|
+
const body = formatMemoriesAsMarkdown(memories, true);
|
|
1724
1928
|
const frontmatter = [
|
|
1725
1929
|
"---",
|
|
1726
1930
|
"description: Project memories and rules from memories.sh",
|
|
@@ -1735,7 +1939,7 @@ function formatCursorMdc(memories) {
|
|
|
1735
1939
|
${body}`;
|
|
1736
1940
|
}
|
|
1737
1941
|
function formatWindsurf(memories) {
|
|
1738
|
-
const full = formatMemoriesAsMarkdown(memories);
|
|
1942
|
+
const full = formatMemoriesAsMarkdown(memories, true);
|
|
1739
1943
|
const LIMIT = 6e3;
|
|
1740
1944
|
if (full.length <= LIMIT) return full;
|
|
1741
1945
|
const truncated = full.slice(0, LIMIT);
|
|
@@ -1811,7 +2015,7 @@ function makeFooter() {
|
|
|
1811
2015
|
}
|
|
1812
2016
|
async function hasOurMarker(filePath) {
|
|
1813
2017
|
try {
|
|
1814
|
-
const content = await
|
|
2018
|
+
const content = await readFile5(filePath, "utf-8");
|
|
1815
2019
|
return content.includes(MARKER);
|
|
1816
2020
|
} catch {
|
|
1817
2021
|
return false;
|
|
@@ -1822,13 +2026,13 @@ async function checkGitignore(filePath) {
|
|
|
1822
2026
|
if (TRACK_BY_DEFAULT.has(filePath)) return;
|
|
1823
2027
|
const gitignorePath = resolve(".gitignore");
|
|
1824
2028
|
try {
|
|
1825
|
-
const content =
|
|
2029
|
+
const content = existsSync5(gitignorePath) ? await readFile5(gitignorePath, "utf-8") : "";
|
|
1826
2030
|
const lines = content.split("\n");
|
|
1827
2031
|
const parentDir = filePath.split("/")[0];
|
|
1828
2032
|
if (lines.some((l) => l.trim() === filePath || l.trim() === parentDir || l.trim() === `${parentDir}/`)) {
|
|
1829
2033
|
return;
|
|
1830
2034
|
}
|
|
1831
|
-
console.log(
|
|
2035
|
+
console.log(chalk11.dim(` hint: add "${filePath}" to .gitignore if you don't want it tracked`));
|
|
1832
2036
|
} catch {
|
|
1833
2037
|
}
|
|
1834
2038
|
}
|
|
@@ -1836,23 +2040,23 @@ async function writeTarget(target, memories, opts) {
|
|
|
1836
2040
|
const outPath = resolve(opts.output ?? target.defaultPath);
|
|
1837
2041
|
const content = target.format(memories) + makeFooter();
|
|
1838
2042
|
if (opts.dryRun) {
|
|
1839
|
-
console.log(
|
|
2043
|
+
console.log(chalk11.dim(`\u2500\u2500 ${target.name} \u2192 ${outPath} \u2500\u2500`));
|
|
1840
2044
|
console.log(content);
|
|
1841
2045
|
console.log();
|
|
1842
2046
|
return;
|
|
1843
2047
|
}
|
|
1844
|
-
if (
|
|
2048
|
+
if (existsSync5(outPath)) {
|
|
1845
2049
|
const ours = await hasOurMarker(outPath);
|
|
1846
2050
|
if (!ours && !opts.force) {
|
|
1847
2051
|
console.error(
|
|
1848
|
-
|
|
2052
|
+
chalk11.yellow("\u26A0") + ` ${outPath} exists and was not generated by memories.sh. Use ${chalk11.bold("--force")} to overwrite.`
|
|
1849
2053
|
);
|
|
1850
2054
|
return;
|
|
1851
2055
|
}
|
|
1852
2056
|
}
|
|
1853
|
-
await
|
|
1854
|
-
await
|
|
1855
|
-
console.log(
|
|
2057
|
+
await mkdir4(dirname(outPath), { recursive: true });
|
|
2058
|
+
await writeFile5(outPath, content, "utf-8");
|
|
2059
|
+
console.log(chalk11.green("\u2713") + ` Wrote ${target.name} \u2192 ${chalk11.dim(outPath)}`);
|
|
1856
2060
|
await checkGitignore(opts.output ?? target.defaultPath);
|
|
1857
2061
|
}
|
|
1858
2062
|
async function fetchMemories(types) {
|
|
@@ -1868,23 +2072,23 @@ function parseTypes(raw) {
|
|
|
1868
2072
|
const types = raw.split(",").map((s) => s.trim());
|
|
1869
2073
|
for (const t of types) {
|
|
1870
2074
|
if (!VALID_TYPES6.includes(t)) {
|
|
1871
|
-
console.error(
|
|
2075
|
+
console.error(chalk11.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES6.join(", ")}`);
|
|
1872
2076
|
process.exit(1);
|
|
1873
2077
|
}
|
|
1874
2078
|
}
|
|
1875
2079
|
return types;
|
|
1876
2080
|
}
|
|
1877
2081
|
function getDbPath2() {
|
|
1878
|
-
const dataDir = process.env.MEMORIES_DATA_DIR ??
|
|
1879
|
-
return
|
|
2082
|
+
const dataDir = process.env.MEMORIES_DATA_DIR ?? join5(homedir4(), ".config", "memories");
|
|
2083
|
+
return join5(dataDir, "local.db");
|
|
1880
2084
|
}
|
|
1881
2085
|
async function runWatch(targets, memories, opts) {
|
|
1882
2086
|
const dbPath = getDbPath2();
|
|
1883
|
-
if (!
|
|
1884
|
-
console.error(
|
|
2087
|
+
if (!existsSync5(dbPath)) {
|
|
2088
|
+
console.error(chalk11.red("\u2717") + " Database not found. Run: memories init");
|
|
1885
2089
|
process.exit(1);
|
|
1886
2090
|
}
|
|
1887
|
-
console.log(
|
|
2091
|
+
console.log(chalk11.dim(`Watching ${dbPath} for changes... (Ctrl+C to stop)
|
|
1888
2092
|
`));
|
|
1889
2093
|
const mems = await memories();
|
|
1890
2094
|
if (mems.length > 0) {
|
|
@@ -1904,7 +2108,7 @@ async function runWatch(targets, memories, opts) {
|
|
|
1904
2108
|
}
|
|
1905
2109
|
}
|
|
1906
2110
|
} catch (err) {
|
|
1907
|
-
console.error(
|
|
2111
|
+
console.error(chalk11.red("\u2717") + " Watch error:", err.message);
|
|
1908
2112
|
}
|
|
1909
2113
|
}, 500);
|
|
1910
2114
|
});
|
|
@@ -1920,19 +2124,19 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1920
2124
|
const selected = await checkbox({
|
|
1921
2125
|
message: "Select targets to generate",
|
|
1922
2126
|
choices: TARGETS.map((t) => ({
|
|
1923
|
-
name: `${t.name} ${
|
|
2127
|
+
name: `${t.name} ${chalk11.dim(`\u2192 ${t.defaultPath}`)}`,
|
|
1924
2128
|
value: t.name,
|
|
1925
2129
|
checked: true
|
|
1926
2130
|
}))
|
|
1927
2131
|
});
|
|
1928
2132
|
if (selected.length === 0) {
|
|
1929
|
-
console.log(
|
|
2133
|
+
console.log(chalk11.dim("No targets selected."));
|
|
1930
2134
|
return;
|
|
1931
2135
|
}
|
|
1932
2136
|
const types = parseTypes(opts.types);
|
|
1933
2137
|
const memories = await fetchMemories(types);
|
|
1934
2138
|
if (memories.length === 0) {
|
|
1935
|
-
console.error(
|
|
2139
|
+
console.error(chalk11.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1936
2140
|
return;
|
|
1937
2141
|
}
|
|
1938
2142
|
const selectedSet = new Set(selected);
|
|
@@ -1942,9 +2146,9 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1942
2146
|
if (opts.watch) {
|
|
1943
2147
|
await runWatch(TARGETS, () => fetchMemories(types), opts);
|
|
1944
2148
|
}
|
|
1945
|
-
} catch (
|
|
1946
|
-
if (
|
|
1947
|
-
console.error(
|
|
2149
|
+
} catch (error2) {
|
|
2150
|
+
if (error2.name === "ExitPromptError") return;
|
|
2151
|
+
console.error(chalk11.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1948
2152
|
process.exit(1);
|
|
1949
2153
|
}
|
|
1950
2154
|
});
|
|
@@ -1955,12 +2159,12 @@ for (const target of TARGETS) {
|
|
|
1955
2159
|
const types = parseTypes(opts.types);
|
|
1956
2160
|
const memories = await fetchMemories(types);
|
|
1957
2161
|
if (memories.length === 0) {
|
|
1958
|
-
console.error(
|
|
2162
|
+
console.error(chalk11.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1959
2163
|
return;
|
|
1960
2164
|
}
|
|
1961
2165
|
await writeTarget(target, memories, opts);
|
|
1962
|
-
} catch (
|
|
1963
|
-
console.error(
|
|
2166
|
+
} catch (error2) {
|
|
2167
|
+
console.error(chalk11.red("\u2717") + ` Failed to generate ${target.name}:`, error2 instanceof Error ? error2.message : "Unknown error");
|
|
1964
2168
|
process.exit(1);
|
|
1965
2169
|
}
|
|
1966
2170
|
})
|
|
@@ -1976,14 +2180,14 @@ generateCommand.addCommand(
|
|
|
1976
2180
|
}
|
|
1977
2181
|
const memories = await fetchMemories(types);
|
|
1978
2182
|
if (memories.length === 0) {
|
|
1979
|
-
console.error(
|
|
2183
|
+
console.error(chalk11.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1980
2184
|
return;
|
|
1981
2185
|
}
|
|
1982
2186
|
for (const target of TARGETS) {
|
|
1983
2187
|
await writeTarget(target, memories, opts);
|
|
1984
2188
|
}
|
|
1985
|
-
} catch (
|
|
1986
|
-
console.error(
|
|
2189
|
+
} catch (error2) {
|
|
2190
|
+
console.error(chalk11.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1987
2191
|
process.exit(1);
|
|
1988
2192
|
}
|
|
1989
2193
|
})
|
|
@@ -1991,11 +2195,11 @@ generateCommand.addCommand(
|
|
|
1991
2195
|
|
|
1992
2196
|
// src/commands/edit.ts
|
|
1993
2197
|
import { Command as Command14 } from "commander";
|
|
1994
|
-
import
|
|
2198
|
+
import chalk12 from "chalk";
|
|
1995
2199
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1996
2200
|
import { writeFileSync, readFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1997
2201
|
import { tmpdir } from "os";
|
|
1998
|
-
import { join as
|
|
2202
|
+
import { join as join6 } from "path";
|
|
1999
2203
|
import { nanoid as nanoid3 } from "nanoid";
|
|
2000
2204
|
import { select } from "@inquirer/prompts";
|
|
2001
2205
|
var VALID_TYPES7 = ["rule", "decision", "fact", "note"];
|
|
@@ -2007,13 +2211,13 @@ async function pickMemory() {
|
|
|
2007
2211
|
const projectId = getProjectId() ?? void 0;
|
|
2008
2212
|
const memories = await listMemories({ limit: 100, projectId });
|
|
2009
2213
|
if (memories.length === 0) {
|
|
2010
|
-
console.error(
|
|
2214
|
+
console.error(chalk12.dim("No memories found."));
|
|
2011
2215
|
process.exit(0);
|
|
2012
2216
|
}
|
|
2013
2217
|
const id = await select({
|
|
2014
2218
|
message: "Select a memory to edit",
|
|
2015
2219
|
choices: memories.map((m) => ({
|
|
2016
|
-
name: `${
|
|
2220
|
+
name: `${chalk12.dim(m.type.padEnd(9))} ${truncate2(m.content, 60)} ${chalk12.dim(m.id)}`,
|
|
2017
2221
|
value: m.id
|
|
2018
2222
|
}))
|
|
2019
2223
|
});
|
|
@@ -2023,7 +2227,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2023
2227
|
try {
|
|
2024
2228
|
if (!id) {
|
|
2025
2229
|
if (!process.stdin.isTTY) {
|
|
2026
|
-
console.error(
|
|
2230
|
+
console.error(chalk12.red("\u2717") + " Memory ID required in non-interactive mode");
|
|
2027
2231
|
process.exit(1);
|
|
2028
2232
|
}
|
|
2029
2233
|
id = await pickMemory();
|
|
@@ -2034,18 +2238,18 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2034
2238
|
args: [id]
|
|
2035
2239
|
});
|
|
2036
2240
|
if (result.rows.length === 0) {
|
|
2037
|
-
console.error(
|
|
2241
|
+
console.error(chalk12.red("\u2717") + ` Memory ${chalk12.dim(id)} not found`);
|
|
2038
2242
|
process.exit(1);
|
|
2039
2243
|
}
|
|
2040
2244
|
const memory = result.rows[0];
|
|
2041
2245
|
if (opts.type && !VALID_TYPES7.includes(opts.type)) {
|
|
2042
|
-
console.error(
|
|
2246
|
+
console.error(chalk12.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES7.join(", ")}`);
|
|
2043
2247
|
process.exit(1);
|
|
2044
2248
|
}
|
|
2045
2249
|
let newContent = opts.content;
|
|
2046
2250
|
if (newContent === void 0 && opts.tags === void 0 && opts.type === void 0) {
|
|
2047
2251
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
2048
|
-
const tmpFile =
|
|
2252
|
+
const tmpFile = join6(tmpdir(), `memories-edit-${nanoid3(6)}.md`);
|
|
2049
2253
|
writeFileSync(tmpFile, memory.content, "utf-8");
|
|
2050
2254
|
try {
|
|
2051
2255
|
execFileSync2(editor, [tmpFile], { stdio: "inherit" });
|
|
@@ -2057,7 +2261,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2057
2261
|
}
|
|
2058
2262
|
}
|
|
2059
2263
|
if (newContent === memory.content) {
|
|
2060
|
-
console.log(
|
|
2264
|
+
console.log(chalk12.dim("No changes made."));
|
|
2061
2265
|
return;
|
|
2062
2266
|
}
|
|
2063
2267
|
}
|
|
@@ -2067,24 +2271,24 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2067
2271
|
if (opts.type !== void 0) updates.type = opts.type;
|
|
2068
2272
|
const updated = await updateMemory(id, updates);
|
|
2069
2273
|
if (!updated) {
|
|
2070
|
-
console.error(
|
|
2274
|
+
console.error(chalk12.red("\u2717") + ` Failed to update memory ${chalk12.dim(id)}`);
|
|
2071
2275
|
process.exit(1);
|
|
2072
2276
|
}
|
|
2073
2277
|
const changes = [];
|
|
2074
2278
|
if (updates.content !== void 0) changes.push("content");
|
|
2075
2279
|
if (updates.tags !== void 0) changes.push("tags");
|
|
2076
2280
|
if (updates.type !== void 0) changes.push(`type\u2192${updates.type}`);
|
|
2077
|
-
console.log(
|
|
2078
|
-
} catch (
|
|
2079
|
-
if (
|
|
2080
|
-
console.error(
|
|
2281
|
+
console.log(chalk12.green("\u2713") + ` Updated ${chalk12.dim(id)} (${changes.join(", ")})`);
|
|
2282
|
+
} catch (error2) {
|
|
2283
|
+
if (error2.name === "ExitPromptError") return;
|
|
2284
|
+
console.error(chalk12.red("\u2717") + " Failed to edit memory:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2081
2285
|
process.exit(1);
|
|
2082
2286
|
}
|
|
2083
2287
|
});
|
|
2084
2288
|
|
|
2085
2289
|
// src/commands/stats.ts
|
|
2086
2290
|
import { Command as Command15 } from "commander";
|
|
2087
|
-
import
|
|
2291
|
+
import chalk13 from "chalk";
|
|
2088
2292
|
var statsCommand = new Command15("stats").description("Show memory statistics").option("--json", "Output as JSON").action(async (opts) => {
|
|
2089
2293
|
try {
|
|
2090
2294
|
const db = await getDb();
|
|
@@ -2121,46 +2325,46 @@ var statsCommand = new Command15("stats").description("Show memory statistics").
|
|
|
2121
2325
|
console.log(JSON.stringify(data, null, 2));
|
|
2122
2326
|
return;
|
|
2123
2327
|
}
|
|
2124
|
-
console.log(
|
|
2328
|
+
console.log(chalk13.bold("Memory Statistics\n"));
|
|
2125
2329
|
if (projectId) {
|
|
2126
|
-
console.log(` Project: ${
|
|
2330
|
+
console.log(` Project: ${chalk13.dim(projectId)}`);
|
|
2127
2331
|
}
|
|
2128
|
-
console.log(` Total: ${
|
|
2332
|
+
console.log(` Total: ${chalk13.bold(String(total))} active, ${chalk13.dim(String(deleted))} deleted
|
|
2129
2333
|
`);
|
|
2130
2334
|
if (rows.length === 0) {
|
|
2131
|
-
console.log(
|
|
2335
|
+
console.log(chalk13.dim(' No memories yet. Add one with: memories add "Your memory"'));
|
|
2132
2336
|
return;
|
|
2133
2337
|
}
|
|
2134
2338
|
const typeWidths = { rule: 8, decision: 8, fact: 8, note: 8 };
|
|
2135
2339
|
console.log(
|
|
2136
|
-
` ${
|
|
2340
|
+
` ${chalk13.dim("Type".padEnd(12))}${chalk13.dim("Scope".padEnd(10))}${chalk13.dim("Count")}`
|
|
2137
2341
|
);
|
|
2138
|
-
console.log(
|
|
2342
|
+
console.log(chalk13.dim(" " + "\u2500".repeat(30)));
|
|
2139
2343
|
for (const row of rows) {
|
|
2140
2344
|
const type = row.type.padEnd(12);
|
|
2141
2345
|
const scope = row.scope.padEnd(10);
|
|
2142
2346
|
console.log(` ${type}${scope}${Number(row.count)}`);
|
|
2143
2347
|
}
|
|
2144
|
-
} catch (
|
|
2145
|
-
console.error(
|
|
2348
|
+
} catch (error2) {
|
|
2349
|
+
console.error(chalk13.red("\u2717") + " Failed to get stats:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2146
2350
|
process.exit(1);
|
|
2147
2351
|
}
|
|
2148
2352
|
});
|
|
2149
2353
|
|
|
2150
2354
|
// src/commands/doctor.ts
|
|
2151
2355
|
import { Command as Command16 } from "commander";
|
|
2152
|
-
import
|
|
2153
|
-
import { existsSync as
|
|
2154
|
-
import { join as
|
|
2356
|
+
import chalk14 from "chalk";
|
|
2357
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2358
|
+
import { join as join7 } from "path";
|
|
2155
2359
|
var doctorCommand = new Command16("doctor").description("Check memories health and diagnose issues").option("--fix", "Attempt to fix issues found").action(async (opts) => {
|
|
2156
2360
|
try {
|
|
2157
|
-
console.log(
|
|
2361
|
+
console.log(chalk14.bold("memories doctor\n"));
|
|
2158
2362
|
const checks = [
|
|
2159
2363
|
{
|
|
2160
2364
|
name: "Database file",
|
|
2161
2365
|
run: async () => {
|
|
2162
|
-
const dbPath =
|
|
2163
|
-
if (
|
|
2366
|
+
const dbPath = join7(getConfigDir(), "local.db");
|
|
2367
|
+
if (existsSync6(dbPath)) {
|
|
2164
2368
|
return { ok: true, message: `Found at ${dbPath}` };
|
|
2165
2369
|
}
|
|
2166
2370
|
return { ok: false, message: `Not found at ${dbPath}. Run: memories init` };
|
|
@@ -2258,43 +2462,43 @@ var doctorCommand = new Command16("doctor").description("Check memories health a
|
|
|
2258
2462
|
let hasIssues = false;
|
|
2259
2463
|
for (const check of checks) {
|
|
2260
2464
|
const { ok, message } = await check.run();
|
|
2261
|
-
const icon = ok ?
|
|
2262
|
-
console.log(` ${icon} ${
|
|
2465
|
+
const icon = ok ? chalk14.green("\u2713") : chalk14.red("\u2717");
|
|
2466
|
+
console.log(` ${icon} ${chalk14.bold(check.name)}: ${message}`);
|
|
2263
2467
|
if (!ok) hasIssues = true;
|
|
2264
2468
|
}
|
|
2265
2469
|
if (opts.fix) {
|
|
2266
|
-
console.log(
|
|
2470
|
+
console.log(chalk14.bold("\nRunning fixes...\n"));
|
|
2267
2471
|
const db = await getDb();
|
|
2268
2472
|
const purged = await db.execute(
|
|
2269
2473
|
"DELETE FROM memories WHERE deleted_at IS NOT NULL"
|
|
2270
2474
|
);
|
|
2271
|
-
console.log(` ${
|
|
2475
|
+
console.log(` ${chalk14.green("\u2713")} Purged ${purged.rowsAffected} soft-deleted records`);
|
|
2272
2476
|
try {
|
|
2273
2477
|
await db.execute("INSERT INTO memories_fts(memories_fts) VALUES('rebuild')");
|
|
2274
|
-
console.log(` ${
|
|
2478
|
+
console.log(` ${chalk14.green("\u2713")} Rebuilt FTS index`);
|
|
2275
2479
|
} catch {
|
|
2276
|
-
console.log(` ${
|
|
2480
|
+
console.log(` ${chalk14.yellow("\u26A0")} Could not rebuild FTS index`);
|
|
2277
2481
|
}
|
|
2278
2482
|
}
|
|
2279
2483
|
console.log();
|
|
2280
2484
|
if (hasIssues) {
|
|
2281
|
-
console.log(
|
|
2485
|
+
console.log(chalk14.yellow("Some issues detected.") + (opts.fix ? "" : " Run with --fix to attempt repairs."));
|
|
2282
2486
|
} else {
|
|
2283
|
-
console.log(
|
|
2487
|
+
console.log(chalk14.green("All checks passed."));
|
|
2284
2488
|
}
|
|
2285
|
-
} catch (
|
|
2286
|
-
console.error(
|
|
2489
|
+
} catch (error2) {
|
|
2490
|
+
console.error(chalk14.red("\u2717") + " Doctor failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2287
2491
|
process.exit(1);
|
|
2288
2492
|
}
|
|
2289
2493
|
});
|
|
2290
2494
|
|
|
2291
2495
|
// src/commands/hook.ts
|
|
2292
2496
|
import { Command as Command17 } from "commander";
|
|
2293
|
-
import
|
|
2294
|
-
import { readFile as
|
|
2295
|
-
import { existsSync as
|
|
2497
|
+
import chalk15 from "chalk";
|
|
2498
|
+
import { readFile as readFile6, writeFile as writeFile6, chmod } from "fs/promises";
|
|
2499
|
+
import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
|
|
2296
2500
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2297
|
-
import { join as
|
|
2501
|
+
import { join as join8 } from "path";
|
|
2298
2502
|
var HOOK_MARKER_START = "# >>> memories.sh hook >>>";
|
|
2299
2503
|
var HOOK_MARKER_END = "# <<< memories.sh hook <<<";
|
|
2300
2504
|
var HOOK_SNIPPET = `
|
|
@@ -2315,22 +2519,22 @@ function getGitDir() {
|
|
|
2315
2519
|
function getHookLocation(hookName) {
|
|
2316
2520
|
const gitDir = getGitDir();
|
|
2317
2521
|
if (!gitDir) return null;
|
|
2318
|
-
const huskyPath =
|
|
2319
|
-
if (
|
|
2522
|
+
const huskyPath = join8(".husky", hookName);
|
|
2523
|
+
if (existsSync7(".husky") && !existsSync7(join8(".husky", "_"))) {
|
|
2320
2524
|
return { path: huskyPath, type: "husky" };
|
|
2321
2525
|
}
|
|
2322
|
-
const huskyLegacyPath =
|
|
2323
|
-
if (
|
|
2526
|
+
const huskyLegacyPath = join8(".husky", "_", hookName);
|
|
2527
|
+
if (existsSync7(join8(".husky", "_"))) {
|
|
2324
2528
|
return { path: huskyLegacyPath, type: "husky" };
|
|
2325
2529
|
}
|
|
2326
|
-
if (
|
|
2530
|
+
if (existsSync7(huskyPath)) {
|
|
2327
2531
|
return { path: huskyPath, type: "husky" };
|
|
2328
2532
|
}
|
|
2329
|
-
return { path:
|
|
2533
|
+
return { path: join8(gitDir, "hooks", hookName), type: "git" };
|
|
2330
2534
|
}
|
|
2331
2535
|
function detectLintStaged() {
|
|
2332
2536
|
try {
|
|
2333
|
-
if (!
|
|
2537
|
+
if (!existsSync7("package.json")) return false;
|
|
2334
2538
|
const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
|
|
2335
2539
|
return !!(pkg["lint-staged"] || pkg.devDependencies?.["lint-staged"] || pkg.dependencies?.["lint-staged"]);
|
|
2336
2540
|
} catch {
|
|
@@ -2343,31 +2547,31 @@ hookCommand.addCommand(
|
|
|
2343
2547
|
try {
|
|
2344
2548
|
const location = getHookLocation(opts.hook);
|
|
2345
2549
|
if (!location) {
|
|
2346
|
-
console.error(
|
|
2550
|
+
console.error(chalk15.red("\u2717") + " Not in a git repository");
|
|
2347
2551
|
process.exit(1);
|
|
2348
2552
|
}
|
|
2349
2553
|
const hookPath = location.path;
|
|
2350
|
-
if (
|
|
2351
|
-
const content = await
|
|
2554
|
+
if (existsSync7(hookPath)) {
|
|
2555
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2352
2556
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2353
|
-
console.log(
|
|
2557
|
+
console.log(chalk15.dim("Hook already installed. Use 'memories hook uninstall' first to reinstall."));
|
|
2354
2558
|
return;
|
|
2355
2559
|
}
|
|
2356
|
-
await
|
|
2560
|
+
await writeFile6(hookPath, content.trimEnd() + "\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2357
2561
|
} else {
|
|
2358
|
-
await
|
|
2562
|
+
await writeFile6(hookPath, "#!/bin/sh\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2359
2563
|
}
|
|
2360
2564
|
await chmod(hookPath, 493);
|
|
2361
2565
|
const locationLabel = location.type === "husky" ? "Husky" : ".git/hooks";
|
|
2362
|
-
console.log(
|
|
2363
|
-
console.log(
|
|
2566
|
+
console.log(chalk15.green("\u2713") + ` Installed memories hook in ${chalk15.dim(opts.hook)} (${locationLabel})`);
|
|
2567
|
+
console.log(chalk15.dim(" Rule files will auto-generate on each commit."));
|
|
2364
2568
|
if (detectLintStaged()) {
|
|
2365
2569
|
console.log(
|
|
2366
|
-
|
|
2570
|
+
chalk15.dim("\n lint-staged detected. You can also add to your lint-staged config:") + chalk15.dim('\n "*.md": "memories generate all --force"')
|
|
2367
2571
|
);
|
|
2368
2572
|
}
|
|
2369
|
-
} catch (
|
|
2370
|
-
console.error(
|
|
2573
|
+
} catch (error2) {
|
|
2574
|
+
console.error(chalk15.red("\u2717") + " Failed to install hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2371
2575
|
process.exit(1);
|
|
2372
2576
|
}
|
|
2373
2577
|
})
|
|
@@ -2377,17 +2581,17 @@ hookCommand.addCommand(
|
|
|
2377
2581
|
try {
|
|
2378
2582
|
const location = getHookLocation(opts.hook);
|
|
2379
2583
|
if (!location) {
|
|
2380
|
-
console.error(
|
|
2584
|
+
console.error(chalk15.red("\u2717") + " Not in a git repository");
|
|
2381
2585
|
process.exit(1);
|
|
2382
2586
|
}
|
|
2383
2587
|
const hookPath = location.path;
|
|
2384
|
-
if (!
|
|
2385
|
-
console.log(
|
|
2588
|
+
if (!existsSync7(hookPath)) {
|
|
2589
|
+
console.log(chalk15.dim("No hook file found."));
|
|
2386
2590
|
return;
|
|
2387
2591
|
}
|
|
2388
|
-
const content = await
|
|
2592
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2389
2593
|
if (!content.includes(HOOK_MARKER_START)) {
|
|
2390
|
-
console.log(
|
|
2594
|
+
console.log(chalk15.dim("No memories hook found in " + opts.hook));
|
|
2391
2595
|
return;
|
|
2392
2596
|
}
|
|
2393
2597
|
const regex = new RegExp(
|
|
@@ -2395,15 +2599,15 @@ hookCommand.addCommand(
|
|
|
2395
2599
|
);
|
|
2396
2600
|
const cleaned = content.replace(regex, "\n");
|
|
2397
2601
|
if (cleaned.trim() === "#!/bin/sh" || cleaned.trim() === "") {
|
|
2398
|
-
const { unlink } = await import("fs/promises");
|
|
2399
|
-
await
|
|
2400
|
-
console.log(
|
|
2602
|
+
const { unlink: unlink2 } = await import("fs/promises");
|
|
2603
|
+
await unlink2(hookPath);
|
|
2604
|
+
console.log(chalk15.green("\u2713") + ` Removed ${chalk15.dim(opts.hook)} hook (was memories-only)`);
|
|
2401
2605
|
} else {
|
|
2402
|
-
await
|
|
2403
|
-
console.log(
|
|
2606
|
+
await writeFile6(hookPath, cleaned, "utf-8");
|
|
2607
|
+
console.log(chalk15.green("\u2713") + ` Removed memories section from ${chalk15.dim(opts.hook)}`);
|
|
2404
2608
|
}
|
|
2405
|
-
} catch (
|
|
2406
|
-
console.error(
|
|
2609
|
+
} catch (error2) {
|
|
2610
|
+
console.error(chalk15.red("\u2717") + " Failed to uninstall hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2407
2611
|
process.exit(1);
|
|
2408
2612
|
}
|
|
2409
2613
|
})
|
|
@@ -2413,21 +2617,21 @@ hookCommand.addCommand(
|
|
|
2413
2617
|
try {
|
|
2414
2618
|
const hookPath = getHookLocation(opts.hook)?.path;
|
|
2415
2619
|
if (!hookPath) {
|
|
2416
|
-
console.error(
|
|
2620
|
+
console.error(chalk15.red("\u2717") + " Not in a git repository");
|
|
2417
2621
|
process.exit(1);
|
|
2418
2622
|
}
|
|
2419
|
-
if (!
|
|
2420
|
-
console.log(
|
|
2623
|
+
if (!existsSync7(hookPath)) {
|
|
2624
|
+
console.log(chalk15.dim("Not installed") + ` \u2014 no ${opts.hook} hook found`);
|
|
2421
2625
|
return;
|
|
2422
2626
|
}
|
|
2423
|
-
const content = await
|
|
2627
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2424
2628
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2425
|
-
console.log(
|
|
2629
|
+
console.log(chalk15.green("\u2713") + ` Installed in ${chalk15.dim(hookPath)}`);
|
|
2426
2630
|
} else {
|
|
2427
|
-
console.log(
|
|
2631
|
+
console.log(chalk15.dim("Not installed") + ` \u2014 ${opts.hook} exists but has no memories section`);
|
|
2428
2632
|
}
|
|
2429
|
-
} catch (
|
|
2430
|
-
console.error(
|
|
2633
|
+
} catch (error2) {
|
|
2634
|
+
console.error(chalk15.red("\u2717") + " Failed to check hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2431
2635
|
process.exit(1);
|
|
2432
2636
|
}
|
|
2433
2637
|
})
|
|
@@ -2438,9 +2642,9 @@ function escapeRegex(str) {
|
|
|
2438
2642
|
|
|
2439
2643
|
// src/commands/ingest.ts
|
|
2440
2644
|
import { Command as Command18 } from "commander";
|
|
2441
|
-
import
|
|
2442
|
-
import { readFile as
|
|
2443
|
-
import { existsSync as
|
|
2645
|
+
import chalk16 from "chalk";
|
|
2646
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2647
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2444
2648
|
var SOURCES = [
|
|
2445
2649
|
{ name: "cursor", paths: [".cursor/rules/memories.mdc", ".cursorrules"], description: "Cursor rules" },
|
|
2446
2650
|
{ name: "claude", paths: ["CLAUDE.md"], description: "Claude Code instructions" },
|
|
@@ -2500,14 +2704,14 @@ function normalize(s) {
|
|
|
2500
2704
|
var ingestCommand = new Command18("ingest").description("Import memories from existing IDE rule files").argument("[source]", "Source to import from (cursor, claude, agents, copilot, windsurf, cline, roo, gemini, or file path)").option("--type <type>", "Override type for all imported memories").option("--dry-run", "Preview without importing").option("--all", "Scan all known IDE rule file locations").option("--no-dedup", "Skip duplicate detection").action(async (source, opts) => {
|
|
2501
2705
|
try {
|
|
2502
2706
|
if (opts.type && !VALID_TYPES8.includes(opts.type)) {
|
|
2503
|
-
console.error(
|
|
2707
|
+
console.error(chalk16.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES8.join(", ")}`);
|
|
2504
2708
|
process.exit(1);
|
|
2505
2709
|
}
|
|
2506
2710
|
const filesToProcess = [];
|
|
2507
2711
|
if (opts.all) {
|
|
2508
2712
|
for (const src of SOURCES) {
|
|
2509
2713
|
for (const p of src.paths) {
|
|
2510
|
-
if (
|
|
2714
|
+
if (existsSync8(p)) {
|
|
2511
2715
|
filesToProcess.push({ name: src.name, path: p });
|
|
2512
2716
|
}
|
|
2513
2717
|
}
|
|
@@ -2516,27 +2720,27 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2516
2720
|
const known = SOURCES.find((s) => s.name === source);
|
|
2517
2721
|
if (known) {
|
|
2518
2722
|
for (const p of known.paths) {
|
|
2519
|
-
if (
|
|
2723
|
+
if (existsSync8(p)) {
|
|
2520
2724
|
filesToProcess.push({ name: known.name, path: p });
|
|
2521
2725
|
break;
|
|
2522
2726
|
}
|
|
2523
2727
|
}
|
|
2524
2728
|
if (filesToProcess.length === 0) {
|
|
2525
|
-
console.error(
|
|
2729
|
+
console.error(chalk16.red("\u2717") + ` No ${known.description} file found at: ${known.paths.join(", ")}`);
|
|
2526
2730
|
process.exit(1);
|
|
2527
2731
|
}
|
|
2528
|
-
} else if (
|
|
2732
|
+
} else if (existsSync8(source)) {
|
|
2529
2733
|
filesToProcess.push({ name: "file", path: source });
|
|
2530
2734
|
} else {
|
|
2531
|
-
console.error(
|
|
2735
|
+
console.error(chalk16.red("\u2717") + ` Unknown source "${source}". Valid: ${SOURCES.map((s) => s.name).join(", ")}, or a file path`);
|
|
2532
2736
|
process.exit(1);
|
|
2533
2737
|
}
|
|
2534
2738
|
} else {
|
|
2535
|
-
console.error(
|
|
2739
|
+
console.error(chalk16.red("\u2717") + " Specify a source or use --all");
|
|
2536
2740
|
process.exit(1);
|
|
2537
2741
|
}
|
|
2538
2742
|
if (filesToProcess.length === 0) {
|
|
2539
|
-
console.log(
|
|
2743
|
+
console.log(chalk16.dim("No IDE rule files found."));
|
|
2540
2744
|
return;
|
|
2541
2745
|
}
|
|
2542
2746
|
const existingSet = /* @__PURE__ */ new Set();
|
|
@@ -2550,29 +2754,29 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2550
2754
|
let totalImported = 0;
|
|
2551
2755
|
let totalSkipped = 0;
|
|
2552
2756
|
for (const file of filesToProcess) {
|
|
2553
|
-
const content = await
|
|
2757
|
+
const content = await readFile7(file.path, "utf-8");
|
|
2554
2758
|
if (content.includes(MARKER2)) {
|
|
2555
|
-
console.log(
|
|
2759
|
+
console.log(chalk16.dim(` Skipping ${file.path} (generated by memories.sh)`));
|
|
2556
2760
|
continue;
|
|
2557
2761
|
}
|
|
2558
2762
|
const memories = extractMemories(content);
|
|
2559
2763
|
if (memories.length === 0) {
|
|
2560
|
-
console.log(
|
|
2764
|
+
console.log(chalk16.dim(` No importable memories found in ${file.path}`));
|
|
2561
2765
|
continue;
|
|
2562
2766
|
}
|
|
2563
|
-
console.log(
|
|
2564
|
-
${file.name}`) +
|
|
2767
|
+
console.log(chalk16.bold(`
|
|
2768
|
+
${file.name}`) + chalk16.dim(` (${file.path}) \u2014 ${memories.length} items`));
|
|
2565
2769
|
for (const mem of memories) {
|
|
2566
2770
|
const type = opts.type ?? mem.type;
|
|
2567
2771
|
if (opts.dedup !== false && existingSet.has(normalize(mem.content))) {
|
|
2568
2772
|
if (opts.dryRun) {
|
|
2569
|
-
console.log(` ${
|
|
2773
|
+
console.log(` ${chalk16.dim("skip")} ${chalk16.dim(mem.content)}`);
|
|
2570
2774
|
}
|
|
2571
2775
|
totalSkipped++;
|
|
2572
2776
|
continue;
|
|
2573
2777
|
}
|
|
2574
2778
|
if (opts.dryRun) {
|
|
2575
|
-
const typeColor = type === "rule" ?
|
|
2779
|
+
const typeColor = type === "rule" ? chalk16.blue : type === "decision" ? chalk16.yellow : type === "fact" ? chalk16.green : chalk16.dim;
|
|
2576
2780
|
console.log(` ${typeColor(type.padEnd(9))} ${mem.content}`);
|
|
2577
2781
|
} else {
|
|
2578
2782
|
await addMemory(mem.content, { type });
|
|
@@ -2583,23 +2787,23 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2583
2787
|
}
|
|
2584
2788
|
if (opts.dryRun) {
|
|
2585
2789
|
const skipMsg = totalSkipped > 0 ? ` (${totalSkipped} duplicates skipped)` : "";
|
|
2586
|
-
console.log(
|
|
2790
|
+
console.log(chalk16.dim(`
|
|
2587
2791
|
Dry run \u2014 no memories imported.${skipMsg} Remove --dry-run to import.`));
|
|
2588
2792
|
} else {
|
|
2589
|
-
const skipMsg = totalSkipped > 0 ?
|
|
2590
|
-
console.log(
|
|
2793
|
+
const skipMsg = totalSkipped > 0 ? chalk16.dim(` (${totalSkipped} duplicates skipped)`) : "";
|
|
2794
|
+
console.log(chalk16.green("\n\u2713") + ` Imported ${totalImported} memories` + skipMsg);
|
|
2591
2795
|
}
|
|
2592
|
-
} catch (
|
|
2593
|
-
console.error(
|
|
2796
|
+
} catch (error2) {
|
|
2797
|
+
console.error(chalk16.red("\u2717") + " Failed to ingest:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2594
2798
|
process.exit(1);
|
|
2595
2799
|
}
|
|
2596
2800
|
});
|
|
2597
2801
|
|
|
2598
2802
|
// src/commands/diff.ts
|
|
2599
2803
|
import { Command as Command19 } from "commander";
|
|
2600
|
-
import
|
|
2601
|
-
import { readFile as
|
|
2602
|
-
import { existsSync as
|
|
2804
|
+
import chalk17 from "chalk";
|
|
2805
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2806
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2603
2807
|
import { resolve as resolve2 } from "path";
|
|
2604
2808
|
var MARKER3 = "Generated by memories.sh";
|
|
2605
2809
|
var VALID_TYPES9 = ["rule", "decision", "fact", "note"];
|
|
@@ -2632,7 +2836,7 @@ function parseTypes2(raw) {
|
|
|
2632
2836
|
const types = raw.split(",").map((s) => s.trim());
|
|
2633
2837
|
for (const t of types) {
|
|
2634
2838
|
if (!VALID_TYPES9.includes(t)) {
|
|
2635
|
-
console.error(
|
|
2839
|
+
console.error(chalk17.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES9.join(", ")}`);
|
|
2636
2840
|
process.exit(1);
|
|
2637
2841
|
}
|
|
2638
2842
|
}
|
|
@@ -2644,7 +2848,7 @@ async function fetchMemories2(types) {
|
|
|
2644
2848
|
}
|
|
2645
2849
|
async function diffTarget(target, currentMemories, outputPath) {
|
|
2646
2850
|
const filePath = resolve2(outputPath ?? target.defaultPath);
|
|
2647
|
-
if (!
|
|
2851
|
+
if (!existsSync9(filePath)) {
|
|
2648
2852
|
return {
|
|
2649
2853
|
added: currentMemories.map((m) => m.content),
|
|
2650
2854
|
removed: [],
|
|
@@ -2655,7 +2859,7 @@ async function diffTarget(target, currentMemories, outputPath) {
|
|
|
2655
2859
|
generatedAt: null
|
|
2656
2860
|
};
|
|
2657
2861
|
}
|
|
2658
|
-
const content = await
|
|
2862
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2659
2863
|
const isOurs = content.includes(MARKER3);
|
|
2660
2864
|
const generatedAt = extractTimestamp(content);
|
|
2661
2865
|
const fileMemories = extractFileMemories(content);
|
|
@@ -2671,7 +2875,7 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2671
2875
|
const memories = await fetchMemories2(types);
|
|
2672
2876
|
const targetsToCheck = target && target !== "all" ? TARGETS2.filter((t) => t.name === target) : TARGETS2;
|
|
2673
2877
|
if (target && target !== "all" && targetsToCheck.length === 0) {
|
|
2674
|
-
console.error(
|
|
2878
|
+
console.error(chalk17.red("\u2717") + ` Unknown target "${target}". Valid: ${TARGETS2.map((t) => t.name).join(", ")}`);
|
|
2675
2879
|
process.exit(1);
|
|
2676
2880
|
}
|
|
2677
2881
|
let anyStale = false;
|
|
@@ -2680,54 +2884,54 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2680
2884
|
if (!result.exists && !target) continue;
|
|
2681
2885
|
const hasChanges = result.added.length > 0 || result.removed.length > 0;
|
|
2682
2886
|
if (!result.exists) {
|
|
2683
|
-
console.log(
|
|
2684
|
-
${t.name}`) +
|
|
2685
|
-
console.log(
|
|
2887
|
+
console.log(chalk17.bold(`
|
|
2888
|
+
${t.name}`) + chalk17.dim(` \u2192 ${result.filePath}`));
|
|
2889
|
+
console.log(chalk17.yellow(" Not generated yet.") + chalk17.dim(` Run: memories generate ${t.name}`));
|
|
2686
2890
|
anyStale = true;
|
|
2687
2891
|
continue;
|
|
2688
2892
|
}
|
|
2689
2893
|
if (!result.isOurs) {
|
|
2690
|
-
console.log(
|
|
2691
|
-
${t.name}`) +
|
|
2692
|
-
console.log(
|
|
2894
|
+
console.log(chalk17.bold(`
|
|
2895
|
+
${t.name}`) + chalk17.dim(` \u2192 ${result.filePath}`));
|
|
2896
|
+
console.log(chalk17.dim(" Not managed by memories.sh (no marker found)"));
|
|
2693
2897
|
continue;
|
|
2694
2898
|
}
|
|
2695
2899
|
if (!hasChanges) {
|
|
2696
2900
|
if (target) {
|
|
2697
|
-
console.log(
|
|
2698
|
-
${t.name}`) +
|
|
2699
|
-
const since = result.generatedAt ?
|
|
2700
|
-
console.log(
|
|
2901
|
+
console.log(chalk17.bold(`
|
|
2902
|
+
${t.name}`) + chalk17.dim(` \u2192 ${result.filePath}`));
|
|
2903
|
+
const since = result.generatedAt ? chalk17.dim(` (generated ${formatRelative(result.generatedAt)})`) : "";
|
|
2904
|
+
console.log(chalk17.green(" Up to date.") + since);
|
|
2701
2905
|
}
|
|
2702
2906
|
continue;
|
|
2703
2907
|
}
|
|
2704
2908
|
anyStale = true;
|
|
2705
|
-
console.log(
|
|
2706
|
-
${t.name}`) +
|
|
2909
|
+
console.log(chalk17.bold(`
|
|
2910
|
+
${t.name}`) + chalk17.dim(` \u2192 ${result.filePath}`));
|
|
2707
2911
|
if (result.generatedAt) {
|
|
2708
|
-
console.log(
|
|
2912
|
+
console.log(chalk17.dim(` Generated ${formatRelative(result.generatedAt)}`));
|
|
2709
2913
|
}
|
|
2710
2914
|
for (const a of result.added) {
|
|
2711
|
-
console.log(
|
|
2915
|
+
console.log(chalk17.green(` + ${a}`));
|
|
2712
2916
|
}
|
|
2713
2917
|
for (const r of result.removed) {
|
|
2714
|
-
console.log(
|
|
2918
|
+
console.log(chalk17.red(` - ${r}`));
|
|
2715
2919
|
}
|
|
2716
2920
|
if (result.unchanged > 0) {
|
|
2717
|
-
console.log(
|
|
2921
|
+
console.log(chalk17.dim(` ${result.unchanged} unchanged`));
|
|
2718
2922
|
}
|
|
2719
2923
|
}
|
|
2720
2924
|
if (!anyStale) {
|
|
2721
2925
|
if (target) {
|
|
2722
2926
|
} else {
|
|
2723
|
-
console.log(
|
|
2927
|
+
console.log(chalk17.green("\n All generated files are up to date."));
|
|
2724
2928
|
}
|
|
2725
2929
|
} else {
|
|
2726
|
-
console.log(
|
|
2727
|
-
Run ${
|
|
2930
|
+
console.log(chalk17.dim(`
|
|
2931
|
+
Run ${chalk17.bold("memories generate")} to update stale files.`));
|
|
2728
2932
|
}
|
|
2729
|
-
} catch (
|
|
2730
|
-
console.error(
|
|
2933
|
+
} catch (error2) {
|
|
2934
|
+
console.error(chalk17.red("\u2717") + " Diff failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2731
2935
|
process.exit(1);
|
|
2732
2936
|
}
|
|
2733
2937
|
});
|
|
@@ -2748,7 +2952,7 @@ function formatRelative(isoDate) {
|
|
|
2748
2952
|
|
|
2749
2953
|
// src/commands/tag.ts
|
|
2750
2954
|
import { Command as Command20 } from "commander";
|
|
2751
|
-
import
|
|
2955
|
+
import chalk18 from "chalk";
|
|
2752
2956
|
var VALID_TYPES10 = ["rule", "decision", "fact", "note"];
|
|
2753
2957
|
function buildWhere(filters) {
|
|
2754
2958
|
const conditions = ["deleted_at IS NULL"];
|
|
@@ -2800,12 +3004,12 @@ tagCommand.addCommand(
|
|
|
2800
3004
|
updated++;
|
|
2801
3005
|
}
|
|
2802
3006
|
if (opts.dryRun) {
|
|
2803
|
-
console.log(
|
|
3007
|
+
console.log(chalk18.dim(`Dry run \u2014 would tag ${updated} memories with "${tag}" (${skipped} already tagged)`));
|
|
2804
3008
|
} else {
|
|
2805
|
-
console.log(
|
|
3009
|
+
console.log(chalk18.green("\u2713") + ` Tagged ${updated} memories with "${tag}"` + (skipped > 0 ? chalk18.dim(` (${skipped} already tagged)`) : ""));
|
|
2806
3010
|
}
|
|
2807
|
-
} catch (
|
|
2808
|
-
console.error(
|
|
3011
|
+
} catch (error2) {
|
|
3012
|
+
console.error(chalk18.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2809
3013
|
process.exit(1);
|
|
2810
3014
|
}
|
|
2811
3015
|
})
|
|
@@ -2834,12 +3038,12 @@ tagCommand.addCommand(
|
|
|
2834
3038
|
updated++;
|
|
2835
3039
|
}
|
|
2836
3040
|
if (opts.dryRun) {
|
|
2837
|
-
console.log(
|
|
3041
|
+
console.log(chalk18.dim(`Dry run \u2014 would remove "${tag}" from ${updated} memories`));
|
|
2838
3042
|
} else {
|
|
2839
|
-
console.log(
|
|
3043
|
+
console.log(chalk18.green("\u2713") + ` Removed "${tag}" from ${updated} memories`);
|
|
2840
3044
|
}
|
|
2841
|
-
} catch (
|
|
2842
|
-
console.error(
|
|
3045
|
+
} catch (error2) {
|
|
3046
|
+
console.error(chalk18.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2843
3047
|
process.exit(1);
|
|
2844
3048
|
}
|
|
2845
3049
|
})
|
|
@@ -2860,22 +3064,110 @@ tagCommand.addCommand(
|
|
|
2860
3064
|
}
|
|
2861
3065
|
}
|
|
2862
3066
|
if (counts.size === 0) {
|
|
2863
|
-
console.log(
|
|
3067
|
+
console.log(chalk18.dim("No tags found."));
|
|
2864
3068
|
return;
|
|
2865
3069
|
}
|
|
2866
3070
|
const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
2867
3071
|
for (const [tag, count] of sorted) {
|
|
2868
|
-
console.log(` ${
|
|
3072
|
+
console.log(` ${chalk18.bold(tag)} ${chalk18.dim(`(${count})`)}`);
|
|
2869
3073
|
}
|
|
2870
|
-
} catch (
|
|
2871
|
-
console.error(
|
|
3074
|
+
} catch (error2) {
|
|
3075
|
+
console.error(chalk18.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2872
3076
|
process.exit(1);
|
|
2873
3077
|
}
|
|
2874
3078
|
})
|
|
2875
3079
|
);
|
|
2876
3080
|
|
|
3081
|
+
// src/commands/login.ts
|
|
3082
|
+
import { Command as Command21 } from "commander";
|
|
3083
|
+
import chalk19 from "chalk";
|
|
3084
|
+
import ora2 from "ora";
|
|
3085
|
+
import { randomBytes } from "crypto";
|
|
3086
|
+
import { execFile } from "child_process";
|
|
3087
|
+
var DEFAULT_API_URL = "https://memories.sh";
|
|
3088
|
+
function openBrowser(url) {
|
|
3089
|
+
const platform = process.platform;
|
|
3090
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
3091
|
+
execFile(cmd, [url], () => {
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
var loginCommand = new Command21("login").description("Log in to memories.sh to enable cloud sync").option("--api-url <url>", "API base URL", DEFAULT_API_URL).action(async (opts) => {
|
|
3095
|
+
banner();
|
|
3096
|
+
const existing = await readAuth();
|
|
3097
|
+
if (existing) {
|
|
3098
|
+
warn(`Already logged in as ${chalk19.bold(existing.email)}`);
|
|
3099
|
+
dim(`Run ${chalk19.cyan("memories logout")} to sign out first.`);
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
box(
|
|
3103
|
+
chalk19.bold("Pro features include:\n\n") + chalk19.dim("\u2192 ") + "Cloud sync & backup\n" + chalk19.dim("\u2192 ") + "Cross-device access\n" + chalk19.dim("\u2192 ") + "Web dashboard\n" + chalk19.dim("\u2192 ") + "Priority support",
|
|
3104
|
+
"Upgrade to Pro"
|
|
3105
|
+
);
|
|
3106
|
+
const code = randomBytes(16).toString("hex");
|
|
3107
|
+
const authUrl = `${opts.apiUrl}/app/auth/cli?code=${code}`;
|
|
3108
|
+
console.log(chalk19.bold("Open this URL in your browser:\n"));
|
|
3109
|
+
console.log(` ${chalk19.cyan(authUrl)}
|
|
3110
|
+
`);
|
|
3111
|
+
try {
|
|
3112
|
+
openBrowser(authUrl);
|
|
3113
|
+
dim("Browser opened automatically");
|
|
3114
|
+
} catch {
|
|
3115
|
+
}
|
|
3116
|
+
const spinner = ora2({
|
|
3117
|
+
text: "Waiting for authorization...",
|
|
3118
|
+
color: "magenta"
|
|
3119
|
+
}).start();
|
|
3120
|
+
const maxAttempts = 60;
|
|
3121
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
3122
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
3123
|
+
spinner.text = `Waiting for authorization... (${Math.floor((maxAttempts - i) * 5 / 60)}m remaining)`;
|
|
3124
|
+
try {
|
|
3125
|
+
const res = await fetch(`${opts.apiUrl}/api/auth/cli`, {
|
|
3126
|
+
method: "POST",
|
|
3127
|
+
headers: { "Content-Type": "application/json" },
|
|
3128
|
+
body: JSON.stringify({ action: "poll", code })
|
|
3129
|
+
});
|
|
3130
|
+
if (res.ok) {
|
|
3131
|
+
const data = await res.json();
|
|
3132
|
+
await saveAuth({
|
|
3133
|
+
token: data.token,
|
|
3134
|
+
email: data.email,
|
|
3135
|
+
apiUrl: opts.apiUrl
|
|
3136
|
+
});
|
|
3137
|
+
spinner.stop();
|
|
3138
|
+
console.log("");
|
|
3139
|
+
success(`Logged in as ${chalk19.bold(data.email)}`);
|
|
3140
|
+
dim("Your cloud database has been provisioned automatically.");
|
|
3141
|
+
nextSteps([
|
|
3142
|
+
`${chalk19.cyan("memories sync")} ${chalk19.dim("to sync your memories")}`,
|
|
3143
|
+
`${chalk19.cyan("memories.sh/app")} ${chalk19.dim("to view your dashboard")}`
|
|
3144
|
+
]);
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
if (res.status !== 202) {
|
|
3148
|
+
const text = await res.text();
|
|
3149
|
+
spinner.stop();
|
|
3150
|
+
error(`Authorization failed: ${text}`);
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
} catch {
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
spinner.stop();
|
|
3157
|
+
error("Authorization timed out. Please try again.");
|
|
3158
|
+
});
|
|
3159
|
+
var logoutCommand = new Command21("logout").description("Log out of memories.sh").action(async () => {
|
|
3160
|
+
const existing = await readAuth();
|
|
3161
|
+
if (!existing) {
|
|
3162
|
+
info("Not logged in.");
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
await clearAuth();
|
|
3166
|
+
success("Logged out successfully.");
|
|
3167
|
+
});
|
|
3168
|
+
|
|
2877
3169
|
// src/index.ts
|
|
2878
|
-
var program = new
|
|
3170
|
+
var program = new Command22().name("memories").description("A local-first memory layer for AI agents").version("0.2.1");
|
|
2879
3171
|
program.addCommand(initCommand);
|
|
2880
3172
|
program.addCommand(addCommand);
|
|
2881
3173
|
program.addCommand(recallCommand);
|
|
@@ -2896,4 +3188,6 @@ program.addCommand(hookCommand);
|
|
|
2896
3188
|
program.addCommand(ingestCommand);
|
|
2897
3189
|
program.addCommand(diffCommand);
|
|
2898
3190
|
program.addCommand(tagCommand);
|
|
3191
|
+
program.addCommand(loginCommand);
|
|
3192
|
+
program.addCommand(logoutCommand);
|
|
2899
3193
|
program.parse();
|