@memories.sh/cli 0.3.0 → 0.3.2
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 +728 -542
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -32,7 +32,8 @@ import { Command as Command29 } from "commander";
|
|
|
32
32
|
|
|
33
33
|
// src/commands/init.ts
|
|
34
34
|
import { Command } from "commander";
|
|
35
|
-
import
|
|
35
|
+
import chalk3 from "chalk";
|
|
36
|
+
import { confirm } from "@inquirer/prompts";
|
|
36
37
|
|
|
37
38
|
// src/lib/auth.ts
|
|
38
39
|
import { readFile, writeFile, mkdir, unlink } from "fs/promises";
|
|
@@ -76,8 +77,120 @@ function getApiClient(auth) {
|
|
|
76
77
|
};
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
// src/lib/
|
|
80
|
+
// src/lib/setup.ts
|
|
81
|
+
import { existsSync as existsSync2 } from "fs";
|
|
82
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
83
|
+
import { join as join2, dirname } from "path";
|
|
84
|
+
import { homedir as homedir2 } from "os";
|
|
80
85
|
import chalk from "chalk";
|
|
86
|
+
var TOOLS = [
|
|
87
|
+
{
|
|
88
|
+
name: "Cursor",
|
|
89
|
+
configDir: ".cursor",
|
|
90
|
+
mcpConfigPath: ".cursor/mcp.json",
|
|
91
|
+
mcpConfigFormat: "cursor",
|
|
92
|
+
instructionFile: ".cursor/rules/memories.mdc",
|
|
93
|
+
generateCmd: "cursor"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "Claude Code",
|
|
97
|
+
configDir: ".claude",
|
|
98
|
+
mcpConfigPath: ".mcp.json",
|
|
99
|
+
mcpConfigFormat: "claude",
|
|
100
|
+
instructionFile: "CLAUDE.md",
|
|
101
|
+
generateCmd: "claude"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "Windsurf",
|
|
105
|
+
configDir: ".windsurf",
|
|
106
|
+
mcpConfigPath: ".windsurf/mcp.json",
|
|
107
|
+
mcpConfigFormat: "cursor",
|
|
108
|
+
instructionFile: ".windsurf/rules/memories.md",
|
|
109
|
+
generateCmd: "windsurf"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "VS Code",
|
|
113
|
+
configDir: ".vscode",
|
|
114
|
+
mcpConfigPath: ".vscode/mcp.json",
|
|
115
|
+
mcpConfigFormat: "vscode",
|
|
116
|
+
instructionFile: ".github/copilot-instructions.md",
|
|
117
|
+
generateCmd: "copilot"
|
|
118
|
+
}
|
|
119
|
+
];
|
|
120
|
+
var MEMORIES_MCP_CONFIG = {
|
|
121
|
+
command: "npx",
|
|
122
|
+
args: ["-y", "@memories.sh/cli", "serve"]
|
|
123
|
+
};
|
|
124
|
+
function detectTools(cwd = process.cwd()) {
|
|
125
|
+
const home = homedir2();
|
|
126
|
+
const detected = [];
|
|
127
|
+
for (const tool of TOOLS) {
|
|
128
|
+
const projectConfigDir = join2(cwd, tool.configDir);
|
|
129
|
+
const projectMcpPath = join2(cwd, tool.mcpConfigPath);
|
|
130
|
+
const projectInstructionPath = join2(cwd, tool.instructionFile);
|
|
131
|
+
const globalConfigDir = join2(home, tool.configDir);
|
|
132
|
+
const globalMcpPath = join2(home, tool.mcpConfigPath);
|
|
133
|
+
const hasProjectConfig = existsSync2(projectConfigDir);
|
|
134
|
+
const hasGlobalConfig = existsSync2(globalConfigDir);
|
|
135
|
+
if (hasProjectConfig || hasGlobalConfig) {
|
|
136
|
+
detected.push({
|
|
137
|
+
tool,
|
|
138
|
+
hasConfig: hasProjectConfig || hasGlobalConfig,
|
|
139
|
+
hasMcp: existsSync2(projectMcpPath) || existsSync2(globalMcpPath),
|
|
140
|
+
hasInstructions: existsSync2(projectInstructionPath),
|
|
141
|
+
globalConfig: !hasProjectConfig && hasGlobalConfig
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return detected;
|
|
146
|
+
}
|
|
147
|
+
async function setupMcp(tool, options = {}) {
|
|
148
|
+
const { cwd = process.cwd(), global: useGlobal = false, dryRun = false } = options;
|
|
149
|
+
const home = homedir2();
|
|
150
|
+
const configPath = useGlobal ? join2(home, tool.mcpConfigPath) : join2(cwd, tool.mcpConfigPath);
|
|
151
|
+
try {
|
|
152
|
+
let config = {};
|
|
153
|
+
if (existsSync2(configPath)) {
|
|
154
|
+
const content = await readFile2(configPath, "utf-8");
|
|
155
|
+
config = JSON.parse(content);
|
|
156
|
+
}
|
|
157
|
+
const serversKey = tool.mcpConfigFormat === "vscode" ? "servers" : "mcpServers";
|
|
158
|
+
const servers = config[serversKey] ?? {};
|
|
159
|
+
if (servers.memories) {
|
|
160
|
+
return {
|
|
161
|
+
success: true,
|
|
162
|
+
message: "MCP already configured",
|
|
163
|
+
path: configPath
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
config[serversKey] = {
|
|
167
|
+
...servers,
|
|
168
|
+
memories: MEMORIES_MCP_CONFIG
|
|
169
|
+
};
|
|
170
|
+
if (dryRun) {
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
message: `Would add MCP config to ${configPath}`,
|
|
174
|
+
path: configPath
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
await mkdir2(dirname(configPath), { recursive: true });
|
|
178
|
+
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
179
|
+
return {
|
|
180
|
+
success: true,
|
|
181
|
+
message: "MCP configured",
|
|
182
|
+
path: configPath
|
|
183
|
+
};
|
|
184
|
+
} catch (error2) {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
message: `Failed to setup MCP: ${error2 instanceof Error ? error2.message : "Unknown error"}`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/lib/ui.ts
|
|
193
|
+
import chalk2 from "chalk";
|
|
81
194
|
import figlet from "figlet";
|
|
82
195
|
import gradient from "gradient-string";
|
|
83
196
|
import boxen from "boxen";
|
|
@@ -88,25 +201,25 @@ function banner() {
|
|
|
88
201
|
horizontalLayout: "fitted"
|
|
89
202
|
});
|
|
90
203
|
console.log(memoriesGradient(text));
|
|
91
|
-
console.log(
|
|
204
|
+
console.log(chalk2.dim(" One memory, every AI tool\n"));
|
|
92
205
|
}
|
|
93
206
|
function success(message) {
|
|
94
|
-
console.log(
|
|
207
|
+
console.log(chalk2.green("\u2713") + " " + message);
|
|
95
208
|
}
|
|
96
209
|
function warn(message) {
|
|
97
|
-
console.log(
|
|
210
|
+
console.log(chalk2.yellow("\u26A0") + " " + message);
|
|
98
211
|
}
|
|
99
212
|
function error(message) {
|
|
100
|
-
console.log(
|
|
213
|
+
console.log(chalk2.red("\u2717") + " " + message);
|
|
101
214
|
}
|
|
102
215
|
function info(message) {
|
|
103
|
-
console.log(
|
|
216
|
+
console.log(chalk2.blue("\u2139") + " " + message);
|
|
104
217
|
}
|
|
105
218
|
function step(num, total, message) {
|
|
106
|
-
console.log(
|
|
219
|
+
console.log(chalk2.dim(`[${num}/${total}]`) + " " + message);
|
|
107
220
|
}
|
|
108
221
|
function dim(message) {
|
|
109
|
-
console.log(
|
|
222
|
+
console.log(chalk2.dim(" " + message));
|
|
110
223
|
}
|
|
111
224
|
function box(content, title) {
|
|
112
225
|
console.log(
|
|
@@ -121,33 +234,28 @@ function box(content, title) {
|
|
|
121
234
|
);
|
|
122
235
|
}
|
|
123
236
|
function nextSteps(steps) {
|
|
124
|
-
const content = steps.map((s) =>
|
|
125
|
-
box(content,
|
|
237
|
+
const content = steps.map((s) => chalk2.dim("\u2192 ") + s).join("\n");
|
|
238
|
+
box(content, chalk2.bold("Next steps"));
|
|
126
239
|
}
|
|
127
240
|
function proFeature(feature) {
|
|
128
241
|
console.log(
|
|
129
|
-
|
|
242
|
+
chalk2.yellow("\u2B50") + " " + chalk2.dim(`${feature} requires `) + chalk2.bold("Pro") + chalk2.dim(". Run ") + chalk2.cyan("memories login") + chalk2.dim(" to upgrade.")
|
|
130
243
|
);
|
|
131
244
|
}
|
|
132
245
|
|
|
133
246
|
// src/commands/init.ts
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{ name: "Claude Code", cmd: "claude" },
|
|
137
|
-
{ name: "GitHub Copilot", cmd: "copilot" },
|
|
138
|
-
{ name: "Windsurf", cmd: "windsurf" },
|
|
139
|
-
{ name: "Gemini CLI", cmd: "gemini" }
|
|
140
|
-
];
|
|
141
|
-
var initCommand = new Command("init").description("Initialize memories - one place for all your AI coding tools").option("-g, --global", "Initialize global rules (apply to all projects)").option("-r, --rule <rule>", "Add an initial rule", (val, acc) => [...acc, val], []).action(async (opts) => {
|
|
247
|
+
import { execSync } from "child_process";
|
|
248
|
+
var initCommand = new Command("init").description("Initialize memories - set up MCP and instruction files for your AI tools").option("-g, --global", "Initialize global rules (apply to all projects)").option("-r, --rule <rule>", "Add an initial rule", (val, acc) => [...acc, val], []).option("--skip-mcp", "Skip MCP configuration").option("--skip-generate", "Skip generating instruction files").option("-y, --yes", "Auto-confirm all prompts").action(async (opts) => {
|
|
142
249
|
try {
|
|
143
250
|
banner();
|
|
144
|
-
console.log(
|
|
145
|
-
step(1,
|
|
251
|
+
console.log(chalk3.dim(" One place for your rules. Works with every tool.\n"));
|
|
252
|
+
step(1, 4, "Setting up local storage...");
|
|
146
253
|
await getDb();
|
|
147
254
|
const configDir = getConfigDir();
|
|
148
|
-
dim(`
|
|
149
|
-
step(2,
|
|
255
|
+
dim(`Database: ${configDir}/local.db`);
|
|
256
|
+
step(2, 4, "Detecting scope...");
|
|
150
257
|
let useGlobal = opts.global;
|
|
258
|
+
const cwd = process.cwd();
|
|
151
259
|
if (!useGlobal) {
|
|
152
260
|
const projectId = getProjectId();
|
|
153
261
|
const gitRoot = getGitRoot();
|
|
@@ -158,48 +266,101 @@ var initCommand = new Command("init").description("Initialize memories - one pla
|
|
|
158
266
|
success("Project scope detected");
|
|
159
267
|
dim(`Project: ${projectId}`);
|
|
160
268
|
dim(`Root: ${gitRoot}`);
|
|
161
|
-
dim("Use --global for rules that apply everywhere");
|
|
162
269
|
}
|
|
163
270
|
} else {
|
|
164
271
|
success("Global scope (rules apply to all projects)");
|
|
165
272
|
}
|
|
166
|
-
step(3,
|
|
167
|
-
const
|
|
168
|
-
|
|
273
|
+
step(3, 4, "Detecting AI coding tools...");
|
|
274
|
+
const detected = detectTools(cwd);
|
|
275
|
+
if (detected.length === 0) {
|
|
276
|
+
dim("No AI coding tools detected (.cursor/, .claude/, .windsurf/, .vscode/)");
|
|
277
|
+
dim("MCP will work with any tool that supports it.");
|
|
278
|
+
} else {
|
|
279
|
+
for (const d of detected) {
|
|
280
|
+
const scope = d.globalConfig ? chalk3.dim(" [global]") : "";
|
|
281
|
+
const mcpStatus = d.hasMcp ? chalk3.green("\u2713 MCP") : chalk3.dim("\u25CB MCP");
|
|
282
|
+
const rulesStatus = d.hasInstructions ? chalk3.green("\u2713 Rules") : chalk3.dim("\u25CB Rules");
|
|
283
|
+
console.log(` ${chalk3.white(d.tool.name)}${scope} ${mcpStatus} ${rulesStatus}`);
|
|
284
|
+
}
|
|
285
|
+
if (!opts.skipMcp) {
|
|
286
|
+
const toolsNeedingMcp = detected.filter((d) => !d.hasMcp);
|
|
287
|
+
if (toolsNeedingMcp.length > 0) {
|
|
288
|
+
console.log("");
|
|
289
|
+
const shouldSetupMcp = opts.yes || await confirm({
|
|
290
|
+
message: `Configure MCP for ${toolsNeedingMcp.map((d) => d.tool.name).join(", ")}?`,
|
|
291
|
+
default: true
|
|
292
|
+
});
|
|
293
|
+
if (shouldSetupMcp) {
|
|
294
|
+
for (const d of toolsNeedingMcp) {
|
|
295
|
+
const result = await setupMcp(d.tool, {
|
|
296
|
+
cwd,
|
|
297
|
+
global: d.globalConfig
|
|
298
|
+
});
|
|
299
|
+
if (result.success) {
|
|
300
|
+
success(`${d.tool.name}: ${result.message}`);
|
|
301
|
+
if (result.path) dim(` \u2192 ${result.path}`);
|
|
302
|
+
} else {
|
|
303
|
+
warn(`${d.tool.name}: ${result.message}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!opts.skipGenerate) {
|
|
310
|
+
const toolsNeedingInstructions = detected.filter((d) => !d.hasInstructions);
|
|
311
|
+
const memories = await listMemories({ limit: 1 });
|
|
312
|
+
if (toolsNeedingInstructions.length > 0 && memories.length > 0) {
|
|
313
|
+
console.log("");
|
|
314
|
+
const shouldGenerate = opts.yes || await confirm({
|
|
315
|
+
message: `Generate instruction files for ${toolsNeedingInstructions.map((d) => d.tool.name).join(", ")}?`,
|
|
316
|
+
default: true
|
|
317
|
+
});
|
|
318
|
+
if (shouldGenerate) {
|
|
319
|
+
for (const d of toolsNeedingInstructions) {
|
|
320
|
+
try {
|
|
321
|
+
execSync(`node ${process.argv[1]} generate ${d.tool.generateCmd} --force`, {
|
|
322
|
+
cwd,
|
|
323
|
+
stdio: "pipe"
|
|
324
|
+
});
|
|
325
|
+
success(`${d.tool.name}: Generated ${d.tool.instructionFile}`);
|
|
326
|
+
} catch {
|
|
327
|
+
warn(`${d.tool.name}: Failed to generate instructions`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
step(4, 4, "Finalizing...");
|
|
169
335
|
if (opts.rule?.length) {
|
|
170
|
-
console.log("");
|
|
171
336
|
info("Adding rules...");
|
|
172
337
|
for (const rule of opts.rule) {
|
|
173
|
-
|
|
338
|
+
await addMemory(rule, {
|
|
174
339
|
type: "rule",
|
|
175
340
|
global: useGlobal
|
|
176
341
|
});
|
|
177
342
|
dim(`+ ${rule}`);
|
|
178
343
|
}
|
|
179
344
|
}
|
|
180
|
-
console.log("");
|
|
181
345
|
const auth = await readAuth();
|
|
182
346
|
if (auth) {
|
|
183
|
-
success(`Syncing as ${
|
|
347
|
+
success(`Syncing as ${chalk3.bold(auth.email)}`);
|
|
184
348
|
} else {
|
|
185
|
-
dim("Local only. Run " +
|
|
349
|
+
dim("Local only. Run " + chalk3.cyan("memories login") + " to sync across machines.");
|
|
186
350
|
}
|
|
187
351
|
console.log("");
|
|
188
|
-
console.log(
|
|
352
|
+
console.log(chalk3.bold(" Quick Start:"));
|
|
189
353
|
console.log("");
|
|
190
|
-
console.log(
|
|
191
|
-
console.log(` ${
|
|
192
|
-
console.log(` ${chalk2.cyan("memories add --rule")} ${chalk2.dim('"Prefer functional components in React"')}`);
|
|
354
|
+
console.log(chalk3.dim(" Add rules:"));
|
|
355
|
+
console.log(` ${chalk3.cyan("memories add --rule")} ${chalk3.dim('"Always use TypeScript strict mode"')}`);
|
|
193
356
|
console.log("");
|
|
194
|
-
console.log(
|
|
195
|
-
console.log(` ${
|
|
196
|
-
console.log(` ${chalk2.cyan("memories generate claude")} ${chalk2.dim("\u2192 CLAUDE.md")}`);
|
|
197
|
-
console.log(` ${chalk2.cyan("memories generate copilot")} ${chalk2.dim("\u2192 .github/copilot-instructions.md")}`);
|
|
198
|
-
console.log(` ${chalk2.cyan("memories generate all")} ${chalk2.dim("\u2192 all tools at once")}`);
|
|
357
|
+
console.log(chalk3.dim(" Regenerate instruction files after adding rules:"));
|
|
358
|
+
console.log(` ${chalk3.cyan("memories generate all")}`);
|
|
199
359
|
console.log("");
|
|
200
|
-
console.log(
|
|
360
|
+
console.log(chalk3.dim(" Your rules will be available via MCP and in generated files."));
|
|
201
361
|
console.log("");
|
|
202
362
|
} catch (error2) {
|
|
363
|
+
if (error2.name === "ExitPromptError") return;
|
|
203
364
|
error("Failed to initialize: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
204
365
|
process.exit(1);
|
|
205
366
|
}
|
|
@@ -207,7 +368,7 @@ var initCommand = new Command("init").description("Initialize memories - one pla
|
|
|
207
368
|
|
|
208
369
|
// src/commands/add.ts
|
|
209
370
|
import { Command as Command2 } from "commander";
|
|
210
|
-
import
|
|
371
|
+
import chalk4 from "chalk";
|
|
211
372
|
|
|
212
373
|
// src/lib/templates.ts
|
|
213
374
|
import { input } from "@inquirer/prompts";
|
|
@@ -370,7 +531,7 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
370
531
|
else if (opts.fact) type = "fact";
|
|
371
532
|
else if (opts.type) {
|
|
372
533
|
if (!VALID_TYPES.includes(opts.type)) {
|
|
373
|
-
console.error(
|
|
534
|
+
console.error(chalk4.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES.join(", ")}`);
|
|
374
535
|
process.exit(1);
|
|
375
536
|
}
|
|
376
537
|
type = opts.type;
|
|
@@ -378,11 +539,11 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
378
539
|
const memory = await addMemory(content, { tags, global: opts.global, type });
|
|
379
540
|
const typeLabel = type === "rule" ? "Rule" : type === "decision" ? "Decision" : type === "fact" ? "Fact" : "Note";
|
|
380
541
|
const scopeInfo = memory.scope === "global" ? "global" : "project";
|
|
381
|
-
success(`Stored ${
|
|
542
|
+
success(`Stored ${chalk4.bold(typeLabel.toLowerCase())} ${chalk4.dim(memory.id)}`);
|
|
382
543
|
dim(`Scope: ${scopeInfo}${tags?.length ? ` \u2022 Tags: ${tags.join(", ")}` : ""}`);
|
|
383
544
|
if (type === "rule") {
|
|
384
545
|
console.log("");
|
|
385
|
-
dim(`Run ${
|
|
546
|
+
dim(`Run ${chalk4.cyan("memories generate")} to update your IDE rule files`);
|
|
386
547
|
}
|
|
387
548
|
} catch (error2) {
|
|
388
549
|
error("Failed to add memory: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
@@ -392,7 +553,7 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
392
553
|
|
|
393
554
|
// src/commands/recall.ts
|
|
394
555
|
import { Command as Command3 } from "commander";
|
|
395
|
-
import
|
|
556
|
+
import chalk5 from "chalk";
|
|
396
557
|
var TYPE_ICONS = {
|
|
397
558
|
rule: "\u{1F4CC}",
|
|
398
559
|
decision: "\u{1F4A1}",
|
|
@@ -400,18 +561,18 @@ var TYPE_ICONS = {
|
|
|
400
561
|
note: "\u{1F4DD}"
|
|
401
562
|
};
|
|
402
563
|
var TYPE_COLORS = {
|
|
403
|
-
rule:
|
|
404
|
-
decision:
|
|
405
|
-
fact:
|
|
406
|
-
note:
|
|
564
|
+
rule: chalk5.yellow,
|
|
565
|
+
decision: chalk5.cyan,
|
|
566
|
+
fact: chalk5.green,
|
|
567
|
+
note: chalk5.white
|
|
407
568
|
};
|
|
408
569
|
function formatMemory(m, verbose) {
|
|
409
570
|
const icon = TYPE_ICONS[m.type] || "\u{1F4DD}";
|
|
410
|
-
const colorFn = TYPE_COLORS[m.type] ||
|
|
411
|
-
const scope = m.scope === "global" ?
|
|
412
|
-
const tags = m.tags ?
|
|
571
|
+
const colorFn = TYPE_COLORS[m.type] || chalk5.white;
|
|
572
|
+
const scope = m.scope === "global" ? chalk5.dim("G") : chalk5.dim("P");
|
|
573
|
+
const tags = m.tags ? chalk5.dim(` [${m.tags}]`) : "";
|
|
413
574
|
if (verbose) {
|
|
414
|
-
return `${icon} ${scope} ${
|
|
575
|
+
return `${icon} ${scope} ${chalk5.dim(m.id)} ${colorFn(m.content)}${tags}`;
|
|
415
576
|
}
|
|
416
577
|
return `${icon} ${colorFn(m.content)}`;
|
|
417
578
|
}
|
|
@@ -425,11 +586,11 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
425
586
|
return;
|
|
426
587
|
}
|
|
427
588
|
if (rules2.length === 0) {
|
|
428
|
-
console.log(
|
|
429
|
-
console.log(
|
|
589
|
+
console.log(chalk5.dim("No rules defined."));
|
|
590
|
+
console.log(chalk5.dim('Add one with: memories add --rule "Your rule here"'));
|
|
430
591
|
return;
|
|
431
592
|
}
|
|
432
|
-
console.log(
|
|
593
|
+
console.log(chalk5.bold("Rules:"));
|
|
433
594
|
for (const rule of rules2) {
|
|
434
595
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
435
596
|
}
|
|
@@ -444,26 +605,26 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
444
605
|
return;
|
|
445
606
|
}
|
|
446
607
|
if (rules.length > 0) {
|
|
447
|
-
console.log(
|
|
608
|
+
console.log(chalk5.bold("Rules:"));
|
|
448
609
|
for (const rule of rules) {
|
|
449
610
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
450
611
|
}
|
|
451
612
|
console.log("");
|
|
452
613
|
}
|
|
453
614
|
if (memories.length > 0) {
|
|
454
|
-
console.log(
|
|
615
|
+
console.log(chalk5.bold(query ? `Relevant to "${query}":` : "Recent memories:"));
|
|
455
616
|
for (const m of memories) {
|
|
456
617
|
console.log(formatMemory(m, opts.verbose ?? false));
|
|
457
618
|
}
|
|
458
619
|
} else if (query) {
|
|
459
|
-
console.log(
|
|
620
|
+
console.log(chalk5.dim(`No memories found matching "${query}"`));
|
|
460
621
|
}
|
|
461
622
|
if (rules.length === 0 && memories.length === 0) {
|
|
462
|
-
console.log(
|
|
463
|
-
console.log(
|
|
623
|
+
console.log(chalk5.dim("No memories found."));
|
|
624
|
+
console.log(chalk5.dim('Add some with: memories add "Your memory here"'));
|
|
464
625
|
}
|
|
465
626
|
} catch (error2) {
|
|
466
|
-
console.error(
|
|
627
|
+
console.error(chalk5.red("\u2717") + " Failed to recall:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
467
628
|
process.exit(1);
|
|
468
629
|
}
|
|
469
630
|
});
|
|
@@ -471,7 +632,7 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
471
632
|
// src/commands/prompt.ts
|
|
472
633
|
import { Command as Command4 } from "commander";
|
|
473
634
|
import { execFileSync } from "child_process";
|
|
474
|
-
import
|
|
635
|
+
import chalk6 from "chalk";
|
|
475
636
|
var VALID_TYPES2 = ["rule", "decision", "fact", "note"];
|
|
476
637
|
function formatMarkdown(sections) {
|
|
477
638
|
return sections.map(({ title, memories }) => {
|
|
@@ -514,7 +675,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
514
675
|
try {
|
|
515
676
|
const format = opts.format ?? "markdown";
|
|
516
677
|
if (!["markdown", "xml", "plain"].includes(format)) {
|
|
517
|
-
console.error(
|
|
678
|
+
console.error(chalk6.red("\u2717") + ` Invalid format "${opts.format}". Use: markdown, xml, plain`);
|
|
518
679
|
process.exit(1);
|
|
519
680
|
}
|
|
520
681
|
const projectId = getProjectId() ?? void 0;
|
|
@@ -526,7 +687,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
526
687
|
for (const t of opts.include.split(",").map((s) => s.trim())) {
|
|
527
688
|
const normalized = t.replace(/s$/, "");
|
|
528
689
|
if (!VALID_TYPES2.includes(normalized)) {
|
|
529
|
-
console.error(
|
|
690
|
+
console.error(chalk6.red("\u2717") + ` Invalid type "${t}". Valid: decisions, facts, notes`);
|
|
530
691
|
process.exit(1);
|
|
531
692
|
}
|
|
532
693
|
if (normalized !== "rule") extraTypes.push(normalized);
|
|
@@ -555,7 +716,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
555
716
|
}
|
|
556
717
|
if (sections.length === 0) {
|
|
557
718
|
if (!opts.quiet) {
|
|
558
|
-
console.error(
|
|
719
|
+
console.error(chalk6.dim('No memories found. Add rules with: memories add --rule "Your rule"'));
|
|
559
720
|
}
|
|
560
721
|
return;
|
|
561
722
|
}
|
|
@@ -575,26 +736,26 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
575
736
|
if (copied) {
|
|
576
737
|
console.log(output);
|
|
577
738
|
if (!opts.quiet) {
|
|
578
|
-
console.error(
|
|
739
|
+
console.error(chalk6.green("\u2713") + " Copied to clipboard");
|
|
579
740
|
}
|
|
580
741
|
} else {
|
|
581
742
|
console.log(output);
|
|
582
743
|
if (!opts.quiet) {
|
|
583
|
-
console.error(
|
|
744
|
+
console.error(chalk6.yellow("\u26A0") + " Could not copy to clipboard (unsupported platform)");
|
|
584
745
|
}
|
|
585
746
|
}
|
|
586
747
|
} else {
|
|
587
748
|
console.log(output);
|
|
588
749
|
}
|
|
589
750
|
} catch (error2) {
|
|
590
|
-
console.error(
|
|
751
|
+
console.error(chalk6.red("\u2717") + " Failed to generate prompt:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
591
752
|
process.exit(1);
|
|
592
753
|
}
|
|
593
754
|
});
|
|
594
755
|
|
|
595
756
|
// src/commands/search.ts
|
|
596
757
|
import { Command as Command5 } from "commander";
|
|
597
|
-
import
|
|
758
|
+
import chalk7 from "chalk";
|
|
598
759
|
var TYPE_ICONS2 = {
|
|
599
760
|
rule: "\u{1F4CC}",
|
|
600
761
|
decision: "\u{1F4A1}",
|
|
@@ -604,17 +765,17 @@ var TYPE_ICONS2 = {
|
|
|
604
765
|
var VALID_TYPES3 = ["rule", "decision", "fact", "note"];
|
|
605
766
|
function formatMemory2(m, score) {
|
|
606
767
|
const icon = TYPE_ICONS2[m.type] || "\u{1F4DD}";
|
|
607
|
-
const scope = m.scope === "global" ?
|
|
608
|
-
const tags = m.tags ?
|
|
609
|
-
const scoreStr = score !== void 0 ?
|
|
610
|
-
return `${icon} ${scope} ${
|
|
768
|
+
const scope = m.scope === "global" ? chalk7.dim("G") : chalk7.dim("P");
|
|
769
|
+
const tags = m.tags ? chalk7.dim(` [${m.tags}]`) : "";
|
|
770
|
+
const scoreStr = score !== void 0 ? chalk7.cyan(` (${Math.round(score * 100)}%)`) : "";
|
|
771
|
+
return `${icon} ${scope} ${chalk7.dim(m.id)} ${m.content}${tags}${scoreStr}`;
|
|
611
772
|
}
|
|
612
773
|
var searchCommand = new Command5("search").description("Search memories using full-text or semantic 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("-s, --semantic", "Use semantic (AI) search instead of keyword search").option("--json", "Output as JSON").action(async (query, opts) => {
|
|
613
774
|
try {
|
|
614
775
|
let types;
|
|
615
776
|
if (opts.type) {
|
|
616
777
|
if (!VALID_TYPES3.includes(opts.type)) {
|
|
617
|
-
console.error(
|
|
778
|
+
console.error(chalk7.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES3.join(", ")}`);
|
|
618
779
|
process.exit(1);
|
|
619
780
|
}
|
|
620
781
|
types = [opts.type];
|
|
@@ -628,7 +789,7 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
628
789
|
includeGlobal = false;
|
|
629
790
|
projectId = getProjectId() ?? void 0;
|
|
630
791
|
if (!projectId) {
|
|
631
|
-
console.log(
|
|
792
|
+
console.log(chalk7.yellow("\u26A0") + " Not in a git repository. No project memories to search.");
|
|
632
793
|
return;
|
|
633
794
|
}
|
|
634
795
|
}
|
|
@@ -636,7 +797,7 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
636
797
|
try {
|
|
637
798
|
const { semanticSearch, isModelAvailable } = await import("./embeddings-6N2NZPRX.js");
|
|
638
799
|
if (!await isModelAvailable()) {
|
|
639
|
-
console.log(
|
|
800
|
+
console.log(chalk7.yellow("\u26A0") + " Loading embedding model for first time (this may take a moment)...");
|
|
640
801
|
}
|
|
641
802
|
const results = await semanticSearch(query, {
|
|
642
803
|
limit: parseInt(opts.limit, 10),
|
|
@@ -647,11 +808,11 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
647
808
|
return;
|
|
648
809
|
}
|
|
649
810
|
if (results.length === 0) {
|
|
650
|
-
console.log(
|
|
651
|
-
console.log(
|
|
811
|
+
console.log(chalk7.dim(`No semantically similar memories found for "${query}"`));
|
|
812
|
+
console.log(chalk7.dim("Try running 'memories embed' to generate embeddings for existing memories."));
|
|
652
813
|
return;
|
|
653
814
|
}
|
|
654
|
-
console.log(
|
|
815
|
+
console.log(chalk7.bold(`Semantic results for "${query}":`));
|
|
655
816
|
console.log("");
|
|
656
817
|
for (const r of results) {
|
|
657
818
|
const { getMemoryById: getMemoryById2 } = await import("./memory-B4NNFKXV.js");
|
|
@@ -660,12 +821,12 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
660
821
|
console.log(formatMemory2(m, r.score));
|
|
661
822
|
}
|
|
662
823
|
}
|
|
663
|
-
console.log(
|
|
824
|
+
console.log(chalk7.dim(`
|
|
664
825
|
${results.length} results (semantic search)`));
|
|
665
826
|
return;
|
|
666
827
|
} catch (error2) {
|
|
667
|
-
console.error(
|
|
668
|
-
console.log(
|
|
828
|
+
console.error(chalk7.red("\u2717") + " Semantic search failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
829
|
+
console.log(chalk7.dim("Falling back to keyword search..."));
|
|
669
830
|
}
|
|
670
831
|
}
|
|
671
832
|
const memories = await searchMemories(query, {
|
|
@@ -680,30 +841,30 @@ ${results.length} results (semantic search)`));
|
|
|
680
841
|
return;
|
|
681
842
|
}
|
|
682
843
|
if (memories.length === 0) {
|
|
683
|
-
console.log(
|
|
844
|
+
console.log(chalk7.dim(`No memories found matching "${query}"`));
|
|
684
845
|
return;
|
|
685
846
|
}
|
|
686
|
-
console.log(
|
|
847
|
+
console.log(chalk7.bold(`Results for "${query}":`));
|
|
687
848
|
console.log("");
|
|
688
849
|
for (const m of memories) {
|
|
689
850
|
console.log(formatMemory2(m));
|
|
690
851
|
}
|
|
691
|
-
console.log(
|
|
852
|
+
console.log(chalk7.dim(`
|
|
692
853
|
${memories.length} results`));
|
|
693
854
|
} catch (error2) {
|
|
694
|
-
console.error(
|
|
855
|
+
console.error(chalk7.red("\u2717") + " Failed to search:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
695
856
|
process.exit(1);
|
|
696
857
|
}
|
|
697
858
|
});
|
|
698
859
|
|
|
699
860
|
// src/commands/list.ts
|
|
700
861
|
import { Command as Command6 } from "commander";
|
|
701
|
-
import
|
|
862
|
+
import chalk8 from "chalk";
|
|
702
863
|
var TYPE_COLORS2 = {
|
|
703
|
-
rule:
|
|
704
|
-
decision:
|
|
705
|
-
fact:
|
|
706
|
-
note:
|
|
864
|
+
rule: chalk8.blue,
|
|
865
|
+
decision: chalk8.yellow,
|
|
866
|
+
fact: chalk8.green,
|
|
867
|
+
note: chalk8.dim
|
|
707
868
|
};
|
|
708
869
|
var TYPE_LABELS = {
|
|
709
870
|
rule: "rule",
|
|
@@ -718,12 +879,12 @@ function truncate(str, max) {
|
|
|
718
879
|
return str.slice(0, max - 1) + "\u2026";
|
|
719
880
|
}
|
|
720
881
|
function formatMemory3(m) {
|
|
721
|
-
const typeColor = TYPE_COLORS2[m.type] ??
|
|
882
|
+
const typeColor = TYPE_COLORS2[m.type] ?? chalk8.dim;
|
|
722
883
|
const typeLabel = typeColor(TYPE_LABELS[m.type].padEnd(9));
|
|
723
|
-
const scope = m.scope === "global" ?
|
|
724
|
-
const id =
|
|
884
|
+
const scope = m.scope === "global" ? chalk8.magenta("G") : chalk8.cyan("P");
|
|
885
|
+
const id = chalk8.dim(m.id);
|
|
725
886
|
const content = truncate(m.content, MAX_CONTENT_WIDTH);
|
|
726
|
-
const tags = m.tags ?
|
|
887
|
+
const tags = m.tags ? chalk8.dim(` [${m.tags}]`) : "";
|
|
727
888
|
return ` ${scope} ${typeLabel} ${id} ${content}${tags}`;
|
|
728
889
|
}
|
|
729
890
|
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) => {
|
|
@@ -732,7 +893,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
732
893
|
let types;
|
|
733
894
|
if (opts.type) {
|
|
734
895
|
if (!VALID_TYPES4.includes(opts.type)) {
|
|
735
|
-
console.error(
|
|
896
|
+
console.error(chalk8.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES4.join(", ")}`);
|
|
736
897
|
process.exit(1);
|
|
737
898
|
}
|
|
738
899
|
types = [opts.type];
|
|
@@ -746,7 +907,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
746
907
|
includeGlobal = false;
|
|
747
908
|
projectId = getProjectId() ?? void 0;
|
|
748
909
|
if (!projectId) {
|
|
749
|
-
console.log(
|
|
910
|
+
console.log(chalk8.yellow("\u26A0") + " Not in a git repository. No project memories to show.");
|
|
750
911
|
return;
|
|
751
912
|
}
|
|
752
913
|
}
|
|
@@ -763,21 +924,21 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
763
924
|
return;
|
|
764
925
|
}
|
|
765
926
|
if (memories.length === 0) {
|
|
766
|
-
console.log(
|
|
927
|
+
console.log(chalk8.dim("No memories found."));
|
|
767
928
|
return;
|
|
768
929
|
}
|
|
769
930
|
const currentProject = getProjectId();
|
|
770
931
|
if (currentProject && !opts.global) {
|
|
771
|
-
console.log(
|
|
932
|
+
console.log(chalk8.dim(` Project: ${currentProject}
|
|
772
933
|
`));
|
|
773
934
|
}
|
|
774
935
|
for (const m of memories) {
|
|
775
936
|
console.log(formatMemory3(m));
|
|
776
937
|
}
|
|
777
|
-
console.log(
|
|
938
|
+
console.log(chalk8.dim(`
|
|
778
939
|
${memories.length} memories`));
|
|
779
940
|
} catch (error2) {
|
|
780
|
-
console.error(
|
|
941
|
+
console.error(chalk8.red("\u2717") + " Failed to list memories:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
781
942
|
process.exit(1);
|
|
782
943
|
}
|
|
783
944
|
});
|
|
@@ -785,7 +946,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
785
946
|
// src/commands/forget.ts
|
|
786
947
|
import { Command as Command7 } from "commander";
|
|
787
948
|
import { createInterface } from "readline";
|
|
788
|
-
import
|
|
949
|
+
import chalk9 from "chalk";
|
|
789
950
|
var VALID_TYPES5 = ["rule", "decision", "fact", "note"];
|
|
790
951
|
var TYPE_ICONS3 = {
|
|
791
952
|
rule: "\u{1F4CC}",
|
|
@@ -793,7 +954,7 @@ var TYPE_ICONS3 = {
|
|
|
793
954
|
fact: "\u{1F4CB}",
|
|
794
955
|
note: "\u{1F4DD}"
|
|
795
956
|
};
|
|
796
|
-
async function
|
|
957
|
+
async function confirm2(message) {
|
|
797
958
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
798
959
|
return new Promise((resolve3) => {
|
|
799
960
|
rl.question(message, (answer) => {
|
|
@@ -807,28 +968,28 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
807
968
|
if (id) {
|
|
808
969
|
const deleted = await forgetMemory(id);
|
|
809
970
|
if (deleted) {
|
|
810
|
-
console.log(
|
|
971
|
+
console.log(chalk9.green("\u2713") + ` Forgot memory ${chalk9.dim(id)}`);
|
|
811
972
|
} else {
|
|
812
|
-
console.error(
|
|
973
|
+
console.error(chalk9.red("\u2717") + ` Memory ${id} not found or already forgotten.`);
|
|
813
974
|
process.exit(1);
|
|
814
975
|
}
|
|
815
976
|
return;
|
|
816
977
|
}
|
|
817
978
|
const hasBulkFilter = opts.type || opts.tag || opts.olderThan || opts.pattern || opts.all;
|
|
818
979
|
if (!hasBulkFilter) {
|
|
819
|
-
console.error(
|
|
980
|
+
console.error(chalk9.red("\u2717") + " Provide a memory ID or a bulk filter (--type, --tag, --older-than, --pattern, --all)");
|
|
820
981
|
process.exit(1);
|
|
821
982
|
}
|
|
822
983
|
if (opts.all && (opts.type || opts.tag || opts.olderThan || opts.pattern)) {
|
|
823
|
-
console.error(
|
|
984
|
+
console.error(chalk9.red("\u2717") + " --all cannot be combined with other filters. Use --all alone to delete everything.");
|
|
824
985
|
process.exit(1);
|
|
825
986
|
}
|
|
826
987
|
if (opts.type && !VALID_TYPES5.includes(opts.type)) {
|
|
827
|
-
console.error(
|
|
988
|
+
console.error(chalk9.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES5.join(", ")}`);
|
|
828
989
|
process.exit(1);
|
|
829
990
|
}
|
|
830
991
|
if (opts.olderThan && (isNaN(parseInt(opts.olderThan, 10)) || parseInt(opts.olderThan, 10) <= 0)) {
|
|
831
|
-
console.error(
|
|
992
|
+
console.error(chalk9.red("\u2717") + " --older-than must be a positive number of days");
|
|
832
993
|
process.exit(1);
|
|
833
994
|
}
|
|
834
995
|
const filter = {
|
|
@@ -841,27 +1002,27 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
841
1002
|
};
|
|
842
1003
|
const matches = await findMemoriesToForget(filter);
|
|
843
1004
|
if (matches.length === 0) {
|
|
844
|
-
console.log(
|
|
1005
|
+
console.log(chalk9.dim("No memories match the filter."));
|
|
845
1006
|
return;
|
|
846
1007
|
}
|
|
847
|
-
console.log(
|
|
1008
|
+
console.log(chalk9.bold(`${matches.length} memories will be forgotten:
|
|
848
1009
|
`));
|
|
849
1010
|
for (const m of matches.slice(0, 30)) {
|
|
850
1011
|
const icon = TYPE_ICONS3[m.type] || "\u{1F4DD}";
|
|
851
|
-
const scope = m.scope === "global" ?
|
|
852
|
-
console.log(` ${icon} ${scope} ${
|
|
1012
|
+
const scope = m.scope === "global" ? chalk9.dim("G") : chalk9.dim("P");
|
|
1013
|
+
console.log(` ${icon} ${scope} ${chalk9.dim(m.id)} ${m.content}`);
|
|
853
1014
|
}
|
|
854
1015
|
if (matches.length > 30) {
|
|
855
|
-
console.log(
|
|
1016
|
+
console.log(chalk9.dim(` ... and ${matches.length - 30} more`));
|
|
856
1017
|
}
|
|
857
1018
|
console.log("");
|
|
858
1019
|
if (opts.dryRun) {
|
|
859
|
-
console.log(
|
|
1020
|
+
console.log(chalk9.yellow("Dry run") + " \u2014 no memories were deleted.");
|
|
860
1021
|
return;
|
|
861
1022
|
}
|
|
862
1023
|
if (!opts.force) {
|
|
863
|
-
const proceed = await
|
|
864
|
-
|
|
1024
|
+
const proceed = await confirm2(
|
|
1025
|
+
chalk9.yellow(`Forget ${matches.length} memories? This is a soft-delete. [y/N] `)
|
|
865
1026
|
);
|
|
866
1027
|
if (!proceed) {
|
|
867
1028
|
console.log("Cancelled.");
|
|
@@ -870,17 +1031,17 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
870
1031
|
}
|
|
871
1032
|
const ids = matches.map((m) => m.id);
|
|
872
1033
|
const count = await bulkForgetByIds(ids);
|
|
873
|
-
console.log(
|
|
1034
|
+
console.log(chalk9.green("\u2713") + ` Forgot ${count} memories.`);
|
|
874
1035
|
} catch (error2) {
|
|
875
|
-
console.error(
|
|
1036
|
+
console.error(chalk9.red("\u2717") + " Failed to forget:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
876
1037
|
process.exit(1);
|
|
877
1038
|
}
|
|
878
1039
|
});
|
|
879
1040
|
|
|
880
1041
|
// src/commands/export.ts
|
|
881
1042
|
import { Command as Command8 } from "commander";
|
|
882
|
-
import
|
|
883
|
-
import { writeFile as
|
|
1043
|
+
import chalk10 from "chalk";
|
|
1044
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
884
1045
|
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) => {
|
|
885
1046
|
try {
|
|
886
1047
|
const projectId = getProjectId();
|
|
@@ -893,7 +1054,7 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
893
1054
|
includeGlobal = false;
|
|
894
1055
|
queryProjectId = projectId ?? void 0;
|
|
895
1056
|
if (!queryProjectId) {
|
|
896
|
-
console.error(
|
|
1057
|
+
console.error(chalk10.red("\u2717") + " Not in a git repository. No project memories to export.");
|
|
897
1058
|
process.exit(1);
|
|
898
1059
|
}
|
|
899
1060
|
}
|
|
@@ -927,24 +1088,24 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
927
1088
|
output = JSON.stringify(exportData, null, 2);
|
|
928
1089
|
}
|
|
929
1090
|
if (opts.output) {
|
|
930
|
-
await
|
|
931
|
-
console.log(
|
|
1091
|
+
await writeFile3(opts.output, output, "utf-8");
|
|
1092
|
+
console.log(chalk10.green("\u2713") + ` Exported ${memories.length} memories to ${chalk10.dim(opts.output)}`);
|
|
932
1093
|
} else {
|
|
933
1094
|
console.log(output);
|
|
934
1095
|
}
|
|
935
1096
|
} catch (error2) {
|
|
936
|
-
console.error(
|
|
1097
|
+
console.error(chalk10.red("\u2717") + " Failed to export:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
937
1098
|
process.exit(1);
|
|
938
1099
|
}
|
|
939
1100
|
});
|
|
940
1101
|
|
|
941
1102
|
// src/commands/import.ts
|
|
942
1103
|
import { Command as Command9 } from "commander";
|
|
943
|
-
import
|
|
944
|
-
import { readFile as
|
|
1104
|
+
import chalk11 from "chalk";
|
|
1105
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
945
1106
|
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) => {
|
|
946
1107
|
try {
|
|
947
|
-
const content = await
|
|
1108
|
+
const content = await readFile3(file, "utf-8");
|
|
948
1109
|
let format = opts.format;
|
|
949
1110
|
if (!format) {
|
|
950
1111
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
@@ -961,7 +1122,7 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
961
1122
|
data = JSON.parse(content);
|
|
962
1123
|
}
|
|
963
1124
|
if (!data || typeof data !== "object" || !("memories" in data) || !Array.isArray(data.memories)) {
|
|
964
|
-
console.error(
|
|
1125
|
+
console.error(chalk11.red("\u2717") + " Invalid import file: missing 'memories' array");
|
|
965
1126
|
process.exit(1);
|
|
966
1127
|
}
|
|
967
1128
|
const VALID_TYPES11 = ["rule", "decision", "fact", "note"];
|
|
@@ -974,23 +1135,23 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
974
1135
|
continue;
|
|
975
1136
|
}
|
|
976
1137
|
if (m.type && !VALID_TYPES11.includes(m.type)) {
|
|
977
|
-
console.error(
|
|
1138
|
+
console.error(chalk11.yellow("\u26A0") + ` Skipping memory with invalid type "${m.type}": ${m.content.slice(0, 50)}`);
|
|
978
1139
|
skipped++;
|
|
979
1140
|
continue;
|
|
980
1141
|
}
|
|
981
1142
|
validMemories.push(m);
|
|
982
1143
|
}
|
|
983
1144
|
if (opts.dryRun) {
|
|
984
|
-
console.log(
|
|
1145
|
+
console.log(chalk11.blue("Dry run - would import:"));
|
|
985
1146
|
for (const m of validMemories) {
|
|
986
1147
|
const type = m.type || "note";
|
|
987
1148
|
const scope = opts.global ? "global" : m.scope || "project";
|
|
988
1149
|
const tags = m.tags?.length ? ` [${m.tags.join(", ")}]` : "";
|
|
989
1150
|
console.log(` ${type} (${scope}): ${m.content}${tags}`);
|
|
990
1151
|
}
|
|
991
|
-
console.log(
|
|
1152
|
+
console.log(chalk11.dim(`
|
|
992
1153
|
${validMemories.length} memories would be imported`));
|
|
993
|
-
if (skipped > 0) console.log(
|
|
1154
|
+
if (skipped > 0) console.log(chalk11.dim(`${skipped} entries skipped (invalid)`));
|
|
994
1155
|
return;
|
|
995
1156
|
}
|
|
996
1157
|
let imported = 0;
|
|
@@ -1004,19 +1165,19 @@ ${validMemories.length} memories would be imported`));
|
|
|
1004
1165
|
});
|
|
1005
1166
|
imported++;
|
|
1006
1167
|
} catch (error2) {
|
|
1007
|
-
console.error(
|
|
1168
|
+
console.error(chalk11.yellow("\u26A0") + ` Failed to import: ${m.content.slice(0, 50)}...`);
|
|
1008
1169
|
failed++;
|
|
1009
1170
|
}
|
|
1010
1171
|
}
|
|
1011
|
-
console.log(
|
|
1172
|
+
console.log(chalk11.green("\u2713") + ` Imported ${imported} memories`);
|
|
1012
1173
|
if (failed > 0) {
|
|
1013
|
-
console.log(
|
|
1174
|
+
console.log(chalk11.yellow("\u26A0") + ` ${failed} memories failed to import`);
|
|
1014
1175
|
}
|
|
1015
1176
|
if (skipped > 0) {
|
|
1016
|
-
console.log(
|
|
1177
|
+
console.log(chalk11.dim(`${skipped} entries skipped (invalid content or type)`));
|
|
1017
1178
|
}
|
|
1018
1179
|
} catch (error2) {
|
|
1019
|
-
console.error(
|
|
1180
|
+
console.error(chalk11.red("\u2717") + " Failed to import:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1020
1181
|
process.exit(1);
|
|
1021
1182
|
}
|
|
1022
1183
|
});
|
|
@@ -1025,17 +1186,17 @@ ${validMemories.length} memories would be imported`));
|
|
|
1025
1186
|
import { Command as Command10 } from "commander";
|
|
1026
1187
|
|
|
1027
1188
|
// src/lib/config.ts
|
|
1028
|
-
import { readFile as
|
|
1029
|
-
import { join as
|
|
1030
|
-
import { existsSync as
|
|
1189
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1190
|
+
import { join as join3 } from "path";
|
|
1191
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1031
1192
|
import { parse, stringify } from "yaml";
|
|
1032
1193
|
var AGENTS_DIR = ".agents";
|
|
1033
1194
|
var CONFIG_FILE = "config.yaml";
|
|
1034
1195
|
async function initConfig(dir) {
|
|
1035
|
-
const agentsDir =
|
|
1036
|
-
await
|
|
1037
|
-
const configPath =
|
|
1038
|
-
if (!
|
|
1196
|
+
const agentsDir = join3(dir, AGENTS_DIR);
|
|
1197
|
+
await mkdir3(agentsDir, { recursive: true });
|
|
1198
|
+
const configPath = join3(agentsDir, CONFIG_FILE);
|
|
1199
|
+
if (!existsSync3(configPath)) {
|
|
1039
1200
|
const defaultConfig = {
|
|
1040
1201
|
name: "my-project",
|
|
1041
1202
|
description: "Agent memory configuration",
|
|
@@ -1045,14 +1206,14 @@ async function initConfig(dir) {
|
|
|
1045
1206
|
store: "~/.config/memories/local.db"
|
|
1046
1207
|
}
|
|
1047
1208
|
};
|
|
1048
|
-
await
|
|
1209
|
+
await writeFile4(configPath, stringify(defaultConfig), "utf-8");
|
|
1049
1210
|
}
|
|
1050
1211
|
return configPath;
|
|
1051
1212
|
}
|
|
1052
1213
|
async function readConfig(dir) {
|
|
1053
|
-
const configPath =
|
|
1054
|
-
if (!
|
|
1055
|
-
const raw = await
|
|
1214
|
+
const configPath = join3(dir, AGENTS_DIR, CONFIG_FILE);
|
|
1215
|
+
if (!existsSync3(configPath)) return null;
|
|
1216
|
+
const raw = await readFile4(configPath, "utf-8");
|
|
1056
1217
|
return parse(raw);
|
|
1057
1218
|
}
|
|
1058
1219
|
|
|
@@ -1528,10 +1689,10 @@ async function createDatabaseToken(org, dbName) {
|
|
|
1528
1689
|
}
|
|
1529
1690
|
|
|
1530
1691
|
// src/commands/sync.ts
|
|
1531
|
-
import { unlinkSync, existsSync as
|
|
1532
|
-
import { join as
|
|
1533
|
-
import { homedir as
|
|
1534
|
-
var DB_PATH =
|
|
1692
|
+
import { unlinkSync, existsSync as existsSync4 } from "fs";
|
|
1693
|
+
import { join as join4 } from "path";
|
|
1694
|
+
import { homedir as homedir3 } from "os";
|
|
1695
|
+
var DB_PATH = join4(homedir3(), ".config", "memories", "local.db");
|
|
1535
1696
|
var syncCommand = new Command12("sync").description(
|
|
1536
1697
|
"Manage remote sync"
|
|
1537
1698
|
);
|
|
@@ -1554,7 +1715,7 @@ syncCommand.command("enable").description("Provision a cloud database and enable
|
|
|
1554
1715
|
const profileRes = await apiFetch("/api/db/credentials");
|
|
1555
1716
|
if (profileRes.ok) {
|
|
1556
1717
|
const creds = await profileRes.json();
|
|
1557
|
-
if (
|
|
1718
|
+
if (existsSync4(DB_PATH)) {
|
|
1558
1719
|
resetDb();
|
|
1559
1720
|
unlinkSync(DB_PATH);
|
|
1560
1721
|
}
|
|
@@ -1590,7 +1751,7 @@ syncCommand.command("enable").description("Provision a cloud database and enable
|
|
|
1590
1751
|
spinner.text = "Generating auth token...";
|
|
1591
1752
|
const token = await createDatabaseToken(opts.org, db.name);
|
|
1592
1753
|
const syncUrl = `libsql://${db.hostname}`;
|
|
1593
|
-
if (
|
|
1754
|
+
if (existsSync4(DB_PATH)) {
|
|
1594
1755
|
resetDb();
|
|
1595
1756
|
unlinkSync(DB_PATH);
|
|
1596
1757
|
}
|
|
@@ -1637,11 +1798,11 @@ syncCommand.command("status").description("Show sync configuration").action(asyn
|
|
|
1637
1798
|
|
|
1638
1799
|
// src/commands/generate.ts
|
|
1639
1800
|
import { Command as Command13 } from "commander";
|
|
1640
|
-
import
|
|
1641
|
-
import { writeFile as
|
|
1642
|
-
import { existsSync as
|
|
1643
|
-
import { dirname, resolve, join as
|
|
1644
|
-
import { homedir as
|
|
1801
|
+
import chalk12 from "chalk";
|
|
1802
|
+
import { writeFile as writeFile5, readFile as readFile5, mkdir as mkdir4 } from "fs/promises";
|
|
1803
|
+
import { existsSync as existsSync5, watch as fsWatch } from "fs";
|
|
1804
|
+
import { dirname as dirname2, resolve, join as join5 } from "path";
|
|
1805
|
+
import { homedir as homedir4 } from "os";
|
|
1645
1806
|
import { checkbox } from "@inquirer/prompts";
|
|
1646
1807
|
var MARKER = "Generated by memories.sh";
|
|
1647
1808
|
var VALID_TYPES6 = ["rule", "decision", "fact", "note"];
|
|
@@ -1778,7 +1939,7 @@ function makeFooter() {
|
|
|
1778
1939
|
}
|
|
1779
1940
|
async function hasOurMarker(filePath) {
|
|
1780
1941
|
try {
|
|
1781
|
-
const content = await
|
|
1942
|
+
const content = await readFile5(filePath, "utf-8");
|
|
1782
1943
|
return content.includes(MARKER);
|
|
1783
1944
|
} catch {
|
|
1784
1945
|
return false;
|
|
@@ -1789,13 +1950,13 @@ async function checkGitignore(filePath) {
|
|
|
1789
1950
|
if (TRACK_BY_DEFAULT.has(filePath)) return;
|
|
1790
1951
|
const gitignorePath = resolve(".gitignore");
|
|
1791
1952
|
try {
|
|
1792
|
-
const content =
|
|
1953
|
+
const content = existsSync5(gitignorePath) ? await readFile5(gitignorePath, "utf-8") : "";
|
|
1793
1954
|
const lines = content.split("\n");
|
|
1794
1955
|
const parentDir = filePath.split("/")[0];
|
|
1795
1956
|
if (lines.some((l) => l.trim() === filePath || l.trim() === parentDir || l.trim() === `${parentDir}/`)) {
|
|
1796
1957
|
return;
|
|
1797
1958
|
}
|
|
1798
|
-
console.log(
|
|
1959
|
+
console.log(chalk12.dim(` hint: add "${filePath}" to .gitignore if you don't want it tracked`));
|
|
1799
1960
|
} catch {
|
|
1800
1961
|
}
|
|
1801
1962
|
}
|
|
@@ -1803,23 +1964,23 @@ async function writeTarget(target, memories, opts) {
|
|
|
1803
1964
|
const outPath = resolve(opts.output ?? target.defaultPath);
|
|
1804
1965
|
const content = target.format(memories) + makeFooter();
|
|
1805
1966
|
if (opts.dryRun) {
|
|
1806
|
-
console.log(
|
|
1967
|
+
console.log(chalk12.dim(`\u2500\u2500 ${target.name} \u2192 ${outPath} \u2500\u2500`));
|
|
1807
1968
|
console.log(content);
|
|
1808
1969
|
console.log();
|
|
1809
1970
|
return;
|
|
1810
1971
|
}
|
|
1811
|
-
if (
|
|
1972
|
+
if (existsSync5(outPath)) {
|
|
1812
1973
|
const ours = await hasOurMarker(outPath);
|
|
1813
1974
|
if (!ours && !opts.force) {
|
|
1814
1975
|
console.error(
|
|
1815
|
-
|
|
1976
|
+
chalk12.yellow("\u26A0") + ` ${outPath} exists and was not generated by memories.sh. Use ${chalk12.bold("--force")} to overwrite.`
|
|
1816
1977
|
);
|
|
1817
1978
|
return;
|
|
1818
1979
|
}
|
|
1819
1980
|
}
|
|
1820
|
-
await
|
|
1821
|
-
await
|
|
1822
|
-
console.log(
|
|
1981
|
+
await mkdir4(dirname2(outPath), { recursive: true });
|
|
1982
|
+
await writeFile5(outPath, content, "utf-8");
|
|
1983
|
+
console.log(chalk12.green("\u2713") + ` Wrote ${target.name} \u2192 ${chalk12.dim(outPath)}`);
|
|
1823
1984
|
await checkGitignore(opts.output ?? target.defaultPath);
|
|
1824
1985
|
}
|
|
1825
1986
|
async function fetchMemories(types) {
|
|
@@ -1835,23 +1996,23 @@ function parseTypes(raw) {
|
|
|
1835
1996
|
const types = raw.split(",").map((s) => s.trim());
|
|
1836
1997
|
for (const t of types) {
|
|
1837
1998
|
if (!VALID_TYPES6.includes(t)) {
|
|
1838
|
-
console.error(
|
|
1999
|
+
console.error(chalk12.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES6.join(", ")}`);
|
|
1839
2000
|
process.exit(1);
|
|
1840
2001
|
}
|
|
1841
2002
|
}
|
|
1842
2003
|
return types;
|
|
1843
2004
|
}
|
|
1844
2005
|
function getDbPath() {
|
|
1845
|
-
const dataDir = process.env.MEMORIES_DATA_DIR ??
|
|
1846
|
-
return
|
|
2006
|
+
const dataDir = process.env.MEMORIES_DATA_DIR ?? join5(homedir4(), ".config", "memories");
|
|
2007
|
+
return join5(dataDir, "local.db");
|
|
1847
2008
|
}
|
|
1848
2009
|
async function runWatch(targets, memories, opts) {
|
|
1849
2010
|
const dbPath = getDbPath();
|
|
1850
|
-
if (!
|
|
1851
|
-
console.error(
|
|
2011
|
+
if (!existsSync5(dbPath)) {
|
|
2012
|
+
console.error(chalk12.red("\u2717") + " Database not found. Run: memories init");
|
|
1852
2013
|
process.exit(1);
|
|
1853
2014
|
}
|
|
1854
|
-
console.log(
|
|
2015
|
+
console.log(chalk12.dim(`Watching ${dbPath} for changes... (Ctrl+C to stop)
|
|
1855
2016
|
`));
|
|
1856
2017
|
const mems = await memories();
|
|
1857
2018
|
if (mems.length > 0) {
|
|
@@ -1871,7 +2032,7 @@ async function runWatch(targets, memories, opts) {
|
|
|
1871
2032
|
}
|
|
1872
2033
|
}
|
|
1873
2034
|
} catch (err) {
|
|
1874
|
-
console.error(
|
|
2035
|
+
console.error(chalk12.red("\u2717") + " Watch error:", err.message);
|
|
1875
2036
|
}
|
|
1876
2037
|
}, 500);
|
|
1877
2038
|
});
|
|
@@ -1887,19 +2048,19 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1887
2048
|
const selected = await checkbox({
|
|
1888
2049
|
message: "Select targets to generate",
|
|
1889
2050
|
choices: TARGETS.map((t) => ({
|
|
1890
|
-
name: `${t.name} ${
|
|
2051
|
+
name: `${t.name} ${chalk12.dim(`\u2192 ${t.defaultPath}`)}`,
|
|
1891
2052
|
value: t.name,
|
|
1892
2053
|
checked: true
|
|
1893
2054
|
}))
|
|
1894
2055
|
});
|
|
1895
2056
|
if (selected.length === 0) {
|
|
1896
|
-
console.log(
|
|
2057
|
+
console.log(chalk12.dim("No targets selected."));
|
|
1897
2058
|
return;
|
|
1898
2059
|
}
|
|
1899
2060
|
const types = parseTypes(opts.types);
|
|
1900
2061
|
const memories = await fetchMemories(types);
|
|
1901
2062
|
if (memories.length === 0) {
|
|
1902
|
-
console.error(
|
|
2063
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1903
2064
|
return;
|
|
1904
2065
|
}
|
|
1905
2066
|
const selectedSet = new Set(selected);
|
|
@@ -1911,7 +2072,7 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1911
2072
|
}
|
|
1912
2073
|
} catch (error2) {
|
|
1913
2074
|
if (error2.name === "ExitPromptError") return;
|
|
1914
|
-
console.error(
|
|
2075
|
+
console.error(chalk12.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1915
2076
|
process.exit(1);
|
|
1916
2077
|
}
|
|
1917
2078
|
});
|
|
@@ -1922,12 +2083,12 @@ for (const target of TARGETS) {
|
|
|
1922
2083
|
const types = parseTypes(opts.types);
|
|
1923
2084
|
const memories = await fetchMemories(types);
|
|
1924
2085
|
if (memories.length === 0) {
|
|
1925
|
-
console.error(
|
|
2086
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1926
2087
|
return;
|
|
1927
2088
|
}
|
|
1928
2089
|
await writeTarget(target, memories, opts);
|
|
1929
2090
|
} catch (error2) {
|
|
1930
|
-
console.error(
|
|
2091
|
+
console.error(chalk12.red("\u2717") + ` Failed to generate ${target.name}:`, error2 instanceof Error ? error2.message : "Unknown error");
|
|
1931
2092
|
process.exit(1);
|
|
1932
2093
|
}
|
|
1933
2094
|
})
|
|
@@ -1943,14 +2104,14 @@ generateCommand.addCommand(
|
|
|
1943
2104
|
}
|
|
1944
2105
|
const memories = await fetchMemories(types);
|
|
1945
2106
|
if (memories.length === 0) {
|
|
1946
|
-
console.error(
|
|
2107
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1947
2108
|
return;
|
|
1948
2109
|
}
|
|
1949
2110
|
for (const target of TARGETS) {
|
|
1950
2111
|
await writeTarget(target, memories, opts);
|
|
1951
2112
|
}
|
|
1952
2113
|
} catch (error2) {
|
|
1953
|
-
console.error(
|
|
2114
|
+
console.error(chalk12.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1954
2115
|
process.exit(1);
|
|
1955
2116
|
}
|
|
1956
2117
|
})
|
|
@@ -1958,11 +2119,11 @@ generateCommand.addCommand(
|
|
|
1958
2119
|
|
|
1959
2120
|
// src/commands/edit.ts
|
|
1960
2121
|
import { Command as Command14 } from "commander";
|
|
1961
|
-
import
|
|
2122
|
+
import chalk13 from "chalk";
|
|
1962
2123
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1963
2124
|
import { writeFileSync, readFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1964
2125
|
import { tmpdir } from "os";
|
|
1965
|
-
import { join as
|
|
2126
|
+
import { join as join6 } from "path";
|
|
1966
2127
|
import { nanoid as nanoid2 } from "nanoid";
|
|
1967
2128
|
import { select } from "@inquirer/prompts";
|
|
1968
2129
|
var VALID_TYPES7 = ["rule", "decision", "fact", "note"];
|
|
@@ -1974,13 +2135,13 @@ async function pickMemory() {
|
|
|
1974
2135
|
const projectId = getProjectId() ?? void 0;
|
|
1975
2136
|
const memories = await listMemories({ limit: 100, projectId });
|
|
1976
2137
|
if (memories.length === 0) {
|
|
1977
|
-
console.error(
|
|
2138
|
+
console.error(chalk13.dim("No memories found."));
|
|
1978
2139
|
process.exit(0);
|
|
1979
2140
|
}
|
|
1980
2141
|
const id = await select({
|
|
1981
2142
|
message: "Select a memory to edit",
|
|
1982
2143
|
choices: memories.map((m) => ({
|
|
1983
|
-
name: `${
|
|
2144
|
+
name: `${chalk13.dim(m.type.padEnd(9))} ${truncate2(m.content, 60)} ${chalk13.dim(m.id)}`,
|
|
1984
2145
|
value: m.id
|
|
1985
2146
|
}))
|
|
1986
2147
|
});
|
|
@@ -1990,7 +2151,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
1990
2151
|
try {
|
|
1991
2152
|
if (!id) {
|
|
1992
2153
|
if (!process.stdin.isTTY) {
|
|
1993
|
-
console.error(
|
|
2154
|
+
console.error(chalk13.red("\u2717") + " Memory ID required in non-interactive mode");
|
|
1994
2155
|
process.exit(1);
|
|
1995
2156
|
}
|
|
1996
2157
|
id = await pickMemory();
|
|
@@ -2001,18 +2162,18 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2001
2162
|
args: [id]
|
|
2002
2163
|
});
|
|
2003
2164
|
if (result.rows.length === 0) {
|
|
2004
|
-
console.error(
|
|
2165
|
+
console.error(chalk13.red("\u2717") + ` Memory ${chalk13.dim(id)} not found`);
|
|
2005
2166
|
process.exit(1);
|
|
2006
2167
|
}
|
|
2007
2168
|
const memory = result.rows[0];
|
|
2008
2169
|
if (opts.type && !VALID_TYPES7.includes(opts.type)) {
|
|
2009
|
-
console.error(
|
|
2170
|
+
console.error(chalk13.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES7.join(", ")}`);
|
|
2010
2171
|
process.exit(1);
|
|
2011
2172
|
}
|
|
2012
2173
|
let newContent = opts.content;
|
|
2013
2174
|
if (newContent === void 0 && opts.tags === void 0 && opts.type === void 0) {
|
|
2014
2175
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
2015
|
-
const tmpFile =
|
|
2176
|
+
const tmpFile = join6(tmpdir(), `memories-edit-${nanoid2(6)}.md`);
|
|
2016
2177
|
writeFileSync(tmpFile, memory.content, "utf-8");
|
|
2017
2178
|
try {
|
|
2018
2179
|
execFileSync2(editor, [tmpFile], { stdio: "inherit" });
|
|
@@ -2024,7 +2185,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2024
2185
|
}
|
|
2025
2186
|
}
|
|
2026
2187
|
if (newContent === memory.content) {
|
|
2027
|
-
console.log(
|
|
2188
|
+
console.log(chalk13.dim("No changes made."));
|
|
2028
2189
|
return;
|
|
2029
2190
|
}
|
|
2030
2191
|
}
|
|
@@ -2034,24 +2195,24 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2034
2195
|
if (opts.type !== void 0) updates.type = opts.type;
|
|
2035
2196
|
const updated = await updateMemory(id, updates);
|
|
2036
2197
|
if (!updated) {
|
|
2037
|
-
console.error(
|
|
2198
|
+
console.error(chalk13.red("\u2717") + ` Failed to update memory ${chalk13.dim(id)}`);
|
|
2038
2199
|
process.exit(1);
|
|
2039
2200
|
}
|
|
2040
2201
|
const changes = [];
|
|
2041
2202
|
if (updates.content !== void 0) changes.push("content");
|
|
2042
2203
|
if (updates.tags !== void 0) changes.push("tags");
|
|
2043
2204
|
if (updates.type !== void 0) changes.push(`type\u2192${updates.type}`);
|
|
2044
|
-
console.log(
|
|
2205
|
+
console.log(chalk13.green("\u2713") + ` Updated ${chalk13.dim(id)} (${changes.join(", ")})`);
|
|
2045
2206
|
} catch (error2) {
|
|
2046
2207
|
if (error2.name === "ExitPromptError") return;
|
|
2047
|
-
console.error(
|
|
2208
|
+
console.error(chalk13.red("\u2717") + " Failed to edit memory:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2048
2209
|
process.exit(1);
|
|
2049
2210
|
}
|
|
2050
2211
|
});
|
|
2051
2212
|
|
|
2052
2213
|
// src/commands/stats.ts
|
|
2053
2214
|
import { Command as Command15 } from "commander";
|
|
2054
|
-
import
|
|
2215
|
+
import chalk14 from "chalk";
|
|
2055
2216
|
var statsCommand = new Command15("stats").description("Show memory statistics").option("--json", "Output as JSON").action(async (opts) => {
|
|
2056
2217
|
try {
|
|
2057
2218
|
const db = await getDb();
|
|
@@ -2088,46 +2249,46 @@ var statsCommand = new Command15("stats").description("Show memory statistics").
|
|
|
2088
2249
|
console.log(JSON.stringify(data, null, 2));
|
|
2089
2250
|
return;
|
|
2090
2251
|
}
|
|
2091
|
-
console.log(
|
|
2252
|
+
console.log(chalk14.bold("Memory Statistics\n"));
|
|
2092
2253
|
if (projectId) {
|
|
2093
|
-
console.log(` Project: ${
|
|
2254
|
+
console.log(` Project: ${chalk14.dim(projectId)}`);
|
|
2094
2255
|
}
|
|
2095
|
-
console.log(` Total: ${
|
|
2256
|
+
console.log(` Total: ${chalk14.bold(String(total))} active, ${chalk14.dim(String(deleted))} deleted
|
|
2096
2257
|
`);
|
|
2097
2258
|
if (rows.length === 0) {
|
|
2098
|
-
console.log(
|
|
2259
|
+
console.log(chalk14.dim(' No memories yet. Add one with: memories add "Your memory"'));
|
|
2099
2260
|
return;
|
|
2100
2261
|
}
|
|
2101
2262
|
const typeWidths = { rule: 8, decision: 8, fact: 8, note: 8 };
|
|
2102
2263
|
console.log(
|
|
2103
|
-
` ${
|
|
2264
|
+
` ${chalk14.dim("Type".padEnd(12))}${chalk14.dim("Scope".padEnd(10))}${chalk14.dim("Count")}`
|
|
2104
2265
|
);
|
|
2105
|
-
console.log(
|
|
2266
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(30)));
|
|
2106
2267
|
for (const row of rows) {
|
|
2107
2268
|
const type = row.type.padEnd(12);
|
|
2108
2269
|
const scope = row.scope.padEnd(10);
|
|
2109
2270
|
console.log(` ${type}${scope}${Number(row.count)}`);
|
|
2110
2271
|
}
|
|
2111
2272
|
} catch (error2) {
|
|
2112
|
-
console.error(
|
|
2273
|
+
console.error(chalk14.red("\u2717") + " Failed to get stats:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2113
2274
|
process.exit(1);
|
|
2114
2275
|
}
|
|
2115
2276
|
});
|
|
2116
2277
|
|
|
2117
2278
|
// src/commands/doctor.ts
|
|
2118
2279
|
import { Command as Command16 } from "commander";
|
|
2119
|
-
import
|
|
2120
|
-
import { existsSync as
|
|
2121
|
-
import { join as
|
|
2280
|
+
import chalk15 from "chalk";
|
|
2281
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2282
|
+
import { join as join7 } from "path";
|
|
2122
2283
|
var doctorCommand = new Command16("doctor").description("Check memories health and diagnose issues").option("--fix", "Attempt to fix issues found").action(async (opts) => {
|
|
2123
2284
|
try {
|
|
2124
|
-
console.log(
|
|
2285
|
+
console.log(chalk15.bold("memories doctor\n"));
|
|
2125
2286
|
const checks = [
|
|
2126
2287
|
{
|
|
2127
2288
|
name: "Database file",
|
|
2128
2289
|
run: async () => {
|
|
2129
|
-
const dbPath =
|
|
2130
|
-
if (
|
|
2290
|
+
const dbPath = join7(getConfigDir(), "local.db");
|
|
2291
|
+
if (existsSync6(dbPath)) {
|
|
2131
2292
|
return { ok: true, message: `Found at ${dbPath}` };
|
|
2132
2293
|
}
|
|
2133
2294
|
return { ok: false, message: `Not found at ${dbPath}. Run: memories init` };
|
|
@@ -2225,43 +2386,43 @@ var doctorCommand = new Command16("doctor").description("Check memories health a
|
|
|
2225
2386
|
let hasIssues = false;
|
|
2226
2387
|
for (const check of checks) {
|
|
2227
2388
|
const { ok, message } = await check.run();
|
|
2228
|
-
const icon = ok ?
|
|
2229
|
-
console.log(` ${icon} ${
|
|
2389
|
+
const icon = ok ? chalk15.green("\u2713") : chalk15.red("\u2717");
|
|
2390
|
+
console.log(` ${icon} ${chalk15.bold(check.name)}: ${message}`);
|
|
2230
2391
|
if (!ok) hasIssues = true;
|
|
2231
2392
|
}
|
|
2232
2393
|
if (opts.fix) {
|
|
2233
|
-
console.log(
|
|
2394
|
+
console.log(chalk15.bold("\nRunning fixes...\n"));
|
|
2234
2395
|
const db = await getDb();
|
|
2235
2396
|
const purged = await db.execute(
|
|
2236
2397
|
"DELETE FROM memories WHERE deleted_at IS NOT NULL"
|
|
2237
2398
|
);
|
|
2238
|
-
console.log(` ${
|
|
2399
|
+
console.log(` ${chalk15.green("\u2713")} Purged ${purged.rowsAffected} soft-deleted records`);
|
|
2239
2400
|
try {
|
|
2240
2401
|
await db.execute("INSERT INTO memories_fts(memories_fts) VALUES('rebuild')");
|
|
2241
|
-
console.log(` ${
|
|
2402
|
+
console.log(` ${chalk15.green("\u2713")} Rebuilt FTS index`);
|
|
2242
2403
|
} catch {
|
|
2243
|
-
console.log(` ${
|
|
2404
|
+
console.log(` ${chalk15.yellow("\u26A0")} Could not rebuild FTS index`);
|
|
2244
2405
|
}
|
|
2245
2406
|
}
|
|
2246
2407
|
console.log();
|
|
2247
2408
|
if (hasIssues) {
|
|
2248
|
-
console.log(
|
|
2409
|
+
console.log(chalk15.yellow("Some issues detected.") + (opts.fix ? "" : " Run with --fix to attempt repairs."));
|
|
2249
2410
|
} else {
|
|
2250
|
-
console.log(
|
|
2411
|
+
console.log(chalk15.green("All checks passed."));
|
|
2251
2412
|
}
|
|
2252
2413
|
} catch (error2) {
|
|
2253
|
-
console.error(
|
|
2414
|
+
console.error(chalk15.red("\u2717") + " Doctor failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2254
2415
|
process.exit(1);
|
|
2255
2416
|
}
|
|
2256
2417
|
});
|
|
2257
2418
|
|
|
2258
2419
|
// src/commands/hook.ts
|
|
2259
2420
|
import { Command as Command17 } from "commander";
|
|
2260
|
-
import
|
|
2261
|
-
import { readFile as
|
|
2262
|
-
import { existsSync as
|
|
2421
|
+
import chalk16 from "chalk";
|
|
2422
|
+
import { readFile as readFile6, writeFile as writeFile6, chmod } from "fs/promises";
|
|
2423
|
+
import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
|
|
2263
2424
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2264
|
-
import { join as
|
|
2425
|
+
import { join as join8 } from "path";
|
|
2265
2426
|
var HOOK_MARKER_START = "# >>> memories.sh hook >>>";
|
|
2266
2427
|
var HOOK_MARKER_END = "# <<< memories.sh hook <<<";
|
|
2267
2428
|
var HOOK_SNIPPET = `
|
|
@@ -2282,22 +2443,22 @@ function getGitDir() {
|
|
|
2282
2443
|
function getHookLocation(hookName) {
|
|
2283
2444
|
const gitDir = getGitDir();
|
|
2284
2445
|
if (!gitDir) return null;
|
|
2285
|
-
const huskyPath =
|
|
2286
|
-
if (
|
|
2446
|
+
const huskyPath = join8(".husky", hookName);
|
|
2447
|
+
if (existsSync7(".husky") && !existsSync7(join8(".husky", "_"))) {
|
|
2287
2448
|
return { path: huskyPath, type: "husky" };
|
|
2288
2449
|
}
|
|
2289
|
-
const huskyLegacyPath =
|
|
2290
|
-
if (
|
|
2450
|
+
const huskyLegacyPath = join8(".husky", "_", hookName);
|
|
2451
|
+
if (existsSync7(join8(".husky", "_"))) {
|
|
2291
2452
|
return { path: huskyLegacyPath, type: "husky" };
|
|
2292
2453
|
}
|
|
2293
|
-
if (
|
|
2454
|
+
if (existsSync7(huskyPath)) {
|
|
2294
2455
|
return { path: huskyPath, type: "husky" };
|
|
2295
2456
|
}
|
|
2296
|
-
return { path:
|
|
2457
|
+
return { path: join8(gitDir, "hooks", hookName), type: "git" };
|
|
2297
2458
|
}
|
|
2298
2459
|
function detectLintStaged() {
|
|
2299
2460
|
try {
|
|
2300
|
-
if (!
|
|
2461
|
+
if (!existsSync7("package.json")) return false;
|
|
2301
2462
|
const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
|
|
2302
2463
|
return !!(pkg["lint-staged"] || pkg.devDependencies?.["lint-staged"] || pkg.dependencies?.["lint-staged"]);
|
|
2303
2464
|
} catch {
|
|
@@ -2310,31 +2471,31 @@ hookCommand.addCommand(
|
|
|
2310
2471
|
try {
|
|
2311
2472
|
const location = getHookLocation(opts.hook);
|
|
2312
2473
|
if (!location) {
|
|
2313
|
-
console.error(
|
|
2474
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2314
2475
|
process.exit(1);
|
|
2315
2476
|
}
|
|
2316
2477
|
const hookPath = location.path;
|
|
2317
|
-
if (
|
|
2318
|
-
const content = await
|
|
2478
|
+
if (existsSync7(hookPath)) {
|
|
2479
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2319
2480
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2320
|
-
console.log(
|
|
2481
|
+
console.log(chalk16.dim("Hook already installed. Use 'memories hook uninstall' first to reinstall."));
|
|
2321
2482
|
return;
|
|
2322
2483
|
}
|
|
2323
|
-
await
|
|
2484
|
+
await writeFile6(hookPath, content.trimEnd() + "\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2324
2485
|
} else {
|
|
2325
|
-
await
|
|
2486
|
+
await writeFile6(hookPath, "#!/bin/sh\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2326
2487
|
}
|
|
2327
2488
|
await chmod(hookPath, 493);
|
|
2328
2489
|
const locationLabel = location.type === "husky" ? "Husky" : ".git/hooks";
|
|
2329
|
-
console.log(
|
|
2330
|
-
console.log(
|
|
2490
|
+
console.log(chalk16.green("\u2713") + ` Installed memories hook in ${chalk16.dim(opts.hook)} (${locationLabel})`);
|
|
2491
|
+
console.log(chalk16.dim(" Rule files will auto-generate on each commit."));
|
|
2331
2492
|
if (detectLintStaged()) {
|
|
2332
2493
|
console.log(
|
|
2333
|
-
|
|
2494
|
+
chalk16.dim("\n lint-staged detected. You can also add to your lint-staged config:") + chalk16.dim('\n "*.md": "memories generate all --force"')
|
|
2334
2495
|
);
|
|
2335
2496
|
}
|
|
2336
2497
|
} catch (error2) {
|
|
2337
|
-
console.error(
|
|
2498
|
+
console.error(chalk16.red("\u2717") + " Failed to install hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2338
2499
|
process.exit(1);
|
|
2339
2500
|
}
|
|
2340
2501
|
})
|
|
@@ -2344,17 +2505,17 @@ hookCommand.addCommand(
|
|
|
2344
2505
|
try {
|
|
2345
2506
|
const location = getHookLocation(opts.hook);
|
|
2346
2507
|
if (!location) {
|
|
2347
|
-
console.error(
|
|
2508
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2348
2509
|
process.exit(1);
|
|
2349
2510
|
}
|
|
2350
2511
|
const hookPath = location.path;
|
|
2351
|
-
if (!
|
|
2352
|
-
console.log(
|
|
2512
|
+
if (!existsSync7(hookPath)) {
|
|
2513
|
+
console.log(chalk16.dim("No hook file found."));
|
|
2353
2514
|
return;
|
|
2354
2515
|
}
|
|
2355
|
-
const content = await
|
|
2516
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2356
2517
|
if (!content.includes(HOOK_MARKER_START)) {
|
|
2357
|
-
console.log(
|
|
2518
|
+
console.log(chalk16.dim("No memories hook found in " + opts.hook));
|
|
2358
2519
|
return;
|
|
2359
2520
|
}
|
|
2360
2521
|
const regex = new RegExp(
|
|
@@ -2364,13 +2525,13 @@ hookCommand.addCommand(
|
|
|
2364
2525
|
if (cleaned.trim() === "#!/bin/sh" || cleaned.trim() === "") {
|
|
2365
2526
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
2366
2527
|
await unlink2(hookPath);
|
|
2367
|
-
console.log(
|
|
2528
|
+
console.log(chalk16.green("\u2713") + ` Removed ${chalk16.dim(opts.hook)} hook (was memories-only)`);
|
|
2368
2529
|
} else {
|
|
2369
|
-
await
|
|
2370
|
-
console.log(
|
|
2530
|
+
await writeFile6(hookPath, cleaned, "utf-8");
|
|
2531
|
+
console.log(chalk16.green("\u2713") + ` Removed memories section from ${chalk16.dim(opts.hook)}`);
|
|
2371
2532
|
}
|
|
2372
2533
|
} catch (error2) {
|
|
2373
|
-
console.error(
|
|
2534
|
+
console.error(chalk16.red("\u2717") + " Failed to uninstall hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2374
2535
|
process.exit(1);
|
|
2375
2536
|
}
|
|
2376
2537
|
})
|
|
@@ -2380,21 +2541,21 @@ hookCommand.addCommand(
|
|
|
2380
2541
|
try {
|
|
2381
2542
|
const hookPath = getHookLocation(opts.hook)?.path;
|
|
2382
2543
|
if (!hookPath) {
|
|
2383
|
-
console.error(
|
|
2544
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2384
2545
|
process.exit(1);
|
|
2385
2546
|
}
|
|
2386
|
-
if (!
|
|
2387
|
-
console.log(
|
|
2547
|
+
if (!existsSync7(hookPath)) {
|
|
2548
|
+
console.log(chalk16.dim("Not installed") + ` \u2014 no ${opts.hook} hook found`);
|
|
2388
2549
|
return;
|
|
2389
2550
|
}
|
|
2390
|
-
const content = await
|
|
2551
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2391
2552
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2392
|
-
console.log(
|
|
2553
|
+
console.log(chalk16.green("\u2713") + ` Installed in ${chalk16.dim(hookPath)}`);
|
|
2393
2554
|
} else {
|
|
2394
|
-
console.log(
|
|
2555
|
+
console.log(chalk16.dim("Not installed") + ` \u2014 ${opts.hook} exists but has no memories section`);
|
|
2395
2556
|
}
|
|
2396
2557
|
} catch (error2) {
|
|
2397
|
-
console.error(
|
|
2558
|
+
console.error(chalk16.red("\u2717") + " Failed to check hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2398
2559
|
process.exit(1);
|
|
2399
2560
|
}
|
|
2400
2561
|
})
|
|
@@ -2405,9 +2566,9 @@ function escapeRegex(str) {
|
|
|
2405
2566
|
|
|
2406
2567
|
// src/commands/ingest.ts
|
|
2407
2568
|
import { Command as Command18 } from "commander";
|
|
2408
|
-
import
|
|
2409
|
-
import { readFile as
|
|
2410
|
-
import { existsSync as
|
|
2569
|
+
import chalk17 from "chalk";
|
|
2570
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2571
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2411
2572
|
var SOURCES = [
|
|
2412
2573
|
{ name: "cursor", paths: [".cursor/rules/memories.mdc", ".cursorrules"], description: "Cursor rules" },
|
|
2413
2574
|
{ name: "claude", paths: ["CLAUDE.md"], description: "Claude Code instructions" },
|
|
@@ -2467,14 +2628,14 @@ function normalize(s) {
|
|
|
2467
2628
|
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) => {
|
|
2468
2629
|
try {
|
|
2469
2630
|
if (opts.type && !VALID_TYPES8.includes(opts.type)) {
|
|
2470
|
-
console.error(
|
|
2631
|
+
console.error(chalk17.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES8.join(", ")}`);
|
|
2471
2632
|
process.exit(1);
|
|
2472
2633
|
}
|
|
2473
2634
|
const filesToProcess = [];
|
|
2474
2635
|
if (opts.all) {
|
|
2475
2636
|
for (const src of SOURCES) {
|
|
2476
2637
|
for (const p of src.paths) {
|
|
2477
|
-
if (
|
|
2638
|
+
if (existsSync8(p)) {
|
|
2478
2639
|
filesToProcess.push({ name: src.name, path: p });
|
|
2479
2640
|
}
|
|
2480
2641
|
}
|
|
@@ -2483,27 +2644,27 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2483
2644
|
const known = SOURCES.find((s) => s.name === source);
|
|
2484
2645
|
if (known) {
|
|
2485
2646
|
for (const p of known.paths) {
|
|
2486
|
-
if (
|
|
2647
|
+
if (existsSync8(p)) {
|
|
2487
2648
|
filesToProcess.push({ name: known.name, path: p });
|
|
2488
2649
|
break;
|
|
2489
2650
|
}
|
|
2490
2651
|
}
|
|
2491
2652
|
if (filesToProcess.length === 0) {
|
|
2492
|
-
console.error(
|
|
2653
|
+
console.error(chalk17.red("\u2717") + ` No ${known.description} file found at: ${known.paths.join(", ")}`);
|
|
2493
2654
|
process.exit(1);
|
|
2494
2655
|
}
|
|
2495
|
-
} else if (
|
|
2656
|
+
} else if (existsSync8(source)) {
|
|
2496
2657
|
filesToProcess.push({ name: "file", path: source });
|
|
2497
2658
|
} else {
|
|
2498
|
-
console.error(
|
|
2659
|
+
console.error(chalk17.red("\u2717") + ` Unknown source "${source}". Valid: ${SOURCES.map((s) => s.name).join(", ")}, or a file path`);
|
|
2499
2660
|
process.exit(1);
|
|
2500
2661
|
}
|
|
2501
2662
|
} else {
|
|
2502
|
-
console.error(
|
|
2663
|
+
console.error(chalk17.red("\u2717") + " Specify a source or use --all");
|
|
2503
2664
|
process.exit(1);
|
|
2504
2665
|
}
|
|
2505
2666
|
if (filesToProcess.length === 0) {
|
|
2506
|
-
console.log(
|
|
2667
|
+
console.log(chalk17.dim("No IDE rule files found."));
|
|
2507
2668
|
return;
|
|
2508
2669
|
}
|
|
2509
2670
|
const existingSet = /* @__PURE__ */ new Set();
|
|
@@ -2517,29 +2678,29 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2517
2678
|
let totalImported = 0;
|
|
2518
2679
|
let totalSkipped = 0;
|
|
2519
2680
|
for (const file of filesToProcess) {
|
|
2520
|
-
const content = await
|
|
2681
|
+
const content = await readFile7(file.path, "utf-8");
|
|
2521
2682
|
if (content.includes(MARKER2)) {
|
|
2522
|
-
console.log(
|
|
2683
|
+
console.log(chalk17.dim(` Skipping ${file.path} (generated by memories.sh)`));
|
|
2523
2684
|
continue;
|
|
2524
2685
|
}
|
|
2525
2686
|
const memories = extractMemories(content);
|
|
2526
2687
|
if (memories.length === 0) {
|
|
2527
|
-
console.log(
|
|
2688
|
+
console.log(chalk17.dim(` No importable memories found in ${file.path}`));
|
|
2528
2689
|
continue;
|
|
2529
2690
|
}
|
|
2530
|
-
console.log(
|
|
2531
|
-
${file.name}`) +
|
|
2691
|
+
console.log(chalk17.bold(`
|
|
2692
|
+
${file.name}`) + chalk17.dim(` (${file.path}) \u2014 ${memories.length} items`));
|
|
2532
2693
|
for (const mem of memories) {
|
|
2533
2694
|
const type = opts.type ?? mem.type;
|
|
2534
2695
|
if (opts.dedup !== false && existingSet.has(normalize(mem.content))) {
|
|
2535
2696
|
if (opts.dryRun) {
|
|
2536
|
-
console.log(` ${
|
|
2697
|
+
console.log(` ${chalk17.dim("skip")} ${chalk17.dim(mem.content)}`);
|
|
2537
2698
|
}
|
|
2538
2699
|
totalSkipped++;
|
|
2539
2700
|
continue;
|
|
2540
2701
|
}
|
|
2541
2702
|
if (opts.dryRun) {
|
|
2542
|
-
const typeColor = type === "rule" ?
|
|
2703
|
+
const typeColor = type === "rule" ? chalk17.blue : type === "decision" ? chalk17.yellow : type === "fact" ? chalk17.green : chalk17.dim;
|
|
2543
2704
|
console.log(` ${typeColor(type.padEnd(9))} ${mem.content}`);
|
|
2544
2705
|
} else {
|
|
2545
2706
|
await addMemory(mem.content, { type });
|
|
@@ -2550,23 +2711,23 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2550
2711
|
}
|
|
2551
2712
|
if (opts.dryRun) {
|
|
2552
2713
|
const skipMsg = totalSkipped > 0 ? ` (${totalSkipped} duplicates skipped)` : "";
|
|
2553
|
-
console.log(
|
|
2714
|
+
console.log(chalk17.dim(`
|
|
2554
2715
|
Dry run \u2014 no memories imported.${skipMsg} Remove --dry-run to import.`));
|
|
2555
2716
|
} else {
|
|
2556
|
-
const skipMsg = totalSkipped > 0 ?
|
|
2557
|
-
console.log(
|
|
2717
|
+
const skipMsg = totalSkipped > 0 ? chalk17.dim(` (${totalSkipped} duplicates skipped)`) : "";
|
|
2718
|
+
console.log(chalk17.green("\n\u2713") + ` Imported ${totalImported} memories` + skipMsg);
|
|
2558
2719
|
}
|
|
2559
2720
|
} catch (error2) {
|
|
2560
|
-
console.error(
|
|
2721
|
+
console.error(chalk17.red("\u2717") + " Failed to ingest:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2561
2722
|
process.exit(1);
|
|
2562
2723
|
}
|
|
2563
2724
|
});
|
|
2564
2725
|
|
|
2565
2726
|
// src/commands/diff.ts
|
|
2566
2727
|
import { Command as Command19 } from "commander";
|
|
2567
|
-
import
|
|
2568
|
-
import { readFile as
|
|
2569
|
-
import { existsSync as
|
|
2728
|
+
import chalk18 from "chalk";
|
|
2729
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2730
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2570
2731
|
import { resolve as resolve2 } from "path";
|
|
2571
2732
|
var MARKER3 = "Generated by memories.sh";
|
|
2572
2733
|
var VALID_TYPES9 = ["rule", "decision", "fact", "note"];
|
|
@@ -2599,7 +2760,7 @@ function parseTypes2(raw) {
|
|
|
2599
2760
|
const types = raw.split(",").map((s) => s.trim());
|
|
2600
2761
|
for (const t of types) {
|
|
2601
2762
|
if (!VALID_TYPES9.includes(t)) {
|
|
2602
|
-
console.error(
|
|
2763
|
+
console.error(chalk18.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES9.join(", ")}`);
|
|
2603
2764
|
process.exit(1);
|
|
2604
2765
|
}
|
|
2605
2766
|
}
|
|
@@ -2611,7 +2772,7 @@ async function fetchMemories2(types) {
|
|
|
2611
2772
|
}
|
|
2612
2773
|
async function diffTarget(target, currentMemories, outputPath) {
|
|
2613
2774
|
const filePath = resolve2(outputPath ?? target.defaultPath);
|
|
2614
|
-
if (!
|
|
2775
|
+
if (!existsSync9(filePath)) {
|
|
2615
2776
|
return {
|
|
2616
2777
|
added: currentMemories.map((m) => m.content),
|
|
2617
2778
|
removed: [],
|
|
@@ -2622,7 +2783,7 @@ async function diffTarget(target, currentMemories, outputPath) {
|
|
|
2622
2783
|
generatedAt: null
|
|
2623
2784
|
};
|
|
2624
2785
|
}
|
|
2625
|
-
const content = await
|
|
2786
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2626
2787
|
const isOurs = content.includes(MARKER3);
|
|
2627
2788
|
const generatedAt = extractTimestamp(content);
|
|
2628
2789
|
const fileMemories = extractFileMemories(content);
|
|
@@ -2638,7 +2799,7 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2638
2799
|
const memories = await fetchMemories2(types);
|
|
2639
2800
|
const targetsToCheck = target && target !== "all" ? TARGETS2.filter((t) => t.name === target) : TARGETS2;
|
|
2640
2801
|
if (target && target !== "all" && targetsToCheck.length === 0) {
|
|
2641
|
-
console.error(
|
|
2802
|
+
console.error(chalk18.red("\u2717") + ` Unknown target "${target}". Valid: ${TARGETS2.map((t) => t.name).join(", ")}`);
|
|
2642
2803
|
process.exit(1);
|
|
2643
2804
|
}
|
|
2644
2805
|
let anyStale = false;
|
|
@@ -2647,54 +2808,54 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2647
2808
|
if (!result.exists && !target) continue;
|
|
2648
2809
|
const hasChanges = result.added.length > 0 || result.removed.length > 0;
|
|
2649
2810
|
if (!result.exists) {
|
|
2650
|
-
console.log(
|
|
2651
|
-
${t.name}`) +
|
|
2652
|
-
console.log(
|
|
2811
|
+
console.log(chalk18.bold(`
|
|
2812
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2813
|
+
console.log(chalk18.yellow(" Not generated yet.") + chalk18.dim(` Run: memories generate ${t.name}`));
|
|
2653
2814
|
anyStale = true;
|
|
2654
2815
|
continue;
|
|
2655
2816
|
}
|
|
2656
2817
|
if (!result.isOurs) {
|
|
2657
|
-
console.log(
|
|
2658
|
-
${t.name}`) +
|
|
2659
|
-
console.log(
|
|
2818
|
+
console.log(chalk18.bold(`
|
|
2819
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2820
|
+
console.log(chalk18.dim(" Not managed by memories.sh (no marker found)"));
|
|
2660
2821
|
continue;
|
|
2661
2822
|
}
|
|
2662
2823
|
if (!hasChanges) {
|
|
2663
2824
|
if (target) {
|
|
2664
|
-
console.log(
|
|
2665
|
-
${t.name}`) +
|
|
2666
|
-
const since = result.generatedAt ?
|
|
2667
|
-
console.log(
|
|
2825
|
+
console.log(chalk18.bold(`
|
|
2826
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2827
|
+
const since = result.generatedAt ? chalk18.dim(` (generated ${formatRelative(result.generatedAt)})`) : "";
|
|
2828
|
+
console.log(chalk18.green(" Up to date.") + since);
|
|
2668
2829
|
}
|
|
2669
2830
|
continue;
|
|
2670
2831
|
}
|
|
2671
2832
|
anyStale = true;
|
|
2672
|
-
console.log(
|
|
2673
|
-
${t.name}`) +
|
|
2833
|
+
console.log(chalk18.bold(`
|
|
2834
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2674
2835
|
if (result.generatedAt) {
|
|
2675
|
-
console.log(
|
|
2836
|
+
console.log(chalk18.dim(` Generated ${formatRelative(result.generatedAt)}`));
|
|
2676
2837
|
}
|
|
2677
2838
|
for (const a of result.added) {
|
|
2678
|
-
console.log(
|
|
2839
|
+
console.log(chalk18.green(` + ${a}`));
|
|
2679
2840
|
}
|
|
2680
2841
|
for (const r of result.removed) {
|
|
2681
|
-
console.log(
|
|
2842
|
+
console.log(chalk18.red(` - ${r}`));
|
|
2682
2843
|
}
|
|
2683
2844
|
if (result.unchanged > 0) {
|
|
2684
|
-
console.log(
|
|
2845
|
+
console.log(chalk18.dim(` ${result.unchanged} unchanged`));
|
|
2685
2846
|
}
|
|
2686
2847
|
}
|
|
2687
2848
|
if (!anyStale) {
|
|
2688
2849
|
if (target) {
|
|
2689
2850
|
} else {
|
|
2690
|
-
console.log(
|
|
2851
|
+
console.log(chalk18.green("\n All generated files are up to date."));
|
|
2691
2852
|
}
|
|
2692
2853
|
} else {
|
|
2693
|
-
console.log(
|
|
2694
|
-
Run ${
|
|
2854
|
+
console.log(chalk18.dim(`
|
|
2855
|
+
Run ${chalk18.bold("memories generate")} to update stale files.`));
|
|
2695
2856
|
}
|
|
2696
2857
|
} catch (error2) {
|
|
2697
|
-
console.error(
|
|
2858
|
+
console.error(chalk18.red("\u2717") + " Diff failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2698
2859
|
process.exit(1);
|
|
2699
2860
|
}
|
|
2700
2861
|
});
|
|
@@ -2715,7 +2876,7 @@ function formatRelative(isoDate) {
|
|
|
2715
2876
|
|
|
2716
2877
|
// src/commands/tag.ts
|
|
2717
2878
|
import { Command as Command20 } from "commander";
|
|
2718
|
-
import
|
|
2879
|
+
import chalk19 from "chalk";
|
|
2719
2880
|
var VALID_TYPES10 = ["rule", "decision", "fact", "note"];
|
|
2720
2881
|
function buildWhere(filters) {
|
|
2721
2882
|
const conditions = ["deleted_at IS NULL"];
|
|
@@ -2767,12 +2928,12 @@ tagCommand.addCommand(
|
|
|
2767
2928
|
updated++;
|
|
2768
2929
|
}
|
|
2769
2930
|
if (opts.dryRun) {
|
|
2770
|
-
console.log(
|
|
2931
|
+
console.log(chalk19.dim(`Dry run \u2014 would tag ${updated} memories with "${tag}" (${skipped} already tagged)`));
|
|
2771
2932
|
} else {
|
|
2772
|
-
console.log(
|
|
2933
|
+
console.log(chalk19.green("\u2713") + ` Tagged ${updated} memories with "${tag}"` + (skipped > 0 ? chalk19.dim(` (${skipped} already tagged)`) : ""));
|
|
2773
2934
|
}
|
|
2774
2935
|
} catch (error2) {
|
|
2775
|
-
console.error(
|
|
2936
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2776
2937
|
process.exit(1);
|
|
2777
2938
|
}
|
|
2778
2939
|
})
|
|
@@ -2801,12 +2962,12 @@ tagCommand.addCommand(
|
|
|
2801
2962
|
updated++;
|
|
2802
2963
|
}
|
|
2803
2964
|
if (opts.dryRun) {
|
|
2804
|
-
console.log(
|
|
2965
|
+
console.log(chalk19.dim(`Dry run \u2014 would remove "${tag}" from ${updated} memories`));
|
|
2805
2966
|
} else {
|
|
2806
|
-
console.log(
|
|
2967
|
+
console.log(chalk19.green("\u2713") + ` Removed "${tag}" from ${updated} memories`);
|
|
2807
2968
|
}
|
|
2808
2969
|
} catch (error2) {
|
|
2809
|
-
console.error(
|
|
2970
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2810
2971
|
process.exit(1);
|
|
2811
2972
|
}
|
|
2812
2973
|
})
|
|
@@ -2827,15 +2988,15 @@ tagCommand.addCommand(
|
|
|
2827
2988
|
}
|
|
2828
2989
|
}
|
|
2829
2990
|
if (counts.size === 0) {
|
|
2830
|
-
console.log(
|
|
2991
|
+
console.log(chalk19.dim("No tags found."));
|
|
2831
2992
|
return;
|
|
2832
2993
|
}
|
|
2833
2994
|
const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
2834
2995
|
for (const [tag, count] of sorted) {
|
|
2835
|
-
console.log(` ${
|
|
2996
|
+
console.log(` ${chalk19.bold(tag)} ${chalk19.dim(`(${count})`)}`);
|
|
2836
2997
|
}
|
|
2837
2998
|
} catch (error2) {
|
|
2838
|
-
console.error(
|
|
2999
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2839
3000
|
process.exit(1);
|
|
2840
3001
|
}
|
|
2841
3002
|
})
|
|
@@ -2843,7 +3004,7 @@ tagCommand.addCommand(
|
|
|
2843
3004
|
|
|
2844
3005
|
// src/commands/validate.ts
|
|
2845
3006
|
import { Command as Command21 } from "commander";
|
|
2846
|
-
import
|
|
3007
|
+
import chalk20 from "chalk";
|
|
2847
3008
|
function levenshtein(a, b) {
|
|
2848
3009
|
const m = a.length, n = b.length;
|
|
2849
3010
|
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
@@ -2904,10 +3065,10 @@ var validateCommand = new Command21("validate").description("Check for conflicti
|
|
|
2904
3065
|
const projectId = getProjectId() ?? void 0;
|
|
2905
3066
|
const rules = await getRules({ projectId });
|
|
2906
3067
|
if (rules.length === 0) {
|
|
2907
|
-
console.log(
|
|
3068
|
+
console.log(chalk20.dim("No rules to validate."));
|
|
2908
3069
|
return;
|
|
2909
3070
|
}
|
|
2910
|
-
console.log(
|
|
3071
|
+
console.log(chalk20.bold("\u{1F50D} Validating rules...\n"));
|
|
2911
3072
|
const issues = [];
|
|
2912
3073
|
for (let i = 0; i < rules.length; i++) {
|
|
2913
3074
|
for (let j = i + 1; j < rules.length; j++) {
|
|
@@ -2939,41 +3100,41 @@ var validateCommand = new Command21("validate").description("Check for conflicti
|
|
|
2939
3100
|
}
|
|
2940
3101
|
}
|
|
2941
3102
|
if (issues.length === 0) {
|
|
2942
|
-
console.log(
|
|
3103
|
+
console.log(chalk20.green("\u2713") + ` ${rules.length} rules validated, no issues found.`);
|
|
2943
3104
|
return;
|
|
2944
3105
|
}
|
|
2945
|
-
console.log(
|
|
3106
|
+
console.log(chalk20.yellow("\u26A0\uFE0F Potential Issues Found:\n"));
|
|
2946
3107
|
let num = 1;
|
|
2947
3108
|
for (const issue of issues) {
|
|
2948
3109
|
const typeLabel = issue.type === "duplicate" ? "Exact duplicate" : issue.type === "near-duplicate" ? "Near duplicate" : "Potential conflict";
|
|
2949
|
-
const color = issue.type === "conflict" ?
|
|
3110
|
+
const color = issue.type === "conflict" ? chalk20.red : chalk20.yellow;
|
|
2950
3111
|
console.log(color(`${num}. ${typeLabel}${issue.detail ? ` (${issue.detail})` : ""}:`));
|
|
2951
3112
|
console.log(` \u{1F4CC} "${issue.memory1.content}"`);
|
|
2952
3113
|
console.log(` \u{1F4CC} "${issue.memory2.content}"`);
|
|
2953
3114
|
if (issue.type === "duplicate" || issue.type === "near-duplicate") {
|
|
2954
|
-
console.log(
|
|
3115
|
+
console.log(chalk20.dim(" \u2192 Consider merging these rules\n"));
|
|
2955
3116
|
} else {
|
|
2956
|
-
console.log(
|
|
3117
|
+
console.log(chalk20.dim(" \u2192 These rules may contradict each other\n"));
|
|
2957
3118
|
}
|
|
2958
3119
|
num++;
|
|
2959
3120
|
}
|
|
2960
3121
|
const duplicates = issues.filter((i) => i.type === "duplicate" || i.type === "near-duplicate").length;
|
|
2961
3122
|
const conflicts = issues.filter((i) => i.type === "conflict").length;
|
|
2962
|
-
console.log(
|
|
2963
|
-
if (duplicates > 0) console.log(
|
|
2964
|
-
if (conflicts > 0) console.log(
|
|
3123
|
+
console.log(chalk20.bold(`${rules.length} rules validated, ${issues.length} issues found`));
|
|
3124
|
+
if (duplicates > 0) console.log(chalk20.dim(` ${duplicates} duplicate(s)`));
|
|
3125
|
+
if (conflicts > 0) console.log(chalk20.dim(` ${conflicts} conflict(s)`));
|
|
2965
3126
|
if (opts.fix) {
|
|
2966
|
-
console.log(
|
|
3127
|
+
console.log(chalk20.dim("\n--fix mode not yet implemented. Review issues above manually."));
|
|
2967
3128
|
}
|
|
2968
3129
|
} catch (error2) {
|
|
2969
|
-
console.error(
|
|
3130
|
+
console.error(chalk20.red("\u2717") + " Validation failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2970
3131
|
process.exit(1);
|
|
2971
3132
|
}
|
|
2972
3133
|
});
|
|
2973
3134
|
|
|
2974
3135
|
// src/commands/stale.ts
|
|
2975
3136
|
import { Command as Command22 } from "commander";
|
|
2976
|
-
import
|
|
3137
|
+
import chalk21 from "chalk";
|
|
2977
3138
|
import { createInterface as createInterface2 } from "readline";
|
|
2978
3139
|
var TYPE_ICONS4 = {
|
|
2979
3140
|
rule: "\u{1F4CC}",
|
|
@@ -2996,7 +3157,7 @@ var staleCommand = new Command22("stale").description("Find memories that haven'
|
|
|
2996
3157
|
const days = parseInt(opts.days, 10);
|
|
2997
3158
|
const projectId = getProjectId() ?? void 0;
|
|
2998
3159
|
if (isNaN(days) || days <= 0) {
|
|
2999
|
-
console.error(
|
|
3160
|
+
console.error(chalk21.red("\u2717") + " --days must be a positive number");
|
|
3000
3161
|
process.exit(1);
|
|
3001
3162
|
}
|
|
3002
3163
|
let sql = `
|
|
@@ -3019,25 +3180,25 @@ var staleCommand = new Command22("stale").description("Find memories that haven'
|
|
|
3019
3180
|
return;
|
|
3020
3181
|
}
|
|
3021
3182
|
if (stale.length === 0) {
|
|
3022
|
-
console.log(
|
|
3183
|
+
console.log(chalk21.green("\u2713") + ` No memories older than ${days} days.`);
|
|
3023
3184
|
return;
|
|
3024
3185
|
}
|
|
3025
|
-
console.log(
|
|
3186
|
+
console.log(chalk21.bold(`\u23F0 Stale Memories (not updated in ${days}+ days)
|
|
3026
3187
|
`));
|
|
3027
3188
|
for (const m of stale.slice(0, 30)) {
|
|
3028
3189
|
const icon = TYPE_ICONS4[m.type] || "\u{1F4DD}";
|
|
3029
|
-
const scope = m.scope === "global" ?
|
|
3190
|
+
const scope = m.scope === "global" ? chalk21.dim("G") : chalk21.dim("P");
|
|
3030
3191
|
const preview = m.content.length > 50 ? m.content.slice(0, 47) + "..." : m.content;
|
|
3031
|
-
console.log(` ${icon} ${scope} ${
|
|
3192
|
+
console.log(` ${icon} ${scope} ${chalk21.dim(m.id)} ${preview} ${chalk21.yellow(`${m.days_old}d`)}`);
|
|
3032
3193
|
}
|
|
3033
3194
|
if (stale.length > 30) {
|
|
3034
|
-
console.log(
|
|
3195
|
+
console.log(chalk21.dim(` ... and ${stale.length - 30} more`));
|
|
3035
3196
|
}
|
|
3036
3197
|
console.log("");
|
|
3037
3198
|
console.log(`${stale.length} stale ${stale.length === 1 ? "memory" : "memories"} found`);
|
|
3038
|
-
console.log(
|
|
3199
|
+
console.log(chalk21.dim("Run 'memories review' to clean up interactively"));
|
|
3039
3200
|
} catch (error2) {
|
|
3040
|
-
console.error(
|
|
3201
|
+
console.error(chalk21.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3041
3202
|
process.exit(1);
|
|
3042
3203
|
}
|
|
3043
3204
|
});
|
|
@@ -3058,18 +3219,18 @@ var reviewCommand = new Command22("review").description("Interactively review an
|
|
|
3058
3219
|
});
|
|
3059
3220
|
const stale = result.rows;
|
|
3060
3221
|
if (stale.length === 0) {
|
|
3061
|
-
console.log(
|
|
3222
|
+
console.log(chalk21.green("\u2713") + ` No stale memories to review.`);
|
|
3062
3223
|
return;
|
|
3063
3224
|
}
|
|
3064
|
-
console.log(
|
|
3225
|
+
console.log(chalk21.bold(`
|
|
3065
3226
|
Reviewing ${stale.length} stale memories...
|
|
3066
3227
|
`));
|
|
3067
3228
|
let kept = 0, deleted = 0, skipped = 0;
|
|
3068
3229
|
for (const m of stale) {
|
|
3069
3230
|
const icon = TYPE_ICONS4[m.type] || "\u{1F4DD}";
|
|
3070
|
-
console.log(`${icon} ${
|
|
3231
|
+
console.log(`${icon} ${chalk21.bold(m.type.toUpperCase())} (${m.scope})`);
|
|
3071
3232
|
console.log(` "${m.content}"`);
|
|
3072
|
-
console.log(
|
|
3233
|
+
console.log(chalk21.dim(` Last updated: ${m.days_old} days ago`));
|
|
3073
3234
|
console.log("");
|
|
3074
3235
|
const answer = await prompt(" [k]eep [d]elete [s]kip [q]uit > ");
|
|
3075
3236
|
if (answer === "q") {
|
|
@@ -3077,31 +3238,31 @@ Reviewing ${stale.length} stale memories...
|
|
|
3077
3238
|
break;
|
|
3078
3239
|
} else if (answer === "d") {
|
|
3079
3240
|
await forgetMemory(m.id);
|
|
3080
|
-
console.log(
|
|
3241
|
+
console.log(chalk21.green(" \u2713 Deleted\n"));
|
|
3081
3242
|
deleted++;
|
|
3082
3243
|
} else if (answer === "k") {
|
|
3083
3244
|
await db.execute({
|
|
3084
3245
|
sql: "UPDATE memories SET updated_at = datetime('now') WHERE id = ?",
|
|
3085
3246
|
args: [m.id]
|
|
3086
3247
|
});
|
|
3087
|
-
console.log(
|
|
3248
|
+
console.log(chalk21.green(" \u2713 Kept (marked as reviewed)\n"));
|
|
3088
3249
|
kept++;
|
|
3089
3250
|
} else {
|
|
3090
|
-
console.log(
|
|
3251
|
+
console.log(chalk21.dim(" Skipped\n"));
|
|
3091
3252
|
skipped++;
|
|
3092
3253
|
}
|
|
3093
3254
|
}
|
|
3094
|
-
console.log(
|
|
3255
|
+
console.log(chalk21.bold("\nReview Summary:"));
|
|
3095
3256
|
console.log(` Kept: ${kept}, Deleted: ${deleted}, Skipped: ${skipped}`);
|
|
3096
3257
|
} catch (error2) {
|
|
3097
|
-
console.error(
|
|
3258
|
+
console.error(chalk21.red("\u2717") + " Review failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3098
3259
|
process.exit(1);
|
|
3099
3260
|
}
|
|
3100
3261
|
});
|
|
3101
3262
|
|
|
3102
3263
|
// src/commands/link.ts
|
|
3103
3264
|
import { Command as Command23 } from "commander";
|
|
3104
|
-
import
|
|
3265
|
+
import chalk22 from "chalk";
|
|
3105
3266
|
import { nanoid as nanoid3 } from "nanoid";
|
|
3106
3267
|
var LINK_TYPES = ["related", "supports", "supersedes", "contradicts"];
|
|
3107
3268
|
var TYPE_ICONS5 = {
|
|
@@ -3167,17 +3328,17 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3167
3328
|
await ensureLinksTable();
|
|
3168
3329
|
const db = await getDb();
|
|
3169
3330
|
if (!LINK_TYPES.includes(opts.type)) {
|
|
3170
|
-
console.error(
|
|
3331
|
+
console.error(chalk22.red("\u2717") + ` Invalid link type. Valid: ${LINK_TYPES.join(", ")}`);
|
|
3171
3332
|
process.exit(1);
|
|
3172
3333
|
}
|
|
3173
3334
|
const m1 = await getMemoryById(id1);
|
|
3174
3335
|
const m2 = await getMemoryById(id2);
|
|
3175
3336
|
if (!m1) {
|
|
3176
|
-
console.error(
|
|
3337
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id1} not found`);
|
|
3177
3338
|
process.exit(1);
|
|
3178
3339
|
}
|
|
3179
3340
|
if (!m2) {
|
|
3180
|
-
console.error(
|
|
3341
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id2} not found`);
|
|
3181
3342
|
process.exit(1);
|
|
3182
3343
|
}
|
|
3183
3344
|
const existing = await db.execute({
|
|
@@ -3186,7 +3347,7 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3186
3347
|
args: [id1, id2, id2, id1]
|
|
3187
3348
|
});
|
|
3188
3349
|
if (existing.rows.length > 0) {
|
|
3189
|
-
console.log(
|
|
3350
|
+
console.log(chalk22.yellow("!") + " These memories are already linked");
|
|
3190
3351
|
return;
|
|
3191
3352
|
}
|
|
3192
3353
|
const linkId = nanoid3(12);
|
|
@@ -3196,12 +3357,12 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3196
3357
|
});
|
|
3197
3358
|
const icon1 = TYPE_ICONS5[m1.type] || "\u{1F4DD}";
|
|
3198
3359
|
const icon2 = TYPE_ICONS5[m2.type] || "\u{1F4DD}";
|
|
3199
|
-
console.log(
|
|
3200
|
-
console.log(` ${icon1} ${
|
|
3201
|
-
console.log(` \u2193 ${
|
|
3202
|
-
console.log(` ${icon2} ${
|
|
3360
|
+
console.log(chalk22.green("\u2713") + " Linked memories:");
|
|
3361
|
+
console.log(` ${icon1} ${chalk22.dim(id1)} "${m1.content.slice(0, 40)}..."`);
|
|
3362
|
+
console.log(` \u2193 ${chalk22.cyan(opts.type)}`);
|
|
3363
|
+
console.log(` ${icon2} ${chalk22.dim(id2)} "${m2.content.slice(0, 40)}..."`);
|
|
3203
3364
|
} catch (error2) {
|
|
3204
|
-
console.error(
|
|
3365
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3205
3366
|
process.exit(1);
|
|
3206
3367
|
}
|
|
3207
3368
|
});
|
|
@@ -3215,12 +3376,12 @@ var unlinkCommand = new Command23("unlink").description("Remove link between two
|
|
|
3215
3376
|
args: [id1, id2, id2, id1]
|
|
3216
3377
|
});
|
|
3217
3378
|
if (result.rowsAffected === 0) {
|
|
3218
|
-
console.log(
|
|
3379
|
+
console.log(chalk22.yellow("!") + " No link found between these memories");
|
|
3219
3380
|
} else {
|
|
3220
|
-
console.log(
|
|
3381
|
+
console.log(chalk22.green("\u2713") + ` Unlinked ${id1} and ${id2}`);
|
|
3221
3382
|
}
|
|
3222
3383
|
} catch (error2) {
|
|
3223
|
-
console.error(
|
|
3384
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3224
3385
|
process.exit(1);
|
|
3225
3386
|
}
|
|
3226
3387
|
});
|
|
@@ -3228,16 +3389,16 @@ var showCommand = new Command23("show").description("Show a memory with its link
|
|
|
3228
3389
|
try {
|
|
3229
3390
|
const memory = await getMemoryById(id);
|
|
3230
3391
|
if (!memory) {
|
|
3231
|
-
console.error(
|
|
3392
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id} not found`);
|
|
3232
3393
|
process.exit(1);
|
|
3233
3394
|
}
|
|
3234
3395
|
const icon = TYPE_ICONS5[memory.type] || "\u{1F4DD}";
|
|
3235
3396
|
const scope = memory.scope === "global" ? "Global" : "Project";
|
|
3236
3397
|
console.log("");
|
|
3237
|
-
console.log(`${icon} ${
|
|
3238
|
-
console.log(
|
|
3239
|
-
console.log(
|
|
3240
|
-
if (memory.tags) console.log(
|
|
3398
|
+
console.log(`${icon} ${chalk22.bold(memory.type.toUpperCase())} (${scope})`);
|
|
3399
|
+
console.log(chalk22.dim(`ID: ${memory.id}`));
|
|
3400
|
+
console.log(chalk22.dim(`Created: ${memory.created_at}`));
|
|
3401
|
+
if (memory.tags) console.log(chalk22.dim(`Tags: ${memory.tags}`));
|
|
3241
3402
|
console.log("");
|
|
3242
3403
|
console.log(memory.content);
|
|
3243
3404
|
if (opts.links) {
|
|
@@ -3245,52 +3406,52 @@ var showCommand = new Command23("show").description("Show a memory with its link
|
|
|
3245
3406
|
const linked = await getLinkedMemories(id);
|
|
3246
3407
|
if (linked.length > 0) {
|
|
3247
3408
|
console.log("");
|
|
3248
|
-
console.log(
|
|
3409
|
+
console.log(chalk22.bold("Linked Memories:"));
|
|
3249
3410
|
for (const { memory: m, linkType, direction } of linked) {
|
|
3250
3411
|
const mIcon = TYPE_ICONS5[m.type] || "\u{1F4DD}";
|
|
3251
3412
|
const arrow = direction === "to" ? "\u2192" : "\u2190";
|
|
3252
3413
|
const preview = m.content.length > 50 ? m.content.slice(0, 47) + "..." : m.content;
|
|
3253
|
-
console.log(` ${arrow} ${
|
|
3414
|
+
console.log(` ${arrow} ${chalk22.cyan(linkType)}: ${mIcon} ${preview}`);
|
|
3254
3415
|
}
|
|
3255
3416
|
}
|
|
3256
3417
|
}
|
|
3257
3418
|
console.log("");
|
|
3258
3419
|
} catch (error2) {
|
|
3259
|
-
console.error(
|
|
3420
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3260
3421
|
process.exit(1);
|
|
3261
3422
|
}
|
|
3262
3423
|
});
|
|
3263
3424
|
|
|
3264
3425
|
// src/commands/template.ts
|
|
3265
3426
|
import { Command as Command24 } from "commander";
|
|
3266
|
-
import
|
|
3427
|
+
import chalk23 from "chalk";
|
|
3267
3428
|
var templateCommand = new Command24("template").description("Manage and use memory templates");
|
|
3268
3429
|
templateCommand.command("list").description("List available templates").action(() => {
|
|
3269
3430
|
const templates = listTemplates();
|
|
3270
|
-
console.log(
|
|
3431
|
+
console.log(chalk23.bold("\nAvailable Templates:\n"));
|
|
3271
3432
|
for (const t of templates) {
|
|
3272
3433
|
const typeIcon = t.type === "rule" ? "\u{1F4CC}" : t.type === "decision" ? "\u{1F4A1}" : t.type === "fact" ? "\u{1F4CB}" : "\u{1F4DD}";
|
|
3273
|
-
console.log(` ${
|
|
3434
|
+
console.log(` ${chalk23.cyan(t.name.padEnd(15))} ${typeIcon} ${t.description}`);
|
|
3274
3435
|
}
|
|
3275
3436
|
console.log("");
|
|
3276
|
-
console.log(
|
|
3437
|
+
console.log(chalk23.dim("Use: memories template use <name>"));
|
|
3277
3438
|
console.log("");
|
|
3278
3439
|
});
|
|
3279
3440
|
templateCommand.command("show <name>").description("Show template details and fields").action((name) => {
|
|
3280
3441
|
const template = getTemplate(name);
|
|
3281
3442
|
if (!template) {
|
|
3282
|
-
console.error(
|
|
3283
|
-
console.log(
|
|
3443
|
+
console.error(chalk23.red("\u2717") + ` Template "${name}" not found`);
|
|
3444
|
+
console.log(chalk23.dim("Run 'memories template list' to see available templates"));
|
|
3284
3445
|
process.exit(1);
|
|
3285
3446
|
}
|
|
3286
3447
|
const typeIcon = template.type === "rule" ? "\u{1F4CC}" : template.type === "decision" ? "\u{1F4A1}" : template.type === "fact" ? "\u{1F4CB}" : "\u{1F4DD}";
|
|
3287
3448
|
console.log("");
|
|
3288
|
-
console.log(
|
|
3289
|
-
console.log(
|
|
3449
|
+
console.log(chalk23.bold(template.name) + ` ${typeIcon} ${template.type}`);
|
|
3450
|
+
console.log(chalk23.dim(template.description));
|
|
3290
3451
|
console.log("");
|
|
3291
|
-
console.log(
|
|
3452
|
+
console.log(chalk23.bold("Fields:"));
|
|
3292
3453
|
for (const field of template.fields) {
|
|
3293
|
-
const required = field.required ?
|
|
3454
|
+
const required = field.required ? chalk23.red("*") : chalk23.dim("(optional)");
|
|
3294
3455
|
console.log(` ${field.name.padEnd(15)} ${required} ${field.prompt}`);
|
|
3295
3456
|
}
|
|
3296
3457
|
console.log("");
|
|
@@ -3298,13 +3459,13 @@ templateCommand.command("show <name>").description("Show template details and fi
|
|
|
3298
3459
|
templateCommand.command("use <name>").description("Create a memory using a template").option("-g, --global", "Store as global memory").action(async (name, opts) => {
|
|
3299
3460
|
const template = getTemplate(name);
|
|
3300
3461
|
if (!template) {
|
|
3301
|
-
console.error(
|
|
3302
|
-
console.log(
|
|
3462
|
+
console.error(chalk23.red("\u2717") + ` Template "${name}" not found`);
|
|
3463
|
+
console.log(chalk23.dim("Run 'memories template list' to see available templates"));
|
|
3303
3464
|
process.exit(1);
|
|
3304
3465
|
}
|
|
3305
3466
|
console.log("");
|
|
3306
|
-
console.log(
|
|
3307
|
-
console.log(
|
|
3467
|
+
console.log(chalk23.bold(`Using template: ${template.name}`));
|
|
3468
|
+
console.log(chalk23.dim(template.description));
|
|
3308
3469
|
console.log("");
|
|
3309
3470
|
try {
|
|
3310
3471
|
const content = await fillTemplate(template);
|
|
@@ -3313,8 +3474,8 @@ templateCommand.command("use <name>").description("Create a memory using a templ
|
|
|
3313
3474
|
global: opts.global
|
|
3314
3475
|
});
|
|
3315
3476
|
console.log("");
|
|
3316
|
-
console.log(
|
|
3317
|
-
console.log(
|
|
3477
|
+
console.log(chalk23.green("\u2713") + ` Created ${template.type}: ${chalk23.dim(memory.id)}`);
|
|
3478
|
+
console.log(chalk23.dim(` "${content}"`));
|
|
3318
3479
|
} catch (error2) {
|
|
3319
3480
|
if (error2.message?.includes("User force closed")) {
|
|
3320
3481
|
console.log("\nCancelled.");
|
|
@@ -3326,7 +3487,7 @@ templateCommand.command("use <name>").description("Create a memory using a templ
|
|
|
3326
3487
|
|
|
3327
3488
|
// src/commands/history.ts
|
|
3328
3489
|
import { Command as Command25 } from "commander";
|
|
3329
|
-
import
|
|
3490
|
+
import chalk24 from "chalk";
|
|
3330
3491
|
var TYPE_ICONS6 = {
|
|
3331
3492
|
rule: "\u{1F4CC}",
|
|
3332
3493
|
decision: "\u{1F4A1}",
|
|
@@ -3375,38 +3536,38 @@ var historyCommand = new Command25("history").description("View version history
|
|
|
3375
3536
|
return;
|
|
3376
3537
|
}
|
|
3377
3538
|
if (!memory && history.length === 0) {
|
|
3378
|
-
console.error(
|
|
3539
|
+
console.error(chalk24.red("\u2717") + ` Memory ${id} not found`);
|
|
3379
3540
|
process.exit(1);
|
|
3380
3541
|
}
|
|
3381
3542
|
const icon = memory ? TYPE_ICONS6[memory.type] || "\u{1F4DD}" : "\u{1F4DD}";
|
|
3382
3543
|
console.log("");
|
|
3383
3544
|
if (memory) {
|
|
3384
|
-
console.log(`${icon} ${
|
|
3385
|
-
console.log(
|
|
3545
|
+
console.log(`${icon} ${chalk24.bold(memory.content)}`);
|
|
3546
|
+
console.log(chalk24.dim(`ID: ${id}`));
|
|
3386
3547
|
} else {
|
|
3387
|
-
console.log(
|
|
3548
|
+
console.log(chalk24.dim(`Memory ${id} (deleted)`));
|
|
3388
3549
|
}
|
|
3389
3550
|
console.log("");
|
|
3390
3551
|
if (history.length === 0) {
|
|
3391
|
-
console.log(
|
|
3392
|
-
console.log(
|
|
3552
|
+
console.log(chalk24.dim("No version history recorded."));
|
|
3553
|
+
console.log(chalk24.dim("History is recorded when memories are updated."));
|
|
3393
3554
|
return;
|
|
3394
3555
|
}
|
|
3395
|
-
console.log(
|
|
3396
|
-
console.log(
|
|
3556
|
+
console.log(chalk24.bold("History:"));
|
|
3557
|
+
console.log(chalk24.dim("\u2500".repeat(60)));
|
|
3397
3558
|
for (const entry of history) {
|
|
3398
3559
|
const changeIcon = entry.change_type === "created" ? "+" : entry.change_type === "updated" ? "~" : "-";
|
|
3399
|
-
const changeColor = entry.change_type === "created" ?
|
|
3560
|
+
const changeColor = entry.change_type === "created" ? chalk24.green : entry.change_type === "updated" ? chalk24.yellow : chalk24.red;
|
|
3400
3561
|
const date = new Date(entry.changed_at).toLocaleDateString();
|
|
3401
3562
|
const time = new Date(entry.changed_at).toLocaleTimeString();
|
|
3402
|
-
console.log(` ${changeColor(changeIcon)} v${entry.version} ${
|
|
3563
|
+
console.log(` ${changeColor(changeIcon)} v${entry.version} ${chalk24.dim(date + " " + time)}`);
|
|
3403
3564
|
console.log(` "${entry.content.slice(0, 60)}${entry.content.length > 60 ? "..." : ""}"`);
|
|
3404
3565
|
}
|
|
3405
|
-
console.log(
|
|
3406
|
-
console.log(
|
|
3566
|
+
console.log(chalk24.dim("\u2500".repeat(60)));
|
|
3567
|
+
console.log(chalk24.dim(`
|
|
3407
3568
|
Use 'memories revert ${id} --to <version>' to restore a previous version`));
|
|
3408
3569
|
} catch (error2) {
|
|
3409
|
-
console.error(
|
|
3570
|
+
console.error(chalk24.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3410
3571
|
process.exit(1);
|
|
3411
3572
|
}
|
|
3412
3573
|
});
|
|
@@ -3416,7 +3577,7 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3416
3577
|
const db = await getDb();
|
|
3417
3578
|
const version = parseInt(opts.to.replace("v", ""), 10);
|
|
3418
3579
|
if (isNaN(version) || version < 1) {
|
|
3419
|
-
console.error(
|
|
3580
|
+
console.error(chalk24.red("\u2717") + " Invalid version number");
|
|
3420
3581
|
process.exit(1);
|
|
3421
3582
|
}
|
|
3422
3583
|
const result = await db.execute({
|
|
@@ -3428,7 +3589,7 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3428
3589
|
const history = result.rows;
|
|
3429
3590
|
const targetEntry = history.find((h) => h.version === version);
|
|
3430
3591
|
if (!targetEntry) {
|
|
3431
|
-
console.error(
|
|
3592
|
+
console.error(chalk24.red("\u2717") + ` Version ${version} not found for memory ${id}`);
|
|
3432
3593
|
process.exit(1);
|
|
3433
3594
|
}
|
|
3434
3595
|
const current = await getMemoryById(id);
|
|
@@ -3440,20 +3601,20 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3440
3601
|
tags: targetEntry.tags ? targetEntry.tags.split(",") : void 0
|
|
3441
3602
|
});
|
|
3442
3603
|
if (!updated) {
|
|
3443
|
-
console.error(
|
|
3604
|
+
console.error(chalk24.red("\u2717") + ` Failed to revert memory ${id}`);
|
|
3444
3605
|
process.exit(1);
|
|
3445
3606
|
}
|
|
3446
|
-
console.log(
|
|
3447
|
-
console.log(
|
|
3607
|
+
console.log(chalk24.green("\u2713") + ` Reverted memory ${chalk24.dim(id)} to version ${version}`);
|
|
3608
|
+
console.log(chalk24.dim(` "${targetEntry.content.slice(0, 60)}${targetEntry.content.length > 60 ? "..." : ""}"`));
|
|
3448
3609
|
} catch (error2) {
|
|
3449
|
-
console.error(
|
|
3610
|
+
console.error(chalk24.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3450
3611
|
process.exit(1);
|
|
3451
3612
|
}
|
|
3452
3613
|
});
|
|
3453
3614
|
|
|
3454
3615
|
// src/commands/embed.ts
|
|
3455
3616
|
import { Command as Command26 } from "commander";
|
|
3456
|
-
import
|
|
3617
|
+
import chalk25 from "chalk";
|
|
3457
3618
|
import ora2 from "ora";
|
|
3458
3619
|
var embedCommand = new Command26("embed").description("Generate embeddings for memories (enables semantic search)").option("--all", "Re-embed all memories, even those with existing embeddings").option("--dry-run", "Show what would be embedded without doing it").action(async (opts) => {
|
|
3459
3620
|
try {
|
|
@@ -3466,24 +3627,24 @@ var embedCommand = new Command26("embed").description("Generate embeddings for m
|
|
|
3466
3627
|
const result = await db.execute(sql);
|
|
3467
3628
|
const memories = result.rows;
|
|
3468
3629
|
if (memories.length === 0) {
|
|
3469
|
-
console.log(
|
|
3630
|
+
console.log(chalk25.green("\u2713") + " All memories already have embeddings.");
|
|
3470
3631
|
return;
|
|
3471
3632
|
}
|
|
3472
3633
|
if (opts.dryRun) {
|
|
3473
|
-
console.log(
|
|
3634
|
+
console.log(chalk25.bold(`Would embed ${memories.length} memories:
|
|
3474
3635
|
`));
|
|
3475
3636
|
for (const m of memories.slice(0, 10)) {
|
|
3476
3637
|
const preview = m.content.length > 60 ? m.content.slice(0, 57) + "..." : m.content;
|
|
3477
|
-
console.log(` ${
|
|
3638
|
+
console.log(` ${chalk25.dim(m.id)} ${preview}`);
|
|
3478
3639
|
}
|
|
3479
3640
|
if (memories.length > 10) {
|
|
3480
|
-
console.log(
|
|
3641
|
+
console.log(chalk25.dim(` ... and ${memories.length - 10} more`));
|
|
3481
3642
|
}
|
|
3482
3643
|
return;
|
|
3483
3644
|
}
|
|
3484
|
-
console.log(
|
|
3645
|
+
console.log(chalk25.bold(`Embedding ${memories.length} memories...
|
|
3485
3646
|
`));
|
|
3486
|
-
console.log(
|
|
3647
|
+
console.log(chalk25.dim("First run downloads the model (~30MB). Subsequent runs are faster.\n"));
|
|
3487
3648
|
const spinner = ora2("Loading embedding model...").start();
|
|
3488
3649
|
let embedded = 0;
|
|
3489
3650
|
let failed = 0;
|
|
@@ -3498,22 +3659,22 @@ var embedCommand = new Command26("embed").description("Generate embeddings for m
|
|
|
3498
3659
|
}
|
|
3499
3660
|
}
|
|
3500
3661
|
spinner.stop();
|
|
3501
|
-
console.log(
|
|
3662
|
+
console.log(chalk25.green("\u2713") + ` Embedded ${embedded} memories`);
|
|
3502
3663
|
if (failed > 0) {
|
|
3503
|
-
console.log(
|
|
3664
|
+
console.log(chalk25.yellow("\u26A0") + ` ${failed} memories failed to embed`);
|
|
3504
3665
|
}
|
|
3505
3666
|
console.log("");
|
|
3506
|
-
console.log(
|
|
3507
|
-
console.log(
|
|
3667
|
+
console.log(chalk25.dim("Now you can use semantic search:"));
|
|
3668
|
+
console.log(chalk25.cyan(' memories search --semantic "your query"'));
|
|
3508
3669
|
} catch (error2) {
|
|
3509
|
-
console.error(
|
|
3670
|
+
console.error(chalk25.red("\u2717") + " Embedding failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3510
3671
|
process.exit(1);
|
|
3511
3672
|
}
|
|
3512
3673
|
});
|
|
3513
3674
|
|
|
3514
3675
|
// src/commands/login.ts
|
|
3515
3676
|
import { Command as Command27 } from "commander";
|
|
3516
|
-
import
|
|
3677
|
+
import chalk26 from "chalk";
|
|
3517
3678
|
import ora3 from "ora";
|
|
3518
3679
|
import { randomBytes } from "crypto";
|
|
3519
3680
|
import { execFile } from "child_process";
|
|
@@ -3528,18 +3689,18 @@ var loginCommand = new Command27("login").description("Log in to memories.sh to
|
|
|
3528
3689
|
banner();
|
|
3529
3690
|
const existing = await readAuth();
|
|
3530
3691
|
if (existing) {
|
|
3531
|
-
warn(`Already logged in as ${
|
|
3532
|
-
dim(`Run ${
|
|
3692
|
+
warn(`Already logged in as ${chalk26.bold(existing.email)}`);
|
|
3693
|
+
dim(`Run ${chalk26.cyan("memories logout")} to sign out first.`);
|
|
3533
3694
|
return;
|
|
3534
3695
|
}
|
|
3535
3696
|
box(
|
|
3536
|
-
|
|
3697
|
+
chalk26.bold("Pro features include:\n\n") + chalk26.dim("\u2192 ") + "Cloud sync & backup\n" + chalk26.dim("\u2192 ") + "Cross-device access\n" + chalk26.dim("\u2192 ") + "Web dashboard\n" + chalk26.dim("\u2192 ") + "Priority support",
|
|
3537
3698
|
"Upgrade to Pro"
|
|
3538
3699
|
);
|
|
3539
3700
|
const code = randomBytes(16).toString("hex");
|
|
3540
3701
|
const authUrl = `${opts.apiUrl}/app/auth/cli?code=${code}`;
|
|
3541
|
-
console.log(
|
|
3542
|
-
console.log(` ${
|
|
3702
|
+
console.log(chalk26.bold("Open this URL in your browser:\n"));
|
|
3703
|
+
console.log(` ${chalk26.cyan(authUrl)}
|
|
3543
3704
|
`);
|
|
3544
3705
|
try {
|
|
3545
3706
|
openBrowser(authUrl);
|
|
@@ -3569,11 +3730,11 @@ var loginCommand = new Command27("login").description("Log in to memories.sh to
|
|
|
3569
3730
|
});
|
|
3570
3731
|
spinner.stop();
|
|
3571
3732
|
console.log("");
|
|
3572
|
-
success(`Logged in as ${
|
|
3733
|
+
success(`Logged in as ${chalk26.bold(data.email)}`);
|
|
3573
3734
|
dim("Your cloud database has been provisioned automatically.");
|
|
3574
3735
|
nextSteps([
|
|
3575
|
-
`${
|
|
3576
|
-
`${
|
|
3736
|
+
`${chalk26.cyan("memories sync")} ${chalk26.dim("to sync your memories")}`,
|
|
3737
|
+
`${chalk26.cyan("memories.sh/app")} ${chalk26.dim("to view your dashboard")}`
|
|
3577
3738
|
]);
|
|
3578
3739
|
return;
|
|
3579
3740
|
}
|
|
@@ -3601,77 +3762,112 @@ var logoutCommand = new Command27("logout").description("Log out of memories.sh"
|
|
|
3601
3762
|
|
|
3602
3763
|
// src/commands/files.ts
|
|
3603
3764
|
import { Command as Command28 } from "commander";
|
|
3604
|
-
import
|
|
3765
|
+
import chalk27 from "chalk";
|
|
3605
3766
|
import ora4 from "ora";
|
|
3606
3767
|
import { nanoid as nanoid4 } from "nanoid";
|
|
3607
3768
|
import { createHash } from "crypto";
|
|
3608
|
-
import { readFile as
|
|
3609
|
-
import { existsSync as
|
|
3610
|
-
import { join as
|
|
3611
|
-
import { homedir as
|
|
3769
|
+
import { readFile as readFile9, writeFile as writeFile7, mkdir as mkdir5, readdir, stat } from "fs/promises";
|
|
3770
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3771
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
3772
|
+
import { homedir as homedir5 } from "os";
|
|
3612
3773
|
function hashContent(content) {
|
|
3613
3774
|
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
3614
3775
|
}
|
|
3615
|
-
var
|
|
3616
|
-
|
|
3617
|
-
{ dir: ".
|
|
3618
|
-
{ dir: ".
|
|
3619
|
-
{ dir: ".
|
|
3620
|
-
{ dir: ".
|
|
3621
|
-
|
|
3622
|
-
{ dir: ".
|
|
3623
|
-
{ dir: ".
|
|
3624
|
-
{ dir: ".
|
|
3625
|
-
{ dir: ".
|
|
3776
|
+
var SYNC_TARGETS = [
|
|
3777
|
+
// .agents - Agent instruction files, commands, tasks, and skills
|
|
3778
|
+
{ dir: ".agents", files: ["AGENTS.md"] },
|
|
3779
|
+
{ dir: ".agents/commands", pattern: /\.md$/ },
|
|
3780
|
+
{ dir: ".agents/tasks", pattern: /\.(md|txt)$/ },
|
|
3781
|
+
{ dir: ".agents/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3782
|
+
// .claude - Claude Code instructions, commands, rules, hooks, and tasks
|
|
3783
|
+
{ dir: ".claude", files: ["CLAUDE.md", "settings.json", "settings.local.json"] },
|
|
3784
|
+
{ dir: ".claude/commands", pattern: /\.md$/ },
|
|
3785
|
+
{ dir: ".claude/rules", pattern: /\.(md|rules)$/ },
|
|
3786
|
+
{ dir: ".claude/hooks", pattern: /\.(json|sh)$/ },
|
|
3787
|
+
{ dir: ".claude/tasks", pattern: /\.(md|txt)$/ },
|
|
3788
|
+
{ dir: ".claude/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3789
|
+
// .cursor - Cursor rules and MCP config
|
|
3790
|
+
{ dir: ".cursor", files: ["mcp.json", "rules.md"] },
|
|
3791
|
+
{ dir: ".cursor/rules", pattern: /\.(md|mdc|txt)$/ },
|
|
3792
|
+
{ dir: ".cursor/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3793
|
+
// .codex - Codex config, rules, and tasks
|
|
3794
|
+
{ dir: ".codex", files: ["config.toml", "AGENTS.md", "instructions.md"] },
|
|
3795
|
+
{ dir: ".codex/rules", pattern: /\.(md|rules)$/ },
|
|
3796
|
+
{ dir: ".codex/tasks", pattern: /\.(md|txt)$/ },
|
|
3797
|
+
{ dir: ".codex/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3798
|
+
// .windsurf - Windsurf rules
|
|
3799
|
+
{ dir: ".windsurf", files: ["rules.md", "cascade.json"] },
|
|
3800
|
+
{ dir: ".windsurf/rules", pattern: /\.(md|txt)$/ },
|
|
3801
|
+
{ dir: ".windsurf/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3802
|
+
// .cline - Cline rules
|
|
3803
|
+
{ dir: ".cline", files: ["rules.md", "CLINE.md", "cline_rules.md"] },
|
|
3804
|
+
{ dir: ".cline/rules", pattern: /\.(md|txt)$/ },
|
|
3805
|
+
{ dir: ".cline/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3806
|
+
// .github/copilot - Copilot instructions
|
|
3807
|
+
{ dir: ".github/copilot", files: ["instructions.md"] },
|
|
3808
|
+
// .gemini - Gemini instructions
|
|
3809
|
+
{ dir: ".gemini", files: ["GEMINI.md", "settings.json"] },
|
|
3810
|
+
{ dir: ".gemini/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3811
|
+
// .roo - Roo config and rules
|
|
3812
|
+
{ dir: ".roo", files: ["config.json", "rules.md"] },
|
|
3813
|
+
{ dir: ".roo/rules", pattern: /\.(md|txt)$/ },
|
|
3814
|
+
{ dir: ".roo/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3815
|
+
// .amp - Amp rules
|
|
3816
|
+
{ dir: ".amp", files: ["AGENTS.md", "rules.md"] },
|
|
3817
|
+
{ dir: ".amp/rules", pattern: /\.(md|txt)$/ },
|
|
3818
|
+
{ dir: ".amp/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3819
|
+
// .opencode - OpenCode instructions
|
|
3820
|
+
{ dir: ".opencode", files: ["instructions.md"] },
|
|
3821
|
+
{ dir: ".opencode/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3822
|
+
// .factory - Factory/Droid config
|
|
3823
|
+
{ dir: ".factory", files: ["config.json", "instructions.md"] },
|
|
3824
|
+
{ dir: ".factory/droids", pattern: /\.(md|yaml|yml)$/ },
|
|
3825
|
+
{ dir: ".factory/tasks", pattern: /\.(md|txt)$/ },
|
|
3826
|
+
{ dir: ".factory/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true }
|
|
3626
3827
|
];
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
/stats-cache/,
|
|
3648
|
-
/telemetry/,
|
|
3649
|
-
/todos/
|
|
3650
|
-
];
|
|
3651
|
-
function shouldIncludeFile(filePath) {
|
|
3652
|
-
for (const pattern of EXCLUDE_PATTERNS) {
|
|
3653
|
-
if (pattern.test(filePath)) return false;
|
|
3654
|
-
}
|
|
3655
|
-
for (const pattern of INCLUDE_PATTERNS) {
|
|
3656
|
-
if (pattern.test(filePath)) return true;
|
|
3828
|
+
async function scanTarget(baseDir, target, relativeTo = "") {
|
|
3829
|
+
const results = [];
|
|
3830
|
+
const targetDir = join9(baseDir, target.dir);
|
|
3831
|
+
if (!existsSync10(targetDir)) return results;
|
|
3832
|
+
const source = target.dir.split("/")[0].replace(/^\./, "").replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
3833
|
+
if (target.files) {
|
|
3834
|
+
for (const file of target.files) {
|
|
3835
|
+
const fullPath = join9(targetDir, file);
|
|
3836
|
+
if (existsSync10(fullPath)) {
|
|
3837
|
+
const stats = await stat(fullPath);
|
|
3838
|
+
if (stats.isFile()) {
|
|
3839
|
+
results.push({
|
|
3840
|
+
path: join9(target.dir, file),
|
|
3841
|
+
fullPath,
|
|
3842
|
+
source
|
|
3843
|
+
});
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
return results;
|
|
3657
3848
|
}
|
|
3658
|
-
return
|
|
3659
|
-
}
|
|
3660
|
-
async function scanDirectory(dir, basePath = "") {
|
|
3661
|
-
const files = [];
|
|
3662
|
-
if (!existsSync9(dir)) return files;
|
|
3663
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
3849
|
+
if (!target.pattern) return results;
|
|
3850
|
+
const entries = await readdir(targetDir, { withFileTypes: true });
|
|
3664
3851
|
for (const entry of entries) {
|
|
3665
|
-
const fullPath =
|
|
3666
|
-
const relativePath =
|
|
3667
|
-
if (entry.isDirectory()) {
|
|
3668
|
-
const
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3852
|
+
const fullPath = join9(targetDir, entry.name);
|
|
3853
|
+
const relativePath = join9(target.dir, entry.name);
|
|
3854
|
+
if (entry.isDirectory() && target.recurse) {
|
|
3855
|
+
const subTarget = { dir: relativePath, pattern: target.pattern, recurse: true };
|
|
3856
|
+
const subResults = await scanTarget(baseDir, subTarget, relativeTo);
|
|
3857
|
+
results.push(...subResults);
|
|
3858
|
+
} else if (entry.isFile() && target.pattern.test(entry.name)) {
|
|
3859
|
+
results.push({ path: relativePath, fullPath, source });
|
|
3672
3860
|
}
|
|
3673
3861
|
}
|
|
3674
|
-
return
|
|
3862
|
+
return results;
|
|
3863
|
+
}
|
|
3864
|
+
async function scanAllTargets(baseDir) {
|
|
3865
|
+
const results = [];
|
|
3866
|
+
for (const target of SYNC_TARGETS) {
|
|
3867
|
+
const targetResults = await scanTarget(baseDir, target);
|
|
3868
|
+
results.push(...targetResults);
|
|
3869
|
+
}
|
|
3870
|
+
return results;
|
|
3675
3871
|
}
|
|
3676
3872
|
var filesCommand = new Command28("files").description("Manage synced config files (.agents, .cursor, .claude, etc.)");
|
|
3677
3873
|
filesCommand.command("list").alias("ls").description("List synced files").option("-s, --scope <scope>", "Filter by scope (global or project path)").action(async (opts) => {
|
|
@@ -3686,8 +3882,8 @@ filesCommand.command("list").alias("ls").description("List synced files").option
|
|
|
3686
3882
|
const result = await db.execute({ sql, args });
|
|
3687
3883
|
const files = result.rows;
|
|
3688
3884
|
if (files.length === 0) {
|
|
3689
|
-
console.log(
|
|
3690
|
-
console.log(
|
|
3885
|
+
console.log(chalk27.dim("No synced files yet."));
|
|
3886
|
+
console.log(chalk27.dim(`Run ${chalk27.cyan("memories files ingest")} to import config files.`));
|
|
3691
3887
|
return;
|
|
3692
3888
|
}
|
|
3693
3889
|
const byScope = /* @__PURE__ */ new Map();
|
|
@@ -3697,61 +3893,51 @@ filesCommand.command("list").alias("ls").description("List synced files").option
|
|
|
3697
3893
|
byScope.get(scope).push(file);
|
|
3698
3894
|
}
|
|
3699
3895
|
for (const [scope, scopeFiles] of byScope) {
|
|
3700
|
-
const scopeLabel = scope === "global" ?
|
|
3896
|
+
const scopeLabel = scope === "global" ? chalk27.blue("Global") : chalk27.yellow(scope.replace("github.com/", ""));
|
|
3701
3897
|
console.log(`
|
|
3702
|
-
${scopeLabel} ${
|
|
3703
|
-
console.log(
|
|
3898
|
+
${scopeLabel} ${chalk27.dim(`(${scopeFiles.length} files)`)}`);
|
|
3899
|
+
console.log(chalk27.dim("\u2500".repeat(50)));
|
|
3704
3900
|
for (const file of scopeFiles) {
|
|
3705
|
-
const source = file.source ?
|
|
3706
|
-
console.log(` ${
|
|
3901
|
+
const source = file.source ? chalk27.dim(` [${file.source}]`) : "";
|
|
3902
|
+
console.log(` ${chalk27.white(file.path)}${source}`);
|
|
3707
3903
|
}
|
|
3708
3904
|
}
|
|
3709
3905
|
console.log();
|
|
3710
3906
|
});
|
|
3711
3907
|
filesCommand.command("ingest").description("Import files from .agents, .cursor, .claude and other config directories").option("-g, --global", "Ingest global configs from home directory", true).option("-p, --project", "Ingest project configs from current directory").option("--dry-run", "Show what would be imported without making changes").action(async (opts) => {
|
|
3712
3908
|
const db = await getDb();
|
|
3713
|
-
const home =
|
|
3909
|
+
const home = homedir5();
|
|
3714
3910
|
const cwd = process.cwd();
|
|
3715
3911
|
const filesToIngest = [];
|
|
3716
3912
|
if (opts.global !== false) {
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
fullPath: file.fullPath,
|
|
3724
|
-
scope: "global",
|
|
3725
|
-
source: name
|
|
3726
|
-
});
|
|
3727
|
-
}
|
|
3913
|
+
const files = await scanAllTargets(home);
|
|
3914
|
+
for (const file of files) {
|
|
3915
|
+
filesToIngest.push({
|
|
3916
|
+
...file,
|
|
3917
|
+
scope: "global"
|
|
3918
|
+
});
|
|
3728
3919
|
}
|
|
3729
3920
|
}
|
|
3730
3921
|
if (opts.project) {
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
scope: "project",
|
|
3739
|
-
// Will be resolved to git remote
|
|
3740
|
-
source: name
|
|
3741
|
-
});
|
|
3742
|
-
}
|
|
3922
|
+
const files = await scanAllTargets(cwd);
|
|
3923
|
+
for (const file of files) {
|
|
3924
|
+
filesToIngest.push({
|
|
3925
|
+
...file,
|
|
3926
|
+
scope: "project"
|
|
3927
|
+
// Will be resolved to git remote
|
|
3928
|
+
});
|
|
3743
3929
|
}
|
|
3744
3930
|
}
|
|
3745
3931
|
if (filesToIngest.length === 0) {
|
|
3746
|
-
console.log(
|
|
3932
|
+
console.log(chalk27.dim("No config files found to import."));
|
|
3747
3933
|
return;
|
|
3748
3934
|
}
|
|
3749
3935
|
if (opts.dryRun) {
|
|
3750
|
-
console.log(
|
|
3936
|
+
console.log(chalk27.bold(`Would import ${filesToIngest.length} files:
|
|
3751
3937
|
`));
|
|
3752
3938
|
for (const file of filesToIngest) {
|
|
3753
|
-
const scopeLabel = file.scope === "global" ?
|
|
3754
|
-
console.log(` ${scopeLabel} ${file.path} ${
|
|
3939
|
+
const scopeLabel = file.scope === "global" ? chalk27.blue("G") : chalk27.yellow("P");
|
|
3940
|
+
console.log(` ${scopeLabel} ${file.path} ${chalk27.dim(`[${file.source}]`)}`);
|
|
3755
3941
|
}
|
|
3756
3942
|
return;
|
|
3757
3943
|
}
|
|
@@ -3761,7 +3947,7 @@ filesCommand.command("ingest").description("Import files from .agents, .cursor,
|
|
|
3761
3947
|
let skipped = 0;
|
|
3762
3948
|
for (const file of filesToIngest) {
|
|
3763
3949
|
try {
|
|
3764
|
-
const content = await
|
|
3950
|
+
const content = await readFile9(file.fullPath, "utf-8");
|
|
3765
3951
|
const hash = hashContent(content);
|
|
3766
3952
|
const existing = await db.execute({
|
|
3767
3953
|
sql: "SELECT id, hash FROM files WHERE path = ? AND scope = ? AND deleted_at IS NULL",
|
|
@@ -3798,7 +3984,7 @@ filesCommand.command("ingest").description("Import files from .agents, .cursor,
|
|
|
3798
3984
|
});
|
|
3799
3985
|
filesCommand.command("apply").description("Write synced files to disk (restore from cloud)").option("-g, --global", "Apply global files to home directory").option("-p, --project", "Apply project files to current directory").option("--dry-run", "Show what would be written without making changes").option("-f, --force", "Overwrite existing files without prompting").action(async (opts) => {
|
|
3800
3986
|
const db = await getDb();
|
|
3801
|
-
const home =
|
|
3987
|
+
const home = homedir5();
|
|
3802
3988
|
const cwd = process.cwd();
|
|
3803
3989
|
let sql = "SELECT id, path, content, scope, source FROM files WHERE deleted_at IS NULL";
|
|
3804
3990
|
const args = [];
|
|
@@ -3810,17 +3996,17 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3810
3996
|
const result = await db.execute({ sql, args });
|
|
3811
3997
|
const files = result.rows;
|
|
3812
3998
|
if (files.length === 0) {
|
|
3813
|
-
console.log(
|
|
3999
|
+
console.log(chalk27.dim("No files to apply."));
|
|
3814
4000
|
return;
|
|
3815
4001
|
}
|
|
3816
4002
|
if (opts.dryRun) {
|
|
3817
|
-
console.log(
|
|
4003
|
+
console.log(chalk27.bold(`Would write ${files.length} files:
|
|
3818
4004
|
`));
|
|
3819
4005
|
for (const file of files) {
|
|
3820
4006
|
const baseDir = file.scope === "global" ? home : cwd;
|
|
3821
|
-
const targetPath =
|
|
3822
|
-
const exists =
|
|
3823
|
-
const status = exists ?
|
|
4007
|
+
const targetPath = join9(baseDir, file.path);
|
|
4008
|
+
const exists = existsSync10(targetPath);
|
|
4009
|
+
const status = exists ? chalk27.yellow("(overwrite)") : chalk27.green("(new)");
|
|
3824
4010
|
console.log(` ${targetPath} ${status}`);
|
|
3825
4011
|
}
|
|
3826
4012
|
return;
|
|
@@ -3830,9 +4016,9 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3830
4016
|
let skippedExisting = 0;
|
|
3831
4017
|
for (const file of files) {
|
|
3832
4018
|
const baseDir = file.scope === "global" ? home : cwd;
|
|
3833
|
-
const targetPath =
|
|
3834
|
-
if (
|
|
3835
|
-
const existingContent = await
|
|
4019
|
+
const targetPath = join9(baseDir, file.path);
|
|
4020
|
+
if (existsSync10(targetPath) && !opts.force) {
|
|
4021
|
+
const existingContent = await readFile9(targetPath, "utf-8");
|
|
3836
4022
|
const existingHash = hashContent(existingContent);
|
|
3837
4023
|
const newHash = hashContent(file.content);
|
|
3838
4024
|
if (existingHash !== newHash) {
|
|
@@ -3840,8 +4026,8 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3840
4026
|
continue;
|
|
3841
4027
|
}
|
|
3842
4028
|
}
|
|
3843
|
-
await
|
|
3844
|
-
await
|
|
4029
|
+
await mkdir5(dirname3(targetPath), { recursive: true });
|
|
4030
|
+
await writeFile7(targetPath, file.content, "utf-8");
|
|
3845
4031
|
written++;
|
|
3846
4032
|
}
|
|
3847
4033
|
if (skippedExisting > 0) {
|
|
@@ -3857,14 +4043,14 @@ filesCommand.command("show <path>").description("Show content of a synced file")
|
|
|
3857
4043
|
args: [path]
|
|
3858
4044
|
});
|
|
3859
4045
|
if (result.rows.length === 0) {
|
|
3860
|
-
console.log(
|
|
4046
|
+
console.log(chalk27.red(`File not found: ${path}`));
|
|
3861
4047
|
return;
|
|
3862
4048
|
}
|
|
3863
4049
|
const file = result.rows[0];
|
|
3864
|
-
console.log(
|
|
3865
|
-
console.log(
|
|
3866
|
-
console.log(
|
|
3867
|
-
console.log(
|
|
4050
|
+
console.log(chalk27.dim(`# ${path}`));
|
|
4051
|
+
console.log(chalk27.dim(`# Scope: ${file.scope} | Source: ${file.source || "unknown"}`));
|
|
4052
|
+
console.log(chalk27.dim(`# Updated: ${file.updated_at}`));
|
|
4053
|
+
console.log(chalk27.dim("\u2500".repeat(50)));
|
|
3868
4054
|
console.log(file.content);
|
|
3869
4055
|
});
|
|
3870
4056
|
filesCommand.command("forget <path>").description("Remove a file from sync (soft delete)").action(async (path) => {
|
|
@@ -3874,14 +4060,14 @@ filesCommand.command("forget <path>").description("Remove a file from sync (soft
|
|
|
3874
4060
|
args: [path]
|
|
3875
4061
|
});
|
|
3876
4062
|
if (result.rowsAffected === 0) {
|
|
3877
|
-
console.log(
|
|
4063
|
+
console.log(chalk27.red(`File not found: ${path}`));
|
|
3878
4064
|
return;
|
|
3879
4065
|
}
|
|
3880
4066
|
const sync = await readSyncConfig();
|
|
3881
4067
|
if (sync) {
|
|
3882
4068
|
await syncDb();
|
|
3883
4069
|
}
|
|
3884
|
-
console.log(
|
|
4070
|
+
console.log(chalk27.green(`\u2713 Removed ${path} from sync`));
|
|
3885
4071
|
});
|
|
3886
4072
|
|
|
3887
4073
|
// src/index.ts
|