@memories.sh/cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +837 -550
- 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, checkbox } from "@inquirer/prompts";
|
|
36
37
|
|
|
37
38
|
// src/lib/auth.ts
|
|
38
39
|
import { readFile, writeFile, mkdir, unlink } from "fs/promises";
|
|
@@ -76,8 +77,123 @@ 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 getAllTools() {
|
|
125
|
+
return [...TOOLS];
|
|
126
|
+
}
|
|
127
|
+
function detectTools(cwd = process.cwd()) {
|
|
128
|
+
const home = homedir2();
|
|
129
|
+
const detected = [];
|
|
130
|
+
for (const tool of TOOLS) {
|
|
131
|
+
const projectConfigDir = join2(cwd, tool.configDir);
|
|
132
|
+
const projectMcpPath = join2(cwd, tool.mcpConfigPath);
|
|
133
|
+
const projectInstructionPath = join2(cwd, tool.instructionFile);
|
|
134
|
+
const globalConfigDir = join2(home, tool.configDir);
|
|
135
|
+
const globalMcpPath = join2(home, tool.mcpConfigPath);
|
|
136
|
+
const hasProjectConfig = existsSync2(projectConfigDir);
|
|
137
|
+
const hasGlobalConfig = existsSync2(globalConfigDir);
|
|
138
|
+
if (hasProjectConfig || hasGlobalConfig) {
|
|
139
|
+
detected.push({
|
|
140
|
+
tool,
|
|
141
|
+
hasConfig: hasProjectConfig || hasGlobalConfig,
|
|
142
|
+
hasMcp: existsSync2(projectMcpPath) || existsSync2(globalMcpPath),
|
|
143
|
+
hasInstructions: existsSync2(projectInstructionPath),
|
|
144
|
+
globalConfig: !hasProjectConfig && hasGlobalConfig
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return detected;
|
|
149
|
+
}
|
|
150
|
+
async function setupMcp(tool, options = {}) {
|
|
151
|
+
const { cwd = process.cwd(), global: useGlobal = false, dryRun = false } = options;
|
|
152
|
+
const home = homedir2();
|
|
153
|
+
const configPath = useGlobal ? join2(home, tool.mcpConfigPath) : join2(cwd, tool.mcpConfigPath);
|
|
154
|
+
try {
|
|
155
|
+
let config = {};
|
|
156
|
+
if (existsSync2(configPath)) {
|
|
157
|
+
const content = await readFile2(configPath, "utf-8");
|
|
158
|
+
config = JSON.parse(content);
|
|
159
|
+
}
|
|
160
|
+
const serversKey = tool.mcpConfigFormat === "vscode" ? "servers" : "mcpServers";
|
|
161
|
+
const servers = config[serversKey] ?? {};
|
|
162
|
+
if (servers.memories) {
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
message: "MCP already configured",
|
|
166
|
+
path: configPath
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
config[serversKey] = {
|
|
170
|
+
...servers,
|
|
171
|
+
memories: MEMORIES_MCP_CONFIG
|
|
172
|
+
};
|
|
173
|
+
if (dryRun) {
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
message: `Would add MCP config to ${configPath}`,
|
|
177
|
+
path: configPath
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
await mkdir2(dirname(configPath), { recursive: true });
|
|
181
|
+
await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
message: "MCP configured",
|
|
185
|
+
path: configPath
|
|
186
|
+
};
|
|
187
|
+
} catch (error2) {
|
|
188
|
+
return {
|
|
189
|
+
success: false,
|
|
190
|
+
message: `Failed to setup MCP: ${error2 instanceof Error ? error2.message : "Unknown error"}`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/lib/ui.ts
|
|
196
|
+
import chalk2 from "chalk";
|
|
81
197
|
import figlet from "figlet";
|
|
82
198
|
import gradient from "gradient-string";
|
|
83
199
|
import boxen from "boxen";
|
|
@@ -88,25 +204,25 @@ function banner() {
|
|
|
88
204
|
horizontalLayout: "fitted"
|
|
89
205
|
});
|
|
90
206
|
console.log(memoriesGradient(text));
|
|
91
|
-
console.log(
|
|
207
|
+
console.log(chalk2.dim(" One memory, every AI tool\n"));
|
|
92
208
|
}
|
|
93
209
|
function success(message) {
|
|
94
|
-
console.log(
|
|
210
|
+
console.log(chalk2.green("\u2713") + " " + message);
|
|
95
211
|
}
|
|
96
212
|
function warn(message) {
|
|
97
|
-
console.log(
|
|
213
|
+
console.log(chalk2.yellow("\u26A0") + " " + message);
|
|
98
214
|
}
|
|
99
215
|
function error(message) {
|
|
100
|
-
console.log(
|
|
216
|
+
console.log(chalk2.red("\u2717") + " " + message);
|
|
101
217
|
}
|
|
102
218
|
function info(message) {
|
|
103
|
-
console.log(
|
|
219
|
+
console.log(chalk2.blue("\u2139") + " " + message);
|
|
104
220
|
}
|
|
105
221
|
function step(num, total, message) {
|
|
106
|
-
console.log(
|
|
222
|
+
console.log(chalk2.dim(`[${num}/${total}]`) + " " + message);
|
|
107
223
|
}
|
|
108
224
|
function dim(message) {
|
|
109
|
-
console.log(
|
|
225
|
+
console.log(chalk2.dim(" " + message));
|
|
110
226
|
}
|
|
111
227
|
function box(content, title) {
|
|
112
228
|
console.log(
|
|
@@ -121,33 +237,28 @@ function box(content, title) {
|
|
|
121
237
|
);
|
|
122
238
|
}
|
|
123
239
|
function nextSteps(steps) {
|
|
124
|
-
const content = steps.map((s) =>
|
|
125
|
-
box(content,
|
|
240
|
+
const content = steps.map((s) => chalk2.dim("\u2192 ") + s).join("\n");
|
|
241
|
+
box(content, chalk2.bold("Next steps"));
|
|
126
242
|
}
|
|
127
243
|
function proFeature(feature) {
|
|
128
244
|
console.log(
|
|
129
|
-
|
|
245
|
+
chalk2.yellow("\u2B50") + " " + chalk2.dim(`${feature} requires `) + chalk2.bold("Pro") + chalk2.dim(". Run ") + chalk2.cyan("memories login") + chalk2.dim(" to upgrade.")
|
|
130
246
|
);
|
|
131
247
|
}
|
|
132
248
|
|
|
133
249
|
// 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) => {
|
|
250
|
+
import { execSync } from "child_process";
|
|
251
|
+
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
252
|
try {
|
|
143
253
|
banner();
|
|
144
|
-
console.log(
|
|
145
|
-
step(1,
|
|
254
|
+
console.log(chalk3.dim(" One place for your rules. Works with every tool.\n"));
|
|
255
|
+
step(1, 4, "Setting up local storage...");
|
|
146
256
|
await getDb();
|
|
147
257
|
const configDir = getConfigDir();
|
|
148
|
-
dim(`
|
|
149
|
-
step(2,
|
|
258
|
+
dim(`Database: ${configDir}/local.db`);
|
|
259
|
+
step(2, 4, "Detecting scope...");
|
|
150
260
|
let useGlobal = opts.global;
|
|
261
|
+
const cwd = process.cwd();
|
|
151
262
|
if (!useGlobal) {
|
|
152
263
|
const projectId = getProjectId();
|
|
153
264
|
const gitRoot = getGitRoot();
|
|
@@ -158,48 +269,123 @@ var initCommand = new Command("init").description("Initialize memories - one pla
|
|
|
158
269
|
success("Project scope detected");
|
|
159
270
|
dim(`Project: ${projectId}`);
|
|
160
271
|
dim(`Root: ${gitRoot}`);
|
|
161
|
-
dim("Use --global for rules that apply everywhere");
|
|
162
272
|
}
|
|
163
273
|
} else {
|
|
164
274
|
success("Global scope (rules apply to all projects)");
|
|
165
275
|
}
|
|
166
|
-
step(3,
|
|
167
|
-
|
|
168
|
-
|
|
276
|
+
step(3, 4, "Detecting AI coding tools...");
|
|
277
|
+
let detected = detectTools(cwd);
|
|
278
|
+
if (detected.length === 0) {
|
|
279
|
+
dim("No AI coding tools auto-detected.");
|
|
280
|
+
if (!opts.skipMcp) {
|
|
281
|
+
const allTools = getAllTools();
|
|
282
|
+
const selected = await checkbox({
|
|
283
|
+
message: "Which tools do you want to configure?",
|
|
284
|
+
choices: allTools.map((t) => ({
|
|
285
|
+
name: t.name,
|
|
286
|
+
value: t,
|
|
287
|
+
checked: false
|
|
288
|
+
}))
|
|
289
|
+
});
|
|
290
|
+
if (selected.length > 0) {
|
|
291
|
+
detected = selected.map((tool) => ({
|
|
292
|
+
tool,
|
|
293
|
+
hasConfig: false,
|
|
294
|
+
hasMcp: false,
|
|
295
|
+
hasInstructions: false,
|
|
296
|
+
globalConfig: false
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (detected.length === 0) {
|
|
302
|
+
dim("No tools selected. MCP will work with any tool that supports it.");
|
|
303
|
+
} else {
|
|
304
|
+
for (const d of detected) {
|
|
305
|
+
const scope = d.globalConfig ? chalk3.dim(" [global]") : "";
|
|
306
|
+
const mcpStatus = d.hasMcp ? chalk3.green("\u2713 MCP") : chalk3.dim("\u25CB MCP");
|
|
307
|
+
const rulesStatus = d.hasInstructions ? chalk3.green("\u2713 Rules") : chalk3.dim("\u25CB Rules");
|
|
308
|
+
console.log(` ${chalk3.white(d.tool.name)}${scope} ${mcpStatus} ${rulesStatus}`);
|
|
309
|
+
}
|
|
310
|
+
if (!opts.skipMcp) {
|
|
311
|
+
const toolsNeedingMcp = detected.filter((d) => !d.hasMcp);
|
|
312
|
+
if (toolsNeedingMcp.length > 0) {
|
|
313
|
+
console.log("");
|
|
314
|
+
const shouldSetupMcp = opts.yes || await confirm({
|
|
315
|
+
message: `Configure MCP for ${toolsNeedingMcp.map((d) => d.tool.name).join(", ")}?`,
|
|
316
|
+
default: true
|
|
317
|
+
});
|
|
318
|
+
if (shouldSetupMcp) {
|
|
319
|
+
for (const d of toolsNeedingMcp) {
|
|
320
|
+
const result = await setupMcp(d.tool, {
|
|
321
|
+
cwd,
|
|
322
|
+
global: d.globalConfig
|
|
323
|
+
});
|
|
324
|
+
if (result.success) {
|
|
325
|
+
success(`${d.tool.name}: ${result.message}`);
|
|
326
|
+
if (result.path) dim(` \u2192 ${result.path}`);
|
|
327
|
+
} else {
|
|
328
|
+
warn(`${d.tool.name}: ${result.message}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (!opts.skipGenerate) {
|
|
335
|
+
const toolsNeedingInstructions = detected.filter((d) => !d.hasInstructions);
|
|
336
|
+
const memories = await listMemories({ limit: 1 });
|
|
337
|
+
if (toolsNeedingInstructions.length > 0 && memories.length > 0) {
|
|
338
|
+
console.log("");
|
|
339
|
+
const shouldGenerate = opts.yes || await confirm({
|
|
340
|
+
message: `Generate instruction files for ${toolsNeedingInstructions.map((d) => d.tool.name).join(", ")}?`,
|
|
341
|
+
default: true
|
|
342
|
+
});
|
|
343
|
+
if (shouldGenerate) {
|
|
344
|
+
for (const d of toolsNeedingInstructions) {
|
|
345
|
+
try {
|
|
346
|
+
execSync(`node ${process.argv[1]} generate ${d.tool.generateCmd} --force`, {
|
|
347
|
+
cwd,
|
|
348
|
+
stdio: "pipe"
|
|
349
|
+
});
|
|
350
|
+
success(`${d.tool.name}: Generated ${d.tool.instructionFile}`);
|
|
351
|
+
} catch {
|
|
352
|
+
warn(`${d.tool.name}: Failed to generate instructions`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
step(4, 4, "Finalizing...");
|
|
169
360
|
if (opts.rule?.length) {
|
|
170
|
-
console.log("");
|
|
171
361
|
info("Adding rules...");
|
|
172
362
|
for (const rule of opts.rule) {
|
|
173
|
-
|
|
363
|
+
await addMemory(rule, {
|
|
174
364
|
type: "rule",
|
|
175
365
|
global: useGlobal
|
|
176
366
|
});
|
|
177
367
|
dim(`+ ${rule}`);
|
|
178
368
|
}
|
|
179
369
|
}
|
|
180
|
-
console.log("");
|
|
181
370
|
const auth = await readAuth();
|
|
182
371
|
if (auth) {
|
|
183
|
-
success(`Syncing as ${
|
|
372
|
+
success(`Syncing as ${chalk3.bold(auth.email)}`);
|
|
184
373
|
} else {
|
|
185
|
-
dim("Local only. Run " +
|
|
374
|
+
dim("Local only. Run " + chalk3.cyan("memories login") + " to sync across machines.");
|
|
186
375
|
}
|
|
187
376
|
console.log("");
|
|
188
|
-
console.log(
|
|
377
|
+
console.log(chalk3.bold(" Quick Start:"));
|
|
189
378
|
console.log("");
|
|
190
|
-
console.log(
|
|
191
|
-
console.log(` ${
|
|
192
|
-
console.log(` ${chalk2.cyan("memories add --rule")} ${chalk2.dim('"Prefer functional components in React"')}`);
|
|
379
|
+
console.log(chalk3.dim(" Add rules:"));
|
|
380
|
+
console.log(` ${chalk3.cyan("memories add --rule")} ${chalk3.dim('"Always use TypeScript strict mode"')}`);
|
|
193
381
|
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")}`);
|
|
382
|
+
console.log(chalk3.dim(" Regenerate instruction files after adding rules:"));
|
|
383
|
+
console.log(` ${chalk3.cyan("memories generate all")}`);
|
|
199
384
|
console.log("");
|
|
200
|
-
console.log(
|
|
385
|
+
console.log(chalk3.dim(" Your rules will be available via MCP and in generated files."));
|
|
201
386
|
console.log("");
|
|
202
387
|
} catch (error2) {
|
|
388
|
+
if (error2.name === "ExitPromptError") return;
|
|
203
389
|
error("Failed to initialize: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
204
390
|
process.exit(1);
|
|
205
391
|
}
|
|
@@ -207,7 +393,7 @@ var initCommand = new Command("init").description("Initialize memories - one pla
|
|
|
207
393
|
|
|
208
394
|
// src/commands/add.ts
|
|
209
395
|
import { Command as Command2 } from "commander";
|
|
210
|
-
import
|
|
396
|
+
import chalk4 from "chalk";
|
|
211
397
|
|
|
212
398
|
// src/lib/templates.ts
|
|
213
399
|
import { input } from "@inquirer/prompts";
|
|
@@ -370,7 +556,7 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
370
556
|
else if (opts.fact) type = "fact";
|
|
371
557
|
else if (opts.type) {
|
|
372
558
|
if (!VALID_TYPES.includes(opts.type)) {
|
|
373
|
-
console.error(
|
|
559
|
+
console.error(chalk4.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES.join(", ")}`);
|
|
374
560
|
process.exit(1);
|
|
375
561
|
}
|
|
376
562
|
type = opts.type;
|
|
@@ -378,11 +564,11 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
378
564
|
const memory = await addMemory(content, { tags, global: opts.global, type });
|
|
379
565
|
const typeLabel = type === "rule" ? "Rule" : type === "decision" ? "Decision" : type === "fact" ? "Fact" : "Note";
|
|
380
566
|
const scopeInfo = memory.scope === "global" ? "global" : "project";
|
|
381
|
-
success(`Stored ${
|
|
567
|
+
success(`Stored ${chalk4.bold(typeLabel.toLowerCase())} ${chalk4.dim(memory.id)}`);
|
|
382
568
|
dim(`Scope: ${scopeInfo}${tags?.length ? ` \u2022 Tags: ${tags.join(", ")}` : ""}`);
|
|
383
569
|
if (type === "rule") {
|
|
384
570
|
console.log("");
|
|
385
|
-
dim(`Run ${
|
|
571
|
+
dim(`Run ${chalk4.cyan("memories generate")} to update your IDE rule files`);
|
|
386
572
|
}
|
|
387
573
|
} catch (error2) {
|
|
388
574
|
error("Failed to add memory: " + (error2 instanceof Error ? error2.message : "Unknown error"));
|
|
@@ -392,7 +578,7 @@ var addCommand = new Command2("add").description("Add a new memory").argument("[
|
|
|
392
578
|
|
|
393
579
|
// src/commands/recall.ts
|
|
394
580
|
import { Command as Command3 } from "commander";
|
|
395
|
-
import
|
|
581
|
+
import chalk5 from "chalk";
|
|
396
582
|
var TYPE_ICONS = {
|
|
397
583
|
rule: "\u{1F4CC}",
|
|
398
584
|
decision: "\u{1F4A1}",
|
|
@@ -400,18 +586,18 @@ var TYPE_ICONS = {
|
|
|
400
586
|
note: "\u{1F4DD}"
|
|
401
587
|
};
|
|
402
588
|
var TYPE_COLORS = {
|
|
403
|
-
rule:
|
|
404
|
-
decision:
|
|
405
|
-
fact:
|
|
406
|
-
note:
|
|
589
|
+
rule: chalk5.yellow,
|
|
590
|
+
decision: chalk5.cyan,
|
|
591
|
+
fact: chalk5.green,
|
|
592
|
+
note: chalk5.white
|
|
407
593
|
};
|
|
408
594
|
function formatMemory(m, verbose) {
|
|
409
595
|
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 ?
|
|
596
|
+
const colorFn = TYPE_COLORS[m.type] || chalk5.white;
|
|
597
|
+
const scope = m.scope === "global" ? chalk5.dim("G") : chalk5.dim("P");
|
|
598
|
+
const tags = m.tags ? chalk5.dim(` [${m.tags}]`) : "";
|
|
413
599
|
if (verbose) {
|
|
414
|
-
return `${icon} ${scope} ${
|
|
600
|
+
return `${icon} ${scope} ${chalk5.dim(m.id)} ${colorFn(m.content)}${tags}`;
|
|
415
601
|
}
|
|
416
602
|
return `${icon} ${colorFn(m.content)}`;
|
|
417
603
|
}
|
|
@@ -425,11 +611,11 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
425
611
|
return;
|
|
426
612
|
}
|
|
427
613
|
if (rules2.length === 0) {
|
|
428
|
-
console.log(
|
|
429
|
-
console.log(
|
|
614
|
+
console.log(chalk5.dim("No rules defined."));
|
|
615
|
+
console.log(chalk5.dim('Add one with: memories add --rule "Your rule here"'));
|
|
430
616
|
return;
|
|
431
617
|
}
|
|
432
|
-
console.log(
|
|
618
|
+
console.log(chalk5.bold("Rules:"));
|
|
433
619
|
for (const rule of rules2) {
|
|
434
620
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
435
621
|
}
|
|
@@ -444,26 +630,26 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
444
630
|
return;
|
|
445
631
|
}
|
|
446
632
|
if (rules.length > 0) {
|
|
447
|
-
console.log(
|
|
633
|
+
console.log(chalk5.bold("Rules:"));
|
|
448
634
|
for (const rule of rules) {
|
|
449
635
|
console.log(formatMemory(rule, opts.verbose ?? false));
|
|
450
636
|
}
|
|
451
637
|
console.log("");
|
|
452
638
|
}
|
|
453
639
|
if (memories.length > 0) {
|
|
454
|
-
console.log(
|
|
640
|
+
console.log(chalk5.bold(query ? `Relevant to "${query}":` : "Recent memories:"));
|
|
455
641
|
for (const m of memories) {
|
|
456
642
|
console.log(formatMemory(m, opts.verbose ?? false));
|
|
457
643
|
}
|
|
458
644
|
} else if (query) {
|
|
459
|
-
console.log(
|
|
645
|
+
console.log(chalk5.dim(`No memories found matching "${query}"`));
|
|
460
646
|
}
|
|
461
647
|
if (rules.length === 0 && memories.length === 0) {
|
|
462
|
-
console.log(
|
|
463
|
-
console.log(
|
|
648
|
+
console.log(chalk5.dim("No memories found."));
|
|
649
|
+
console.log(chalk5.dim('Add some with: memories add "Your memory here"'));
|
|
464
650
|
}
|
|
465
651
|
} catch (error2) {
|
|
466
|
-
console.error(
|
|
652
|
+
console.error(chalk5.red("\u2717") + " Failed to recall:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
467
653
|
process.exit(1);
|
|
468
654
|
}
|
|
469
655
|
});
|
|
@@ -471,7 +657,7 @@ var recallCommand = new Command3("recall").description("Recall context - get rul
|
|
|
471
657
|
// src/commands/prompt.ts
|
|
472
658
|
import { Command as Command4 } from "commander";
|
|
473
659
|
import { execFileSync } from "child_process";
|
|
474
|
-
import
|
|
660
|
+
import chalk6 from "chalk";
|
|
475
661
|
var VALID_TYPES2 = ["rule", "decision", "fact", "note"];
|
|
476
662
|
function formatMarkdown(sections) {
|
|
477
663
|
return sections.map(({ title, memories }) => {
|
|
@@ -514,7 +700,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
514
700
|
try {
|
|
515
701
|
const format = opts.format ?? "markdown";
|
|
516
702
|
if (!["markdown", "xml", "plain"].includes(format)) {
|
|
517
|
-
console.error(
|
|
703
|
+
console.error(chalk6.red("\u2717") + ` Invalid format "${opts.format}". Use: markdown, xml, plain`);
|
|
518
704
|
process.exit(1);
|
|
519
705
|
}
|
|
520
706
|
const projectId = getProjectId() ?? void 0;
|
|
@@ -526,7 +712,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
526
712
|
for (const t of opts.include.split(",").map((s) => s.trim())) {
|
|
527
713
|
const normalized = t.replace(/s$/, "");
|
|
528
714
|
if (!VALID_TYPES2.includes(normalized)) {
|
|
529
|
-
console.error(
|
|
715
|
+
console.error(chalk6.red("\u2717") + ` Invalid type "${t}". Valid: decisions, facts, notes`);
|
|
530
716
|
process.exit(1);
|
|
531
717
|
}
|
|
532
718
|
if (normalized !== "rule") extraTypes.push(normalized);
|
|
@@ -555,7 +741,7 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
555
741
|
}
|
|
556
742
|
if (sections.length === 0) {
|
|
557
743
|
if (!opts.quiet) {
|
|
558
|
-
console.error(
|
|
744
|
+
console.error(chalk6.dim('No memories found. Add rules with: memories add --rule "Your rule"'));
|
|
559
745
|
}
|
|
560
746
|
return;
|
|
561
747
|
}
|
|
@@ -575,26 +761,26 @@ var promptCommand = new Command4("prompt").description("Output memories formatte
|
|
|
575
761
|
if (copied) {
|
|
576
762
|
console.log(output);
|
|
577
763
|
if (!opts.quiet) {
|
|
578
|
-
console.error(
|
|
764
|
+
console.error(chalk6.green("\u2713") + " Copied to clipboard");
|
|
579
765
|
}
|
|
580
766
|
} else {
|
|
581
767
|
console.log(output);
|
|
582
768
|
if (!opts.quiet) {
|
|
583
|
-
console.error(
|
|
769
|
+
console.error(chalk6.yellow("\u26A0") + " Could not copy to clipboard (unsupported platform)");
|
|
584
770
|
}
|
|
585
771
|
}
|
|
586
772
|
} else {
|
|
587
773
|
console.log(output);
|
|
588
774
|
}
|
|
589
775
|
} catch (error2) {
|
|
590
|
-
console.error(
|
|
776
|
+
console.error(chalk6.red("\u2717") + " Failed to generate prompt:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
591
777
|
process.exit(1);
|
|
592
778
|
}
|
|
593
779
|
});
|
|
594
780
|
|
|
595
781
|
// src/commands/search.ts
|
|
596
782
|
import { Command as Command5 } from "commander";
|
|
597
|
-
import
|
|
783
|
+
import chalk7 from "chalk";
|
|
598
784
|
var TYPE_ICONS2 = {
|
|
599
785
|
rule: "\u{1F4CC}",
|
|
600
786
|
decision: "\u{1F4A1}",
|
|
@@ -604,17 +790,17 @@ var TYPE_ICONS2 = {
|
|
|
604
790
|
var VALID_TYPES3 = ["rule", "decision", "fact", "note"];
|
|
605
791
|
function formatMemory2(m, score) {
|
|
606
792
|
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} ${
|
|
793
|
+
const scope = m.scope === "global" ? chalk7.dim("G") : chalk7.dim("P");
|
|
794
|
+
const tags = m.tags ? chalk7.dim(` [${m.tags}]`) : "";
|
|
795
|
+
const scoreStr = score !== void 0 ? chalk7.cyan(` (${Math.round(score * 100)}%)`) : "";
|
|
796
|
+
return `${icon} ${scope} ${chalk7.dim(m.id)} ${m.content}${tags}${scoreStr}`;
|
|
611
797
|
}
|
|
612
798
|
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
799
|
try {
|
|
614
800
|
let types;
|
|
615
801
|
if (opts.type) {
|
|
616
802
|
if (!VALID_TYPES3.includes(opts.type)) {
|
|
617
|
-
console.error(
|
|
803
|
+
console.error(chalk7.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES3.join(", ")}`);
|
|
618
804
|
process.exit(1);
|
|
619
805
|
}
|
|
620
806
|
types = [opts.type];
|
|
@@ -628,7 +814,7 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
628
814
|
includeGlobal = false;
|
|
629
815
|
projectId = getProjectId() ?? void 0;
|
|
630
816
|
if (!projectId) {
|
|
631
|
-
console.log(
|
|
817
|
+
console.log(chalk7.yellow("\u26A0") + " Not in a git repository. No project memories to search.");
|
|
632
818
|
return;
|
|
633
819
|
}
|
|
634
820
|
}
|
|
@@ -636,7 +822,7 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
636
822
|
try {
|
|
637
823
|
const { semanticSearch, isModelAvailable } = await import("./embeddings-6N2NZPRX.js");
|
|
638
824
|
if (!await isModelAvailable()) {
|
|
639
|
-
console.log(
|
|
825
|
+
console.log(chalk7.yellow("\u26A0") + " Loading embedding model for first time (this may take a moment)...");
|
|
640
826
|
}
|
|
641
827
|
const results = await semanticSearch(query, {
|
|
642
828
|
limit: parseInt(opts.limit, 10),
|
|
@@ -647,11 +833,11 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
647
833
|
return;
|
|
648
834
|
}
|
|
649
835
|
if (results.length === 0) {
|
|
650
|
-
console.log(
|
|
651
|
-
console.log(
|
|
836
|
+
console.log(chalk7.dim(`No semantically similar memories found for "${query}"`));
|
|
837
|
+
console.log(chalk7.dim("Try running 'memories embed' to generate embeddings for existing memories."));
|
|
652
838
|
return;
|
|
653
839
|
}
|
|
654
|
-
console.log(
|
|
840
|
+
console.log(chalk7.bold(`Semantic results for "${query}":`));
|
|
655
841
|
console.log("");
|
|
656
842
|
for (const r of results) {
|
|
657
843
|
const { getMemoryById: getMemoryById2 } = await import("./memory-B4NNFKXV.js");
|
|
@@ -660,12 +846,12 @@ var searchCommand = new Command5("search").description("Search memories using fu
|
|
|
660
846
|
console.log(formatMemory2(m, r.score));
|
|
661
847
|
}
|
|
662
848
|
}
|
|
663
|
-
console.log(
|
|
849
|
+
console.log(chalk7.dim(`
|
|
664
850
|
${results.length} results (semantic search)`));
|
|
665
851
|
return;
|
|
666
852
|
} catch (error2) {
|
|
667
|
-
console.error(
|
|
668
|
-
console.log(
|
|
853
|
+
console.error(chalk7.red("\u2717") + " Semantic search failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
854
|
+
console.log(chalk7.dim("Falling back to keyword search..."));
|
|
669
855
|
}
|
|
670
856
|
}
|
|
671
857
|
const memories = await searchMemories(query, {
|
|
@@ -680,30 +866,30 @@ ${results.length} results (semantic search)`));
|
|
|
680
866
|
return;
|
|
681
867
|
}
|
|
682
868
|
if (memories.length === 0) {
|
|
683
|
-
console.log(
|
|
869
|
+
console.log(chalk7.dim(`No memories found matching "${query}"`));
|
|
684
870
|
return;
|
|
685
871
|
}
|
|
686
|
-
console.log(
|
|
872
|
+
console.log(chalk7.bold(`Results for "${query}":`));
|
|
687
873
|
console.log("");
|
|
688
874
|
for (const m of memories) {
|
|
689
875
|
console.log(formatMemory2(m));
|
|
690
876
|
}
|
|
691
|
-
console.log(
|
|
877
|
+
console.log(chalk7.dim(`
|
|
692
878
|
${memories.length} results`));
|
|
693
879
|
} catch (error2) {
|
|
694
|
-
console.error(
|
|
880
|
+
console.error(chalk7.red("\u2717") + " Failed to search:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
695
881
|
process.exit(1);
|
|
696
882
|
}
|
|
697
883
|
});
|
|
698
884
|
|
|
699
885
|
// src/commands/list.ts
|
|
700
886
|
import { Command as Command6 } from "commander";
|
|
701
|
-
import
|
|
887
|
+
import chalk8 from "chalk";
|
|
702
888
|
var TYPE_COLORS2 = {
|
|
703
|
-
rule:
|
|
704
|
-
decision:
|
|
705
|
-
fact:
|
|
706
|
-
note:
|
|
889
|
+
rule: chalk8.blue,
|
|
890
|
+
decision: chalk8.yellow,
|
|
891
|
+
fact: chalk8.green,
|
|
892
|
+
note: chalk8.dim
|
|
707
893
|
};
|
|
708
894
|
var TYPE_LABELS = {
|
|
709
895
|
rule: "rule",
|
|
@@ -718,12 +904,12 @@ function truncate(str, max) {
|
|
|
718
904
|
return str.slice(0, max - 1) + "\u2026";
|
|
719
905
|
}
|
|
720
906
|
function formatMemory3(m) {
|
|
721
|
-
const typeColor = TYPE_COLORS2[m.type] ??
|
|
907
|
+
const typeColor = TYPE_COLORS2[m.type] ?? chalk8.dim;
|
|
722
908
|
const typeLabel = typeColor(TYPE_LABELS[m.type].padEnd(9));
|
|
723
|
-
const scope = m.scope === "global" ?
|
|
724
|
-
const id =
|
|
909
|
+
const scope = m.scope === "global" ? chalk8.magenta("G") : chalk8.cyan("P");
|
|
910
|
+
const id = chalk8.dim(m.id);
|
|
725
911
|
const content = truncate(m.content, MAX_CONTENT_WIDTH);
|
|
726
|
-
const tags = m.tags ?
|
|
912
|
+
const tags = m.tags ? chalk8.dim(` [${m.tags}]`) : "";
|
|
727
913
|
return ` ${scope} ${typeLabel} ${id} ${content}${tags}`;
|
|
728
914
|
}
|
|
729
915
|
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 +918,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
732
918
|
let types;
|
|
733
919
|
if (opts.type) {
|
|
734
920
|
if (!VALID_TYPES4.includes(opts.type)) {
|
|
735
|
-
console.error(
|
|
921
|
+
console.error(chalk8.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES4.join(", ")}`);
|
|
736
922
|
process.exit(1);
|
|
737
923
|
}
|
|
738
924
|
types = [opts.type];
|
|
@@ -746,7 +932,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
746
932
|
includeGlobal = false;
|
|
747
933
|
projectId = getProjectId() ?? void 0;
|
|
748
934
|
if (!projectId) {
|
|
749
|
-
console.log(
|
|
935
|
+
console.log(chalk8.yellow("\u26A0") + " Not in a git repository. No project memories to show.");
|
|
750
936
|
return;
|
|
751
937
|
}
|
|
752
938
|
}
|
|
@@ -763,21 +949,21 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
763
949
|
return;
|
|
764
950
|
}
|
|
765
951
|
if (memories.length === 0) {
|
|
766
|
-
console.log(
|
|
952
|
+
console.log(chalk8.dim("No memories found."));
|
|
767
953
|
return;
|
|
768
954
|
}
|
|
769
955
|
const currentProject = getProjectId();
|
|
770
956
|
if (currentProject && !opts.global) {
|
|
771
|
-
console.log(
|
|
957
|
+
console.log(chalk8.dim(` Project: ${currentProject}
|
|
772
958
|
`));
|
|
773
959
|
}
|
|
774
960
|
for (const m of memories) {
|
|
775
961
|
console.log(formatMemory3(m));
|
|
776
962
|
}
|
|
777
|
-
console.log(
|
|
963
|
+
console.log(chalk8.dim(`
|
|
778
964
|
${memories.length} memories`));
|
|
779
965
|
} catch (error2) {
|
|
780
|
-
console.error(
|
|
966
|
+
console.error(chalk8.red("\u2717") + " Failed to list memories:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
781
967
|
process.exit(1);
|
|
782
968
|
}
|
|
783
969
|
});
|
|
@@ -785,7 +971,7 @@ var listCommand = new Command6("list").description("List memories").option("-l,
|
|
|
785
971
|
// src/commands/forget.ts
|
|
786
972
|
import { Command as Command7 } from "commander";
|
|
787
973
|
import { createInterface } from "readline";
|
|
788
|
-
import
|
|
974
|
+
import chalk9 from "chalk";
|
|
789
975
|
var VALID_TYPES5 = ["rule", "decision", "fact", "note"];
|
|
790
976
|
var TYPE_ICONS3 = {
|
|
791
977
|
rule: "\u{1F4CC}",
|
|
@@ -793,7 +979,7 @@ var TYPE_ICONS3 = {
|
|
|
793
979
|
fact: "\u{1F4CB}",
|
|
794
980
|
note: "\u{1F4DD}"
|
|
795
981
|
};
|
|
796
|
-
async function
|
|
982
|
+
async function confirm2(message) {
|
|
797
983
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
798
984
|
return new Promise((resolve3) => {
|
|
799
985
|
rl.question(message, (answer) => {
|
|
@@ -807,28 +993,28 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
807
993
|
if (id) {
|
|
808
994
|
const deleted = await forgetMemory(id);
|
|
809
995
|
if (deleted) {
|
|
810
|
-
console.log(
|
|
996
|
+
console.log(chalk9.green("\u2713") + ` Forgot memory ${chalk9.dim(id)}`);
|
|
811
997
|
} else {
|
|
812
|
-
console.error(
|
|
998
|
+
console.error(chalk9.red("\u2717") + ` Memory ${id} not found or already forgotten.`);
|
|
813
999
|
process.exit(1);
|
|
814
1000
|
}
|
|
815
1001
|
return;
|
|
816
1002
|
}
|
|
817
1003
|
const hasBulkFilter = opts.type || opts.tag || opts.olderThan || opts.pattern || opts.all;
|
|
818
1004
|
if (!hasBulkFilter) {
|
|
819
|
-
console.error(
|
|
1005
|
+
console.error(chalk9.red("\u2717") + " Provide a memory ID or a bulk filter (--type, --tag, --older-than, --pattern, --all)");
|
|
820
1006
|
process.exit(1);
|
|
821
1007
|
}
|
|
822
1008
|
if (opts.all && (opts.type || opts.tag || opts.olderThan || opts.pattern)) {
|
|
823
|
-
console.error(
|
|
1009
|
+
console.error(chalk9.red("\u2717") + " --all cannot be combined with other filters. Use --all alone to delete everything.");
|
|
824
1010
|
process.exit(1);
|
|
825
1011
|
}
|
|
826
1012
|
if (opts.type && !VALID_TYPES5.includes(opts.type)) {
|
|
827
|
-
console.error(
|
|
1013
|
+
console.error(chalk9.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES5.join(", ")}`);
|
|
828
1014
|
process.exit(1);
|
|
829
1015
|
}
|
|
830
1016
|
if (opts.olderThan && (isNaN(parseInt(opts.olderThan, 10)) || parseInt(opts.olderThan, 10) <= 0)) {
|
|
831
|
-
console.error(
|
|
1017
|
+
console.error(chalk9.red("\u2717") + " --older-than must be a positive number of days");
|
|
832
1018
|
process.exit(1);
|
|
833
1019
|
}
|
|
834
1020
|
const filter = {
|
|
@@ -841,27 +1027,27 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
841
1027
|
};
|
|
842
1028
|
const matches = await findMemoriesToForget(filter);
|
|
843
1029
|
if (matches.length === 0) {
|
|
844
|
-
console.log(
|
|
1030
|
+
console.log(chalk9.dim("No memories match the filter."));
|
|
845
1031
|
return;
|
|
846
1032
|
}
|
|
847
|
-
console.log(
|
|
1033
|
+
console.log(chalk9.bold(`${matches.length} memories will be forgotten:
|
|
848
1034
|
`));
|
|
849
1035
|
for (const m of matches.slice(0, 30)) {
|
|
850
1036
|
const icon = TYPE_ICONS3[m.type] || "\u{1F4DD}";
|
|
851
|
-
const scope = m.scope === "global" ?
|
|
852
|
-
console.log(` ${icon} ${scope} ${
|
|
1037
|
+
const scope = m.scope === "global" ? chalk9.dim("G") : chalk9.dim("P");
|
|
1038
|
+
console.log(` ${icon} ${scope} ${chalk9.dim(m.id)} ${m.content}`);
|
|
853
1039
|
}
|
|
854
1040
|
if (matches.length > 30) {
|
|
855
|
-
console.log(
|
|
1041
|
+
console.log(chalk9.dim(` ... and ${matches.length - 30} more`));
|
|
856
1042
|
}
|
|
857
1043
|
console.log("");
|
|
858
1044
|
if (opts.dryRun) {
|
|
859
|
-
console.log(
|
|
1045
|
+
console.log(chalk9.yellow("Dry run") + " \u2014 no memories were deleted.");
|
|
860
1046
|
return;
|
|
861
1047
|
}
|
|
862
1048
|
if (!opts.force) {
|
|
863
|
-
const proceed = await
|
|
864
|
-
|
|
1049
|
+
const proceed = await confirm2(
|
|
1050
|
+
chalk9.yellow(`Forget ${matches.length} memories? This is a soft-delete. [y/N] `)
|
|
865
1051
|
);
|
|
866
1052
|
if (!proceed) {
|
|
867
1053
|
console.log("Cancelled.");
|
|
@@ -870,17 +1056,17 @@ var forgetCommand = new Command7("forget").description("Soft-delete memories by
|
|
|
870
1056
|
}
|
|
871
1057
|
const ids = matches.map((m) => m.id);
|
|
872
1058
|
const count = await bulkForgetByIds(ids);
|
|
873
|
-
console.log(
|
|
1059
|
+
console.log(chalk9.green("\u2713") + ` Forgot ${count} memories.`);
|
|
874
1060
|
} catch (error2) {
|
|
875
|
-
console.error(
|
|
1061
|
+
console.error(chalk9.red("\u2717") + " Failed to forget:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
876
1062
|
process.exit(1);
|
|
877
1063
|
}
|
|
878
1064
|
});
|
|
879
1065
|
|
|
880
1066
|
// src/commands/export.ts
|
|
881
1067
|
import { Command as Command8 } from "commander";
|
|
882
|
-
import
|
|
883
|
-
import { writeFile as
|
|
1068
|
+
import chalk10 from "chalk";
|
|
1069
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
884
1070
|
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
1071
|
try {
|
|
886
1072
|
const projectId = getProjectId();
|
|
@@ -893,7 +1079,7 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
893
1079
|
includeGlobal = false;
|
|
894
1080
|
queryProjectId = projectId ?? void 0;
|
|
895
1081
|
if (!queryProjectId) {
|
|
896
|
-
console.error(
|
|
1082
|
+
console.error(chalk10.red("\u2717") + " Not in a git repository. No project memories to export.");
|
|
897
1083
|
process.exit(1);
|
|
898
1084
|
}
|
|
899
1085
|
}
|
|
@@ -927,24 +1113,24 @@ var exportCommand = new Command8("export").description("Export memories to JSON
|
|
|
927
1113
|
output = JSON.stringify(exportData, null, 2);
|
|
928
1114
|
}
|
|
929
1115
|
if (opts.output) {
|
|
930
|
-
await
|
|
931
|
-
console.log(
|
|
1116
|
+
await writeFile3(opts.output, output, "utf-8");
|
|
1117
|
+
console.log(chalk10.green("\u2713") + ` Exported ${memories.length} memories to ${chalk10.dim(opts.output)}`);
|
|
932
1118
|
} else {
|
|
933
1119
|
console.log(output);
|
|
934
1120
|
}
|
|
935
1121
|
} catch (error2) {
|
|
936
|
-
console.error(
|
|
1122
|
+
console.error(chalk10.red("\u2717") + " Failed to export:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
937
1123
|
process.exit(1);
|
|
938
1124
|
}
|
|
939
1125
|
});
|
|
940
1126
|
|
|
941
1127
|
// src/commands/import.ts
|
|
942
1128
|
import { Command as Command9 } from "commander";
|
|
943
|
-
import
|
|
944
|
-
import { readFile as
|
|
1129
|
+
import chalk11 from "chalk";
|
|
1130
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
945
1131
|
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
1132
|
try {
|
|
947
|
-
const content = await
|
|
1133
|
+
const content = await readFile3(file, "utf-8");
|
|
948
1134
|
let format = opts.format;
|
|
949
1135
|
if (!format) {
|
|
950
1136
|
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
|
|
@@ -961,7 +1147,7 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
961
1147
|
data = JSON.parse(content);
|
|
962
1148
|
}
|
|
963
1149
|
if (!data || typeof data !== "object" || !("memories" in data) || !Array.isArray(data.memories)) {
|
|
964
|
-
console.error(
|
|
1150
|
+
console.error(chalk11.red("\u2717") + " Invalid import file: missing 'memories' array");
|
|
965
1151
|
process.exit(1);
|
|
966
1152
|
}
|
|
967
1153
|
const VALID_TYPES11 = ["rule", "decision", "fact", "note"];
|
|
@@ -974,23 +1160,23 @@ var importCommand = new Command9("import").description("Import memories from JSO
|
|
|
974
1160
|
continue;
|
|
975
1161
|
}
|
|
976
1162
|
if (m.type && !VALID_TYPES11.includes(m.type)) {
|
|
977
|
-
console.error(
|
|
1163
|
+
console.error(chalk11.yellow("\u26A0") + ` Skipping memory with invalid type "${m.type}": ${m.content.slice(0, 50)}`);
|
|
978
1164
|
skipped++;
|
|
979
1165
|
continue;
|
|
980
1166
|
}
|
|
981
1167
|
validMemories.push(m);
|
|
982
1168
|
}
|
|
983
1169
|
if (opts.dryRun) {
|
|
984
|
-
console.log(
|
|
1170
|
+
console.log(chalk11.blue("Dry run - would import:"));
|
|
985
1171
|
for (const m of validMemories) {
|
|
986
1172
|
const type = m.type || "note";
|
|
987
1173
|
const scope = opts.global ? "global" : m.scope || "project";
|
|
988
1174
|
const tags = m.tags?.length ? ` [${m.tags.join(", ")}]` : "";
|
|
989
1175
|
console.log(` ${type} (${scope}): ${m.content}${tags}`);
|
|
990
1176
|
}
|
|
991
|
-
console.log(
|
|
1177
|
+
console.log(chalk11.dim(`
|
|
992
1178
|
${validMemories.length} memories would be imported`));
|
|
993
|
-
if (skipped > 0) console.log(
|
|
1179
|
+
if (skipped > 0) console.log(chalk11.dim(`${skipped} entries skipped (invalid)`));
|
|
994
1180
|
return;
|
|
995
1181
|
}
|
|
996
1182
|
let imported = 0;
|
|
@@ -1004,19 +1190,19 @@ ${validMemories.length} memories would be imported`));
|
|
|
1004
1190
|
});
|
|
1005
1191
|
imported++;
|
|
1006
1192
|
} catch (error2) {
|
|
1007
|
-
console.error(
|
|
1193
|
+
console.error(chalk11.yellow("\u26A0") + ` Failed to import: ${m.content.slice(0, 50)}...`);
|
|
1008
1194
|
failed++;
|
|
1009
1195
|
}
|
|
1010
1196
|
}
|
|
1011
|
-
console.log(
|
|
1197
|
+
console.log(chalk11.green("\u2713") + ` Imported ${imported} memories`);
|
|
1012
1198
|
if (failed > 0) {
|
|
1013
|
-
console.log(
|
|
1199
|
+
console.log(chalk11.yellow("\u26A0") + ` ${failed} memories failed to import`);
|
|
1014
1200
|
}
|
|
1015
1201
|
if (skipped > 0) {
|
|
1016
|
-
console.log(
|
|
1202
|
+
console.log(chalk11.dim(`${skipped} entries skipped (invalid content or type)`));
|
|
1017
1203
|
}
|
|
1018
1204
|
} catch (error2) {
|
|
1019
|
-
console.error(
|
|
1205
|
+
console.error(chalk11.red("\u2717") + " Failed to import:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1020
1206
|
process.exit(1);
|
|
1021
1207
|
}
|
|
1022
1208
|
});
|
|
@@ -1025,17 +1211,17 @@ ${validMemories.length} memories would be imported`));
|
|
|
1025
1211
|
import { Command as Command10 } from "commander";
|
|
1026
1212
|
|
|
1027
1213
|
// src/lib/config.ts
|
|
1028
|
-
import { readFile as
|
|
1029
|
-
import { join as
|
|
1030
|
-
import { existsSync as
|
|
1214
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1215
|
+
import { join as join3 } from "path";
|
|
1216
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1031
1217
|
import { parse, stringify } from "yaml";
|
|
1032
1218
|
var AGENTS_DIR = ".agents";
|
|
1033
1219
|
var CONFIG_FILE = "config.yaml";
|
|
1034
1220
|
async function initConfig(dir) {
|
|
1035
|
-
const agentsDir =
|
|
1036
|
-
await
|
|
1037
|
-
const configPath =
|
|
1038
|
-
if (!
|
|
1221
|
+
const agentsDir = join3(dir, AGENTS_DIR);
|
|
1222
|
+
await mkdir3(agentsDir, { recursive: true });
|
|
1223
|
+
const configPath = join3(agentsDir, CONFIG_FILE);
|
|
1224
|
+
if (!existsSync3(configPath)) {
|
|
1039
1225
|
const defaultConfig = {
|
|
1040
1226
|
name: "my-project",
|
|
1041
1227
|
description: "Agent memory configuration",
|
|
@@ -1045,14 +1231,14 @@ async function initConfig(dir) {
|
|
|
1045
1231
|
store: "~/.config/memories/local.db"
|
|
1046
1232
|
}
|
|
1047
1233
|
};
|
|
1048
|
-
await
|
|
1234
|
+
await writeFile4(configPath, stringify(defaultConfig), "utf-8");
|
|
1049
1235
|
}
|
|
1050
1236
|
return configPath;
|
|
1051
1237
|
}
|
|
1052
1238
|
async function readConfig(dir) {
|
|
1053
|
-
const configPath =
|
|
1054
|
-
if (!
|
|
1055
|
-
const raw = await
|
|
1239
|
+
const configPath = join3(dir, AGENTS_DIR, CONFIG_FILE);
|
|
1240
|
+
if (!existsSync3(configPath)) return null;
|
|
1241
|
+
const raw = await readFile4(configPath, "utf-8");
|
|
1056
1242
|
return parse(raw);
|
|
1057
1243
|
}
|
|
1058
1244
|
|
|
@@ -1077,6 +1263,8 @@ import { Command as Command11 } from "commander";
|
|
|
1077
1263
|
// src/mcp/index.ts
|
|
1078
1264
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1079
1265
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1266
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
1267
|
+
import { createServer } from "http";
|
|
1080
1268
|
import { z } from "zod";
|
|
1081
1269
|
var TYPE_LABELS2 = {
|
|
1082
1270
|
rule: "\u{1F4CC} RULE",
|
|
@@ -1100,7 +1288,7 @@ function formatMemoriesSection(memories, title) {
|
|
|
1100
1288
|
return `## ${title}
|
|
1101
1289
|
${memories.map(formatMemory4).join("\n")}`;
|
|
1102
1290
|
}
|
|
1103
|
-
async function
|
|
1291
|
+
async function createMcpServer() {
|
|
1104
1292
|
const projectId = getProjectId();
|
|
1105
1293
|
const server = new McpServer({
|
|
1106
1294
|
name: "memories",
|
|
@@ -1458,19 +1646,93 @@ Find the memory ID first with search_memories or list_memories.`,
|
|
|
1458
1646
|
}
|
|
1459
1647
|
}
|
|
1460
1648
|
);
|
|
1649
|
+
return server;
|
|
1650
|
+
}
|
|
1651
|
+
async function startMcpServer() {
|
|
1652
|
+
const server = await createMcpServer();
|
|
1461
1653
|
const transport = new StdioServerTransport();
|
|
1462
1654
|
await server.connect(transport);
|
|
1463
1655
|
}
|
|
1656
|
+
async function startMcpHttpServer(options) {
|
|
1657
|
+
const { port, host, cors } = options;
|
|
1658
|
+
const server = await createMcpServer();
|
|
1659
|
+
const transport = new StreamableHTTPServerTransport({
|
|
1660
|
+
sessionIdGenerator: void 0
|
|
1661
|
+
// Stateless mode
|
|
1662
|
+
});
|
|
1663
|
+
await server.connect(transport);
|
|
1664
|
+
const httpServer = createServer(async (req, res) => {
|
|
1665
|
+
if (cors) {
|
|
1666
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1667
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
1668
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
|
|
1669
|
+
}
|
|
1670
|
+
if (req.method === "OPTIONS") {
|
|
1671
|
+
res.writeHead(204);
|
|
1672
|
+
res.end();
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
1676
|
+
if (url.pathname === "/health") {
|
|
1677
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1678
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
if (url.pathname === "/mcp" || url.pathname === "/") {
|
|
1682
|
+
try {
|
|
1683
|
+
let body = void 0;
|
|
1684
|
+
if (req.method === "POST") {
|
|
1685
|
+
const chunks = [];
|
|
1686
|
+
for await (const chunk of req) {
|
|
1687
|
+
chunks.push(chunk);
|
|
1688
|
+
}
|
|
1689
|
+
const rawBody = Buffer.concat(chunks).toString("utf-8");
|
|
1690
|
+
if (rawBody) {
|
|
1691
|
+
body = JSON.parse(rawBody);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
await transport.handleRequest(req, res, body);
|
|
1695
|
+
} catch (error2) {
|
|
1696
|
+
console.error("[memories] Error handling MCP request:", error2);
|
|
1697
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1698
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
1699
|
+
}
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1703
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
1704
|
+
});
|
|
1705
|
+
httpServer.listen(port, host);
|
|
1706
|
+
await new Promise(() => {
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1464
1709
|
|
|
1465
1710
|
// src/commands/serve.ts
|
|
1466
|
-
var serveCommand = new Command11("serve").description("Start the MCP server
|
|
1711
|
+
var serveCommand = new Command11("serve").description("Start the MCP server").option("--sse", "Use SSE/HTTP transport instead of stdio (for web clients like v0)").option("-p, --port <port>", "Port for SSE server", "3030").option("--host <host>", "Host to bind to", "127.0.0.1").option("--cors", "Enable CORS for cross-origin requests").action(async (opts) => {
|
|
1467
1712
|
const projectId = getProjectId();
|
|
1468
|
-
if (
|
|
1469
|
-
|
|
1713
|
+
if (opts.sse) {
|
|
1714
|
+
const port = parseInt(opts.port || "3030", 10);
|
|
1715
|
+
const host = opts.host || "127.0.0.1";
|
|
1716
|
+
console.log(`[memories] Starting MCP server with SSE transport`);
|
|
1717
|
+
console.log(`[memories] Listening on http://${host}:${port}`);
|
|
1718
|
+
if (projectId) {
|
|
1719
|
+
console.log(`[memories] Project: ${projectId}`);
|
|
1720
|
+
} else {
|
|
1721
|
+
console.log(`[memories] Global only (not in a git repo)`);
|
|
1722
|
+
}
|
|
1723
|
+
if (opts.cors) {
|
|
1724
|
+
console.log(`[memories] CORS enabled`);
|
|
1725
|
+
}
|
|
1726
|
+
console.log(`[memories] Connect v0 or other clients to: http://${host}:${port}/mcp`);
|
|
1727
|
+
await startMcpHttpServer({ port, host, cors: opts.cors });
|
|
1470
1728
|
} else {
|
|
1471
|
-
|
|
1729
|
+
if (projectId) {
|
|
1730
|
+
console.error(`[memories] MCP server starting (project: ${projectId})`);
|
|
1731
|
+
} else {
|
|
1732
|
+
console.error("[memories] MCP server starting (global only - not in a git repo)");
|
|
1733
|
+
}
|
|
1734
|
+
await startMcpServer();
|
|
1472
1735
|
}
|
|
1473
|
-
await startMcpServer();
|
|
1474
1736
|
});
|
|
1475
1737
|
|
|
1476
1738
|
// src/commands/sync.ts
|
|
@@ -1528,10 +1790,10 @@ async function createDatabaseToken(org, dbName) {
|
|
|
1528
1790
|
}
|
|
1529
1791
|
|
|
1530
1792
|
// src/commands/sync.ts
|
|
1531
|
-
import { unlinkSync, existsSync as
|
|
1532
|
-
import { join as
|
|
1533
|
-
import { homedir as
|
|
1534
|
-
var DB_PATH =
|
|
1793
|
+
import { unlinkSync, existsSync as existsSync4 } from "fs";
|
|
1794
|
+
import { join as join4 } from "path";
|
|
1795
|
+
import { homedir as homedir3 } from "os";
|
|
1796
|
+
var DB_PATH = join4(homedir3(), ".config", "memories", "local.db");
|
|
1535
1797
|
var syncCommand = new Command12("sync").description(
|
|
1536
1798
|
"Manage remote sync"
|
|
1537
1799
|
);
|
|
@@ -1554,7 +1816,7 @@ syncCommand.command("enable").description("Provision a cloud database and enable
|
|
|
1554
1816
|
const profileRes = await apiFetch("/api/db/credentials");
|
|
1555
1817
|
if (profileRes.ok) {
|
|
1556
1818
|
const creds = await profileRes.json();
|
|
1557
|
-
if (
|
|
1819
|
+
if (existsSync4(DB_PATH)) {
|
|
1558
1820
|
resetDb();
|
|
1559
1821
|
unlinkSync(DB_PATH);
|
|
1560
1822
|
}
|
|
@@ -1590,7 +1852,7 @@ syncCommand.command("enable").description("Provision a cloud database and enable
|
|
|
1590
1852
|
spinner.text = "Generating auth token...";
|
|
1591
1853
|
const token = await createDatabaseToken(opts.org, db.name);
|
|
1592
1854
|
const syncUrl = `libsql://${db.hostname}`;
|
|
1593
|
-
if (
|
|
1855
|
+
if (existsSync4(DB_PATH)) {
|
|
1594
1856
|
resetDb();
|
|
1595
1857
|
unlinkSync(DB_PATH);
|
|
1596
1858
|
}
|
|
@@ -1637,12 +1899,12 @@ syncCommand.command("status").description("Show sync configuration").action(asyn
|
|
|
1637
1899
|
|
|
1638
1900
|
// src/commands/generate.ts
|
|
1639
1901
|
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
|
|
1645
|
-
import { checkbox } from "@inquirer/prompts";
|
|
1902
|
+
import chalk12 from "chalk";
|
|
1903
|
+
import { writeFile as writeFile5, readFile as readFile5, mkdir as mkdir4 } from "fs/promises";
|
|
1904
|
+
import { existsSync as existsSync5, watch as fsWatch } from "fs";
|
|
1905
|
+
import { dirname as dirname2, resolve, join as join5 } from "path";
|
|
1906
|
+
import { homedir as homedir4 } from "os";
|
|
1907
|
+
import { checkbox as checkbox2 } from "@inquirer/prompts";
|
|
1646
1908
|
var MARKER = "Generated by memories.sh";
|
|
1647
1909
|
var VALID_TYPES6 = ["rule", "decision", "fact", "note"];
|
|
1648
1910
|
function groupByType(memories) {
|
|
@@ -1778,7 +2040,7 @@ function makeFooter() {
|
|
|
1778
2040
|
}
|
|
1779
2041
|
async function hasOurMarker(filePath) {
|
|
1780
2042
|
try {
|
|
1781
|
-
const content = await
|
|
2043
|
+
const content = await readFile5(filePath, "utf-8");
|
|
1782
2044
|
return content.includes(MARKER);
|
|
1783
2045
|
} catch {
|
|
1784
2046
|
return false;
|
|
@@ -1789,13 +2051,13 @@ async function checkGitignore(filePath) {
|
|
|
1789
2051
|
if (TRACK_BY_DEFAULT.has(filePath)) return;
|
|
1790
2052
|
const gitignorePath = resolve(".gitignore");
|
|
1791
2053
|
try {
|
|
1792
|
-
const content =
|
|
2054
|
+
const content = existsSync5(gitignorePath) ? await readFile5(gitignorePath, "utf-8") : "";
|
|
1793
2055
|
const lines = content.split("\n");
|
|
1794
2056
|
const parentDir = filePath.split("/")[0];
|
|
1795
2057
|
if (lines.some((l) => l.trim() === filePath || l.trim() === parentDir || l.trim() === `${parentDir}/`)) {
|
|
1796
2058
|
return;
|
|
1797
2059
|
}
|
|
1798
|
-
console.log(
|
|
2060
|
+
console.log(chalk12.dim(` hint: add "${filePath}" to .gitignore if you don't want it tracked`));
|
|
1799
2061
|
} catch {
|
|
1800
2062
|
}
|
|
1801
2063
|
}
|
|
@@ -1803,23 +2065,23 @@ async function writeTarget(target, memories, opts) {
|
|
|
1803
2065
|
const outPath = resolve(opts.output ?? target.defaultPath);
|
|
1804
2066
|
const content = target.format(memories) + makeFooter();
|
|
1805
2067
|
if (opts.dryRun) {
|
|
1806
|
-
console.log(
|
|
2068
|
+
console.log(chalk12.dim(`\u2500\u2500 ${target.name} \u2192 ${outPath} \u2500\u2500`));
|
|
1807
2069
|
console.log(content);
|
|
1808
2070
|
console.log();
|
|
1809
2071
|
return;
|
|
1810
2072
|
}
|
|
1811
|
-
if (
|
|
2073
|
+
if (existsSync5(outPath)) {
|
|
1812
2074
|
const ours = await hasOurMarker(outPath);
|
|
1813
2075
|
if (!ours && !opts.force) {
|
|
1814
2076
|
console.error(
|
|
1815
|
-
|
|
2077
|
+
chalk12.yellow("\u26A0") + ` ${outPath} exists and was not generated by memories.sh. Use ${chalk12.bold("--force")} to overwrite.`
|
|
1816
2078
|
);
|
|
1817
2079
|
return;
|
|
1818
2080
|
}
|
|
1819
2081
|
}
|
|
1820
|
-
await
|
|
1821
|
-
await
|
|
1822
|
-
console.log(
|
|
2082
|
+
await mkdir4(dirname2(outPath), { recursive: true });
|
|
2083
|
+
await writeFile5(outPath, content, "utf-8");
|
|
2084
|
+
console.log(chalk12.green("\u2713") + ` Wrote ${target.name} \u2192 ${chalk12.dim(outPath)}`);
|
|
1823
2085
|
await checkGitignore(opts.output ?? target.defaultPath);
|
|
1824
2086
|
}
|
|
1825
2087
|
async function fetchMemories(types) {
|
|
@@ -1835,23 +2097,23 @@ function parseTypes(raw) {
|
|
|
1835
2097
|
const types = raw.split(",").map((s) => s.trim());
|
|
1836
2098
|
for (const t of types) {
|
|
1837
2099
|
if (!VALID_TYPES6.includes(t)) {
|
|
1838
|
-
console.error(
|
|
2100
|
+
console.error(chalk12.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES6.join(", ")}`);
|
|
1839
2101
|
process.exit(1);
|
|
1840
2102
|
}
|
|
1841
2103
|
}
|
|
1842
2104
|
return types;
|
|
1843
2105
|
}
|
|
1844
2106
|
function getDbPath() {
|
|
1845
|
-
const dataDir = process.env.MEMORIES_DATA_DIR ??
|
|
1846
|
-
return
|
|
2107
|
+
const dataDir = process.env.MEMORIES_DATA_DIR ?? join5(homedir4(), ".config", "memories");
|
|
2108
|
+
return join5(dataDir, "local.db");
|
|
1847
2109
|
}
|
|
1848
2110
|
async function runWatch(targets, memories, opts) {
|
|
1849
2111
|
const dbPath = getDbPath();
|
|
1850
|
-
if (!
|
|
1851
|
-
console.error(
|
|
2112
|
+
if (!existsSync5(dbPath)) {
|
|
2113
|
+
console.error(chalk12.red("\u2717") + " Database not found. Run: memories init");
|
|
1852
2114
|
process.exit(1);
|
|
1853
2115
|
}
|
|
1854
|
-
console.log(
|
|
2116
|
+
console.log(chalk12.dim(`Watching ${dbPath} for changes... (Ctrl+C to stop)
|
|
1855
2117
|
`));
|
|
1856
2118
|
const mems = await memories();
|
|
1857
2119
|
if (mems.length > 0) {
|
|
@@ -1871,7 +2133,7 @@ async function runWatch(targets, memories, opts) {
|
|
|
1871
2133
|
}
|
|
1872
2134
|
}
|
|
1873
2135
|
} catch (err) {
|
|
1874
|
-
console.error(
|
|
2136
|
+
console.error(chalk12.red("\u2717") + " Watch error:", err.message);
|
|
1875
2137
|
}
|
|
1876
2138
|
}, 500);
|
|
1877
2139
|
});
|
|
@@ -1884,22 +2146,22 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1884
2146
|
generateCommand.outputHelp();
|
|
1885
2147
|
return;
|
|
1886
2148
|
}
|
|
1887
|
-
const selected = await
|
|
2149
|
+
const selected = await checkbox2({
|
|
1888
2150
|
message: "Select targets to generate",
|
|
1889
2151
|
choices: TARGETS.map((t) => ({
|
|
1890
|
-
name: `${t.name} ${
|
|
2152
|
+
name: `${t.name} ${chalk12.dim(`\u2192 ${t.defaultPath}`)}`,
|
|
1891
2153
|
value: t.name,
|
|
1892
2154
|
checked: true
|
|
1893
2155
|
}))
|
|
1894
2156
|
});
|
|
1895
2157
|
if (selected.length === 0) {
|
|
1896
|
-
console.log(
|
|
2158
|
+
console.log(chalk12.dim("No targets selected."));
|
|
1897
2159
|
return;
|
|
1898
2160
|
}
|
|
1899
2161
|
const types = parseTypes(opts.types);
|
|
1900
2162
|
const memories = await fetchMemories(types);
|
|
1901
2163
|
if (memories.length === 0) {
|
|
1902
|
-
console.error(
|
|
2164
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1903
2165
|
return;
|
|
1904
2166
|
}
|
|
1905
2167
|
const selectedSet = new Set(selected);
|
|
@@ -1911,7 +2173,7 @@ var generateCommand = new Command13("generate").description("Generate IDE rule/i
|
|
|
1911
2173
|
}
|
|
1912
2174
|
} catch (error2) {
|
|
1913
2175
|
if (error2.name === "ExitPromptError") return;
|
|
1914
|
-
console.error(
|
|
2176
|
+
console.error(chalk12.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1915
2177
|
process.exit(1);
|
|
1916
2178
|
}
|
|
1917
2179
|
});
|
|
@@ -1922,12 +2184,12 @@ for (const target of TARGETS) {
|
|
|
1922
2184
|
const types = parseTypes(opts.types);
|
|
1923
2185
|
const memories = await fetchMemories(types);
|
|
1924
2186
|
if (memories.length === 0) {
|
|
1925
|
-
console.error(
|
|
2187
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1926
2188
|
return;
|
|
1927
2189
|
}
|
|
1928
2190
|
await writeTarget(target, memories, opts);
|
|
1929
2191
|
} catch (error2) {
|
|
1930
|
-
console.error(
|
|
2192
|
+
console.error(chalk12.red("\u2717") + ` Failed to generate ${target.name}:`, error2 instanceof Error ? error2.message : "Unknown error");
|
|
1931
2193
|
process.exit(1);
|
|
1932
2194
|
}
|
|
1933
2195
|
})
|
|
@@ -1943,14 +2205,14 @@ generateCommand.addCommand(
|
|
|
1943
2205
|
}
|
|
1944
2206
|
const memories = await fetchMemories(types);
|
|
1945
2207
|
if (memories.length === 0) {
|
|
1946
|
-
console.error(
|
|
2208
|
+
console.error(chalk12.dim('No memories found. Add some with: memories add --rule "Your rule"'));
|
|
1947
2209
|
return;
|
|
1948
2210
|
}
|
|
1949
2211
|
for (const target of TARGETS) {
|
|
1950
2212
|
await writeTarget(target, memories, opts);
|
|
1951
2213
|
}
|
|
1952
2214
|
} catch (error2) {
|
|
1953
|
-
console.error(
|
|
2215
|
+
console.error(chalk12.red("\u2717") + " Failed to generate:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
1954
2216
|
process.exit(1);
|
|
1955
2217
|
}
|
|
1956
2218
|
})
|
|
@@ -1958,11 +2220,11 @@ generateCommand.addCommand(
|
|
|
1958
2220
|
|
|
1959
2221
|
// src/commands/edit.ts
|
|
1960
2222
|
import { Command as Command14 } from "commander";
|
|
1961
|
-
import
|
|
2223
|
+
import chalk13 from "chalk";
|
|
1962
2224
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1963
2225
|
import { writeFileSync, readFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1964
2226
|
import { tmpdir } from "os";
|
|
1965
|
-
import { join as
|
|
2227
|
+
import { join as join6 } from "path";
|
|
1966
2228
|
import { nanoid as nanoid2 } from "nanoid";
|
|
1967
2229
|
import { select } from "@inquirer/prompts";
|
|
1968
2230
|
var VALID_TYPES7 = ["rule", "decision", "fact", "note"];
|
|
@@ -1974,13 +2236,13 @@ async function pickMemory() {
|
|
|
1974
2236
|
const projectId = getProjectId() ?? void 0;
|
|
1975
2237
|
const memories = await listMemories({ limit: 100, projectId });
|
|
1976
2238
|
if (memories.length === 0) {
|
|
1977
|
-
console.error(
|
|
2239
|
+
console.error(chalk13.dim("No memories found."));
|
|
1978
2240
|
process.exit(0);
|
|
1979
2241
|
}
|
|
1980
2242
|
const id = await select({
|
|
1981
2243
|
message: "Select a memory to edit",
|
|
1982
2244
|
choices: memories.map((m) => ({
|
|
1983
|
-
name: `${
|
|
2245
|
+
name: `${chalk13.dim(m.type.padEnd(9))} ${truncate2(m.content, 60)} ${chalk13.dim(m.id)}`,
|
|
1984
2246
|
value: m.id
|
|
1985
2247
|
}))
|
|
1986
2248
|
});
|
|
@@ -1990,7 +2252,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
1990
2252
|
try {
|
|
1991
2253
|
if (!id) {
|
|
1992
2254
|
if (!process.stdin.isTTY) {
|
|
1993
|
-
console.error(
|
|
2255
|
+
console.error(chalk13.red("\u2717") + " Memory ID required in non-interactive mode");
|
|
1994
2256
|
process.exit(1);
|
|
1995
2257
|
}
|
|
1996
2258
|
id = await pickMemory();
|
|
@@ -2001,18 +2263,18 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2001
2263
|
args: [id]
|
|
2002
2264
|
});
|
|
2003
2265
|
if (result.rows.length === 0) {
|
|
2004
|
-
console.error(
|
|
2266
|
+
console.error(chalk13.red("\u2717") + ` Memory ${chalk13.dim(id)} not found`);
|
|
2005
2267
|
process.exit(1);
|
|
2006
2268
|
}
|
|
2007
2269
|
const memory = result.rows[0];
|
|
2008
2270
|
if (opts.type && !VALID_TYPES7.includes(opts.type)) {
|
|
2009
|
-
console.error(
|
|
2271
|
+
console.error(chalk13.red("\u2717") + ` Invalid type "${opts.type}". Valid: ${VALID_TYPES7.join(", ")}`);
|
|
2010
2272
|
process.exit(1);
|
|
2011
2273
|
}
|
|
2012
2274
|
let newContent = opts.content;
|
|
2013
2275
|
if (newContent === void 0 && opts.tags === void 0 && opts.type === void 0) {
|
|
2014
2276
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
2015
|
-
const tmpFile =
|
|
2277
|
+
const tmpFile = join6(tmpdir(), `memories-edit-${nanoid2(6)}.md`);
|
|
2016
2278
|
writeFileSync(tmpFile, memory.content, "utf-8");
|
|
2017
2279
|
try {
|
|
2018
2280
|
execFileSync2(editor, [tmpFile], { stdio: "inherit" });
|
|
@@ -2024,7 +2286,7 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2024
2286
|
}
|
|
2025
2287
|
}
|
|
2026
2288
|
if (newContent === memory.content) {
|
|
2027
|
-
console.log(
|
|
2289
|
+
console.log(chalk13.dim("No changes made."));
|
|
2028
2290
|
return;
|
|
2029
2291
|
}
|
|
2030
2292
|
}
|
|
@@ -2034,24 +2296,24 @@ var editCommand = new Command14("edit").description("Edit an existing memory").a
|
|
|
2034
2296
|
if (opts.type !== void 0) updates.type = opts.type;
|
|
2035
2297
|
const updated = await updateMemory(id, updates);
|
|
2036
2298
|
if (!updated) {
|
|
2037
|
-
console.error(
|
|
2299
|
+
console.error(chalk13.red("\u2717") + ` Failed to update memory ${chalk13.dim(id)}`);
|
|
2038
2300
|
process.exit(1);
|
|
2039
2301
|
}
|
|
2040
2302
|
const changes = [];
|
|
2041
2303
|
if (updates.content !== void 0) changes.push("content");
|
|
2042
2304
|
if (updates.tags !== void 0) changes.push("tags");
|
|
2043
2305
|
if (updates.type !== void 0) changes.push(`type\u2192${updates.type}`);
|
|
2044
|
-
console.log(
|
|
2306
|
+
console.log(chalk13.green("\u2713") + ` Updated ${chalk13.dim(id)} (${changes.join(", ")})`);
|
|
2045
2307
|
} catch (error2) {
|
|
2046
2308
|
if (error2.name === "ExitPromptError") return;
|
|
2047
|
-
console.error(
|
|
2309
|
+
console.error(chalk13.red("\u2717") + " Failed to edit memory:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2048
2310
|
process.exit(1);
|
|
2049
2311
|
}
|
|
2050
2312
|
});
|
|
2051
2313
|
|
|
2052
2314
|
// src/commands/stats.ts
|
|
2053
2315
|
import { Command as Command15 } from "commander";
|
|
2054
|
-
import
|
|
2316
|
+
import chalk14 from "chalk";
|
|
2055
2317
|
var statsCommand = new Command15("stats").description("Show memory statistics").option("--json", "Output as JSON").action(async (opts) => {
|
|
2056
2318
|
try {
|
|
2057
2319
|
const db = await getDb();
|
|
@@ -2088,46 +2350,46 @@ var statsCommand = new Command15("stats").description("Show memory statistics").
|
|
|
2088
2350
|
console.log(JSON.stringify(data, null, 2));
|
|
2089
2351
|
return;
|
|
2090
2352
|
}
|
|
2091
|
-
console.log(
|
|
2353
|
+
console.log(chalk14.bold("Memory Statistics\n"));
|
|
2092
2354
|
if (projectId) {
|
|
2093
|
-
console.log(` Project: ${
|
|
2355
|
+
console.log(` Project: ${chalk14.dim(projectId)}`);
|
|
2094
2356
|
}
|
|
2095
|
-
console.log(` Total: ${
|
|
2357
|
+
console.log(` Total: ${chalk14.bold(String(total))} active, ${chalk14.dim(String(deleted))} deleted
|
|
2096
2358
|
`);
|
|
2097
2359
|
if (rows.length === 0) {
|
|
2098
|
-
console.log(
|
|
2360
|
+
console.log(chalk14.dim(' No memories yet. Add one with: memories add "Your memory"'));
|
|
2099
2361
|
return;
|
|
2100
2362
|
}
|
|
2101
2363
|
const typeWidths = { rule: 8, decision: 8, fact: 8, note: 8 };
|
|
2102
2364
|
console.log(
|
|
2103
|
-
` ${
|
|
2365
|
+
` ${chalk14.dim("Type".padEnd(12))}${chalk14.dim("Scope".padEnd(10))}${chalk14.dim("Count")}`
|
|
2104
2366
|
);
|
|
2105
|
-
console.log(
|
|
2367
|
+
console.log(chalk14.dim(" " + "\u2500".repeat(30)));
|
|
2106
2368
|
for (const row of rows) {
|
|
2107
2369
|
const type = row.type.padEnd(12);
|
|
2108
2370
|
const scope = row.scope.padEnd(10);
|
|
2109
2371
|
console.log(` ${type}${scope}${Number(row.count)}`);
|
|
2110
2372
|
}
|
|
2111
2373
|
} catch (error2) {
|
|
2112
|
-
console.error(
|
|
2374
|
+
console.error(chalk14.red("\u2717") + " Failed to get stats:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2113
2375
|
process.exit(1);
|
|
2114
2376
|
}
|
|
2115
2377
|
});
|
|
2116
2378
|
|
|
2117
2379
|
// src/commands/doctor.ts
|
|
2118
2380
|
import { Command as Command16 } from "commander";
|
|
2119
|
-
import
|
|
2120
|
-
import { existsSync as
|
|
2121
|
-
import { join as
|
|
2381
|
+
import chalk15 from "chalk";
|
|
2382
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2383
|
+
import { join as join7 } from "path";
|
|
2122
2384
|
var doctorCommand = new Command16("doctor").description("Check memories health and diagnose issues").option("--fix", "Attempt to fix issues found").action(async (opts) => {
|
|
2123
2385
|
try {
|
|
2124
|
-
console.log(
|
|
2386
|
+
console.log(chalk15.bold("memories doctor\n"));
|
|
2125
2387
|
const checks = [
|
|
2126
2388
|
{
|
|
2127
2389
|
name: "Database file",
|
|
2128
2390
|
run: async () => {
|
|
2129
|
-
const dbPath =
|
|
2130
|
-
if (
|
|
2391
|
+
const dbPath = join7(getConfigDir(), "local.db");
|
|
2392
|
+
if (existsSync6(dbPath)) {
|
|
2131
2393
|
return { ok: true, message: `Found at ${dbPath}` };
|
|
2132
2394
|
}
|
|
2133
2395
|
return { ok: false, message: `Not found at ${dbPath}. Run: memories init` };
|
|
@@ -2225,43 +2487,43 @@ var doctorCommand = new Command16("doctor").description("Check memories health a
|
|
|
2225
2487
|
let hasIssues = false;
|
|
2226
2488
|
for (const check of checks) {
|
|
2227
2489
|
const { ok, message } = await check.run();
|
|
2228
|
-
const icon = ok ?
|
|
2229
|
-
console.log(` ${icon} ${
|
|
2490
|
+
const icon = ok ? chalk15.green("\u2713") : chalk15.red("\u2717");
|
|
2491
|
+
console.log(` ${icon} ${chalk15.bold(check.name)}: ${message}`);
|
|
2230
2492
|
if (!ok) hasIssues = true;
|
|
2231
2493
|
}
|
|
2232
2494
|
if (opts.fix) {
|
|
2233
|
-
console.log(
|
|
2495
|
+
console.log(chalk15.bold("\nRunning fixes...\n"));
|
|
2234
2496
|
const db = await getDb();
|
|
2235
2497
|
const purged = await db.execute(
|
|
2236
2498
|
"DELETE FROM memories WHERE deleted_at IS NOT NULL"
|
|
2237
2499
|
);
|
|
2238
|
-
console.log(` ${
|
|
2500
|
+
console.log(` ${chalk15.green("\u2713")} Purged ${purged.rowsAffected} soft-deleted records`);
|
|
2239
2501
|
try {
|
|
2240
2502
|
await db.execute("INSERT INTO memories_fts(memories_fts) VALUES('rebuild')");
|
|
2241
|
-
console.log(` ${
|
|
2503
|
+
console.log(` ${chalk15.green("\u2713")} Rebuilt FTS index`);
|
|
2242
2504
|
} catch {
|
|
2243
|
-
console.log(` ${
|
|
2505
|
+
console.log(` ${chalk15.yellow("\u26A0")} Could not rebuild FTS index`);
|
|
2244
2506
|
}
|
|
2245
2507
|
}
|
|
2246
2508
|
console.log();
|
|
2247
2509
|
if (hasIssues) {
|
|
2248
|
-
console.log(
|
|
2510
|
+
console.log(chalk15.yellow("Some issues detected.") + (opts.fix ? "" : " Run with --fix to attempt repairs."));
|
|
2249
2511
|
} else {
|
|
2250
|
-
console.log(
|
|
2512
|
+
console.log(chalk15.green("All checks passed."));
|
|
2251
2513
|
}
|
|
2252
2514
|
} catch (error2) {
|
|
2253
|
-
console.error(
|
|
2515
|
+
console.error(chalk15.red("\u2717") + " Doctor failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2254
2516
|
process.exit(1);
|
|
2255
2517
|
}
|
|
2256
2518
|
});
|
|
2257
2519
|
|
|
2258
2520
|
// src/commands/hook.ts
|
|
2259
2521
|
import { Command as Command17 } from "commander";
|
|
2260
|
-
import
|
|
2261
|
-
import { readFile as
|
|
2262
|
-
import { existsSync as
|
|
2522
|
+
import chalk16 from "chalk";
|
|
2523
|
+
import { readFile as readFile6, writeFile as writeFile6, chmod } from "fs/promises";
|
|
2524
|
+
import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
|
|
2263
2525
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2264
|
-
import { join as
|
|
2526
|
+
import { join as join8 } from "path";
|
|
2265
2527
|
var HOOK_MARKER_START = "# >>> memories.sh hook >>>";
|
|
2266
2528
|
var HOOK_MARKER_END = "# <<< memories.sh hook <<<";
|
|
2267
2529
|
var HOOK_SNIPPET = `
|
|
@@ -2282,22 +2544,22 @@ function getGitDir() {
|
|
|
2282
2544
|
function getHookLocation(hookName) {
|
|
2283
2545
|
const gitDir = getGitDir();
|
|
2284
2546
|
if (!gitDir) return null;
|
|
2285
|
-
const huskyPath =
|
|
2286
|
-
if (
|
|
2547
|
+
const huskyPath = join8(".husky", hookName);
|
|
2548
|
+
if (existsSync7(".husky") && !existsSync7(join8(".husky", "_"))) {
|
|
2287
2549
|
return { path: huskyPath, type: "husky" };
|
|
2288
2550
|
}
|
|
2289
|
-
const huskyLegacyPath =
|
|
2290
|
-
if (
|
|
2551
|
+
const huskyLegacyPath = join8(".husky", "_", hookName);
|
|
2552
|
+
if (existsSync7(join8(".husky", "_"))) {
|
|
2291
2553
|
return { path: huskyLegacyPath, type: "husky" };
|
|
2292
2554
|
}
|
|
2293
|
-
if (
|
|
2555
|
+
if (existsSync7(huskyPath)) {
|
|
2294
2556
|
return { path: huskyPath, type: "husky" };
|
|
2295
2557
|
}
|
|
2296
|
-
return { path:
|
|
2558
|
+
return { path: join8(gitDir, "hooks", hookName), type: "git" };
|
|
2297
2559
|
}
|
|
2298
2560
|
function detectLintStaged() {
|
|
2299
2561
|
try {
|
|
2300
|
-
if (!
|
|
2562
|
+
if (!existsSync7("package.json")) return false;
|
|
2301
2563
|
const pkg = JSON.parse(readFileSync2("package.json", "utf-8"));
|
|
2302
2564
|
return !!(pkg["lint-staged"] || pkg.devDependencies?.["lint-staged"] || pkg.dependencies?.["lint-staged"]);
|
|
2303
2565
|
} catch {
|
|
@@ -2310,31 +2572,31 @@ hookCommand.addCommand(
|
|
|
2310
2572
|
try {
|
|
2311
2573
|
const location = getHookLocation(opts.hook);
|
|
2312
2574
|
if (!location) {
|
|
2313
|
-
console.error(
|
|
2575
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2314
2576
|
process.exit(1);
|
|
2315
2577
|
}
|
|
2316
2578
|
const hookPath = location.path;
|
|
2317
|
-
if (
|
|
2318
|
-
const content = await
|
|
2579
|
+
if (existsSync7(hookPath)) {
|
|
2580
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2319
2581
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2320
|
-
console.log(
|
|
2582
|
+
console.log(chalk16.dim("Hook already installed. Use 'memories hook uninstall' first to reinstall."));
|
|
2321
2583
|
return;
|
|
2322
2584
|
}
|
|
2323
|
-
await
|
|
2585
|
+
await writeFile6(hookPath, content.trimEnd() + "\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2324
2586
|
} else {
|
|
2325
|
-
await
|
|
2587
|
+
await writeFile6(hookPath, "#!/bin/sh\n" + HOOK_SNIPPET + "\n", "utf-8");
|
|
2326
2588
|
}
|
|
2327
2589
|
await chmod(hookPath, 493);
|
|
2328
2590
|
const locationLabel = location.type === "husky" ? "Husky" : ".git/hooks";
|
|
2329
|
-
console.log(
|
|
2330
|
-
console.log(
|
|
2591
|
+
console.log(chalk16.green("\u2713") + ` Installed memories hook in ${chalk16.dim(opts.hook)} (${locationLabel})`);
|
|
2592
|
+
console.log(chalk16.dim(" Rule files will auto-generate on each commit."));
|
|
2331
2593
|
if (detectLintStaged()) {
|
|
2332
2594
|
console.log(
|
|
2333
|
-
|
|
2595
|
+
chalk16.dim("\n lint-staged detected. You can also add to your lint-staged config:") + chalk16.dim('\n "*.md": "memories generate all --force"')
|
|
2334
2596
|
);
|
|
2335
2597
|
}
|
|
2336
2598
|
} catch (error2) {
|
|
2337
|
-
console.error(
|
|
2599
|
+
console.error(chalk16.red("\u2717") + " Failed to install hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2338
2600
|
process.exit(1);
|
|
2339
2601
|
}
|
|
2340
2602
|
})
|
|
@@ -2344,17 +2606,17 @@ hookCommand.addCommand(
|
|
|
2344
2606
|
try {
|
|
2345
2607
|
const location = getHookLocation(opts.hook);
|
|
2346
2608
|
if (!location) {
|
|
2347
|
-
console.error(
|
|
2609
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2348
2610
|
process.exit(1);
|
|
2349
2611
|
}
|
|
2350
2612
|
const hookPath = location.path;
|
|
2351
|
-
if (!
|
|
2352
|
-
console.log(
|
|
2613
|
+
if (!existsSync7(hookPath)) {
|
|
2614
|
+
console.log(chalk16.dim("No hook file found."));
|
|
2353
2615
|
return;
|
|
2354
2616
|
}
|
|
2355
|
-
const content = await
|
|
2617
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2356
2618
|
if (!content.includes(HOOK_MARKER_START)) {
|
|
2357
|
-
console.log(
|
|
2619
|
+
console.log(chalk16.dim("No memories hook found in " + opts.hook));
|
|
2358
2620
|
return;
|
|
2359
2621
|
}
|
|
2360
2622
|
const regex = new RegExp(
|
|
@@ -2364,13 +2626,13 @@ hookCommand.addCommand(
|
|
|
2364
2626
|
if (cleaned.trim() === "#!/bin/sh" || cleaned.trim() === "") {
|
|
2365
2627
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
2366
2628
|
await unlink2(hookPath);
|
|
2367
|
-
console.log(
|
|
2629
|
+
console.log(chalk16.green("\u2713") + ` Removed ${chalk16.dim(opts.hook)} hook (was memories-only)`);
|
|
2368
2630
|
} else {
|
|
2369
|
-
await
|
|
2370
|
-
console.log(
|
|
2631
|
+
await writeFile6(hookPath, cleaned, "utf-8");
|
|
2632
|
+
console.log(chalk16.green("\u2713") + ` Removed memories section from ${chalk16.dim(opts.hook)}`);
|
|
2371
2633
|
}
|
|
2372
2634
|
} catch (error2) {
|
|
2373
|
-
console.error(
|
|
2635
|
+
console.error(chalk16.red("\u2717") + " Failed to uninstall hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2374
2636
|
process.exit(1);
|
|
2375
2637
|
}
|
|
2376
2638
|
})
|
|
@@ -2380,21 +2642,21 @@ hookCommand.addCommand(
|
|
|
2380
2642
|
try {
|
|
2381
2643
|
const hookPath = getHookLocation(opts.hook)?.path;
|
|
2382
2644
|
if (!hookPath) {
|
|
2383
|
-
console.error(
|
|
2645
|
+
console.error(chalk16.red("\u2717") + " Not in a git repository");
|
|
2384
2646
|
process.exit(1);
|
|
2385
2647
|
}
|
|
2386
|
-
if (!
|
|
2387
|
-
console.log(
|
|
2648
|
+
if (!existsSync7(hookPath)) {
|
|
2649
|
+
console.log(chalk16.dim("Not installed") + ` \u2014 no ${opts.hook} hook found`);
|
|
2388
2650
|
return;
|
|
2389
2651
|
}
|
|
2390
|
-
const content = await
|
|
2652
|
+
const content = await readFile6(hookPath, "utf-8");
|
|
2391
2653
|
if (content.includes(HOOK_MARKER_START)) {
|
|
2392
|
-
console.log(
|
|
2654
|
+
console.log(chalk16.green("\u2713") + ` Installed in ${chalk16.dim(hookPath)}`);
|
|
2393
2655
|
} else {
|
|
2394
|
-
console.log(
|
|
2656
|
+
console.log(chalk16.dim("Not installed") + ` \u2014 ${opts.hook} exists but has no memories section`);
|
|
2395
2657
|
}
|
|
2396
2658
|
} catch (error2) {
|
|
2397
|
-
console.error(
|
|
2659
|
+
console.error(chalk16.red("\u2717") + " Failed to check hook:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2398
2660
|
process.exit(1);
|
|
2399
2661
|
}
|
|
2400
2662
|
})
|
|
@@ -2405,9 +2667,9 @@ function escapeRegex(str) {
|
|
|
2405
2667
|
|
|
2406
2668
|
// src/commands/ingest.ts
|
|
2407
2669
|
import { Command as Command18 } from "commander";
|
|
2408
|
-
import
|
|
2409
|
-
import { readFile as
|
|
2410
|
-
import { existsSync as
|
|
2670
|
+
import chalk17 from "chalk";
|
|
2671
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2672
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2411
2673
|
var SOURCES = [
|
|
2412
2674
|
{ name: "cursor", paths: [".cursor/rules/memories.mdc", ".cursorrules"], description: "Cursor rules" },
|
|
2413
2675
|
{ name: "claude", paths: ["CLAUDE.md"], description: "Claude Code instructions" },
|
|
@@ -2467,14 +2729,14 @@ function normalize(s) {
|
|
|
2467
2729
|
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
2730
|
try {
|
|
2469
2731
|
if (opts.type && !VALID_TYPES8.includes(opts.type)) {
|
|
2470
|
-
console.error(
|
|
2732
|
+
console.error(chalk17.red("\u2717") + ` Invalid type "${opts.type}". Valid types: ${VALID_TYPES8.join(", ")}`);
|
|
2471
2733
|
process.exit(1);
|
|
2472
2734
|
}
|
|
2473
2735
|
const filesToProcess = [];
|
|
2474
2736
|
if (opts.all) {
|
|
2475
2737
|
for (const src of SOURCES) {
|
|
2476
2738
|
for (const p of src.paths) {
|
|
2477
|
-
if (
|
|
2739
|
+
if (existsSync8(p)) {
|
|
2478
2740
|
filesToProcess.push({ name: src.name, path: p });
|
|
2479
2741
|
}
|
|
2480
2742
|
}
|
|
@@ -2483,27 +2745,27 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2483
2745
|
const known = SOURCES.find((s) => s.name === source);
|
|
2484
2746
|
if (known) {
|
|
2485
2747
|
for (const p of known.paths) {
|
|
2486
|
-
if (
|
|
2748
|
+
if (existsSync8(p)) {
|
|
2487
2749
|
filesToProcess.push({ name: known.name, path: p });
|
|
2488
2750
|
break;
|
|
2489
2751
|
}
|
|
2490
2752
|
}
|
|
2491
2753
|
if (filesToProcess.length === 0) {
|
|
2492
|
-
console.error(
|
|
2754
|
+
console.error(chalk17.red("\u2717") + ` No ${known.description} file found at: ${known.paths.join(", ")}`);
|
|
2493
2755
|
process.exit(1);
|
|
2494
2756
|
}
|
|
2495
|
-
} else if (
|
|
2757
|
+
} else if (existsSync8(source)) {
|
|
2496
2758
|
filesToProcess.push({ name: "file", path: source });
|
|
2497
2759
|
} else {
|
|
2498
|
-
console.error(
|
|
2760
|
+
console.error(chalk17.red("\u2717") + ` Unknown source "${source}". Valid: ${SOURCES.map((s) => s.name).join(", ")}, or a file path`);
|
|
2499
2761
|
process.exit(1);
|
|
2500
2762
|
}
|
|
2501
2763
|
} else {
|
|
2502
|
-
console.error(
|
|
2764
|
+
console.error(chalk17.red("\u2717") + " Specify a source or use --all");
|
|
2503
2765
|
process.exit(1);
|
|
2504
2766
|
}
|
|
2505
2767
|
if (filesToProcess.length === 0) {
|
|
2506
|
-
console.log(
|
|
2768
|
+
console.log(chalk17.dim("No IDE rule files found."));
|
|
2507
2769
|
return;
|
|
2508
2770
|
}
|
|
2509
2771
|
const existingSet = /* @__PURE__ */ new Set();
|
|
@@ -2517,29 +2779,29 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2517
2779
|
let totalImported = 0;
|
|
2518
2780
|
let totalSkipped = 0;
|
|
2519
2781
|
for (const file of filesToProcess) {
|
|
2520
|
-
const content = await
|
|
2782
|
+
const content = await readFile7(file.path, "utf-8");
|
|
2521
2783
|
if (content.includes(MARKER2)) {
|
|
2522
|
-
console.log(
|
|
2784
|
+
console.log(chalk17.dim(` Skipping ${file.path} (generated by memories.sh)`));
|
|
2523
2785
|
continue;
|
|
2524
2786
|
}
|
|
2525
2787
|
const memories = extractMemories(content);
|
|
2526
2788
|
if (memories.length === 0) {
|
|
2527
|
-
console.log(
|
|
2789
|
+
console.log(chalk17.dim(` No importable memories found in ${file.path}`));
|
|
2528
2790
|
continue;
|
|
2529
2791
|
}
|
|
2530
|
-
console.log(
|
|
2531
|
-
${file.name}`) +
|
|
2792
|
+
console.log(chalk17.bold(`
|
|
2793
|
+
${file.name}`) + chalk17.dim(` (${file.path}) \u2014 ${memories.length} items`));
|
|
2532
2794
|
for (const mem of memories) {
|
|
2533
2795
|
const type = opts.type ?? mem.type;
|
|
2534
2796
|
if (opts.dedup !== false && existingSet.has(normalize(mem.content))) {
|
|
2535
2797
|
if (opts.dryRun) {
|
|
2536
|
-
console.log(` ${
|
|
2798
|
+
console.log(` ${chalk17.dim("skip")} ${chalk17.dim(mem.content)}`);
|
|
2537
2799
|
}
|
|
2538
2800
|
totalSkipped++;
|
|
2539
2801
|
continue;
|
|
2540
2802
|
}
|
|
2541
2803
|
if (opts.dryRun) {
|
|
2542
|
-
const typeColor = type === "rule" ?
|
|
2804
|
+
const typeColor = type === "rule" ? chalk17.blue : type === "decision" ? chalk17.yellow : type === "fact" ? chalk17.green : chalk17.dim;
|
|
2543
2805
|
console.log(` ${typeColor(type.padEnd(9))} ${mem.content}`);
|
|
2544
2806
|
} else {
|
|
2545
2807
|
await addMemory(mem.content, { type });
|
|
@@ -2550,23 +2812,23 @@ var ingestCommand = new Command18("ingest").description("Import memories from ex
|
|
|
2550
2812
|
}
|
|
2551
2813
|
if (opts.dryRun) {
|
|
2552
2814
|
const skipMsg = totalSkipped > 0 ? ` (${totalSkipped} duplicates skipped)` : "";
|
|
2553
|
-
console.log(
|
|
2815
|
+
console.log(chalk17.dim(`
|
|
2554
2816
|
Dry run \u2014 no memories imported.${skipMsg} Remove --dry-run to import.`));
|
|
2555
2817
|
} else {
|
|
2556
|
-
const skipMsg = totalSkipped > 0 ?
|
|
2557
|
-
console.log(
|
|
2818
|
+
const skipMsg = totalSkipped > 0 ? chalk17.dim(` (${totalSkipped} duplicates skipped)`) : "";
|
|
2819
|
+
console.log(chalk17.green("\n\u2713") + ` Imported ${totalImported} memories` + skipMsg);
|
|
2558
2820
|
}
|
|
2559
2821
|
} catch (error2) {
|
|
2560
|
-
console.error(
|
|
2822
|
+
console.error(chalk17.red("\u2717") + " Failed to ingest:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2561
2823
|
process.exit(1);
|
|
2562
2824
|
}
|
|
2563
2825
|
});
|
|
2564
2826
|
|
|
2565
2827
|
// src/commands/diff.ts
|
|
2566
2828
|
import { Command as Command19 } from "commander";
|
|
2567
|
-
import
|
|
2568
|
-
import { readFile as
|
|
2569
|
-
import { existsSync as
|
|
2829
|
+
import chalk18 from "chalk";
|
|
2830
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2831
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2570
2832
|
import { resolve as resolve2 } from "path";
|
|
2571
2833
|
var MARKER3 = "Generated by memories.sh";
|
|
2572
2834
|
var VALID_TYPES9 = ["rule", "decision", "fact", "note"];
|
|
@@ -2599,7 +2861,7 @@ function parseTypes2(raw) {
|
|
|
2599
2861
|
const types = raw.split(",").map((s) => s.trim());
|
|
2600
2862
|
for (const t of types) {
|
|
2601
2863
|
if (!VALID_TYPES9.includes(t)) {
|
|
2602
|
-
console.error(
|
|
2864
|
+
console.error(chalk18.red("\u2717") + ` Invalid type "${t}". Valid: ${VALID_TYPES9.join(", ")}`);
|
|
2603
2865
|
process.exit(1);
|
|
2604
2866
|
}
|
|
2605
2867
|
}
|
|
@@ -2611,7 +2873,7 @@ async function fetchMemories2(types) {
|
|
|
2611
2873
|
}
|
|
2612
2874
|
async function diffTarget(target, currentMemories, outputPath) {
|
|
2613
2875
|
const filePath = resolve2(outputPath ?? target.defaultPath);
|
|
2614
|
-
if (!
|
|
2876
|
+
if (!existsSync9(filePath)) {
|
|
2615
2877
|
return {
|
|
2616
2878
|
added: currentMemories.map((m) => m.content),
|
|
2617
2879
|
removed: [],
|
|
@@ -2622,7 +2884,7 @@ async function diffTarget(target, currentMemories, outputPath) {
|
|
|
2622
2884
|
generatedAt: null
|
|
2623
2885
|
};
|
|
2624
2886
|
}
|
|
2625
|
-
const content = await
|
|
2887
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2626
2888
|
const isOurs = content.includes(MARKER3);
|
|
2627
2889
|
const generatedAt = extractTimestamp(content);
|
|
2628
2890
|
const fileMemories = extractFileMemories(content);
|
|
@@ -2638,7 +2900,7 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2638
2900
|
const memories = await fetchMemories2(types);
|
|
2639
2901
|
const targetsToCheck = target && target !== "all" ? TARGETS2.filter((t) => t.name === target) : TARGETS2;
|
|
2640
2902
|
if (target && target !== "all" && targetsToCheck.length === 0) {
|
|
2641
|
-
console.error(
|
|
2903
|
+
console.error(chalk18.red("\u2717") + ` Unknown target "${target}". Valid: ${TARGETS2.map((t) => t.name).join(", ")}`);
|
|
2642
2904
|
process.exit(1);
|
|
2643
2905
|
}
|
|
2644
2906
|
let anyStale = false;
|
|
@@ -2647,54 +2909,54 @@ var diffCommand = new Command19("diff").description("Show what changed since las
|
|
|
2647
2909
|
if (!result.exists && !target) continue;
|
|
2648
2910
|
const hasChanges = result.added.length > 0 || result.removed.length > 0;
|
|
2649
2911
|
if (!result.exists) {
|
|
2650
|
-
console.log(
|
|
2651
|
-
${t.name}`) +
|
|
2652
|
-
console.log(
|
|
2912
|
+
console.log(chalk18.bold(`
|
|
2913
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2914
|
+
console.log(chalk18.yellow(" Not generated yet.") + chalk18.dim(` Run: memories generate ${t.name}`));
|
|
2653
2915
|
anyStale = true;
|
|
2654
2916
|
continue;
|
|
2655
2917
|
}
|
|
2656
2918
|
if (!result.isOurs) {
|
|
2657
|
-
console.log(
|
|
2658
|
-
${t.name}`) +
|
|
2659
|
-
console.log(
|
|
2919
|
+
console.log(chalk18.bold(`
|
|
2920
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2921
|
+
console.log(chalk18.dim(" Not managed by memories.sh (no marker found)"));
|
|
2660
2922
|
continue;
|
|
2661
2923
|
}
|
|
2662
2924
|
if (!hasChanges) {
|
|
2663
2925
|
if (target) {
|
|
2664
|
-
console.log(
|
|
2665
|
-
${t.name}`) +
|
|
2666
|
-
const since = result.generatedAt ?
|
|
2667
|
-
console.log(
|
|
2926
|
+
console.log(chalk18.bold(`
|
|
2927
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2928
|
+
const since = result.generatedAt ? chalk18.dim(` (generated ${formatRelative(result.generatedAt)})`) : "";
|
|
2929
|
+
console.log(chalk18.green(" Up to date.") + since);
|
|
2668
2930
|
}
|
|
2669
2931
|
continue;
|
|
2670
2932
|
}
|
|
2671
2933
|
anyStale = true;
|
|
2672
|
-
console.log(
|
|
2673
|
-
${t.name}`) +
|
|
2934
|
+
console.log(chalk18.bold(`
|
|
2935
|
+
${t.name}`) + chalk18.dim(` \u2192 ${result.filePath}`));
|
|
2674
2936
|
if (result.generatedAt) {
|
|
2675
|
-
console.log(
|
|
2937
|
+
console.log(chalk18.dim(` Generated ${formatRelative(result.generatedAt)}`));
|
|
2676
2938
|
}
|
|
2677
2939
|
for (const a of result.added) {
|
|
2678
|
-
console.log(
|
|
2940
|
+
console.log(chalk18.green(` + ${a}`));
|
|
2679
2941
|
}
|
|
2680
2942
|
for (const r of result.removed) {
|
|
2681
|
-
console.log(
|
|
2943
|
+
console.log(chalk18.red(` - ${r}`));
|
|
2682
2944
|
}
|
|
2683
2945
|
if (result.unchanged > 0) {
|
|
2684
|
-
console.log(
|
|
2946
|
+
console.log(chalk18.dim(` ${result.unchanged} unchanged`));
|
|
2685
2947
|
}
|
|
2686
2948
|
}
|
|
2687
2949
|
if (!anyStale) {
|
|
2688
2950
|
if (target) {
|
|
2689
2951
|
} else {
|
|
2690
|
-
console.log(
|
|
2952
|
+
console.log(chalk18.green("\n All generated files are up to date."));
|
|
2691
2953
|
}
|
|
2692
2954
|
} else {
|
|
2693
|
-
console.log(
|
|
2694
|
-
Run ${
|
|
2955
|
+
console.log(chalk18.dim(`
|
|
2956
|
+
Run ${chalk18.bold("memories generate")} to update stale files.`));
|
|
2695
2957
|
}
|
|
2696
2958
|
} catch (error2) {
|
|
2697
|
-
console.error(
|
|
2959
|
+
console.error(chalk18.red("\u2717") + " Diff failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2698
2960
|
process.exit(1);
|
|
2699
2961
|
}
|
|
2700
2962
|
});
|
|
@@ -2715,7 +2977,7 @@ function formatRelative(isoDate) {
|
|
|
2715
2977
|
|
|
2716
2978
|
// src/commands/tag.ts
|
|
2717
2979
|
import { Command as Command20 } from "commander";
|
|
2718
|
-
import
|
|
2980
|
+
import chalk19 from "chalk";
|
|
2719
2981
|
var VALID_TYPES10 = ["rule", "decision", "fact", "note"];
|
|
2720
2982
|
function buildWhere(filters) {
|
|
2721
2983
|
const conditions = ["deleted_at IS NULL"];
|
|
@@ -2767,12 +3029,12 @@ tagCommand.addCommand(
|
|
|
2767
3029
|
updated++;
|
|
2768
3030
|
}
|
|
2769
3031
|
if (opts.dryRun) {
|
|
2770
|
-
console.log(
|
|
3032
|
+
console.log(chalk19.dim(`Dry run \u2014 would tag ${updated} memories with "${tag}" (${skipped} already tagged)`));
|
|
2771
3033
|
} else {
|
|
2772
|
-
console.log(
|
|
3034
|
+
console.log(chalk19.green("\u2713") + ` Tagged ${updated} memories with "${tag}"` + (skipped > 0 ? chalk19.dim(` (${skipped} already tagged)`) : ""));
|
|
2773
3035
|
}
|
|
2774
3036
|
} catch (error2) {
|
|
2775
|
-
console.error(
|
|
3037
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2776
3038
|
process.exit(1);
|
|
2777
3039
|
}
|
|
2778
3040
|
})
|
|
@@ -2801,12 +3063,12 @@ tagCommand.addCommand(
|
|
|
2801
3063
|
updated++;
|
|
2802
3064
|
}
|
|
2803
3065
|
if (opts.dryRun) {
|
|
2804
|
-
console.log(
|
|
3066
|
+
console.log(chalk19.dim(`Dry run \u2014 would remove "${tag}" from ${updated} memories`));
|
|
2805
3067
|
} else {
|
|
2806
|
-
console.log(
|
|
3068
|
+
console.log(chalk19.green("\u2713") + ` Removed "${tag}" from ${updated} memories`);
|
|
2807
3069
|
}
|
|
2808
3070
|
} catch (error2) {
|
|
2809
|
-
console.error(
|
|
3071
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2810
3072
|
process.exit(1);
|
|
2811
3073
|
}
|
|
2812
3074
|
})
|
|
@@ -2827,15 +3089,15 @@ tagCommand.addCommand(
|
|
|
2827
3089
|
}
|
|
2828
3090
|
}
|
|
2829
3091
|
if (counts.size === 0) {
|
|
2830
|
-
console.log(
|
|
3092
|
+
console.log(chalk19.dim("No tags found."));
|
|
2831
3093
|
return;
|
|
2832
3094
|
}
|
|
2833
3095
|
const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
2834
3096
|
for (const [tag, count] of sorted) {
|
|
2835
|
-
console.log(` ${
|
|
3097
|
+
console.log(` ${chalk19.bold(tag)} ${chalk19.dim(`(${count})`)}`);
|
|
2836
3098
|
}
|
|
2837
3099
|
} catch (error2) {
|
|
2838
|
-
console.error(
|
|
3100
|
+
console.error(chalk19.red("\u2717") + ` Failed: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2839
3101
|
process.exit(1);
|
|
2840
3102
|
}
|
|
2841
3103
|
})
|
|
@@ -2843,7 +3105,7 @@ tagCommand.addCommand(
|
|
|
2843
3105
|
|
|
2844
3106
|
// src/commands/validate.ts
|
|
2845
3107
|
import { Command as Command21 } from "commander";
|
|
2846
|
-
import
|
|
3108
|
+
import chalk20 from "chalk";
|
|
2847
3109
|
function levenshtein(a, b) {
|
|
2848
3110
|
const m = a.length, n = b.length;
|
|
2849
3111
|
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
@@ -2904,10 +3166,10 @@ var validateCommand = new Command21("validate").description("Check for conflicti
|
|
|
2904
3166
|
const projectId = getProjectId() ?? void 0;
|
|
2905
3167
|
const rules = await getRules({ projectId });
|
|
2906
3168
|
if (rules.length === 0) {
|
|
2907
|
-
console.log(
|
|
3169
|
+
console.log(chalk20.dim("No rules to validate."));
|
|
2908
3170
|
return;
|
|
2909
3171
|
}
|
|
2910
|
-
console.log(
|
|
3172
|
+
console.log(chalk20.bold("\u{1F50D} Validating rules...\n"));
|
|
2911
3173
|
const issues = [];
|
|
2912
3174
|
for (let i = 0; i < rules.length; i++) {
|
|
2913
3175
|
for (let j = i + 1; j < rules.length; j++) {
|
|
@@ -2939,41 +3201,41 @@ var validateCommand = new Command21("validate").description("Check for conflicti
|
|
|
2939
3201
|
}
|
|
2940
3202
|
}
|
|
2941
3203
|
if (issues.length === 0) {
|
|
2942
|
-
console.log(
|
|
3204
|
+
console.log(chalk20.green("\u2713") + ` ${rules.length} rules validated, no issues found.`);
|
|
2943
3205
|
return;
|
|
2944
3206
|
}
|
|
2945
|
-
console.log(
|
|
3207
|
+
console.log(chalk20.yellow("\u26A0\uFE0F Potential Issues Found:\n"));
|
|
2946
3208
|
let num = 1;
|
|
2947
3209
|
for (const issue of issues) {
|
|
2948
3210
|
const typeLabel = issue.type === "duplicate" ? "Exact duplicate" : issue.type === "near-duplicate" ? "Near duplicate" : "Potential conflict";
|
|
2949
|
-
const color = issue.type === "conflict" ?
|
|
3211
|
+
const color = issue.type === "conflict" ? chalk20.red : chalk20.yellow;
|
|
2950
3212
|
console.log(color(`${num}. ${typeLabel}${issue.detail ? ` (${issue.detail})` : ""}:`));
|
|
2951
3213
|
console.log(` \u{1F4CC} "${issue.memory1.content}"`);
|
|
2952
3214
|
console.log(` \u{1F4CC} "${issue.memory2.content}"`);
|
|
2953
3215
|
if (issue.type === "duplicate" || issue.type === "near-duplicate") {
|
|
2954
|
-
console.log(
|
|
3216
|
+
console.log(chalk20.dim(" \u2192 Consider merging these rules\n"));
|
|
2955
3217
|
} else {
|
|
2956
|
-
console.log(
|
|
3218
|
+
console.log(chalk20.dim(" \u2192 These rules may contradict each other\n"));
|
|
2957
3219
|
}
|
|
2958
3220
|
num++;
|
|
2959
3221
|
}
|
|
2960
3222
|
const duplicates = issues.filter((i) => i.type === "duplicate" || i.type === "near-duplicate").length;
|
|
2961
3223
|
const conflicts = issues.filter((i) => i.type === "conflict").length;
|
|
2962
|
-
console.log(
|
|
2963
|
-
if (duplicates > 0) console.log(
|
|
2964
|
-
if (conflicts > 0) console.log(
|
|
3224
|
+
console.log(chalk20.bold(`${rules.length} rules validated, ${issues.length} issues found`));
|
|
3225
|
+
if (duplicates > 0) console.log(chalk20.dim(` ${duplicates} duplicate(s)`));
|
|
3226
|
+
if (conflicts > 0) console.log(chalk20.dim(` ${conflicts} conflict(s)`));
|
|
2965
3227
|
if (opts.fix) {
|
|
2966
|
-
console.log(
|
|
3228
|
+
console.log(chalk20.dim("\n--fix mode not yet implemented. Review issues above manually."));
|
|
2967
3229
|
}
|
|
2968
3230
|
} catch (error2) {
|
|
2969
|
-
console.error(
|
|
3231
|
+
console.error(chalk20.red("\u2717") + " Validation failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
2970
3232
|
process.exit(1);
|
|
2971
3233
|
}
|
|
2972
3234
|
});
|
|
2973
3235
|
|
|
2974
3236
|
// src/commands/stale.ts
|
|
2975
3237
|
import { Command as Command22 } from "commander";
|
|
2976
|
-
import
|
|
3238
|
+
import chalk21 from "chalk";
|
|
2977
3239
|
import { createInterface as createInterface2 } from "readline";
|
|
2978
3240
|
var TYPE_ICONS4 = {
|
|
2979
3241
|
rule: "\u{1F4CC}",
|
|
@@ -2996,7 +3258,7 @@ var staleCommand = new Command22("stale").description("Find memories that haven'
|
|
|
2996
3258
|
const days = parseInt(opts.days, 10);
|
|
2997
3259
|
const projectId = getProjectId() ?? void 0;
|
|
2998
3260
|
if (isNaN(days) || days <= 0) {
|
|
2999
|
-
console.error(
|
|
3261
|
+
console.error(chalk21.red("\u2717") + " --days must be a positive number");
|
|
3000
3262
|
process.exit(1);
|
|
3001
3263
|
}
|
|
3002
3264
|
let sql = `
|
|
@@ -3019,25 +3281,25 @@ var staleCommand = new Command22("stale").description("Find memories that haven'
|
|
|
3019
3281
|
return;
|
|
3020
3282
|
}
|
|
3021
3283
|
if (stale.length === 0) {
|
|
3022
|
-
console.log(
|
|
3284
|
+
console.log(chalk21.green("\u2713") + ` No memories older than ${days} days.`);
|
|
3023
3285
|
return;
|
|
3024
3286
|
}
|
|
3025
|
-
console.log(
|
|
3287
|
+
console.log(chalk21.bold(`\u23F0 Stale Memories (not updated in ${days}+ days)
|
|
3026
3288
|
`));
|
|
3027
3289
|
for (const m of stale.slice(0, 30)) {
|
|
3028
3290
|
const icon = TYPE_ICONS4[m.type] || "\u{1F4DD}";
|
|
3029
|
-
const scope = m.scope === "global" ?
|
|
3291
|
+
const scope = m.scope === "global" ? chalk21.dim("G") : chalk21.dim("P");
|
|
3030
3292
|
const preview = m.content.length > 50 ? m.content.slice(0, 47) + "..." : m.content;
|
|
3031
|
-
console.log(` ${icon} ${scope} ${
|
|
3293
|
+
console.log(` ${icon} ${scope} ${chalk21.dim(m.id)} ${preview} ${chalk21.yellow(`${m.days_old}d`)}`);
|
|
3032
3294
|
}
|
|
3033
3295
|
if (stale.length > 30) {
|
|
3034
|
-
console.log(
|
|
3296
|
+
console.log(chalk21.dim(` ... and ${stale.length - 30} more`));
|
|
3035
3297
|
}
|
|
3036
3298
|
console.log("");
|
|
3037
3299
|
console.log(`${stale.length} stale ${stale.length === 1 ? "memory" : "memories"} found`);
|
|
3038
|
-
console.log(
|
|
3300
|
+
console.log(chalk21.dim("Run 'memories review' to clean up interactively"));
|
|
3039
3301
|
} catch (error2) {
|
|
3040
|
-
console.error(
|
|
3302
|
+
console.error(chalk21.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3041
3303
|
process.exit(1);
|
|
3042
3304
|
}
|
|
3043
3305
|
});
|
|
@@ -3058,18 +3320,18 @@ var reviewCommand = new Command22("review").description("Interactively review an
|
|
|
3058
3320
|
});
|
|
3059
3321
|
const stale = result.rows;
|
|
3060
3322
|
if (stale.length === 0) {
|
|
3061
|
-
console.log(
|
|
3323
|
+
console.log(chalk21.green("\u2713") + ` No stale memories to review.`);
|
|
3062
3324
|
return;
|
|
3063
3325
|
}
|
|
3064
|
-
console.log(
|
|
3326
|
+
console.log(chalk21.bold(`
|
|
3065
3327
|
Reviewing ${stale.length} stale memories...
|
|
3066
3328
|
`));
|
|
3067
3329
|
let kept = 0, deleted = 0, skipped = 0;
|
|
3068
3330
|
for (const m of stale) {
|
|
3069
3331
|
const icon = TYPE_ICONS4[m.type] || "\u{1F4DD}";
|
|
3070
|
-
console.log(`${icon} ${
|
|
3332
|
+
console.log(`${icon} ${chalk21.bold(m.type.toUpperCase())} (${m.scope})`);
|
|
3071
3333
|
console.log(` "${m.content}"`);
|
|
3072
|
-
console.log(
|
|
3334
|
+
console.log(chalk21.dim(` Last updated: ${m.days_old} days ago`));
|
|
3073
3335
|
console.log("");
|
|
3074
3336
|
const answer = await prompt(" [k]eep [d]elete [s]kip [q]uit > ");
|
|
3075
3337
|
if (answer === "q") {
|
|
@@ -3077,31 +3339,31 @@ Reviewing ${stale.length} stale memories...
|
|
|
3077
3339
|
break;
|
|
3078
3340
|
} else if (answer === "d") {
|
|
3079
3341
|
await forgetMemory(m.id);
|
|
3080
|
-
console.log(
|
|
3342
|
+
console.log(chalk21.green(" \u2713 Deleted\n"));
|
|
3081
3343
|
deleted++;
|
|
3082
3344
|
} else if (answer === "k") {
|
|
3083
3345
|
await db.execute({
|
|
3084
3346
|
sql: "UPDATE memories SET updated_at = datetime('now') WHERE id = ?",
|
|
3085
3347
|
args: [m.id]
|
|
3086
3348
|
});
|
|
3087
|
-
console.log(
|
|
3349
|
+
console.log(chalk21.green(" \u2713 Kept (marked as reviewed)\n"));
|
|
3088
3350
|
kept++;
|
|
3089
3351
|
} else {
|
|
3090
|
-
console.log(
|
|
3352
|
+
console.log(chalk21.dim(" Skipped\n"));
|
|
3091
3353
|
skipped++;
|
|
3092
3354
|
}
|
|
3093
3355
|
}
|
|
3094
|
-
console.log(
|
|
3356
|
+
console.log(chalk21.bold("\nReview Summary:"));
|
|
3095
3357
|
console.log(` Kept: ${kept}, Deleted: ${deleted}, Skipped: ${skipped}`);
|
|
3096
3358
|
} catch (error2) {
|
|
3097
|
-
console.error(
|
|
3359
|
+
console.error(chalk21.red("\u2717") + " Review failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3098
3360
|
process.exit(1);
|
|
3099
3361
|
}
|
|
3100
3362
|
});
|
|
3101
3363
|
|
|
3102
3364
|
// src/commands/link.ts
|
|
3103
3365
|
import { Command as Command23 } from "commander";
|
|
3104
|
-
import
|
|
3366
|
+
import chalk22 from "chalk";
|
|
3105
3367
|
import { nanoid as nanoid3 } from "nanoid";
|
|
3106
3368
|
var LINK_TYPES = ["related", "supports", "supersedes", "contradicts"];
|
|
3107
3369
|
var TYPE_ICONS5 = {
|
|
@@ -3167,17 +3429,17 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3167
3429
|
await ensureLinksTable();
|
|
3168
3430
|
const db = await getDb();
|
|
3169
3431
|
if (!LINK_TYPES.includes(opts.type)) {
|
|
3170
|
-
console.error(
|
|
3432
|
+
console.error(chalk22.red("\u2717") + ` Invalid link type. Valid: ${LINK_TYPES.join(", ")}`);
|
|
3171
3433
|
process.exit(1);
|
|
3172
3434
|
}
|
|
3173
3435
|
const m1 = await getMemoryById(id1);
|
|
3174
3436
|
const m2 = await getMemoryById(id2);
|
|
3175
3437
|
if (!m1) {
|
|
3176
|
-
console.error(
|
|
3438
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id1} not found`);
|
|
3177
3439
|
process.exit(1);
|
|
3178
3440
|
}
|
|
3179
3441
|
if (!m2) {
|
|
3180
|
-
console.error(
|
|
3442
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id2} not found`);
|
|
3181
3443
|
process.exit(1);
|
|
3182
3444
|
}
|
|
3183
3445
|
const existing = await db.execute({
|
|
@@ -3186,7 +3448,7 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3186
3448
|
args: [id1, id2, id2, id1]
|
|
3187
3449
|
});
|
|
3188
3450
|
if (existing.rows.length > 0) {
|
|
3189
|
-
console.log(
|
|
3451
|
+
console.log(chalk22.yellow("!") + " These memories are already linked");
|
|
3190
3452
|
return;
|
|
3191
3453
|
}
|
|
3192
3454
|
const linkId = nanoid3(12);
|
|
@@ -3196,12 +3458,12 @@ var linkCommand = new Command23("link").description("Link two related memories t
|
|
|
3196
3458
|
});
|
|
3197
3459
|
const icon1 = TYPE_ICONS5[m1.type] || "\u{1F4DD}";
|
|
3198
3460
|
const icon2 = TYPE_ICONS5[m2.type] || "\u{1F4DD}";
|
|
3199
|
-
console.log(
|
|
3200
|
-
console.log(` ${icon1} ${
|
|
3201
|
-
console.log(` \u2193 ${
|
|
3202
|
-
console.log(` ${icon2} ${
|
|
3461
|
+
console.log(chalk22.green("\u2713") + " Linked memories:");
|
|
3462
|
+
console.log(` ${icon1} ${chalk22.dim(id1)} "${m1.content.slice(0, 40)}..."`);
|
|
3463
|
+
console.log(` \u2193 ${chalk22.cyan(opts.type)}`);
|
|
3464
|
+
console.log(` ${icon2} ${chalk22.dim(id2)} "${m2.content.slice(0, 40)}..."`);
|
|
3203
3465
|
} catch (error2) {
|
|
3204
|
-
console.error(
|
|
3466
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3205
3467
|
process.exit(1);
|
|
3206
3468
|
}
|
|
3207
3469
|
});
|
|
@@ -3215,12 +3477,12 @@ var unlinkCommand = new Command23("unlink").description("Remove link between two
|
|
|
3215
3477
|
args: [id1, id2, id2, id1]
|
|
3216
3478
|
});
|
|
3217
3479
|
if (result.rowsAffected === 0) {
|
|
3218
|
-
console.log(
|
|
3480
|
+
console.log(chalk22.yellow("!") + " No link found between these memories");
|
|
3219
3481
|
} else {
|
|
3220
|
-
console.log(
|
|
3482
|
+
console.log(chalk22.green("\u2713") + ` Unlinked ${id1} and ${id2}`);
|
|
3221
3483
|
}
|
|
3222
3484
|
} catch (error2) {
|
|
3223
|
-
console.error(
|
|
3485
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3224
3486
|
process.exit(1);
|
|
3225
3487
|
}
|
|
3226
3488
|
});
|
|
@@ -3228,16 +3490,16 @@ var showCommand = new Command23("show").description("Show a memory with its link
|
|
|
3228
3490
|
try {
|
|
3229
3491
|
const memory = await getMemoryById(id);
|
|
3230
3492
|
if (!memory) {
|
|
3231
|
-
console.error(
|
|
3493
|
+
console.error(chalk22.red("\u2717") + ` Memory ${id} not found`);
|
|
3232
3494
|
process.exit(1);
|
|
3233
3495
|
}
|
|
3234
3496
|
const icon = TYPE_ICONS5[memory.type] || "\u{1F4DD}";
|
|
3235
3497
|
const scope = memory.scope === "global" ? "Global" : "Project";
|
|
3236
3498
|
console.log("");
|
|
3237
|
-
console.log(`${icon} ${
|
|
3238
|
-
console.log(
|
|
3239
|
-
console.log(
|
|
3240
|
-
if (memory.tags) console.log(
|
|
3499
|
+
console.log(`${icon} ${chalk22.bold(memory.type.toUpperCase())} (${scope})`);
|
|
3500
|
+
console.log(chalk22.dim(`ID: ${memory.id}`));
|
|
3501
|
+
console.log(chalk22.dim(`Created: ${memory.created_at}`));
|
|
3502
|
+
if (memory.tags) console.log(chalk22.dim(`Tags: ${memory.tags}`));
|
|
3241
3503
|
console.log("");
|
|
3242
3504
|
console.log(memory.content);
|
|
3243
3505
|
if (opts.links) {
|
|
@@ -3245,52 +3507,52 @@ var showCommand = new Command23("show").description("Show a memory with its link
|
|
|
3245
3507
|
const linked = await getLinkedMemories(id);
|
|
3246
3508
|
if (linked.length > 0) {
|
|
3247
3509
|
console.log("");
|
|
3248
|
-
console.log(
|
|
3510
|
+
console.log(chalk22.bold("Linked Memories:"));
|
|
3249
3511
|
for (const { memory: m, linkType, direction } of linked) {
|
|
3250
3512
|
const mIcon = TYPE_ICONS5[m.type] || "\u{1F4DD}";
|
|
3251
3513
|
const arrow = direction === "to" ? "\u2192" : "\u2190";
|
|
3252
3514
|
const preview = m.content.length > 50 ? m.content.slice(0, 47) + "..." : m.content;
|
|
3253
|
-
console.log(` ${arrow} ${
|
|
3515
|
+
console.log(` ${arrow} ${chalk22.cyan(linkType)}: ${mIcon} ${preview}`);
|
|
3254
3516
|
}
|
|
3255
3517
|
}
|
|
3256
3518
|
}
|
|
3257
3519
|
console.log("");
|
|
3258
3520
|
} catch (error2) {
|
|
3259
|
-
console.error(
|
|
3521
|
+
console.error(chalk22.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3260
3522
|
process.exit(1);
|
|
3261
3523
|
}
|
|
3262
3524
|
});
|
|
3263
3525
|
|
|
3264
3526
|
// src/commands/template.ts
|
|
3265
3527
|
import { Command as Command24 } from "commander";
|
|
3266
|
-
import
|
|
3528
|
+
import chalk23 from "chalk";
|
|
3267
3529
|
var templateCommand = new Command24("template").description("Manage and use memory templates");
|
|
3268
3530
|
templateCommand.command("list").description("List available templates").action(() => {
|
|
3269
3531
|
const templates = listTemplates();
|
|
3270
|
-
console.log(
|
|
3532
|
+
console.log(chalk23.bold("\nAvailable Templates:\n"));
|
|
3271
3533
|
for (const t of templates) {
|
|
3272
3534
|
const typeIcon = t.type === "rule" ? "\u{1F4CC}" : t.type === "decision" ? "\u{1F4A1}" : t.type === "fact" ? "\u{1F4CB}" : "\u{1F4DD}";
|
|
3273
|
-
console.log(` ${
|
|
3535
|
+
console.log(` ${chalk23.cyan(t.name.padEnd(15))} ${typeIcon} ${t.description}`);
|
|
3274
3536
|
}
|
|
3275
3537
|
console.log("");
|
|
3276
|
-
console.log(
|
|
3538
|
+
console.log(chalk23.dim("Use: memories template use <name>"));
|
|
3277
3539
|
console.log("");
|
|
3278
3540
|
});
|
|
3279
3541
|
templateCommand.command("show <name>").description("Show template details and fields").action((name) => {
|
|
3280
3542
|
const template = getTemplate(name);
|
|
3281
3543
|
if (!template) {
|
|
3282
|
-
console.error(
|
|
3283
|
-
console.log(
|
|
3544
|
+
console.error(chalk23.red("\u2717") + ` Template "${name}" not found`);
|
|
3545
|
+
console.log(chalk23.dim("Run 'memories template list' to see available templates"));
|
|
3284
3546
|
process.exit(1);
|
|
3285
3547
|
}
|
|
3286
3548
|
const typeIcon = template.type === "rule" ? "\u{1F4CC}" : template.type === "decision" ? "\u{1F4A1}" : template.type === "fact" ? "\u{1F4CB}" : "\u{1F4DD}";
|
|
3287
3549
|
console.log("");
|
|
3288
|
-
console.log(
|
|
3289
|
-
console.log(
|
|
3550
|
+
console.log(chalk23.bold(template.name) + ` ${typeIcon} ${template.type}`);
|
|
3551
|
+
console.log(chalk23.dim(template.description));
|
|
3290
3552
|
console.log("");
|
|
3291
|
-
console.log(
|
|
3553
|
+
console.log(chalk23.bold("Fields:"));
|
|
3292
3554
|
for (const field of template.fields) {
|
|
3293
|
-
const required = field.required ?
|
|
3555
|
+
const required = field.required ? chalk23.red("*") : chalk23.dim("(optional)");
|
|
3294
3556
|
console.log(` ${field.name.padEnd(15)} ${required} ${field.prompt}`);
|
|
3295
3557
|
}
|
|
3296
3558
|
console.log("");
|
|
@@ -3298,13 +3560,13 @@ templateCommand.command("show <name>").description("Show template details and fi
|
|
|
3298
3560
|
templateCommand.command("use <name>").description("Create a memory using a template").option("-g, --global", "Store as global memory").action(async (name, opts) => {
|
|
3299
3561
|
const template = getTemplate(name);
|
|
3300
3562
|
if (!template) {
|
|
3301
|
-
console.error(
|
|
3302
|
-
console.log(
|
|
3563
|
+
console.error(chalk23.red("\u2717") + ` Template "${name}" not found`);
|
|
3564
|
+
console.log(chalk23.dim("Run 'memories template list' to see available templates"));
|
|
3303
3565
|
process.exit(1);
|
|
3304
3566
|
}
|
|
3305
3567
|
console.log("");
|
|
3306
|
-
console.log(
|
|
3307
|
-
console.log(
|
|
3568
|
+
console.log(chalk23.bold(`Using template: ${template.name}`));
|
|
3569
|
+
console.log(chalk23.dim(template.description));
|
|
3308
3570
|
console.log("");
|
|
3309
3571
|
try {
|
|
3310
3572
|
const content = await fillTemplate(template);
|
|
@@ -3313,8 +3575,8 @@ templateCommand.command("use <name>").description("Create a memory using a templ
|
|
|
3313
3575
|
global: opts.global
|
|
3314
3576
|
});
|
|
3315
3577
|
console.log("");
|
|
3316
|
-
console.log(
|
|
3317
|
-
console.log(
|
|
3578
|
+
console.log(chalk23.green("\u2713") + ` Created ${template.type}: ${chalk23.dim(memory.id)}`);
|
|
3579
|
+
console.log(chalk23.dim(` "${content}"`));
|
|
3318
3580
|
} catch (error2) {
|
|
3319
3581
|
if (error2.message?.includes("User force closed")) {
|
|
3320
3582
|
console.log("\nCancelled.");
|
|
@@ -3326,7 +3588,7 @@ templateCommand.command("use <name>").description("Create a memory using a templ
|
|
|
3326
3588
|
|
|
3327
3589
|
// src/commands/history.ts
|
|
3328
3590
|
import { Command as Command25 } from "commander";
|
|
3329
|
-
import
|
|
3591
|
+
import chalk24 from "chalk";
|
|
3330
3592
|
var TYPE_ICONS6 = {
|
|
3331
3593
|
rule: "\u{1F4CC}",
|
|
3332
3594
|
decision: "\u{1F4A1}",
|
|
@@ -3375,38 +3637,38 @@ var historyCommand = new Command25("history").description("View version history
|
|
|
3375
3637
|
return;
|
|
3376
3638
|
}
|
|
3377
3639
|
if (!memory && history.length === 0) {
|
|
3378
|
-
console.error(
|
|
3640
|
+
console.error(chalk24.red("\u2717") + ` Memory ${id} not found`);
|
|
3379
3641
|
process.exit(1);
|
|
3380
3642
|
}
|
|
3381
3643
|
const icon = memory ? TYPE_ICONS6[memory.type] || "\u{1F4DD}" : "\u{1F4DD}";
|
|
3382
3644
|
console.log("");
|
|
3383
3645
|
if (memory) {
|
|
3384
|
-
console.log(`${icon} ${
|
|
3385
|
-
console.log(
|
|
3646
|
+
console.log(`${icon} ${chalk24.bold(memory.content)}`);
|
|
3647
|
+
console.log(chalk24.dim(`ID: ${id}`));
|
|
3386
3648
|
} else {
|
|
3387
|
-
console.log(
|
|
3649
|
+
console.log(chalk24.dim(`Memory ${id} (deleted)`));
|
|
3388
3650
|
}
|
|
3389
3651
|
console.log("");
|
|
3390
3652
|
if (history.length === 0) {
|
|
3391
|
-
console.log(
|
|
3392
|
-
console.log(
|
|
3653
|
+
console.log(chalk24.dim("No version history recorded."));
|
|
3654
|
+
console.log(chalk24.dim("History is recorded when memories are updated."));
|
|
3393
3655
|
return;
|
|
3394
3656
|
}
|
|
3395
|
-
console.log(
|
|
3396
|
-
console.log(
|
|
3657
|
+
console.log(chalk24.bold("History:"));
|
|
3658
|
+
console.log(chalk24.dim("\u2500".repeat(60)));
|
|
3397
3659
|
for (const entry of history) {
|
|
3398
3660
|
const changeIcon = entry.change_type === "created" ? "+" : entry.change_type === "updated" ? "~" : "-";
|
|
3399
|
-
const changeColor = entry.change_type === "created" ?
|
|
3661
|
+
const changeColor = entry.change_type === "created" ? chalk24.green : entry.change_type === "updated" ? chalk24.yellow : chalk24.red;
|
|
3400
3662
|
const date = new Date(entry.changed_at).toLocaleDateString();
|
|
3401
3663
|
const time = new Date(entry.changed_at).toLocaleTimeString();
|
|
3402
|
-
console.log(` ${changeColor(changeIcon)} v${entry.version} ${
|
|
3664
|
+
console.log(` ${changeColor(changeIcon)} v${entry.version} ${chalk24.dim(date + " " + time)}`);
|
|
3403
3665
|
console.log(` "${entry.content.slice(0, 60)}${entry.content.length > 60 ? "..." : ""}"`);
|
|
3404
3666
|
}
|
|
3405
|
-
console.log(
|
|
3406
|
-
console.log(
|
|
3667
|
+
console.log(chalk24.dim("\u2500".repeat(60)));
|
|
3668
|
+
console.log(chalk24.dim(`
|
|
3407
3669
|
Use 'memories revert ${id} --to <version>' to restore a previous version`));
|
|
3408
3670
|
} catch (error2) {
|
|
3409
|
-
console.error(
|
|
3671
|
+
console.error(chalk24.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3410
3672
|
process.exit(1);
|
|
3411
3673
|
}
|
|
3412
3674
|
});
|
|
@@ -3416,7 +3678,7 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3416
3678
|
const db = await getDb();
|
|
3417
3679
|
const version = parseInt(opts.to.replace("v", ""), 10);
|
|
3418
3680
|
if (isNaN(version) || version < 1) {
|
|
3419
|
-
console.error(
|
|
3681
|
+
console.error(chalk24.red("\u2717") + " Invalid version number");
|
|
3420
3682
|
process.exit(1);
|
|
3421
3683
|
}
|
|
3422
3684
|
const result = await db.execute({
|
|
@@ -3428,7 +3690,7 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3428
3690
|
const history = result.rows;
|
|
3429
3691
|
const targetEntry = history.find((h) => h.version === version);
|
|
3430
3692
|
if (!targetEntry) {
|
|
3431
|
-
console.error(
|
|
3693
|
+
console.error(chalk24.red("\u2717") + ` Version ${version} not found for memory ${id}`);
|
|
3432
3694
|
process.exit(1);
|
|
3433
3695
|
}
|
|
3434
3696
|
const current = await getMemoryById(id);
|
|
@@ -3440,20 +3702,20 @@ var revertCommand = new Command25("revert").description("Revert a memory to a pr
|
|
|
3440
3702
|
tags: targetEntry.tags ? targetEntry.tags.split(",") : void 0
|
|
3441
3703
|
});
|
|
3442
3704
|
if (!updated) {
|
|
3443
|
-
console.error(
|
|
3705
|
+
console.error(chalk24.red("\u2717") + ` Failed to revert memory ${id}`);
|
|
3444
3706
|
process.exit(1);
|
|
3445
3707
|
}
|
|
3446
|
-
console.log(
|
|
3447
|
-
console.log(
|
|
3708
|
+
console.log(chalk24.green("\u2713") + ` Reverted memory ${chalk24.dim(id)} to version ${version}`);
|
|
3709
|
+
console.log(chalk24.dim(` "${targetEntry.content.slice(0, 60)}${targetEntry.content.length > 60 ? "..." : ""}"`));
|
|
3448
3710
|
} catch (error2) {
|
|
3449
|
-
console.error(
|
|
3711
|
+
console.error(chalk24.red("\u2717") + " Failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3450
3712
|
process.exit(1);
|
|
3451
3713
|
}
|
|
3452
3714
|
});
|
|
3453
3715
|
|
|
3454
3716
|
// src/commands/embed.ts
|
|
3455
3717
|
import { Command as Command26 } from "commander";
|
|
3456
|
-
import
|
|
3718
|
+
import chalk25 from "chalk";
|
|
3457
3719
|
import ora2 from "ora";
|
|
3458
3720
|
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
3721
|
try {
|
|
@@ -3466,24 +3728,24 @@ var embedCommand = new Command26("embed").description("Generate embeddings for m
|
|
|
3466
3728
|
const result = await db.execute(sql);
|
|
3467
3729
|
const memories = result.rows;
|
|
3468
3730
|
if (memories.length === 0) {
|
|
3469
|
-
console.log(
|
|
3731
|
+
console.log(chalk25.green("\u2713") + " All memories already have embeddings.");
|
|
3470
3732
|
return;
|
|
3471
3733
|
}
|
|
3472
3734
|
if (opts.dryRun) {
|
|
3473
|
-
console.log(
|
|
3735
|
+
console.log(chalk25.bold(`Would embed ${memories.length} memories:
|
|
3474
3736
|
`));
|
|
3475
3737
|
for (const m of memories.slice(0, 10)) {
|
|
3476
3738
|
const preview = m.content.length > 60 ? m.content.slice(0, 57) + "..." : m.content;
|
|
3477
|
-
console.log(` ${
|
|
3739
|
+
console.log(` ${chalk25.dim(m.id)} ${preview}`);
|
|
3478
3740
|
}
|
|
3479
3741
|
if (memories.length > 10) {
|
|
3480
|
-
console.log(
|
|
3742
|
+
console.log(chalk25.dim(` ... and ${memories.length - 10} more`));
|
|
3481
3743
|
}
|
|
3482
3744
|
return;
|
|
3483
3745
|
}
|
|
3484
|
-
console.log(
|
|
3746
|
+
console.log(chalk25.bold(`Embedding ${memories.length} memories...
|
|
3485
3747
|
`));
|
|
3486
|
-
console.log(
|
|
3748
|
+
console.log(chalk25.dim("First run downloads the model (~30MB). Subsequent runs are faster.\n"));
|
|
3487
3749
|
const spinner = ora2("Loading embedding model...").start();
|
|
3488
3750
|
let embedded = 0;
|
|
3489
3751
|
let failed = 0;
|
|
@@ -3498,22 +3760,22 @@ var embedCommand = new Command26("embed").description("Generate embeddings for m
|
|
|
3498
3760
|
}
|
|
3499
3761
|
}
|
|
3500
3762
|
spinner.stop();
|
|
3501
|
-
console.log(
|
|
3763
|
+
console.log(chalk25.green("\u2713") + ` Embedded ${embedded} memories`);
|
|
3502
3764
|
if (failed > 0) {
|
|
3503
|
-
console.log(
|
|
3765
|
+
console.log(chalk25.yellow("\u26A0") + ` ${failed} memories failed to embed`);
|
|
3504
3766
|
}
|
|
3505
3767
|
console.log("");
|
|
3506
|
-
console.log(
|
|
3507
|
-
console.log(
|
|
3768
|
+
console.log(chalk25.dim("Now you can use semantic search:"));
|
|
3769
|
+
console.log(chalk25.cyan(' memories search --semantic "your query"'));
|
|
3508
3770
|
} catch (error2) {
|
|
3509
|
-
console.error(
|
|
3771
|
+
console.error(chalk25.red("\u2717") + " Embedding failed:", error2 instanceof Error ? error2.message : "Unknown error");
|
|
3510
3772
|
process.exit(1);
|
|
3511
3773
|
}
|
|
3512
3774
|
});
|
|
3513
3775
|
|
|
3514
3776
|
// src/commands/login.ts
|
|
3515
3777
|
import { Command as Command27 } from "commander";
|
|
3516
|
-
import
|
|
3778
|
+
import chalk26 from "chalk";
|
|
3517
3779
|
import ora3 from "ora";
|
|
3518
3780
|
import { randomBytes } from "crypto";
|
|
3519
3781
|
import { execFile } from "child_process";
|
|
@@ -3528,18 +3790,18 @@ var loginCommand = new Command27("login").description("Log in to memories.sh to
|
|
|
3528
3790
|
banner();
|
|
3529
3791
|
const existing = await readAuth();
|
|
3530
3792
|
if (existing) {
|
|
3531
|
-
warn(`Already logged in as ${
|
|
3532
|
-
dim(`Run ${
|
|
3793
|
+
warn(`Already logged in as ${chalk26.bold(existing.email)}`);
|
|
3794
|
+
dim(`Run ${chalk26.cyan("memories logout")} to sign out first.`);
|
|
3533
3795
|
return;
|
|
3534
3796
|
}
|
|
3535
3797
|
box(
|
|
3536
|
-
|
|
3798
|
+
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
3799
|
"Upgrade to Pro"
|
|
3538
3800
|
);
|
|
3539
3801
|
const code = randomBytes(16).toString("hex");
|
|
3540
3802
|
const authUrl = `${opts.apiUrl}/app/auth/cli?code=${code}`;
|
|
3541
|
-
console.log(
|
|
3542
|
-
console.log(` ${
|
|
3803
|
+
console.log(chalk26.bold("Open this URL in your browser:\n"));
|
|
3804
|
+
console.log(` ${chalk26.cyan(authUrl)}
|
|
3543
3805
|
`);
|
|
3544
3806
|
try {
|
|
3545
3807
|
openBrowser(authUrl);
|
|
@@ -3569,11 +3831,11 @@ var loginCommand = new Command27("login").description("Log in to memories.sh to
|
|
|
3569
3831
|
});
|
|
3570
3832
|
spinner.stop();
|
|
3571
3833
|
console.log("");
|
|
3572
|
-
success(`Logged in as ${
|
|
3834
|
+
success(`Logged in as ${chalk26.bold(data.email)}`);
|
|
3573
3835
|
dim("Your cloud database has been provisioned automatically.");
|
|
3574
3836
|
nextSteps([
|
|
3575
|
-
`${
|
|
3576
|
-
`${
|
|
3837
|
+
`${chalk26.cyan("memories sync")} ${chalk26.dim("to sync your memories")}`,
|
|
3838
|
+
`${chalk26.cyan("memories.sh/app")} ${chalk26.dim("to view your dashboard")}`
|
|
3577
3839
|
]);
|
|
3578
3840
|
return;
|
|
3579
3841
|
}
|
|
@@ -3601,77 +3863,112 @@ var logoutCommand = new Command27("logout").description("Log out of memories.sh"
|
|
|
3601
3863
|
|
|
3602
3864
|
// src/commands/files.ts
|
|
3603
3865
|
import { Command as Command28 } from "commander";
|
|
3604
|
-
import
|
|
3866
|
+
import chalk27 from "chalk";
|
|
3605
3867
|
import ora4 from "ora";
|
|
3606
3868
|
import { nanoid as nanoid4 } from "nanoid";
|
|
3607
3869
|
import { createHash } from "crypto";
|
|
3608
|
-
import { readFile as
|
|
3609
|
-
import { existsSync as
|
|
3610
|
-
import { join as
|
|
3611
|
-
import { homedir as
|
|
3870
|
+
import { readFile as readFile9, writeFile as writeFile7, mkdir as mkdir5, readdir, stat } from "fs/promises";
|
|
3871
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3872
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
3873
|
+
import { homedir as homedir5 } from "os";
|
|
3612
3874
|
function hashContent(content) {
|
|
3613
3875
|
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
3614
3876
|
}
|
|
3615
|
-
var
|
|
3616
|
-
|
|
3617
|
-
{ dir: ".
|
|
3618
|
-
{ dir: ".
|
|
3619
|
-
{ dir: ".
|
|
3620
|
-
{ dir: ".
|
|
3621
|
-
|
|
3622
|
-
{ dir: ".
|
|
3623
|
-
{ dir: ".
|
|
3624
|
-
{ dir: ".
|
|
3625
|
-
{ dir: ".
|
|
3877
|
+
var SYNC_TARGETS = [
|
|
3878
|
+
// .agents - Agent instruction files, commands, tasks, and skills
|
|
3879
|
+
{ dir: ".agents", files: ["AGENTS.md"] },
|
|
3880
|
+
{ dir: ".agents/commands", pattern: /\.md$/ },
|
|
3881
|
+
{ dir: ".agents/tasks", pattern: /\.(md|txt)$/ },
|
|
3882
|
+
{ dir: ".agents/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3883
|
+
// .claude - Claude Code instructions, commands, rules, hooks, and tasks
|
|
3884
|
+
{ dir: ".claude", files: ["CLAUDE.md", "settings.json", "settings.local.json"] },
|
|
3885
|
+
{ dir: ".claude/commands", pattern: /\.md$/ },
|
|
3886
|
+
{ dir: ".claude/rules", pattern: /\.(md|rules)$/ },
|
|
3887
|
+
{ dir: ".claude/hooks", pattern: /\.(json|sh)$/ },
|
|
3888
|
+
{ dir: ".claude/tasks", pattern: /\.(md|txt)$/ },
|
|
3889
|
+
{ dir: ".claude/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3890
|
+
// .cursor - Cursor rules and MCP config
|
|
3891
|
+
{ dir: ".cursor", files: ["mcp.json", "rules.md"] },
|
|
3892
|
+
{ dir: ".cursor/rules", pattern: /\.(md|mdc|txt)$/ },
|
|
3893
|
+
{ dir: ".cursor/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3894
|
+
// .codex - Codex config, rules, and tasks
|
|
3895
|
+
{ dir: ".codex", files: ["config.toml", "AGENTS.md", "instructions.md"] },
|
|
3896
|
+
{ dir: ".codex/rules", pattern: /\.(md|rules)$/ },
|
|
3897
|
+
{ dir: ".codex/tasks", pattern: /\.(md|txt)$/ },
|
|
3898
|
+
{ dir: ".codex/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3899
|
+
// .windsurf - Windsurf rules
|
|
3900
|
+
{ dir: ".windsurf", files: ["rules.md", "cascade.json"] },
|
|
3901
|
+
{ dir: ".windsurf/rules", pattern: /\.(md|txt)$/ },
|
|
3902
|
+
{ dir: ".windsurf/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3903
|
+
// .cline - Cline rules
|
|
3904
|
+
{ dir: ".cline", files: ["rules.md", "CLINE.md", "cline_rules.md"] },
|
|
3905
|
+
{ dir: ".cline/rules", pattern: /\.(md|txt)$/ },
|
|
3906
|
+
{ dir: ".cline/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3907
|
+
// .github/copilot - Copilot instructions
|
|
3908
|
+
{ dir: ".github/copilot", files: ["instructions.md"] },
|
|
3909
|
+
// .gemini - Gemini instructions
|
|
3910
|
+
{ dir: ".gemini", files: ["GEMINI.md", "settings.json"] },
|
|
3911
|
+
{ dir: ".gemini/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3912
|
+
// .roo - Roo config and rules
|
|
3913
|
+
{ dir: ".roo", files: ["config.json", "rules.md"] },
|
|
3914
|
+
{ dir: ".roo/rules", pattern: /\.(md|txt)$/ },
|
|
3915
|
+
{ dir: ".roo/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3916
|
+
// .amp - Amp rules
|
|
3917
|
+
{ dir: ".amp", files: ["AGENTS.md", "rules.md"] },
|
|
3918
|
+
{ dir: ".amp/rules", pattern: /\.(md|txt)$/ },
|
|
3919
|
+
{ dir: ".amp/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3920
|
+
// .opencode - OpenCode instructions
|
|
3921
|
+
{ dir: ".opencode", files: ["instructions.md"] },
|
|
3922
|
+
{ dir: ".opencode/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true },
|
|
3923
|
+
// .factory - Factory/Droid config
|
|
3924
|
+
{ dir: ".factory", files: ["config.json", "instructions.md"] },
|
|
3925
|
+
{ dir: ".factory/droids", pattern: /\.(md|yaml|yml)$/ },
|
|
3926
|
+
{ dir: ".factory/tasks", pattern: /\.(md|txt)$/ },
|
|
3927
|
+
{ dir: ".factory/skills", pattern: /\.(md|json|yaml|yml|toml|txt)$/, recurse: true }
|
|
3626
3928
|
];
|
|
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;
|
|
3929
|
+
async function scanTarget(baseDir, target, relativeTo = "") {
|
|
3930
|
+
const results = [];
|
|
3931
|
+
const targetDir = join9(baseDir, target.dir);
|
|
3932
|
+
if (!existsSync10(targetDir)) return results;
|
|
3933
|
+
const source = target.dir.split("/")[0].replace(/^\./, "").replace(/^(.)/, (_, c) => c.toUpperCase());
|
|
3934
|
+
if (target.files) {
|
|
3935
|
+
for (const file of target.files) {
|
|
3936
|
+
const fullPath = join9(targetDir, file);
|
|
3937
|
+
if (existsSync10(fullPath)) {
|
|
3938
|
+
const stats = await stat(fullPath);
|
|
3939
|
+
if (stats.isFile()) {
|
|
3940
|
+
results.push({
|
|
3941
|
+
path: join9(target.dir, file),
|
|
3942
|
+
fullPath,
|
|
3943
|
+
source
|
|
3944
|
+
});
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
return results;
|
|
3657
3949
|
}
|
|
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 });
|
|
3950
|
+
if (!target.pattern) return results;
|
|
3951
|
+
const entries = await readdir(targetDir, { withFileTypes: true });
|
|
3664
3952
|
for (const entry of entries) {
|
|
3665
|
-
const fullPath =
|
|
3666
|
-
const relativePath =
|
|
3667
|
-
if (entry.isDirectory()) {
|
|
3668
|
-
const
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3953
|
+
const fullPath = join9(targetDir, entry.name);
|
|
3954
|
+
const relativePath = join9(target.dir, entry.name);
|
|
3955
|
+
if (entry.isDirectory() && target.recurse) {
|
|
3956
|
+
const subTarget = { dir: relativePath, pattern: target.pattern, recurse: true };
|
|
3957
|
+
const subResults = await scanTarget(baseDir, subTarget, relativeTo);
|
|
3958
|
+
results.push(...subResults);
|
|
3959
|
+
} else if (entry.isFile() && target.pattern.test(entry.name)) {
|
|
3960
|
+
results.push({ path: relativePath, fullPath, source });
|
|
3672
3961
|
}
|
|
3673
3962
|
}
|
|
3674
|
-
return
|
|
3963
|
+
return results;
|
|
3964
|
+
}
|
|
3965
|
+
async function scanAllTargets(baseDir) {
|
|
3966
|
+
const results = [];
|
|
3967
|
+
for (const target of SYNC_TARGETS) {
|
|
3968
|
+
const targetResults = await scanTarget(baseDir, target);
|
|
3969
|
+
results.push(...targetResults);
|
|
3970
|
+
}
|
|
3971
|
+
return results;
|
|
3675
3972
|
}
|
|
3676
3973
|
var filesCommand = new Command28("files").description("Manage synced config files (.agents, .cursor, .claude, etc.)");
|
|
3677
3974
|
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 +3983,8 @@ filesCommand.command("list").alias("ls").description("List synced files").option
|
|
|
3686
3983
|
const result = await db.execute({ sql, args });
|
|
3687
3984
|
const files = result.rows;
|
|
3688
3985
|
if (files.length === 0) {
|
|
3689
|
-
console.log(
|
|
3690
|
-
console.log(
|
|
3986
|
+
console.log(chalk27.dim("No synced files yet."));
|
|
3987
|
+
console.log(chalk27.dim(`Run ${chalk27.cyan("memories files ingest")} to import config files.`));
|
|
3691
3988
|
return;
|
|
3692
3989
|
}
|
|
3693
3990
|
const byScope = /* @__PURE__ */ new Map();
|
|
@@ -3697,61 +3994,51 @@ filesCommand.command("list").alias("ls").description("List synced files").option
|
|
|
3697
3994
|
byScope.get(scope).push(file);
|
|
3698
3995
|
}
|
|
3699
3996
|
for (const [scope, scopeFiles] of byScope) {
|
|
3700
|
-
const scopeLabel = scope === "global" ?
|
|
3997
|
+
const scopeLabel = scope === "global" ? chalk27.blue("Global") : chalk27.yellow(scope.replace("github.com/", ""));
|
|
3701
3998
|
console.log(`
|
|
3702
|
-
${scopeLabel} ${
|
|
3703
|
-
console.log(
|
|
3999
|
+
${scopeLabel} ${chalk27.dim(`(${scopeFiles.length} files)`)}`);
|
|
4000
|
+
console.log(chalk27.dim("\u2500".repeat(50)));
|
|
3704
4001
|
for (const file of scopeFiles) {
|
|
3705
|
-
const source = file.source ?
|
|
3706
|
-
console.log(` ${
|
|
4002
|
+
const source = file.source ? chalk27.dim(` [${file.source}]`) : "";
|
|
4003
|
+
console.log(` ${chalk27.white(file.path)}${source}`);
|
|
3707
4004
|
}
|
|
3708
4005
|
}
|
|
3709
4006
|
console.log();
|
|
3710
4007
|
});
|
|
3711
4008
|
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
4009
|
const db = await getDb();
|
|
3713
|
-
const home =
|
|
4010
|
+
const home = homedir5();
|
|
3714
4011
|
const cwd = process.cwd();
|
|
3715
4012
|
const filesToIngest = [];
|
|
3716
4013
|
if (opts.global !== false) {
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
fullPath: file.fullPath,
|
|
3724
|
-
scope: "global",
|
|
3725
|
-
source: name
|
|
3726
|
-
});
|
|
3727
|
-
}
|
|
4014
|
+
const files = await scanAllTargets(home);
|
|
4015
|
+
for (const file of files) {
|
|
4016
|
+
filesToIngest.push({
|
|
4017
|
+
...file,
|
|
4018
|
+
scope: "global"
|
|
4019
|
+
});
|
|
3728
4020
|
}
|
|
3729
4021
|
}
|
|
3730
4022
|
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
|
-
}
|
|
4023
|
+
const files = await scanAllTargets(cwd);
|
|
4024
|
+
for (const file of files) {
|
|
4025
|
+
filesToIngest.push({
|
|
4026
|
+
...file,
|
|
4027
|
+
scope: "project"
|
|
4028
|
+
// Will be resolved to git remote
|
|
4029
|
+
});
|
|
3743
4030
|
}
|
|
3744
4031
|
}
|
|
3745
4032
|
if (filesToIngest.length === 0) {
|
|
3746
|
-
console.log(
|
|
4033
|
+
console.log(chalk27.dim("No config files found to import."));
|
|
3747
4034
|
return;
|
|
3748
4035
|
}
|
|
3749
4036
|
if (opts.dryRun) {
|
|
3750
|
-
console.log(
|
|
4037
|
+
console.log(chalk27.bold(`Would import ${filesToIngest.length} files:
|
|
3751
4038
|
`));
|
|
3752
4039
|
for (const file of filesToIngest) {
|
|
3753
|
-
const scopeLabel = file.scope === "global" ?
|
|
3754
|
-
console.log(` ${scopeLabel} ${file.path} ${
|
|
4040
|
+
const scopeLabel = file.scope === "global" ? chalk27.blue("G") : chalk27.yellow("P");
|
|
4041
|
+
console.log(` ${scopeLabel} ${file.path} ${chalk27.dim(`[${file.source}]`)}`);
|
|
3755
4042
|
}
|
|
3756
4043
|
return;
|
|
3757
4044
|
}
|
|
@@ -3761,7 +4048,7 @@ filesCommand.command("ingest").description("Import files from .agents, .cursor,
|
|
|
3761
4048
|
let skipped = 0;
|
|
3762
4049
|
for (const file of filesToIngest) {
|
|
3763
4050
|
try {
|
|
3764
|
-
const content = await
|
|
4051
|
+
const content = await readFile9(file.fullPath, "utf-8");
|
|
3765
4052
|
const hash = hashContent(content);
|
|
3766
4053
|
const existing = await db.execute({
|
|
3767
4054
|
sql: "SELECT id, hash FROM files WHERE path = ? AND scope = ? AND deleted_at IS NULL",
|
|
@@ -3798,7 +4085,7 @@ filesCommand.command("ingest").description("Import files from .agents, .cursor,
|
|
|
3798
4085
|
});
|
|
3799
4086
|
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
4087
|
const db = await getDb();
|
|
3801
|
-
const home =
|
|
4088
|
+
const home = homedir5();
|
|
3802
4089
|
const cwd = process.cwd();
|
|
3803
4090
|
let sql = "SELECT id, path, content, scope, source FROM files WHERE deleted_at IS NULL";
|
|
3804
4091
|
const args = [];
|
|
@@ -3810,17 +4097,17 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3810
4097
|
const result = await db.execute({ sql, args });
|
|
3811
4098
|
const files = result.rows;
|
|
3812
4099
|
if (files.length === 0) {
|
|
3813
|
-
console.log(
|
|
4100
|
+
console.log(chalk27.dim("No files to apply."));
|
|
3814
4101
|
return;
|
|
3815
4102
|
}
|
|
3816
4103
|
if (opts.dryRun) {
|
|
3817
|
-
console.log(
|
|
4104
|
+
console.log(chalk27.bold(`Would write ${files.length} files:
|
|
3818
4105
|
`));
|
|
3819
4106
|
for (const file of files) {
|
|
3820
4107
|
const baseDir = file.scope === "global" ? home : cwd;
|
|
3821
|
-
const targetPath =
|
|
3822
|
-
const exists =
|
|
3823
|
-
const status = exists ?
|
|
4108
|
+
const targetPath = join9(baseDir, file.path);
|
|
4109
|
+
const exists = existsSync10(targetPath);
|
|
4110
|
+
const status = exists ? chalk27.yellow("(overwrite)") : chalk27.green("(new)");
|
|
3824
4111
|
console.log(` ${targetPath} ${status}`);
|
|
3825
4112
|
}
|
|
3826
4113
|
return;
|
|
@@ -3830,9 +4117,9 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3830
4117
|
let skippedExisting = 0;
|
|
3831
4118
|
for (const file of files) {
|
|
3832
4119
|
const baseDir = file.scope === "global" ? home : cwd;
|
|
3833
|
-
const targetPath =
|
|
3834
|
-
if (
|
|
3835
|
-
const existingContent = await
|
|
4120
|
+
const targetPath = join9(baseDir, file.path);
|
|
4121
|
+
if (existsSync10(targetPath) && !opts.force) {
|
|
4122
|
+
const existingContent = await readFile9(targetPath, "utf-8");
|
|
3836
4123
|
const existingHash = hashContent(existingContent);
|
|
3837
4124
|
const newHash = hashContent(file.content);
|
|
3838
4125
|
if (existingHash !== newHash) {
|
|
@@ -3840,8 +4127,8 @@ filesCommand.command("apply").description("Write synced files to disk (restore f
|
|
|
3840
4127
|
continue;
|
|
3841
4128
|
}
|
|
3842
4129
|
}
|
|
3843
|
-
await
|
|
3844
|
-
await
|
|
4130
|
+
await mkdir5(dirname3(targetPath), { recursive: true });
|
|
4131
|
+
await writeFile7(targetPath, file.content, "utf-8");
|
|
3845
4132
|
written++;
|
|
3846
4133
|
}
|
|
3847
4134
|
if (skippedExisting > 0) {
|
|
@@ -3857,14 +4144,14 @@ filesCommand.command("show <path>").description("Show content of a synced file")
|
|
|
3857
4144
|
args: [path]
|
|
3858
4145
|
});
|
|
3859
4146
|
if (result.rows.length === 0) {
|
|
3860
|
-
console.log(
|
|
4147
|
+
console.log(chalk27.red(`File not found: ${path}`));
|
|
3861
4148
|
return;
|
|
3862
4149
|
}
|
|
3863
4150
|
const file = result.rows[0];
|
|
3864
|
-
console.log(
|
|
3865
|
-
console.log(
|
|
3866
|
-
console.log(
|
|
3867
|
-
console.log(
|
|
4151
|
+
console.log(chalk27.dim(`# ${path}`));
|
|
4152
|
+
console.log(chalk27.dim(`# Scope: ${file.scope} | Source: ${file.source || "unknown"}`));
|
|
4153
|
+
console.log(chalk27.dim(`# Updated: ${file.updated_at}`));
|
|
4154
|
+
console.log(chalk27.dim("\u2500".repeat(50)));
|
|
3868
4155
|
console.log(file.content);
|
|
3869
4156
|
});
|
|
3870
4157
|
filesCommand.command("forget <path>").description("Remove a file from sync (soft delete)").action(async (path) => {
|
|
@@ -3874,14 +4161,14 @@ filesCommand.command("forget <path>").description("Remove a file from sync (soft
|
|
|
3874
4161
|
args: [path]
|
|
3875
4162
|
});
|
|
3876
4163
|
if (result.rowsAffected === 0) {
|
|
3877
|
-
console.log(
|
|
4164
|
+
console.log(chalk27.red(`File not found: ${path}`));
|
|
3878
4165
|
return;
|
|
3879
4166
|
}
|
|
3880
4167
|
const sync = await readSyncConfig();
|
|
3881
4168
|
if (sync) {
|
|
3882
4169
|
await syncDb();
|
|
3883
4170
|
}
|
|
3884
|
-
console.log(
|
|
4171
|
+
console.log(chalk27.green(`\u2713 Removed ${path} from sync`));
|
|
3885
4172
|
});
|
|
3886
4173
|
|
|
3887
4174
|
// src/index.ts
|