@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,269 +0,0 @@
1
- /**
2
- * Memory list command for browsing memories with optional filtering.
3
- *
4
- * Lists all memories in a category, or all memories across all categories
5
- * if no category is specified. Expired memories are excluded by default
6
- * unless the `--include-expired` flag is provided.
7
- *
8
- * When no category is specified, the command dynamically discovers all
9
- * root categories from the store's index rather than using a hardcoded list.
10
- *
11
- * @example
12
- * ```bash
13
- * # List all memories
14
- * cortex memory list
15
- *
16
- * # List memories in a specific category
17
- * cortex memory list project/cortex
18
- *
19
- * # Include expired memories
20
- * cortex memory list --include-expired
21
- *
22
- * # Output in JSON format
23
- * cortex memory list --format json
24
- *
25
- * # Use a specific store (either placement works)
26
- * cortex memory list -s my-store
27
- * cortex memory -s my-store list
28
- * ```
29
- */
30
-
31
- import { Command } from '@commander-js/extra-typings';
32
- import { throwCliError } from '../../errors.ts';
33
-
34
- import { CategoryPath, type CategoryClient } from '@yeseh/cortex-core/category';
35
- import { serialize, type CortexContext } from '@yeseh/cortex-core';
36
- import type { SubcategoryEntry } from '@yeseh/cortex-core/category';
37
- import { type OutputFormat } from '../../output.ts';
38
- import { createCliCommandContext } from '../../context.ts';
39
- import { resolveDefaultStore } from '../../utils/resolve-default-store.ts';
40
-
41
- /**
42
- * Options for the list command.
43
- */
44
- export interface ListCommandOptions {
45
- /** Include expired memories in the output */
46
- includeExpired?: boolean;
47
- /** Output format (yaml, json, toon) */
48
- format?: string;
49
- /** Store name (can be specified on subcommand or parent) */
50
- store?: string;
51
- }
52
-
53
- /**
54
- * Dependencies for the list command handler.
55
- * Allows injection for testing.
56
- */
57
- export interface ListHandlerDeps {
58
- /** Output stream for writing results (defaults to process.stdout) */
59
- stdout?: NodeJS.WritableStream;
60
- /** Current time for expiration checks */
61
- now?: Date;
62
- }
63
-
64
- /**
65
- * Entry representing a memory in the list output.
66
- */
67
- export interface ListMemoryEntry {
68
- path: string;
69
- tokenEstimate: number;
70
- summary?: string;
71
- expiresAt?: Date;
72
- isExpired: boolean;
73
- }
74
-
75
- /**
76
- * Entry representing a subcategory in the list output.
77
- */
78
- export interface ListSubcategoryEntry {
79
- path: string;
80
- memoryCount: number;
81
- description?: string;
82
- }
83
-
84
- /**
85
- * Result of the list command containing memories and subcategories.
86
- */
87
- export interface ListResult {
88
- memories: ListMemoryEntry[];
89
- subcategories: ListSubcategoryEntry[];
90
- }
91
-
92
- /**
93
- * Handles the list command execution.
94
- *
95
- * This function:
96
- * 1. Resolves the store context
97
- * 2. Loads category index (or all categories if none specified)
98
- * 3. Collects memories and subcategories, filtering expired if needed
99
- * 4. Formats and outputs the result
100
- *
101
- * @param category - Optional category path to list (lists all if omitted)
102
- * @param options - Command options (includeExpired, format)
103
- * @param storeName - Optional store name from parent command
104
- * @param deps - Optional dependencies for testing
105
- * @throws {InvalidArgumentError} When arguments are invalid
106
- * @throws {CommanderError} When read or parse fails
107
- */
108
- export async function handleList(
109
- ctx: CortexContext,
110
- storeName: string | undefined,
111
- category: string | undefined,
112
- options: ListCommandOptions,
113
- deps: ListHandlerDeps = {}
114
- ): Promise<void> {
115
- const categoryResult = CategoryPath.fromString(category ?? '');
116
- if (!categoryResult.ok()) {
117
- throwCliError(categoryResult.error);
118
- }
119
-
120
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
121
- if (!storeResult.ok()) {
122
- throwCliError(storeResult.error);
123
- }
124
-
125
- const rootResult = storeResult.value.root();
126
- if (!rootResult.ok()) {
127
- throwCliError(rootResult.error);
128
- }
129
-
130
- const root = rootResult.value;
131
- let categoryClient: CategoryClient;
132
-
133
- if (categoryResult.value.isRoot) {
134
- categoryClient = root;
135
- } else {
136
- const categoryClientResult = root.getCategory(categoryResult.value.toString());
137
- if (!categoryClientResult.ok()) {
138
- throwCliError(categoryClientResult.error);
139
- }
140
-
141
- categoryClient = categoryClientResult.value;
142
- }
143
- const now = deps.now ?? ctx.now();
144
- const includeExpired = options.includeExpired ?? false;
145
- const visited = new Set<string>();
146
-
147
- const collectMemories = async (client: CategoryClient): Promise<ListMemoryEntry[]> => {
148
- const categoryKey = client.rawPath;
149
- if (visited.has(categoryKey)) {
150
- return [];
151
- }
152
- visited.add(categoryKey);
153
-
154
- const memoriesResult = await client.listMemories({ includeExpired });
155
- if (!memoriesResult.ok()) {
156
- throwCliError(memoriesResult.error);
157
- }
158
-
159
- const memories: ListMemoryEntry[] = [];
160
- for (const entry of memoriesResult.value) {
161
- const memoryClient = client.getMemory(entry.path.slug.toString());
162
- const memoryResult = await memoryClient.get({ includeExpired: true, now });
163
- if (!memoryResult.ok()) {
164
- if (memoryResult.error.code === 'MEMORY_NOT_FOUND') {
165
- continue;
166
- }
167
- if (!includeExpired && memoryResult.error.code === 'MEMORY_EXPIRED') {
168
- continue;
169
- }
170
- throwCliError(memoryResult.error);
171
- }
172
-
173
- const memory = memoryResult.value;
174
- const isExpired = memory.isExpired(now);
175
- if (!includeExpired && isExpired) {
176
- continue;
177
- }
178
-
179
- memories.push({
180
- path: entry.path.toString(),
181
- tokenEstimate: entry.tokenEstimate,
182
- summary: undefined,
183
- expiresAt: memory.metadata.expiresAt,
184
- isExpired,
185
- });
186
- }
187
-
188
- const subcategoriesResult = await client.listSubcategories();
189
- if (!subcategoriesResult.ok()) {
190
- throwCliError(subcategoriesResult.error);
191
- }
192
-
193
- for (const subcategory of subcategoriesResult.value) {
194
- const subcategoryClientResult = root.getCategory(subcategory.path.toString());
195
- if (!subcategoryClientResult.ok()) {
196
- throwCliError(subcategoryClientResult.error);
197
- }
198
- const subMemories = await collectMemories(subcategoryClientResult.value);
199
- memories.push(...subMemories);
200
- }
201
-
202
- return memories;
203
- };
204
-
205
- const subcategoriesResult = await categoryClient.listSubcategories();
206
- if (!subcategoriesResult.ok()) {
207
- throwCliError(subcategoriesResult.error);
208
- }
209
-
210
- const memories = await collectMemories(categoryClient);
211
- const result: ListResult = {
212
- memories,
213
- subcategories: subcategoriesResult.value.map((subcategory: SubcategoryEntry) => ({
214
- path: subcategory.path.toString(),
215
- memoryCount: subcategory.memoryCount,
216
- description: subcategory.description,
217
- })),
218
- };
219
-
220
- // 3. Format and output
221
- const validFormats = ['yaml', 'json', 'toon'] as const;
222
- const format: OutputFormat = validFormats.includes(options.format as OutputFormat)
223
- ? (options.format as OutputFormat)
224
- : 'yaml';
225
- const output = serialize(result, format);
226
- if (!output.ok()) {
227
- throwCliError({ code: 'SERIALIZE_FAILED', message: output.error.message });
228
- }
229
-
230
- const out = deps.stdout ?? ctx.stdout;
231
- out.write(output.value + '\n');
232
- }
233
-
234
- /**
235
- * The `list` subcommand for browsing memories.
236
- *
237
- * Lists memories in a category, or all memories across all root categories
238
- * if no category is specified. By default, expired memories are excluded.
239
- *
240
- * The `--store` option can be specified either on this command or on the
241
- * parent `memory` command for flexibility.
242
- *
243
- * @example
244
- * ```bash
245
- * cortex memory list
246
- * cortex memory list project/cortex
247
- * cortex memory list --include-expired
248
- * cortex memory list --format json
249
- * cortex memory list -s my-store
250
- * cortex memory -s my-store list
251
- * ```
252
- */
253
- export const listCommand = new Command('list')
254
- .description('List memories in a category')
255
- .argument('[category]', 'Category path to list (lists all if omitted)')
256
- .option('-s, --store <name>', 'Use a specific named store')
257
- .option('-x, --include-expired', 'Include expired memories')
258
- .option('-o, --format <format>', 'Output format (yaml, json, toon)', 'yaml')
259
- .action(async (category, options, command) => {
260
- const parentOpts = command.parent?.opts() as { store?: string } | undefined;
261
- // Allow store to be specified on either the subcommand or parent command
262
- const storeName = options.store ?? parentOpts?.store;
263
- const context = await createCliCommandContext();
264
- if (!context.ok()) {
265
- throwCliError(context.error);
266
- }
267
-
268
- await handleList(context.value, storeName, category, options);
269
- });
@@ -1,85 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
2
- import { CommanderError, InvalidArgumentError } from '@commander-js/extra-typings';
3
- import { mkdtemp, rm } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { MemoryPath, ok } from '@yeseh/cortex-core';
7
-
8
- import { handleMove } from './move.ts';
9
- import {
10
- createCaptureStream,
11
- createMemoryCommandContext,
12
- createMemoryFixture,
13
- createMockMemoryCommandAdapter,
14
- } from './test-helpers.spec.ts';
15
-
16
- describe('handleMove', () => {
17
- let tempDir: string;
18
-
19
- beforeEach(async () => {
20
- tempDir = await mkdtemp(join(tmpdir(), 'cortex-cli-memory-move-'));
21
- });
22
-
23
- afterEach(async () => {
24
- await rm(tempDir, { recursive: true, force: true });
25
- });
26
-
27
- it('should throw InvalidArgumentError for invalid memory paths', async () => {
28
- const ctx = createMemoryCommandContext({
29
- adapter: createMockMemoryCommandAdapter(),
30
- storePath: tempDir,
31
- });
32
-
33
- await expect(handleMove(ctx, undefined, 'invalid', 'project/two')).rejects.toThrow(
34
- InvalidArgumentError,
35
- );
36
- });
37
-
38
- it('should throw CommanderError when store is missing', async () => {
39
- const ctx = createMemoryCommandContext({
40
- adapter: createMockMemoryCommandAdapter(),
41
- storePath: tempDir,
42
- stores: undefined,
43
- });
44
-
45
- await expect(handleMove(ctx, 'missing-store', 'project/one', 'project/two')).rejects.toThrow(
46
- CommanderError,
47
- );
48
- });
49
-
50
- it('should move memory and report output', async () => {
51
- const memory = createMemoryFixture('project/from');
52
- const moveCalls: { from: MemoryPath; to: MemoryPath }[] = [];
53
-
54
- const adapter = createMockMemoryCommandAdapter({
55
- memories: {
56
- load: async (path: MemoryPath) => {
57
- if (path.toString() === 'project/from') {
58
- return ok(memory);
59
- }
60
- return ok(null);
61
- },
62
- move: async (from: MemoryPath, to: MemoryPath) => {
63
- moveCalls.push({ from, to });
64
- return ok(undefined);
65
- },
66
- },
67
- });
68
-
69
- const capture = createCaptureStream();
70
- const ctx = createMemoryCommandContext({
71
- adapter,
72
- storePath: tempDir,
73
- stdout: capture.stream,
74
- });
75
-
76
- await handleMove(ctx, undefined, 'project/from', 'project/to');
77
-
78
- expect(moveCalls).toHaveLength(1);
79
- if (moveCalls[0]) {
80
- expect(moveCalls[0].from.toString()).toBe('project/from');
81
- expect(moveCalls[0].to.toString()).toBe('project/to');
82
- }
83
- expect(capture.getOutput()).toContain('Moved memory project/from to project/to');
84
- });
85
- });
@@ -1,119 +0,0 @@
1
- /**
2
- * Memory move command implementation using Commander.js.
3
- *
4
- * Moves a memory from one path to another within the same store.
5
- *
6
- * @example
7
- * ```bash
8
- * # Move a memory to a new location
9
- * cortex memory move project/old-name project/new-name
10
- *
11
- * # Move with explicit store
12
- * cortex memory --store my-store move project/old project/new
13
- * ```\n */
14
-
15
- import { Command } from '@commander-js/extra-typings';
16
- import { throwCliError } from '../../errors.ts';
17
- import { MemoryPath, type CortexContext } from '@yeseh/cortex-core';
18
- import { createCliCommandContext } from '../../context.ts';
19
- import { serializeOutput, type OutputFormat } from '../../output.ts';
20
- import { resolveDefaultStore } from '../../utils/resolve-default-store.ts';
21
-
22
- /** Options for the move command. */
23
- export interface MoveCommandOptions {
24
- /** Output format (yaml, json, toon) */
25
- format?: string;
26
- }
27
-
28
- /**
29
- * Handler for the memory move command.
30
- * Exported for direct testing without Commander parsing.
31
- *
32
- * @param ctx - CLI context containing Cortex client and streams
33
- * @param storeName - Optional store name from parent command
34
- * @param from - Source memory path
35
- * @param to - Destination memory path
36
- */
37
- export async function handleMove(
38
- ctx: CortexContext,
39
- storeName: string | undefined,
40
- from: string,
41
- to: string,
42
- options: MoveCommandOptions = {}
43
- ): Promise<void> {
44
- const fromResult = MemoryPath.fromString(from);
45
- if (!fromResult.ok()) {
46
- throwCliError(fromResult.error);
47
- }
48
-
49
- const toResult = MemoryPath.fromString(to);
50
- if (!toResult.ok()) {
51
- throwCliError(toResult.error);
52
- }
53
-
54
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
55
- if (!storeResult.ok()) {
56
- throwCliError(storeResult.error);
57
- }
58
-
59
- const store = storeResult.value;
60
- const rootResult = store.root();
61
- if (!rootResult.ok()) {
62
- throwCliError(rootResult.error);
63
- }
64
-
65
- const sourceCategoryResult = fromResult.value.category.isRoot
66
- ? rootResult
67
- : rootResult.value.getCategory(fromResult.value.category.toString());
68
- if (!sourceCategoryResult.ok()) {
69
- throwCliError(sourceCategoryResult.error);
70
- }
71
-
72
- const sourceMemory = sourceCategoryResult.value.getMemory(fromResult.value.slug.toString());
73
- const moveResult = await sourceMemory.move(toResult.value);
74
- if (!moveResult.ok()) {
75
- throwCliError(moveResult.error);
76
- }
77
-
78
- const out = ctx.stdout ?? process.stdout;
79
- const rawFormat = options.format;
80
- if (!rawFormat) {
81
- out.write(`Moved memory ${fromResult.value.toString()} to ${toResult.value.toString()}.\n`);
82
- } else {
83
- const format = rawFormat as OutputFormat;
84
- const serialized = serializeOutput(
85
- {
86
- kind: 'moved-memory',
87
- value: { from: fromResult.value.toString(), to: toResult.value.toString() },
88
- },
89
- format
90
- );
91
- if (!serialized.ok()) {
92
- throwCliError({ code: 'SERIALIZE_FAILED', message: serialized.error.message });
93
- }
94
- out.write(serialized.value + '\n');
95
- }
96
- }
97
-
98
- /**
99
- * The `memory move` subcommand.
100
- *
101
- * Moves a memory from one path to another within the store.
102
- * Both paths must be valid memory slug paths.
103
- *
104
- * The `--store` option is inherited from the parent `memory` command.
105
- */
106
- export const moveCommand = new Command('move')
107
- .description('Move a memory to a new path')
108
- .argument('<from>', 'Source memory path')
109
- .argument('<to>', 'Destination memory path')
110
- .option('-o, --format <format>', 'Output format (yaml, json, toon)')
111
- .action(async (from, to, options, command) => {
112
- const parentOpts = command.parent?.opts() as { store?: string } | undefined;
113
- const context = await createCliCommandContext();
114
- if (!context.ok()) {
115
- throwCliError(context.error);
116
- }
117
-
118
- await handleMove(context.value, parentOpts?.store, from, to, options);
119
- });
@@ -1,79 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
2
- import { CommanderError, InvalidArgumentError } from '@commander-js/extra-typings';
3
- import { mkdtemp, rm } from 'node:fs/promises';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { MemoryPath, ok } from '@yeseh/cortex-core';
7
-
8
- import { handleRemove } from './remove.ts';
9
- import {
10
- createCaptureStream,
11
- createMemoryCommandContext,
12
- createMemoryFixture,
13
- createMockMemoryCommandAdapter,
14
- } from './test-helpers.spec.ts';
15
-
16
- describe('handleRemove', () => {
17
- let tempDir: string;
18
-
19
- beforeEach(async () => {
20
- tempDir = await mkdtemp(join(tmpdir(), 'cortex-cli-memory-remove-'));
21
- });
22
-
23
- afterEach(async () => {
24
- await rm(tempDir, { recursive: true, force: true });
25
- });
26
-
27
- it('should throw InvalidArgumentError for invalid memory paths', async () => {
28
- const ctx = createMemoryCommandContext({
29
- adapter: createMockMemoryCommandAdapter(),
30
- storePath: tempDir,
31
- });
32
-
33
- await expect(handleRemove(ctx, undefined, 'invalid')).rejects.toThrow(
34
- InvalidArgumentError,
35
- );
36
- });
37
-
38
- it('should throw CommanderError when store is missing', async () => {
39
- const ctx = createMemoryCommandContext({
40
- adapter: createMockMemoryCommandAdapter(),
41
- storePath: tempDir,
42
- stores: undefined,
43
- });
44
-
45
- await expect(handleRemove(ctx, 'missing-store', 'project/one')).rejects.toThrow(
46
- CommanderError,
47
- );
48
- });
49
-
50
- it('should remove memory and report output', async () => {
51
- const memory = createMemoryFixture('project/remove');
52
- const removeCalls: MemoryPath[] = [];
53
-
54
- const adapter = createMockMemoryCommandAdapter({
55
- memories: {
56
- load: async () => ok(memory),
57
- remove: async (path: MemoryPath) => {
58
- removeCalls.push(path);
59
- return ok(undefined);
60
- },
61
- },
62
- });
63
-
64
- const capture = createCaptureStream();
65
- const ctx = createMemoryCommandContext({
66
- adapter,
67
- storePath: tempDir,
68
- stdout: capture.stream,
69
- });
70
-
71
- await handleRemove(ctx, undefined, 'project/remove');
72
-
73
- expect(removeCalls).toHaveLength(1);
74
- if (removeCalls[0]) {
75
- expect(removeCalls[0].toString()).toBe('project/remove');
76
- }
77
- expect(capture.getOutput()).toContain('Removed memory project/remove.');
78
- });
79
- });
@@ -1,108 +0,0 @@
1
- /**
2
- * Memory remove command implementation using Commander.js.
3
- *
4
- * Deletes an existing memory at the specified path.
5
- *
6
- * @example
7
- * ```bash
8
- * # Remove a memory
9
- * cortex memory remove project/tech-stack
10
- *
11
- * # Remove memory from a specific store
12
- * cortex memory --store work remove project/notes
13
- * ```
14
- */
15
-
16
- import { Command } from '@commander-js/extra-typings';
17
- import { throwCliError } from '../../errors.ts';
18
- import { MemoryPath, type CortexContext } from '@yeseh/cortex-core';
19
- import { createCliCommandContext } from '../../context.ts';
20
- import { serializeOutput, type OutputFormat } from '../../output.ts';
21
- import { resolveDefaultStore } from '../../utils/resolve-default-store.ts';
22
-
23
- /** Options for the remove command. */
24
- export interface RemoveCommandOptions {
25
- /** Output format (yaml, json, toon) */
26
- format?: string;
27
- }
28
-
29
- /**
30
- * Handler for the memory remove command.
31
- * Exported for direct testing without Commander parsing.
32
- *
33
- * @param ctx - CLI context containing Cortex client and streams
34
- * @param storeName - Optional store name from parent command
35
- * @param path - Memory path to remove (e.g., "project/tech-stack")
36
- */
37
- export async function handleRemove(
38
- ctx: CortexContext,
39
- storeName: string | undefined,
40
- path: string,
41
- options: RemoveCommandOptions = {}
42
- ): Promise<void> {
43
- const pathResult = MemoryPath.fromString(path);
44
- if (!pathResult.ok()) {
45
- throwCliError(pathResult.error);
46
- }
47
-
48
- const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
49
- if (!storeResult.ok()) {
50
- throwCliError(storeResult.error);
51
- }
52
-
53
- const store = storeResult.value;
54
- const rootResult = store.root();
55
- if (!rootResult.ok()) {
56
- throwCliError(rootResult.error);
57
- }
58
-
59
- const categoryResult = pathResult.value.category.isRoot
60
- ? rootResult
61
- : rootResult.value.getCategory(pathResult.value.category.toString());
62
- if (!categoryResult.ok()) {
63
- throwCliError(categoryResult.error);
64
- }
65
-
66
- const memoryClient = categoryResult.value.getMemory(pathResult.value.slug.toString());
67
- const removeResult = await memoryClient.delete();
68
- if (!removeResult.ok()) {
69
- throwCliError(removeResult.error);
70
- }
71
-
72
- const out = ctx.stdout ?? process.stdout;
73
- const rawFormat = options.format;
74
- if (!rawFormat) {
75
- out.write(`Removed memory ${pathResult.value.toString()}.\n`);
76
- } else {
77
- const format = rawFormat as OutputFormat;
78
- const serialized = serializeOutput(
79
- { kind: 'path', value: { path: pathResult.value.toString() } },
80
- format
81
- );
82
- if (!serialized.ok()) {
83
- throwCliError({ code: 'SERIALIZE_FAILED', message: serialized.error.message });
84
- }
85
- out.write(serialized.value + '\n');
86
- }
87
- }
88
-
89
- /**
90
- * The `memory remove` subcommand.
91
- *
92
- * Deletes an existing memory at the specified path.
93
- *
94
- * The `--store` option is inherited from the parent `memory` command.
95
- */
96
- export const removeCommand = new Command('remove')
97
- .description('Delete a memory')
98
- .argument('<path>', 'Memory path to remove')
99
- .option('-o, --format <format>', 'Output format (yaml, json, toon)')
100
- .action(async (path, options, command) => {
101
- const parentOpts = command.parent?.opts() as { store?: string } | undefined;
102
- const context = await createCliCommandContext();
103
- if (!context.ok()) {
104
- throwCliError(context.error);
105
- }
106
-
107
- await handleRemove(context.value, parentOpts?.store, path, options);
108
- });