@yeseh/cortex-cli 0.6.8 → 0.6.9

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.
Files changed (74) hide show
  1. package/dist/program.js +1538 -5
  2. package/dist/program.js.map +32 -3
  3. package/dist/run.d.ts +0 -1
  4. package/dist/run.d.ts.map +1 -1
  5. package/dist/run.js +3 -4
  6. package/dist/run.js.map +3 -3
  7. package/package.json +4 -6
  8. package/dist/chunk-dsfj4baj.js +0 -1543
  9. package/dist/chunk-dsfj4baj.js.map +0 -38
  10. package/src/category/commands/create.spec.ts +0 -139
  11. package/src/category/commands/create.ts +0 -119
  12. package/src/category/index.ts +0 -24
  13. package/src/commands/init.spec.ts +0 -203
  14. package/src/commands/init.ts +0 -301
  15. package/src/context.spec.ts +0 -60
  16. package/src/context.ts +0 -170
  17. package/src/errors.spec.ts +0 -264
  18. package/src/errors.ts +0 -105
  19. package/src/memory/commands/add.spec.ts +0 -169
  20. package/src/memory/commands/add.ts +0 -158
  21. package/src/memory/commands/definitions.spec.ts +0 -80
  22. package/src/memory/commands/list.spec.ts +0 -123
  23. package/src/memory/commands/list.ts +0 -269
  24. package/src/memory/commands/move.spec.ts +0 -85
  25. package/src/memory/commands/move.ts +0 -119
  26. package/src/memory/commands/remove.spec.ts +0 -79
  27. package/src/memory/commands/remove.ts +0 -108
  28. package/src/memory/commands/show.spec.ts +0 -71
  29. package/src/memory/commands/show.ts +0 -165
  30. package/src/memory/commands/test-helpers.spec.ts +0 -127
  31. package/src/memory/commands/update.spec.ts +0 -86
  32. package/src/memory/commands/update.ts +0 -230
  33. package/src/memory/index.spec.ts +0 -59
  34. package/src/memory/index.ts +0 -44
  35. package/src/memory/parsing.spec.ts +0 -105
  36. package/src/memory/parsing.ts +0 -22
  37. package/src/observability.spec.ts +0 -126
  38. package/src/observability.ts +0 -82
  39. package/src/output.spec.ts +0 -835
  40. package/src/output.ts +0 -119
  41. package/src/program.spec.ts +0 -46
  42. package/src/program.ts +0 -75
  43. package/src/run.spec.ts +0 -31
  44. package/src/run.ts +0 -9
  45. package/src/store/commands/add.spec.ts +0 -131
  46. package/src/store/commands/add.ts +0 -231
  47. package/src/store/commands/init.spec.ts +0 -220
  48. package/src/store/commands/init.ts +0 -272
  49. package/src/store/commands/list.spec.ts +0 -175
  50. package/src/store/commands/list.ts +0 -102
  51. package/src/store/commands/prune.spec.ts +0 -120
  52. package/src/store/commands/prune.ts +0 -152
  53. package/src/store/commands/reindexs.spec.ts +0 -94
  54. package/src/store/commands/reindexs.ts +0 -97
  55. package/src/store/commands/remove.spec.ts +0 -97
  56. package/src/store/commands/remove.ts +0 -189
  57. package/src/store/index.spec.ts +0 -60
  58. package/src/store/index.ts +0 -49
  59. package/src/store/utils/resolve-store-name.spec.ts +0 -62
  60. package/src/store/utils/resolve-store-name.ts +0 -79
  61. package/src/test-helpers.spec.ts +0 -430
  62. package/src/tests/cli.integration.spec.ts +0 -1306
  63. package/src/toon.spec.ts +0 -183
  64. package/src/toon.ts +0 -462
  65. package/src/utils/git.spec.ts +0 -95
  66. package/src/utils/git.ts +0 -51
  67. package/src/utils/input.spec.ts +0 -326
  68. package/src/utils/input.ts +0 -150
  69. package/src/utils/paths.spec.ts +0 -235
  70. package/src/utils/paths.ts +0 -75
  71. package/src/utils/prompts.spec.ts +0 -23
  72. package/src/utils/prompts.ts +0 -88
  73. package/src/utils/resolve-default-store.spec.ts +0 -135
  74. package/src/utils/resolve-default-store.ts +0 -74
@@ -1,1543 +0,0 @@
1
- // @bun
2
- // src/program.ts
3
- import { Command as Command18 } from "@commander-js/extra-typings";
4
-
5
- // src/memory/index.ts
6
- import { Command as Command7 } from "@commander-js/extra-typings";
7
-
8
- // src/memory/commands/add.ts
9
- import { Command } from "@commander-js/extra-typings";
10
-
11
- // src/errors.ts
12
- import { InvalidArgumentError, CommanderError } from "@commander-js/extra-typings";
13
- var ARGUMENT_ERROR_CODES = new Set([
14
- "INVALID_PATH",
15
- "INVALID_FILE_PATH",
16
- "INVALID_SOURCE_PATH",
17
- "INVALID_DESTINATION_PATH",
18
- "INVALID_ARGUMENTS",
19
- "INVALID_STORE_NAME",
20
- "INVALID_STORE_PATH",
21
- "MISSING_CONTENT",
22
- "MULTIPLE_CONTENT_SOURCES",
23
- "CONTENT_INPUT_FAILED",
24
- "INVALID_COMMAND",
25
- "GIT_REPO_REQUIRED"
26
- ]);
27
- function throwCliError(error) {
28
- if (ARGUMENT_ERROR_CODES.has(error.code)) {
29
- throw new InvalidArgumentError(error.message);
30
- }
31
- throw new CommanderError(1, error.code, error.message);
32
- }
33
-
34
- // src/utils/input.ts
35
- import { readFile } from "fs/promises";
36
- import { err, ok } from "@yeseh/cortex-core";
37
- var readContentFromFile = async (filePath) => {
38
- if (filePath === undefined) {
39
- return ok(null);
40
- }
41
- const trimmed = filePath.trim();
42
- if (!trimmed) {
43
- return err({
44
- code: "INVALID_FILE_PATH",
45
- message: "File path must be a non-empty string."
46
- });
47
- }
48
- try {
49
- const content = await readFile(trimmed, "utf8");
50
- return ok({ content, source: "file" });
51
- } catch (error) {
52
- return err({
53
- code: "FILE_READ_FAILED",
54
- message: `Failed to read content file: ${trimmed}.`,
55
- path: trimmed,
56
- cause: error
57
- });
58
- }
59
- };
60
- var readContentFromStream = async (stream) => {
61
- const isTty = "isTTY" in stream ? Boolean(stream.isTTY) : false;
62
- if (isTty) {
63
- return ok(null);
64
- }
65
- if ("setEncoding" in stream && typeof stream.setEncoding === "function") {
66
- stream.setEncoding("utf8");
67
- }
68
- let data = "";
69
- for await (const chunk of stream) {
70
- data += String(chunk);
71
- }
72
- return ok({ content: data, source: "stdin" });
73
- };
74
- var resolveInput = async (source) => {
75
- const contentProvided = source.content !== undefined;
76
- const fileProvided = source.filePath !== undefined && source.filePath.trim() !== "";
77
- const streamRequested = source.stdinRequested === true && source.stream !== null && source.stream !== undefined && !(("isTTY" in source.stream) && Boolean(source.stream.isTTY));
78
- const requestedSources = [
79
- contentProvided,
80
- fileProvided,
81
- streamRequested
82
- ].filter(Boolean);
83
- if (requestedSources.length > 1) {
84
- return err({
85
- code: "MULTIPLE_CONTENT_SOURCES",
86
- message: "Provide either --content, --file, or --stdin, not multiple sources."
87
- });
88
- }
89
- if (contentProvided) {
90
- return ok({ content: source.content ?? "", source: "flag" });
91
- }
92
- const fileResult = await readContentFromFile(source.filePath);
93
- if (!fileResult.ok()) {
94
- return fileResult;
95
- }
96
- if (fileResult.value) {
97
- return ok(fileResult.value);
98
- }
99
- if (streamRequested) {
100
- const stdinResult = await readContentFromStream(source.stream);
101
- if (!stdinResult.ok()) {
102
- return stdinResult;
103
- }
104
- if (stdinResult.value) {
105
- return ok(stdinResult.value);
106
- }
107
- }
108
- return ok({ content: null, source: "none" });
109
- };
110
-
111
- // src/memory/parsing.ts
112
- var parseTags = (raw) => raw ? raw.flatMap((tag) => tag.split(",")).map((tag) => tag.trim()).filter(Boolean) : [];
113
- var parseExpiresAt = (raw) => {
114
- if (!raw) {
115
- return;
116
- }
117
- const parsed = new Date(raw);
118
- if (Number.isNaN(parsed.getTime())) {
119
- throwCliError({ code: "INVALID_ARGUMENTS", message: "Invalid expiration date format" });
120
- }
121
- return parsed;
122
- };
123
-
124
- // src/context.ts
125
- import {
126
- Cortex,
127
- err as err2,
128
- getDefaultSettings,
129
- ok as ok2
130
- } from "@yeseh/cortex-core";
131
- import { homedir } from "os";
132
- import { isAbsolute, resolve } from "path";
133
- import { FilesystemStorageAdapter, FilesystemConfigAdapter } from "@yeseh/cortex-storage-fs";
134
- import { stdin, stdout } from "process";
135
-
136
- // src/observability.ts
137
- var createCliLogger = () => {
138
- const debugEnabled = typeof process.env.DEBUG === "string" && process.env.DEBUG.includes("cortex");
139
- const stringifyMetaValue = (value) => {
140
- if (typeof value === "string") {
141
- return value.includes(" ") ? JSON.stringify(value) : value;
142
- }
143
- if (typeof value === "number" || typeof value === "boolean" || value === null) {
144
- return String(value);
145
- }
146
- return JSON.stringify(value);
147
- };
148
- const formatMeta = (meta) => {
149
- if (!meta || Object.keys(meta).length === 0)
150
- return "";
151
- return Object.entries(meta).map(([key, value]) => `${key}=${stringifyMetaValue(value)}`).join(" ");
152
- };
153
- const write = (level, msg, meta) => {
154
- const line = `${level.toUpperCase()}: ${msg}`;
155
- const metaText = formatMeta(meta);
156
- process.stderr.write(metaText.length > 0 ? `${line} ${metaText}
157
- ` : `${line}
158
- `);
159
- };
160
- return {
161
- debug(msg, meta) {
162
- if (debugEnabled)
163
- write("debug", msg, meta);
164
- },
165
- info(msg, meta) {
166
- write("info", msg, meta);
167
- },
168
- warn(msg, meta) {
169
- write("warn", msg, meta);
170
- },
171
- error(msg, err2, meta) {
172
- const errMeta = err2 instanceof Error ? debugEnabled ? { error: err2.message, stack: err2.stack } : { error: err2.message } : err2 !== null && err2 !== undefined ? { error: String(err2) } : {};
173
- write("error", msg, { ...meta, ...errMeta });
174
- }
175
- };
176
- };
177
-
178
- // src/context.ts
179
- var makeAbsolute = (pathStr) => {
180
- if (pathStr.startsWith("~")) {
181
- return resolve(homedir(), pathStr.slice(1).replace(/^[/\\]/, ""));
182
- }
183
- return isAbsolute(pathStr) ? pathStr : resolve(pathStr);
184
- };
185
- var createCliConfigAdapter = (configPath) => {
186
- return new FilesystemConfigAdapter(configPath);
187
- };
188
- var createCliAdapterFactory = (configAdapter) => {
189
- return (storeName) => {
190
- const stores = configAdapter.stores;
191
- const storeEntry = stores[storeName];
192
- if (!storeEntry) {
193
- const available = Object.keys(stores).join(", ") || "none";
194
- throw new Error(`Store '${storeName}' is not registered. Available stores: ${available}. ` + "Use --store to specify a registered store, or run `cortex store init` to create one.");
195
- }
196
- const storePath = storeEntry.properties?.path;
197
- if (!storePath) {
198
- throw new Error(`Store '${storeName}' has no path configured. ` + "Check your cortex config or re-run `cortex store init`.");
199
- }
200
- return new FilesystemStorageAdapter(configAdapter, {
201
- rootDirectory: storePath
202
- });
203
- };
204
- };
205
- var createCliConfigContext = async (options = {}) => {
206
- const envConfigPath = process.env.CORTEX_CONFIG;
207
- const envConfigDir = process.env.CORTEX_CONFIG_DIR;
208
- const envConfigCwd = process.env.CORTEX_CONFIG_CWD;
209
- const explicitConfigPath = typeof envConfigPath === "string" && envConfigPath.length > 0 ? makeAbsolute(envConfigPath) : undefined;
210
- const dir = options.configDir ?? envConfigDir ?? resolve(homedir(), ".config", "cortex");
211
- const absoluteDir = makeAbsolute(dir);
212
- const configPath = explicitConfigPath ?? resolve(absoluteDir, "config.yaml");
213
- const effectiveCwd = options.configCwd ?? (typeof envConfigCwd === "string" && envConfigCwd.length > 0 ? envConfigCwd : process.cwd());
214
- const configAdapter = createCliConfigAdapter(configPath);
215
- const initResult = await configAdapter.initializeConfig();
216
- if (!initResult.ok()) {
217
- return initResult;
218
- }
219
- return ok2({
220
- configAdapter,
221
- settings: configAdapter.settings,
222
- stores: configAdapter.stores,
223
- effectiveCwd
224
- });
225
- };
226
- var createCliCommandContext = async (configDir) => {
227
- try {
228
- const configContextResult = await createCliConfigContext({
229
- configDir
230
- });
231
- if (!configContextResult.ok()) {
232
- return configContextResult;
233
- }
234
- const { configAdapter, settings, stores } = configContextResult.value;
235
- const adapterFactory = createCliAdapterFactory(configAdapter);
236
- const now = () => new Date;
237
- const cortex = Cortex.init({
238
- settings,
239
- stores,
240
- adapterFactory
241
- });
242
- const logger = createCliLogger();
243
- const context = {
244
- config: configAdapter,
245
- settings: settings ?? getDefaultSettings(),
246
- stores: stores ?? {},
247
- cortex,
248
- now,
249
- stdin,
250
- stdout,
251
- logger
252
- };
253
- return ok2(context);
254
- } catch (error) {
255
- return err2({
256
- code: "CONTEXT_CREATION_FAILED",
257
- message: `Unexpected error creating CLI command context: ${error instanceof Error ? error.message : String(error)}`
258
- });
259
- }
260
- };
261
-
262
- // src/output.ts
263
- import { err as err3, ok as ok3 } from "@yeseh/cortex-core";
264
- import { serialize } from "@yeseh/cortex-core";
265
- var serializeOutput = (payload, format) => {
266
- const result = serialize(payload, format);
267
- if (result.ok()) {
268
- return ok3(result.value);
269
- }
270
- return err3({
271
- code: result.error.code === "INVALID_FORMAT" ? "INVALID_FORMAT" : "SERIALIZE_FAILED",
272
- message: result.error.message
273
- });
274
- };
275
-
276
- // src/utils/resolve-default-store.ts
277
- import { join } from "path";
278
- function resolveDefaultStore(ctx, explicit) {
279
- if (explicit)
280
- return explicit;
281
- const cwd = ctx.cwd ?? process.cwd();
282
- const stores = ctx.stores ?? {};
283
- const localPaths = [
284
- join(cwd, ".cortex"),
285
- join(cwd, ".cortex", "memory")
286
- ];
287
- for (const [
288
- name,
289
- store
290
- ] of Object.entries(stores)) {
291
- const storePath = store.properties?.path;
292
- if (storePath && localPaths.includes(storePath)) {
293
- return name;
294
- }
295
- }
296
- const defaultStore = ctx.settings?.defaultStore;
297
- if (defaultStore)
298
- return defaultStore;
299
- return "global";
300
- }
301
-
302
- // src/memory/commands/add.ts
303
- async function handleAdd(ctx, storeName, path, options) {
304
- const content = await resolveInput({
305
- content: options.content,
306
- filePath: options.file,
307
- stream: ctx.stdin,
308
- stdinRequested: options.content === undefined && options.file === undefined
309
- });
310
- if (!content.ok()) {
311
- throwCliError(content.error);
312
- }
313
- if (!content.value.content) {
314
- throwCliError({
315
- code: "MISSING_CONTENT",
316
- message: "Memory content is required via --content, --file, or stdin."
317
- });
318
- }
319
- const { content: memoryContent, source } = content.value;
320
- const tags = parseTags(options.tags);
321
- const expiresAt = parseExpiresAt(options.expiresAt);
322
- const citations = options.citations ?? [];
323
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
324
- if (!storeResult.ok()) {
325
- throwCliError(storeResult.error);
326
- }
327
- const store = storeResult.value;
328
- const timestamp = ctx.now() ?? new Date;
329
- const memoryClient = store.getMemory(path);
330
- const memoryResult = await memoryClient.create({
331
- content: memoryContent,
332
- metadata: {
333
- tags,
334
- source,
335
- createdAt: timestamp,
336
- updatedAt: timestamp,
337
- expiresAt,
338
- citations
339
- }
340
- });
341
- if (!memoryResult.ok()) {
342
- throwCliError(memoryResult.error);
343
- }
344
- const memory = memoryResult.value;
345
- const out = ctx.stdout ?? process.stdout;
346
- const rawFormat = options.format;
347
- if (!rawFormat) {
348
- out.write(`Added memory ${memory.path} (${source}).
349
- `);
350
- } else {
351
- const format = rawFormat;
352
- const serialized = serializeOutput({
353
- kind: "memory",
354
- value: {
355
- path: memory.path.toString(),
356
- metadata: memory.metadata,
357
- content: memory.content
358
- }
359
- }, format);
360
- if (!serialized.ok()) {
361
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
362
- }
363
- out.write(serialized.value + `
364
- `);
365
- }
366
- }
367
- var addCommand = new Command("add").description("Create a new memory").argument("<path>", "Memory path (e.g., project/tech-stack)").option("-c, --content <text>", "Memory content as inline text").option("-f, --file <filepath>", "Read content from a file").option("-t, --tags <value...>", "Tags (can be repeated or comma-separated)").option("-e, --expires-at <date>", "Expiration date (ISO 8601)").option("--citation <value...>", "Citation references (file paths or URLs)").option("-o, --format <format>", "Output format (yaml, json, toon)").action(async (path, options, command) => {
368
- const parentOpts = command.parent?.opts();
369
- const context = await createCliCommandContext();
370
- if (!context.ok()) {
371
- throwCliError(context.error);
372
- }
373
- await handleAdd(context.value, parentOpts?.store, path, options);
374
- });
375
-
376
- // src/memory/commands/show.ts
377
- import { Command as Command2 } from "@commander-js/extra-typings";
378
- import { defaultTokenizer, MemoryPath } from "@yeseh/cortex-core";
379
- async function handleShow(ctx, storeName, path, options, deps = {}) {
380
- const pathResult = MemoryPath.fromString(path);
381
- if (!pathResult.ok()) {
382
- throwCliError(pathResult.error);
383
- }
384
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
385
- if (!storeResult.ok()) {
386
- throwCliError(storeResult.error);
387
- }
388
- const store = deps.store ?? storeResult.value;
389
- const rootResult = store.root();
390
- if (!rootResult.ok()) {
391
- throwCliError(rootResult.error);
392
- }
393
- const categoryResult = pathResult.value.category.isRoot ? rootResult : rootResult.value.getCategory(pathResult.value.category.toString());
394
- if (!categoryResult.ok()) {
395
- throwCliError(categoryResult.error);
396
- }
397
- const memoryClient = categoryResult.value.getMemory(pathResult.value.slug.toString());
398
- const readResult = await memoryClient.get({
399
- includeExpired: options.includeExpired ?? false,
400
- now: ctx.now()
401
- });
402
- if (!readResult.ok()) {
403
- throwCliError(readResult.error);
404
- }
405
- const memory = readResult.value;
406
- const tokenEstimateResult = defaultTokenizer.estimateTokens(memory.content);
407
- const tokenEstimate = tokenEstimateResult.ok() ? tokenEstimateResult.value : undefined;
408
- const outputMemory = {
409
- path: memory.path.toString(),
410
- metadata: {
411
- createdAt: memory.metadata.createdAt,
412
- updatedAt: memory.metadata.updatedAt,
413
- tags: memory.metadata.tags,
414
- source: memory.metadata.source,
415
- tokenEstimate,
416
- expiresAt: memory.metadata.expiresAt
417
- },
418
- content: memory.content
419
- };
420
- const format = options.format ?? "yaml";
421
- const serialized = serializeOutput({ kind: "memory", value: outputMemory }, format);
422
- if (!serialized.ok()) {
423
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
424
- }
425
- const out = deps.stdout ?? ctx.stdout;
426
- out.write(serialized.value + `
427
- `);
428
- }
429
- var showCommand = new Command2("show").description("Display a memory").argument("<path>", "Memory path to show").option("-x, --include-expired", "Include expired memories").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (path, options, command) => {
430
- const parentOpts = command.parent?.opts();
431
- const context = await createCliCommandContext();
432
- if (!context.ok()) {
433
- throwCliError(context.error);
434
- }
435
- await handleShow(context.value, parentOpts?.store, path, options);
436
- });
437
-
438
- // src/memory/commands/update.ts
439
- import { Command as Command3 } from "@commander-js/extra-typings";
440
- import { MemoryPath as MemoryPath2 } from "@yeseh/cortex-core";
441
- var parseUpdateExpiresAt = (raw) => {
442
- if (raw === false) {
443
- return null;
444
- }
445
- if (!raw) {
446
- return;
447
- }
448
- return parseExpiresAt(raw);
449
- };
450
- var resolveUpdateContent = async (ctx, options) => {
451
- if (options.content === undefined && options.file === undefined) {
452
- return null;
453
- }
454
- const content = await resolveInput({
455
- content: options.content,
456
- filePath: options.file,
457
- stream: ctx.stdin,
458
- stdinRequested: false
459
- });
460
- if (!content.ok()) {
461
- throwCliError(content.error);
462
- }
463
- if (!content.value.content) {
464
- throwCliError({
465
- code: "MISSING_CONTENT",
466
- message: "Memory content is required via --content or --file."
467
- });
468
- }
469
- return content.value.content;
470
- };
471
- var buildUpdates = (content, tags, expiresAt, citations) => {
472
- const updates = {};
473
- if (content !== null) {
474
- updates.content = content;
475
- }
476
- if (tags !== undefined) {
477
- updates.tags = tags;
478
- }
479
- if (expiresAt !== undefined) {
480
- updates.expiresAt = expiresAt;
481
- }
482
- if (citations !== undefined) {
483
- updates.citations = citations;
484
- }
485
- if (Object.keys(updates).length === 0) {
486
- throwCliError({
487
- code: "INVALID_ARGUMENTS",
488
- message: "No updates provided. Use --content, --file, --tags, --citation, or expiry flags."
489
- });
490
- }
491
- return updates;
492
- };
493
- async function handleUpdate(ctx, storeName, path, options) {
494
- const pathResult = MemoryPath2.fromString(path);
495
- if (!pathResult.ok()) {
496
- throwCliError(pathResult.error);
497
- }
498
- const content = await resolveUpdateContent(ctx, options);
499
- const tags = options.tags === undefined ? undefined : parseTags(options.tags);
500
- const expiresAt = parseUpdateExpiresAt(options.expiresAt);
501
- const updates = buildUpdates(content, tags, expiresAt, options.citation);
502
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
503
- if (!storeResult.ok()) {
504
- throwCliError(storeResult.error);
505
- }
506
- const store = storeResult.value;
507
- const rootResult = store.root();
508
- if (!rootResult.ok()) {
509
- throwCliError(rootResult.error);
510
- }
511
- const categoryResult = pathResult.value.category.isRoot ? rootResult : rootResult.value.getCategory(pathResult.value.category.toString());
512
- if (!categoryResult.ok()) {
513
- throwCliError(categoryResult.error);
514
- }
515
- const memoryClient = categoryResult.value.getMemory(pathResult.value.slug.toString());
516
- const updateResult = await memoryClient.update(updates);
517
- if (!updateResult.ok()) {
518
- throwCliError(updateResult.error);
519
- }
520
- const memory = updateResult.value;
521
- const stdout2 = ctx.stdout ?? process.stdout;
522
- const rawFormat = options.format;
523
- if (!rawFormat) {
524
- stdout2.write(`Updated memory ${memory.path.toString()}.
525
- `);
526
- } else {
527
- const format = rawFormat;
528
- const serialized = serializeOutput({
529
- kind: "memory",
530
- value: {
531
- path: memory.path.toString(),
532
- metadata: memory.metadata,
533
- content: memory.content
534
- }
535
- }, format);
536
- if (!serialized.ok()) {
537
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
538
- }
539
- stdout2.write(serialized.value + `
540
- `);
541
- }
542
- }
543
- var updateCommand = new Command3("update").description("Update an existing memory").argument("<path>", "Memory path to update").option("-c, --content <text>", "New memory content as inline text").option("-f, --file <filepath>", "Read new content from a file").option("-t, --tags <value...>", "Tags (can be repeated or comma-separated, replaces existing)").option("-e, --expires-at <date>", "New expiration date (ISO 8601)").option("--no-expires-at", "Remove expiration date").option("--citation <value...>", "Citation references (replaces existing)").option("-o, --format <format>", "Output format (yaml, json, toon)").action(async (path, options, command) => {
544
- const parentOpts = command.parent?.opts();
545
- const context = await createCliCommandContext();
546
- if (!context.ok()) {
547
- throwCliError(context.error);
548
- }
549
- await handleUpdate(context.value, parentOpts?.store, path, options);
550
- });
551
-
552
- // src/memory/commands/remove.ts
553
- import { Command as Command4 } from "@commander-js/extra-typings";
554
- import { MemoryPath as MemoryPath3 } from "@yeseh/cortex-core";
555
- async function handleRemove(ctx, storeName, path, options = {}) {
556
- const pathResult = MemoryPath3.fromString(path);
557
- if (!pathResult.ok()) {
558
- throwCliError(pathResult.error);
559
- }
560
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
561
- if (!storeResult.ok()) {
562
- throwCliError(storeResult.error);
563
- }
564
- const store = storeResult.value;
565
- const rootResult = store.root();
566
- if (!rootResult.ok()) {
567
- throwCliError(rootResult.error);
568
- }
569
- const categoryResult = pathResult.value.category.isRoot ? rootResult : rootResult.value.getCategory(pathResult.value.category.toString());
570
- if (!categoryResult.ok()) {
571
- throwCliError(categoryResult.error);
572
- }
573
- const memoryClient = categoryResult.value.getMemory(pathResult.value.slug.toString());
574
- const removeResult = await memoryClient.delete();
575
- if (!removeResult.ok()) {
576
- throwCliError(removeResult.error);
577
- }
578
- const out = ctx.stdout ?? process.stdout;
579
- const rawFormat = options.format;
580
- if (!rawFormat) {
581
- out.write(`Removed memory ${pathResult.value.toString()}.
582
- `);
583
- } else {
584
- const format = rawFormat;
585
- const serialized = serializeOutput({ kind: "path", value: { path: pathResult.value.toString() } }, format);
586
- if (!serialized.ok()) {
587
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
588
- }
589
- out.write(serialized.value + `
590
- `);
591
- }
592
- }
593
- var removeCommand = new Command4("remove").description("Delete a memory").argument("<path>", "Memory path to remove").option("-o, --format <format>", "Output format (yaml, json, toon)").action(async (path, options, command) => {
594
- const parentOpts = command.parent?.opts();
595
- const context = await createCliCommandContext();
596
- if (!context.ok()) {
597
- throwCliError(context.error);
598
- }
599
- await handleRemove(context.value, parentOpts?.store, path, options);
600
- });
601
-
602
- // src/memory/commands/move.ts
603
- import { Command as Command5 } from "@commander-js/extra-typings";
604
- import { MemoryPath as MemoryPath4 } from "@yeseh/cortex-core";
605
- async function handleMove(ctx, storeName, from, to, options = {}) {
606
- const fromResult = MemoryPath4.fromString(from);
607
- if (!fromResult.ok()) {
608
- throwCliError(fromResult.error);
609
- }
610
- const toResult = MemoryPath4.fromString(to);
611
- if (!toResult.ok()) {
612
- throwCliError(toResult.error);
613
- }
614
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
615
- if (!storeResult.ok()) {
616
- throwCliError(storeResult.error);
617
- }
618
- const store = storeResult.value;
619
- const rootResult = store.root();
620
- if (!rootResult.ok()) {
621
- throwCliError(rootResult.error);
622
- }
623
- const sourceCategoryResult = fromResult.value.category.isRoot ? rootResult : rootResult.value.getCategory(fromResult.value.category.toString());
624
- if (!sourceCategoryResult.ok()) {
625
- throwCliError(sourceCategoryResult.error);
626
- }
627
- const sourceMemory = sourceCategoryResult.value.getMemory(fromResult.value.slug.toString());
628
- const moveResult = await sourceMemory.move(toResult.value);
629
- if (!moveResult.ok()) {
630
- throwCliError(moveResult.error);
631
- }
632
- const out = ctx.stdout ?? process.stdout;
633
- const rawFormat = options.format;
634
- if (!rawFormat) {
635
- out.write(`Moved memory ${fromResult.value.toString()} to ${toResult.value.toString()}.
636
- `);
637
- } else {
638
- const format = rawFormat;
639
- const serialized = serializeOutput({
640
- kind: "moved-memory",
641
- value: { from: fromResult.value.toString(), to: toResult.value.toString() }
642
- }, format);
643
- if (!serialized.ok()) {
644
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
645
- }
646
- out.write(serialized.value + `
647
- `);
648
- }
649
- }
650
- var moveCommand = new Command5("move").description("Move a memory to a new path").argument("<from>", "Source memory path").argument("<to>", "Destination memory path").option("-o, --format <format>", "Output format (yaml, json, toon)").action(async (from, to, options, command) => {
651
- const parentOpts = command.parent?.opts();
652
- const context = await createCliCommandContext();
653
- if (!context.ok()) {
654
- throwCliError(context.error);
655
- }
656
- await handleMove(context.value, parentOpts?.store, from, to, options);
657
- });
658
-
659
- // src/memory/commands/list.ts
660
- import { Command as Command6 } from "@commander-js/extra-typings";
661
- import { CategoryPath } from "@yeseh/cortex-core/category";
662
- import { serialize as serialize2 } from "@yeseh/cortex-core";
663
- async function handleList(ctx, storeName, category, options, deps = {}) {
664
- const categoryResult = CategoryPath.fromString(category ?? "");
665
- if (!categoryResult.ok()) {
666
- throwCliError(categoryResult.error);
667
- }
668
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
669
- if (!storeResult.ok()) {
670
- throwCliError(storeResult.error);
671
- }
672
- const rootResult = storeResult.value.root();
673
- if (!rootResult.ok()) {
674
- throwCliError(rootResult.error);
675
- }
676
- const root = rootResult.value;
677
- let categoryClient;
678
- if (categoryResult.value.isRoot) {
679
- categoryClient = root;
680
- } else {
681
- const categoryClientResult = root.getCategory(categoryResult.value.toString());
682
- if (!categoryClientResult.ok()) {
683
- throwCliError(categoryClientResult.error);
684
- }
685
- categoryClient = categoryClientResult.value;
686
- }
687
- const now = deps.now ?? ctx.now();
688
- const includeExpired = options.includeExpired ?? false;
689
- const visited = new Set;
690
- const collectMemories = async (client) => {
691
- const categoryKey = client.rawPath;
692
- if (visited.has(categoryKey)) {
693
- return [];
694
- }
695
- visited.add(categoryKey);
696
- const memoriesResult = await client.listMemories({ includeExpired });
697
- if (!memoriesResult.ok()) {
698
- throwCliError(memoriesResult.error);
699
- }
700
- const memories2 = [];
701
- for (const entry of memoriesResult.value) {
702
- const memoryClient = client.getMemory(entry.path.slug.toString());
703
- const memoryResult = await memoryClient.get({ includeExpired: true, now });
704
- if (!memoryResult.ok()) {
705
- if (memoryResult.error.code === "MEMORY_NOT_FOUND") {
706
- continue;
707
- }
708
- if (!includeExpired && memoryResult.error.code === "MEMORY_EXPIRED") {
709
- continue;
710
- }
711
- throwCliError(memoryResult.error);
712
- }
713
- const memory = memoryResult.value;
714
- const isExpired = memory.isExpired(now);
715
- if (!includeExpired && isExpired) {
716
- continue;
717
- }
718
- memories2.push({
719
- path: entry.path.toString(),
720
- tokenEstimate: entry.tokenEstimate,
721
- summary: undefined,
722
- expiresAt: memory.metadata.expiresAt,
723
- isExpired
724
- });
725
- }
726
- const subcategoriesResult2 = await client.listSubcategories();
727
- if (!subcategoriesResult2.ok()) {
728
- throwCliError(subcategoriesResult2.error);
729
- }
730
- for (const subcategory of subcategoriesResult2.value) {
731
- const subcategoryClientResult = root.getCategory(subcategory.path.toString());
732
- if (!subcategoryClientResult.ok()) {
733
- throwCliError(subcategoryClientResult.error);
734
- }
735
- const subMemories = await collectMemories(subcategoryClientResult.value);
736
- memories2.push(...subMemories);
737
- }
738
- return memories2;
739
- };
740
- const subcategoriesResult = await categoryClient.listSubcategories();
741
- if (!subcategoriesResult.ok()) {
742
- throwCliError(subcategoriesResult.error);
743
- }
744
- const memories = await collectMemories(categoryClient);
745
- const result = {
746
- memories,
747
- subcategories: subcategoriesResult.value.map((subcategory) => ({
748
- path: subcategory.path.toString(),
749
- memoryCount: subcategory.memoryCount,
750
- description: subcategory.description
751
- }))
752
- };
753
- const validFormats = ["yaml", "json", "toon"];
754
- const format = validFormats.includes(options.format) ? options.format : "yaml";
755
- const output = serialize2(result, format);
756
- if (!output.ok()) {
757
- throwCliError({ code: "SERIALIZE_FAILED", message: output.error.message });
758
- }
759
- const out = deps.stdout ?? ctx.stdout;
760
- out.write(output.value + `
761
- `);
762
- }
763
- var listCommand = new Command6("list").description("List memories in a category").argument("[category]", "Category path to list (lists all if omitted)").option("-s, --store <name>", "Use a specific named store").option("-x, --include-expired", "Include expired memories").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (category, options, command) => {
764
- const parentOpts = command.parent?.opts();
765
- const storeName = options.store ?? parentOpts?.store;
766
- const context = await createCliCommandContext();
767
- if (!context.ok()) {
768
- throwCliError(context.error);
769
- }
770
- await handleList(context.value, storeName, category, options);
771
- });
772
-
773
- // src/memory/index.ts
774
- var memoryCommand = new Command7("memory").description("Memory operations").option("-s, --store <name>", "Use a specific named store");
775
- memoryCommand.addCommand(addCommand);
776
- memoryCommand.addCommand(showCommand);
777
- memoryCommand.addCommand(updateCommand);
778
- memoryCommand.addCommand(removeCommand);
779
- memoryCommand.addCommand(moveCommand);
780
- memoryCommand.addCommand(listCommand);
781
-
782
- // src/store/index.ts
783
- import { Command as Command14 } from "@commander-js/extra-typings";
784
-
785
- // src/store/commands/list.ts
786
- import { Command as Command8 } from "@commander-js/extra-typings";
787
- async function handleList2(ctx, options, deps = {}) {
788
- const stores = Object.entries(ctx.stores).map(([
789
- name,
790
- def
791
- ]) => ({
792
- name,
793
- path: def.properties.path
794
- })).sort((a, b) => a.name.localeCompare(b.name));
795
- const output = { stores };
796
- const format = options.format ?? "yaml";
797
- const serialized = serializeOutput({ kind: "store-registry", value: output }, format);
798
- if (!serialized.ok()) {
799
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
800
- }
801
- const out = deps.stdout ?? process.stdout;
802
- out.write(serialized.value + `
803
- `);
804
- }
805
- var listCommand2 = new Command8("list").description("List all registered stores").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (options) => {
806
- const context = await createCliCommandContext();
807
- if (!context.ok()) {
808
- throwCliError(context.error);
809
- }
810
- await handleList2(context.value, options);
811
- });
812
-
813
- // src/store/commands/add.ts
814
- import { mkdir } from "fs/promises";
815
- import { dirname } from "path";
816
- import { Command as Command9 } from "@commander-js/extra-typings";
817
-
818
- // src/utils/paths.ts
819
- import { homedir as homedir2 } from "os";
820
- import { resolve as resolve2 } from "path";
821
- function isAbsolutePath(inputPath) {
822
- if (inputPath.startsWith("/"))
823
- return true;
824
- if (/^[a-zA-Z]:[/\\]/.test(inputPath))
825
- return true;
826
- if (inputPath.startsWith("\\\\") || inputPath.startsWith("//"))
827
- return true;
828
- return false;
829
- }
830
- function resolveUserPath(inputPath, cwd) {
831
- if (inputPath.startsWith("~")) {
832
- const home = homedir2();
833
- return resolve2(home, inputPath.slice(1).replace(/^[/\\]/, ""));
834
- }
835
- if (isAbsolutePath(inputPath)) {
836
- return resolve2(inputPath);
837
- }
838
- return resolve2(cwd, inputPath);
839
- }
840
- var getDefaultConfigPath = () => {
841
- const envConfigPath = process.env.CORTEX_CONFIG;
842
- if (envConfigPath) {
843
- return resolve2(envConfigPath);
844
- }
845
- const envConfigDir = process.env.CORTEX_CONFIG_DIR;
846
- if (envConfigDir) {
847
- return resolve2(envConfigDir, "config.yaml");
848
- }
849
- return resolve2(homedir2(), ".config", "cortex", "config.yaml");
850
- };
851
-
852
- // src/store/commands/add.ts
853
- import { Slug, parseConfig } from "@yeseh/cortex-core";
854
- function validateStoreName(name) {
855
- const slugResult = Slug.from(name);
856
- if (!slugResult.ok()) {
857
- throwCliError({
858
- code: "INVALID_STORE_NAME",
859
- message: "Store name must be a lowercase slug (letters, numbers, hyphens)."
860
- });
861
- }
862
- return slugResult.value.toString();
863
- }
864
- function validateAndResolvePath(storePath, cwd) {
865
- const trimmed = storePath.trim();
866
- if (!trimmed) {
867
- throwCliError({ code: "INVALID_STORE_PATH", message: "Store path is required." });
868
- }
869
- return resolveUserPath(trimmed, cwd);
870
- }
871
- function writeOutput(output, format, stdout2) {
872
- const serialized = serializeOutput({ kind: "store", value: output }, format);
873
- if (!serialized.ok()) {
874
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
875
- }
876
- stdout2.write(serialized.value + `
877
- `);
878
- }
879
- async function handleAdd2(ctx, name, storePath, options = {}, deps = {}) {
880
- const cwd = ctx.cwd ?? process.cwd();
881
- const stdout2 = deps.stdout ?? ctx.stdout ?? process.stdout;
882
- const configPath = getDefaultConfigPath();
883
- const trimmedName = validateStoreName(name);
884
- const resolvedPath = validateAndResolvePath(storePath, cwd);
885
- if (ctx.stores[trimmedName]) {
886
- throwCliError({
887
- code: "STORE_ALREADY_EXISTS",
888
- message: `Store '${trimmedName}' is already registered.`
889
- });
890
- }
891
- const configFile = Bun.file(configPath);
892
- let parsedConfig;
893
- if (await configFile.exists()) {
894
- let configContents;
895
- try {
896
- configContents = await configFile.text();
897
- } catch {
898
- throwCliError({
899
- code: "CONFIG_READ_FAILED",
900
- message: `Failed to read config at ${configPath}`
901
- });
902
- }
903
- const parsed = parseConfig(configContents);
904
- if (!parsed.ok()) {
905
- throwCliError(parsed.error);
906
- }
907
- parsedConfig = parsed.value;
908
- } else {
909
- parsedConfig = { settings: undefined, stores: {} };
910
- await mkdir(dirname(configPath), { recursive: true });
911
- }
912
- const newStore = {
913
- kind: "filesystem",
914
- categoryMode: "free",
915
- categories: {},
916
- properties: { path: resolvedPath }
917
- };
918
- const updatedConfig = {
919
- ...parsedConfig,
920
- stores: {
921
- ...parsedConfig.stores,
922
- [trimmedName]: newStore
923
- }
924
- };
925
- const serialized = Bun.YAML.stringify(updatedConfig, null, 2);
926
- try {
927
- await Bun.write(configPath, serialized);
928
- } catch {
929
- throwCliError({
930
- code: "CONFIG_WRITE_FAILED",
931
- message: `Failed to write config at ${configPath}`
932
- });
933
- }
934
- const output = { name: trimmedName, path: resolvedPath };
935
- const format = options.format ?? "yaml";
936
- writeOutput(output, format, stdout2);
937
- }
938
- var addCommand2 = new Command9("add").description("Register a new store").argument("<name>", "Store name (lowercase slug)").argument("<path>", "Filesystem path to the store").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (name, path, options) => {
939
- const context = await createCliCommandContext();
940
- if (!context.ok()) {
941
- throwCliError(context.error);
942
- }
943
- await handleAdd2(context.value, name, path, options);
944
- });
945
-
946
- // src/store/commands/remove.ts
947
- import { Command as Command10 } from "@commander-js/extra-typings";
948
- import { Slug as Slug2, parseConfig as parseConfig2 } from "@yeseh/cortex-core";
949
- function validateStoreName2(name) {
950
- const slugResult = Slug2.from(name);
951
- if (!slugResult.ok()) {
952
- throwCliError({
953
- code: "INVALID_STORE_NAME",
954
- message: "Store name must be a lowercase slug (letters, numbers, hyphens)."
955
- });
956
- }
957
- return slugResult.value.toString();
958
- }
959
- function writeOutput2(output, format, stdout2) {
960
- const serialized = serializeOutput({ kind: "store", value: output }, format);
961
- if (!serialized.ok()) {
962
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
963
- }
964
- stdout2.write(serialized.value + `
965
- `);
966
- }
967
- async function handleRemove2(ctx, name, options = {}, deps = {}) {
968
- const stdout2 = deps.stdout ?? ctx.stdout ?? process.stdout;
969
- const configPath = getDefaultConfigPath();
970
- const trimmedName = validateStoreName2(name);
971
- const existingStore = ctx.stores[trimmedName];
972
- if (!existingStore) {
973
- throwCliError({
974
- code: "STORE_NOT_FOUND",
975
- message: `Store '${trimmedName}' is not registered.`
976
- });
977
- }
978
- const storePath = existingStore.properties.path;
979
- const configFile = Bun.file(configPath);
980
- if (!await configFile.exists()) {
981
- throwCliError({
982
- code: "CONFIG_NOT_FOUND",
983
- message: `Config file not found at ${configPath}. Cannot remove store.`
984
- });
985
- }
986
- let configContents;
987
- try {
988
- configContents = await configFile.text();
989
- } catch {
990
- throwCliError({
991
- code: "CONFIG_READ_FAILED",
992
- message: `Failed to read config at ${configPath}`
993
- });
994
- }
995
- const configResult = parseConfig2(configContents);
996
- if (!configResult.ok()) {
997
- throwCliError(configResult.error);
998
- }
999
- const { [trimmedName]: _removed, ...remainingStores } = configResult.value.stores ?? {};
1000
- const updatedConfig = {
1001
- ...configResult.value,
1002
- stores: remainingStores
1003
- };
1004
- const serialized = Bun.YAML.stringify(updatedConfig, null, 2);
1005
- try {
1006
- await Bun.write(configPath, serialized);
1007
- } catch {
1008
- throwCliError({
1009
- code: "CONFIG_WRITE_FAILED",
1010
- message: `Failed to write config at ${configPath}`
1011
- });
1012
- }
1013
- const output = { name: trimmedName, path: storePath };
1014
- const format = options.format ?? "yaml";
1015
- writeOutput2(output, format, stdout2);
1016
- }
1017
- var removeCommand2 = new Command10("remove").description("Unregister a store from the registry").argument("<name>", "Store name to remove").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (name, options) => {
1018
- const context = await createCliCommandContext();
1019
- if (!context.ok()) {
1020
- throwCliError(context.error);
1021
- }
1022
- await handleRemove2(context.value, name, options);
1023
- });
1024
-
1025
- // src/store/commands/init.ts
1026
- import { Command as Command11 } from "@commander-js/extra-typings";
1027
- import { resolve as resolve3 } from "path";
1028
- import { stdin as stdin2, stdout as processStdout } from "process";
1029
-
1030
- // src/store/utils/resolve-store-name.ts
1031
- import { Slug as Slug3 } from "@yeseh/cortex-core";
1032
- import { basename as basename2 } from "path";
1033
-
1034
- // src/utils/git.ts
1035
- import { spawn } from "child_process";
1036
- import { basename } from "path";
1037
- var runGitCommand = (args, cwd) => {
1038
- return new Promise((resolvePromise) => {
1039
- const proc = spawn("git", args, { cwd });
1040
- let stdout2 = "";
1041
- proc.stdout.on("data", (data) => {
1042
- stdout2 += data.toString();
1043
- });
1044
- proc.on("close", (code) => {
1045
- if (code === 0) {
1046
- resolvePromise({ ok: true, value: stdout2.trim() });
1047
- } else {
1048
- resolvePromise({ ok: false });
1049
- }
1050
- });
1051
- proc.on("error", () => {
1052
- resolvePromise({ ok: false });
1053
- });
1054
- });
1055
- };
1056
- var detectGitRepoName = async (cwd) => {
1057
- const result = await runGitCommand([
1058
- "rev-parse",
1059
- "--show-toplevel"
1060
- ], cwd);
1061
- if (!result.ok) {
1062
- return null;
1063
- }
1064
- return basename(result.value);
1065
- };
1066
-
1067
- // src/store/utils/resolve-store-name.ts
1068
- async function resolveStoreName(cwd, explicitName) {
1069
- if (explicitName) {
1070
- const slugResult2 = Slug3.from(explicitName);
1071
- if (!slugResult2.ok()) {
1072
- throwCliError({
1073
- code: "INVALID_STORE_NAME",
1074
- message: "Store name must be a lowercase slug (letters, numbers, hyphens)."
1075
- });
1076
- }
1077
- return slugResult2.value.toString();
1078
- }
1079
- const gitName = await detectGitRepoName(cwd);
1080
- if (gitName) {
1081
- const normalized = gitName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1082
- const slugResult2 = Slug3.from(normalized);
1083
- if (!slugResult2.ok()) {
1084
- throwCliError({
1085
- code: "INVALID_STORE_NAME",
1086
- message: "Could not derive valid store name from git repo."
1087
- });
1088
- }
1089
- return slugResult2.value.toString();
1090
- }
1091
- const folderName = basename2(cwd);
1092
- const slugResult = Slug3.from(folderName);
1093
- if (slugResult.ok()) {
1094
- return slugResult.value.toString();
1095
- }
1096
- throwCliError({
1097
- code: "GIT_REPO_REQUIRED",
1098
- message: "Not in a git repository. Use --name to specify the store name."
1099
- });
1100
- }
1101
-
1102
- // src/store/commands/init.ts
1103
- import { initializeStore } from "@yeseh/cortex-core/store";
1104
-
1105
- // src/utils/prompts.ts
1106
- import { input, confirm } from "@inquirer/prompts";
1107
- var defaultPromptDeps = { input, confirm };
1108
- function isTTY(stream) {
1109
- return stream?.isTTY === true;
1110
- }
1111
-
1112
- // src/store/commands/init.ts
1113
- import { FilesystemStorageAdapter as FilesystemStorageAdapter2 } from "@yeseh/cortex-storage-fs";
1114
- function writeOutput3(output, format, stdout2) {
1115
- const serialized = serializeOutput({ kind: "store-init", value: output }, format);
1116
- if (!serialized.ok()) {
1117
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
1118
- }
1119
- stdout2.write(serialized.value + `
1120
- `);
1121
- }
1122
- async function promptStoreInitOptions(ctx, resolved, explicit, promptDeps) {
1123
- if (!isTTY(ctx.stdin))
1124
- return resolved;
1125
- const storeName = explicit.name ? resolved.storeName : await promptDeps.input({ message: "Store name:", default: resolved.storeName });
1126
- let storePath;
1127
- if (explicit.path) {
1128
- storePath = resolved.storePath;
1129
- } else {
1130
- const promptedPath = await promptDeps.input({
1131
- message: "Store path:",
1132
- default: resolved.storePath
1133
- });
1134
- const trimmedPath = promptedPath.trim();
1135
- storePath = resolveUserPath(trimmedPath, process.cwd());
1136
- }
1137
- return { storeName, storePath };
1138
- }
1139
- async function resolveStoreNameOrEmpty(cwd, explicitName, tty) {
1140
- try {
1141
- return await resolveStoreName(cwd, explicitName);
1142
- } catch (e) {
1143
- if (tty && !explicitName)
1144
- return "";
1145
- throw e;
1146
- }
1147
- }
1148
- async function handleInit(ctx, targetPath, options = {}, promptDeps = defaultPromptDeps) {
1149
- const cwd = ctx.cwd ?? process.cwd();
1150
- const stdout2 = ctx.stdout ?? process.stdout;
1151
- const storeName = await resolveStoreNameOrEmpty(cwd, options.name, isTTY(ctx.stdin));
1152
- const storePath = targetPath ? resolveUserPath(targetPath.trim(), cwd) : resolve3(cwd, ".cortex");
1153
- const resolved = await promptStoreInitOptions(ctx, { storeName, storePath }, { name: options.name, path: targetPath }, promptDeps);
1154
- const finalStoreName = resolved.storeName;
1155
- const finalStorePath = resolved.storePath;
1156
- if (!finalStoreName) {
1157
- throwCliError({
1158
- code: "INVALID_STORE_NAME",
1159
- message: "Store name is required. Use --name to specify one."
1160
- });
1161
- }
1162
- const storeData = {
1163
- kind: "filesystem",
1164
- categoryMode: options.categoryMode ?? "free",
1165
- categories: [],
1166
- properties: {
1167
- path: finalStorePath
1168
- },
1169
- description: options.description
1170
- };
1171
- const adapter = new FilesystemStorageAdapter2(ctx.config, {
1172
- rootDirectory: finalStorePath
1173
- });
1174
- const createResult = await initializeStore(adapter, finalStoreName, storeData);
1175
- if (!createResult.ok()) {
1176
- throwCliError(createResult.error);
1177
- }
1178
- const output = { path: finalStorePath, name: finalStoreName };
1179
- const format = options.format ?? "yaml";
1180
- writeOutput3(output, format, stdout2);
1181
- }
1182
- var initCommand = new Command11("init").description("Initialize a new memory store").argument("[path]", "Path for the store (defaults to .cortex in current directory)").option("-n, --name <name>", "Explicit store name (otherwise auto-detected from git)").option("-d, --description <description>", "Optional description for the store").option("-c, --category-mode <mode>", "Category mode (free, strict, flat)", "free").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (path, options) => {
1183
- const configCtx = await createCliConfigContext();
1184
- if (!configCtx.ok()) {
1185
- throwCliError(configCtx.error);
1186
- }
1187
- const { configAdapter, effectiveCwd } = configCtx.value;
1188
- const ctx = {
1189
- config: configAdapter,
1190
- cwd: effectiveCwd,
1191
- stdin: stdin2,
1192
- stdout: processStdout
1193
- };
1194
- await handleInit(ctx, path, {
1195
- name: options.name,
1196
- description: options.description,
1197
- categoryMode: options.categoryMode,
1198
- format: options.format
1199
- });
1200
- });
1201
-
1202
- // src/store/commands/prune.ts
1203
- import { Command as Command12 } from "@commander-js/extra-typings";
1204
- async function handlePrune(ctx, storeName, options, deps = {}) {
1205
- const now = deps.now ?? ctx.now();
1206
- const stdout2 = deps.stdout ?? ctx.stdout ?? process.stdout;
1207
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
1208
- if (!storeResult.ok()) {
1209
- throwCliError(storeResult.error);
1210
- }
1211
- const store = storeResult.value;
1212
- const rootResult = store.root();
1213
- if (!rootResult.ok()) {
1214
- throwCliError(rootResult.error);
1215
- }
1216
- const result = await rootResult.value.prune({
1217
- dryRun: options.dryRun,
1218
- now
1219
- });
1220
- if (!result.ok()) {
1221
- throwCliError(result.error);
1222
- }
1223
- const pruned = result.value.pruned;
1224
- if (pruned.length === 0) {
1225
- stdout2.write(`No expired memories found.
1226
- `);
1227
- return;
1228
- }
1229
- const paths = pruned.map((entry) => entry.path).join(`
1230
- `);
1231
- if (options.dryRun) {
1232
- stdout2.write(`Would prune ${pruned.length} expired memories:
1233
- ${paths}
1234
- `);
1235
- } else {
1236
- stdout2.write(`Pruned ${pruned.length} expired memories:
1237
- ${paths}
1238
- `);
1239
- }
1240
- }
1241
- var pruneCommand = new Command12("prune").description("Remove expired memories from the store").option("--dry-run", "Show what would be pruned without deleting").action(async (options, command) => {
1242
- const parentOpts = command.parent?.opts();
1243
- const context = await createCliCommandContext();
1244
- if (!context.ok()) {
1245
- throwCliError(context.error);
1246
- }
1247
- await handlePrune(context.value, parentOpts?.store, options);
1248
- });
1249
-
1250
- // src/store/commands/reindexs.ts
1251
- import { Command as Command13 } from "@commander-js/extra-typings";
1252
- async function handleReindex(ctx, storeName, deps = {}) {
1253
- const stdout2 = deps.stdout ?? ctx.stdout ?? process.stdout;
1254
- const effectiveStoreName = resolveDefaultStore(ctx, storeName);
1255
- const storeResult = ctx.cortex.getStore(effectiveStoreName);
1256
- if (!storeResult.ok()) {
1257
- throwCliError(storeResult.error);
1258
- }
1259
- const store = storeResult.value;
1260
- const rootResult = store.root();
1261
- if (!rootResult.ok()) {
1262
- throwCliError(rootResult.error);
1263
- }
1264
- const reindexResult = await rootResult.value.reindex();
1265
- if (!reindexResult.ok()) {
1266
- throwCliError({ code: "REINDEX_FAILED", message: reindexResult.error.message });
1267
- }
1268
- stdout2.write(`Reindexed category indexes for store '${effectiveStoreName}'.
1269
- `);
1270
- }
1271
- var reindexCommand = new Command13("reindex").description("Rebuild category indexes for the store").action(async (_options, command) => {
1272
- const parentOpts = command.parent?.opts();
1273
- const context = await createCliCommandContext();
1274
- if (!context.ok()) {
1275
- throwCliError(context.error);
1276
- }
1277
- await handleReindex(context.value, parentOpts?.store);
1278
- });
1279
-
1280
- // src/store/index.ts
1281
- var storeCommand = new Command14("store").description("Store management").option("-s, --store <name>", "Use a specific named store");
1282
- storeCommand.addCommand(listCommand2);
1283
- storeCommand.addCommand(addCommand2);
1284
- storeCommand.addCommand(removeCommand2);
1285
- storeCommand.addCommand(initCommand);
1286
- storeCommand.addCommand(pruneCommand);
1287
- storeCommand.addCommand(reindexCommand);
1288
-
1289
- // src/commands/init.ts
1290
- import { homedir as homedir3 } from "os";
1291
- import { resolve as resolve4 } from "path";
1292
- import { Command as Command15 } from "@commander-js/extra-typings";
1293
- import { defaultGlobalStoreCategories } from "@yeseh/cortex-core/category";
1294
- import {
1295
- configCategoriesToStoreCategories,
1296
- getDefaultSettings as getDefaultSettings2
1297
- } from "@yeseh/cortex-core";
1298
- var initCommand2 = new Command15("init").description("Initialize global cortex configuration").option("-F, --force", "Reinitialize even if already initialized").option("-o, --format <format>", "Output format (yaml, json, toon)", "yaml").action(async (options) => {
1299
- const context = await createCliCommandContext();
1300
- if (!context.ok()) {
1301
- throwCliError({
1302
- code: "CONTEXT_CREATION_FAILED",
1303
- message: `Failed to create command context: ${context.error.message}`
1304
- });
1305
- }
1306
- await handleInit2(context.value, options);
1307
- });
1308
- function resolveUserPath2(userPath) {
1309
- if (!userPath)
1310
- return userPath;
1311
- let expanded = userPath;
1312
- if (userPath.startsWith("~")) {
1313
- expanded = resolve4(homedir3(), userPath.slice(1));
1314
- }
1315
- return resolve4(expanded);
1316
- }
1317
- function normalizeStoreName(input2, fallback) {
1318
- const trimmed = input2.trim();
1319
- if (!trimmed)
1320
- return fallback;
1321
- const slug = trimmed.toLowerCase().replace(/[^a-z0-9_-]+/gi, "-").replace(/^-+|-+$/g, "");
1322
- return slug || fallback;
1323
- }
1324
- function normalizeStorePath(input2, fallback) {
1325
- const trimmed = input2.trim();
1326
- const base = trimmed || fallback;
1327
- return resolveUserPath2(base);
1328
- }
1329
- async function promptInitOptions(ctx, resolved, promptDeps) {
1330
- if (!isTTY(ctx.stdin)) {
1331
- return {
1332
- storeName: normalizeStoreName(resolved.storeName, resolved.storeName),
1333
- storePath: normalizeStorePath(resolved.storePath, resolved.storePath)
1334
- };
1335
- }
1336
- const storePathInput = await promptDeps.input({
1337
- message: "Global store path:",
1338
- default: resolved.storePath
1339
- });
1340
- const storeNameInput = await promptDeps.input({
1341
- message: "Global store name:",
1342
- default: resolved.storeName
1343
- });
1344
- const storeName = normalizeStoreName(storeNameInput, resolved.storeName);
1345
- const storePath = normalizeStorePath(storePathInput, resolved.storePath);
1346
- return { storePath, storeName };
1347
- }
1348
- async function handleInit2(ctx, options = {}, promptDeps = defaultPromptDeps) {
1349
- const cortexConfigDir = resolve4(homedir3(), ".config", "cortex");
1350
- const globalStorePath = resolve4(cortexConfigDir, "memory");
1351
- const resolved = await promptInitOptions(ctx, { storeName: "global", storePath: globalStorePath }, promptDeps);
1352
- const finalStorePath = resolved.storePath;
1353
- const finalStoreName = resolved.storeName;
1354
- await initializeConfigAdapter(ctx);
1355
- await ensureNotInitialized(ctx, finalStoreName, finalStorePath, options.force);
1356
- await createGlobalStore(ctx, finalStoreName, finalStorePath);
1357
- const output = {
1358
- kind: "init",
1359
- value: formatInit(finalStorePath, Object.keys(defaultGlobalStoreCategories))
1360
- };
1361
- const format = options.format ?? "yaml";
1362
- const outputSerialized = serializeOrThrow(output, format);
1363
- const out = ctx.stdout ?? process.stdout;
1364
- out.write(outputSerialized.value + `
1365
- `);
1366
- }
1367
- var ensureNotInitialized = async (ctx, storeName, globalStorePath, force = false) => {
1368
- if (force) {
1369
- return;
1370
- }
1371
- const existingStoreResult = await ctx.config.getStore(storeName);
1372
- if (!existingStoreResult.ok()) {
1373
- throwCliError(existingStoreResult.error);
1374
- }
1375
- if (!existingStoreResult.value) {
1376
- return;
1377
- }
1378
- throwCliError({
1379
- code: "ALREADY_INITIALIZED",
1380
- message: `Global config store already exists at ${globalStorePath}. Use --force to reinitialize.`
1381
- });
1382
- };
1383
- var initializeConfigAdapter = async (ctx) => {
1384
- const config = {
1385
- settings: getDefaultSettings2(),
1386
- stores: {}
1387
- };
1388
- const initConfigResult = await ctx.config.initializeConfig(config);
1389
- if (!initConfigResult.ok()) {
1390
- throwCliError(initConfigResult.error);
1391
- }
1392
- };
1393
- var serializeOrThrow = (value, format) => {
1394
- const serialized = serializeOutput(value, format);
1395
- if (!serialized.ok()) {
1396
- throwCliError(serialized.error);
1397
- }
1398
- return serialized;
1399
- };
1400
- var createGlobalStore = async (ctx, storeName, globalStorePath) => {
1401
- const existingStoreResult = await ctx.config.getStore(storeName);
1402
- if (!existingStoreResult.ok()) {
1403
- throwCliError(existingStoreResult.error);
1404
- }
1405
- if (existingStoreResult.value) {
1406
- return;
1407
- }
1408
- const templateCategories = configCategoriesToStoreCategories(defaultGlobalStoreCategories).unwrap();
1409
- const globalStoreData = {
1410
- kind: "filesystem",
1411
- categoryMode: "free",
1412
- description: "Global memory store for Cortex. Use for cross-project memories and configurations.",
1413
- categories: templateCategories,
1414
- properties: {
1415
- path: globalStorePath
1416
- }
1417
- };
1418
- const saveStoreResult = await ctx.config.saveStore(storeName, globalStoreData);
1419
- if (!saveStoreResult.ok()) {
1420
- throwCliError(saveStoreResult.error);
1421
- }
1422
- };
1423
- var formatInit = (path, categories) => ({
1424
- path,
1425
- categories: [...categories]
1426
- });
1427
-
1428
- // src/category/index.ts
1429
- import { Command as Command17 } from "@commander-js/extra-typings";
1430
-
1431
- // src/category/commands/create.ts
1432
- import { Command as Command16 } from "@commander-js/extra-typings";
1433
- function writeCreateOutput(payload, options, stdout2) {
1434
- const rawFormat = options.format;
1435
- if (!rawFormat) {
1436
- const verb = payload.created ? "Created" : "Category already exists:";
1437
- stdout2.write(`${verb} ${payload.path}
1438
- `);
1439
- return;
1440
- }
1441
- const serialized = serializeOutput({ kind: "created-category", value: payload }, rawFormat);
1442
- if (!serialized.ok()) {
1443
- throwCliError({ code: "SERIALIZE_FAILED", message: serialized.error.message });
1444
- }
1445
- stdout2.write(serialized.value + `
1446
- `);
1447
- }
1448
- function unwrapOrThrow(result) {
1449
- if (!result.ok()) {
1450
- throwCliError(result.error);
1451
- }
1452
- return result.value;
1453
- }
1454
- async function handleCreate(ctx, storeName, path, options = {}) {
1455
- const store = unwrapOrThrow(ctx.cortex.getStore(resolveDefaultStore(ctx, storeName)));
1456
- const root = unwrapOrThrow(store.root());
1457
- const category = unwrapOrThrow(root.getCategory(path));
1458
- const result = unwrapOrThrow(await category.create());
1459
- const out = ctx.stdout ?? process.stdout;
1460
- writeCreateOutput(result, options, out);
1461
- }
1462
- var createCommand = new Command16("create").description("Create a category (and any missing ancestors)").argument("<path>", "Category path (e.g., standards/typescript)").option("-d, --description <text>", "Optional description for the category").option("-o, --format <format>", "Output format (yaml, json, toon)").action(async (path, options, command) => {
1463
- const parentOpts = command.parent?.opts();
1464
- const context = await createCliCommandContext();
1465
- if (!context.ok()) {
1466
- throwCliError(context.error);
1467
- }
1468
- await handleCreate(context.value, parentOpts?.store, path, options);
1469
- });
1470
-
1471
- // src/category/index.ts
1472
- var categoryCommand = new Command17("category").description("Category operations").option("-s, --store <name>", "Use a specific named store");
1473
- categoryCommand.addCommand(createCommand);
1474
- // package.json
1475
- var package_default = {
1476
- name: "@yeseh/cortex-cli",
1477
- version: "0.6.8",
1478
- license: "MIT",
1479
- type: "module",
1480
- repository: {
1481
- type: "git",
1482
- url: "https://github.com/Yeseh/cortex.git",
1483
- directory: "packages/cli"
1484
- },
1485
- publishConfig: {
1486
- access: "public"
1487
- },
1488
- bin: {
1489
- cortex: "./dist/run.js"
1490
- },
1491
- exports: {
1492
- ".": {
1493
- bun: "./src/program.ts",
1494
- types: "./src/program.ts",
1495
- import: "./dist/program.js"
1496
- }
1497
- },
1498
- files: [
1499
- "src",
1500
- "dist"
1501
- ],
1502
- scripts: {
1503
- build: "rm -f tsconfig.tsbuildinfo && bun run build.ts && tsc",
1504
- test: "bun test",
1505
- start: "bun run src/run.ts"
1506
- },
1507
- peerDependencies: {
1508
- typescript: "^5.9.3"
1509
- },
1510
- dependencies: {
1511
- "@yeseh/cortex-core": "workspace:*",
1512
- "@yeseh/cortex-storage-fs": "workspace:*",
1513
- "@commander-js/extra-typings": "^14.0.0",
1514
- "@inquirer/prompts": "^8.3.0",
1515
- "@toon-format/toon": "^1.4.0",
1516
- commander: "^14.0.3"
1517
- },
1518
- sideEffects: false
1519
- };
1520
-
1521
- // src/program.ts
1522
- var program = new Command18().name("cortex").description("Memory system for AI agents").version(package_default.version);
1523
- program.addCommand(memoryCommand);
1524
- program.addCommand(categoryCommand);
1525
- program.addCommand(storeCommand);
1526
- program.addCommand(initCommand2);
1527
- var runProgram = async () => {
1528
- try {
1529
- await program.parseAsync(process.argv);
1530
- } catch (error) {
1531
- const logger = createCliLogger();
1532
- if (error instanceof Error) {
1533
- logger.error(error.message);
1534
- } else {
1535
- logger.error("An unexpected error occurred");
1536
- }
1537
- process.exitCode = 1;
1538
- }
1539
- };
1540
-
1541
- export { program, runProgram };
1542
-
1543
- //# debugId=11E06996B740BD2864756E2164756E21