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