@yeseh/cortex-cli 0.6.7 → 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-tgrm2cc9.js +0 -1543
  9. package/dist/chunk-tgrm2cc9.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
package/src/output.ts DELETED
@@ -1,119 +0,0 @@
1
- /**
2
- * Output format types and serialization helpers.
3
- *
4
- * This module provides type definitions for CLI output payloads and a thin
5
- * wrapper around the core serialize function. Validation is expected to happen
6
- * at object construction time, not during serialization.
7
- */
8
-
9
- import { err, ok, type Result } from '@yeseh/cortex-core';
10
- import { serialize, type OutputFormat } from '@yeseh/cortex-core';
11
-
12
- // Re-export OutputFormat from core
13
- export type { OutputFormat };
14
-
15
- export interface OutputMemoryMetadata {
16
- createdAt: Date;
17
- updatedAt?: Date;
18
- tags: string[];
19
- source?: string;
20
- tokenEstimate?: number;
21
- expiresAt?: Date;
22
- }
23
-
24
- export interface OutputPath {
25
- path: string;
26
- }
27
-
28
- export interface OutputMemory {
29
- path: string;
30
- metadata: OutputMemoryMetadata;
31
- content: string;
32
- }
33
- export interface OutputMovedMemory {
34
- from: string;
35
- to: string;
36
- }
37
-
38
- export interface OutputCategoryMemory {
39
- path: string;
40
- tokenEstimate?: number;
41
- summary?: string;
42
- }
43
-
44
- export interface OutputSubcategory {
45
- path: string;
46
- memoryCount: number;
47
- }
48
-
49
- export interface OutputCategory {
50
- path: string;
51
- memories: OutputCategoryMemory[];
52
- subcategories: OutputSubcategory[];
53
- }
54
-
55
- export interface OutputCreatedCategory {
56
- path: string;
57
- created: boolean;
58
- }
59
-
60
- export interface OutputStore {
61
- name: string;
62
- path: string;
63
- }
64
-
65
- export interface OutputStoreRegistry {
66
- stores: OutputStore[];
67
- }
68
-
69
- export interface OutputStoreInit {
70
- path: string;
71
- name: string;
72
- }
73
-
74
- export interface OutputInit {
75
- path: string;
76
- categories: string[];
77
- }
78
-
79
- export type OutputPayload =
80
- | { kind: 'memory'; value: OutputMemory }
81
- | { kind: 'moved-memory'; value: OutputMovedMemory }
82
- | { kind: 'path'; value: OutputPath }
83
- | { kind: 'category'; value: OutputCategory }
84
- | { kind: 'created-category'; value: OutputCreatedCategory}
85
- | { kind: 'store'; value: OutputStore }
86
- | { kind: 'store-registry'; value: OutputStoreRegistry }
87
- | { kind: 'store-init'; value: OutputStoreInit }
88
- | { kind: 'init'; value: OutputInit };
89
-
90
- export interface OutputSerializeError {
91
- code: 'INVALID_FORMAT' | 'SERIALIZE_FAILED';
92
- message: string;
93
- }
94
-
95
- /**
96
- * Serialize an output payload to the specified format.
97
- *
98
- * This is a thin wrapper around the core serialize function that extracts
99
- * the value from the discriminated union and handles errors.
100
- *
101
- * @param payload - The output payload to serialize
102
- * @param format - The output format ('yaml', 'json', or 'toon')
103
- * @returns Result with serialized string or error
104
- */
105
- export const serializeOutput = (
106
- payload: OutputPayload,
107
- format: OutputFormat,
108
- ): Result<string, OutputSerializeError> => {
109
- const result = serialize(payload, format);
110
-
111
- if (result.ok()) {
112
- return ok(result.value);
113
- }
114
-
115
- return err({
116
- code: result.error.code === 'INVALID_FORMAT' ? 'INVALID_FORMAT' : 'SERIALIZE_FAILED',
117
- message: result.error.message,
118
- });
119
- };
@@ -1,46 +0,0 @@
1
- /**
2
- * Unit tests for the Commander.js program setup and runProgram entrypoint.
3
- *
4
- * Verifies that the program is correctly wired with the expected name,
5
- * version, and top-level subcommands, and that `runProgram` is exported
6
- * as a callable function.
7
- *
8
- * @module cli/program.spec
9
- */
10
-
11
- import { describe, it, expect } from 'bun:test';
12
- import { program, runProgram } from './program.ts';
13
- import packageInfo from '../package.json'
14
-
15
- describe('program', () => {
16
- it('should have name "cortex"', () => {
17
- expect(program.name()).toBe('cortex');
18
- });
19
-
20
- it(`should have version "${packageInfo.version}"`, () => {
21
- expect(program.version()).toBe(packageInfo.version);
22
- });
23
-
24
- it('should have "memory" command registered', () => {
25
- const names = program.commands.map((c) => c.name());
26
- expect(names).toContain('memory');
27
- });
28
-
29
- it('should have "store" command registered', () => {
30
- const names = program.commands.map((c) => c.name());
31
- expect(names).toContain('store');
32
- });
33
-
34
- it('should have "init" command registered', () => {
35
- const names = program.commands.map((c) => c.name());
36
- expect(names).toContain('init');
37
- });
38
- });
39
-
40
- describe('runProgram', () => {
41
- it('should be a function', () => {
42
- // Do NOT call runProgram() — it would parse process.argv and trigger
43
- // real command execution. We only verify it is exported correctly.
44
- expect(typeof runProgram).toBe('function');
45
- });
46
- });
package/src/program.ts DELETED
@@ -1,75 +0,0 @@
1
- /**
2
- * Commander.js program setup for Cortex CLI.
3
- *
4
- * This module sets up the main Commander program and wires together
5
- * all command groups. It serves as the entry point for the CLI.
6
- *
7
- * @module cli/program
8
- */
9
-
10
- import { Command } from '@commander-js/extra-typings';
11
-
12
- import { memoryCommand } from './memory/index.ts';
13
- import { storeCommand } from './store/index.ts';
14
- import { initCommand } from './commands/init.ts';
15
- import { categoryCommand } from './category/index.ts';
16
- import { createCliLogger } from './observability.ts';
17
- import packageInfo from '../package.json'
18
-
19
- /**
20
- * The main Commander program instance for Cortex CLI.
21
- *
22
- * Configured with:
23
- * - Name: cortex
24
- * - Description: Memory system for AI agents
25
- * - Version: 0.1.0
26
- *
27
- * @example
28
- * ```ts
29
- * import { program } from './program.ts';
30
- * await program.parseAsync(process.argv);
31
- * ```
32
- */
33
- const program = new Command()
34
- .name('cortex')
35
- .description('Memory system for AI agents')
36
- .version(packageInfo.version);
37
-
38
- program.addCommand(memoryCommand);
39
- program.addCommand(categoryCommand);
40
- program.addCommand(storeCommand);
41
- program.addCommand(initCommand);
42
-
43
- export { program };
44
-
45
- /**
46
- * Runs the CLI program by parsing command-line arguments.
47
- *
48
- * This function handles errors gracefully and sets the appropriate
49
- * exit code on failure.
50
- *
51
- * @returns A promise that resolves when the program completes
52
- *
53
- * @example
54
- * ```ts
55
- * import { runProgram } from './program.ts';
56
- * await runProgram();
57
- * ```
58
- */
59
- export const runProgram = async (): Promise<void> => {
60
- try {
61
- await program.parseAsync(process.argv);
62
- }
63
- catch (error) {
64
- // Commander.js handles most errors by writing to stderr and exiting.
65
- // This catch handles any unexpected errors that slip through.
66
- const logger = createCliLogger();
67
- if (error instanceof Error) {
68
- logger.error(error.message);
69
- }
70
- else {
71
- logger.error('An unexpected error occurred');
72
- }
73
- process.exitCode = 1;
74
- }
75
- };
package/src/run.spec.ts DELETED
@@ -1,31 +0,0 @@
1
- /**
2
- * Unit tests for the run.ts CLI entrypoint.
3
- *
4
- * `run.ts` is a side-effect-only module that calls `runProgram()` on import.
5
- * Because importing it would immediately parse `process.argv`, we do NOT
6
- * import it directly in tests. Instead we verify that the function it
7
- * delegates to (`runProgram`) is correctly exported from `program.ts`.
8
- *
9
- * @module cli/run.spec
10
- */
11
-
12
- import { describe, it, expect } from 'bun:test';
13
-
14
- import { runProgram } from './program.ts';
15
-
16
- describe('run module', () => {
17
- it('should delegate to runProgram from program.ts', () => {
18
- // run.ts calls runProgram() — verify the delegated function is callable
19
- expect(typeof runProgram).toBe('function');
20
- });
21
-
22
- it('runProgram should return a Promise', () => {
23
- // Verify the return type without actually executing
24
- // (calling it would parse process.argv)
25
- const returnType = runProgram.constructor.name;
26
- // AsyncFunction or Function — both are acceptable
27
- expect([
28
- 'Function', 'AsyncFunction',
29
- ]).toContain(returnType);
30
- });
31
- });
package/src/run.ts DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * CLI runner script for integration testing.
4
- * This script is spawned as a subprocess by the integration tests.
5
- */
6
-
7
- import { runProgram } from './program.ts';
8
-
9
- runProgram();
@@ -1,131 +0,0 @@
1
- /**
2
- * Unit tests for the store add command handler.
3
- *
4
- * Note: `handleAdd` reads and writes the global config file via `Bun.file()` and
5
- * `Bun.write()`. These I/O calls are not dependency-injected, so the success
6
- * path is not testable at the unit level without filesystem mocking (prohibited
7
- * by project rules). Only the validation paths that throw before any I/O are
8
- * covered here.
9
- *
10
- * Slug normalization behavior: `Slug.from()` normalizes most inputs rather than
11
- * rejecting them (e.g. 'INVALID' → 'invalid', 'my store' → 'my-store'). Only
12
- * empty/whitespace strings fail `Slug.from()` and produce `InvalidArgumentError`.
13
- *
14
- * @module cli/store/commands/add.spec
15
- */
16
-
17
- import { describe, it } from 'bun:test';
18
- import { handleAdd } from './add.ts';
19
- import {
20
- createMockContext,
21
- expectInvalidArgumentError,
22
- expectCommanderError,
23
- } from '../../test-helpers.spec.ts';
24
-
25
- describe('handleAdd', () => {
26
- describe('store name validation', () => {
27
- it('should throw InvalidArgumentError for an empty store name', async () => {
28
- const { ctx } = createMockContext({ stores: {} });
29
-
30
- await expectInvalidArgumentError(
31
- () => handleAdd(ctx, '', '/some/path'),
32
- 'Store name must be a lowercase slug',
33
- );
34
- });
35
-
36
- it('should throw InvalidArgumentError for a whitespace-only store name', async () => {
37
- const { ctx } = createMockContext({ stores: {} });
38
-
39
- await expectInvalidArgumentError(
40
- () => handleAdd(ctx, ' ', '/some/path'),
41
- 'Store name must be a lowercase slug',
42
- );
43
- });
44
- });
45
-
46
- describe('store path validation', () => {
47
- it('should throw InvalidArgumentError for an empty store path', async () => {
48
- const { ctx } = createMockContext({ stores: {} });
49
-
50
- await expectInvalidArgumentError(
51
- () => handleAdd(ctx, 'valid-name', ''),
52
- 'Store path is required',
53
- );
54
- });
55
-
56
- it('should throw InvalidArgumentError for a whitespace-only store path', async () => {
57
- const { ctx } = createMockContext({ stores: {} });
58
-
59
- await expectInvalidArgumentError(
60
- () => handleAdd(ctx, 'valid-name', ' '),
61
- 'Store path is required',
62
- );
63
- });
64
- });
65
-
66
- describe('store already exists check', () => {
67
- it('should throw CommanderError when the store name already exists in context', async () => {
68
- // 'global' is pre-registered in the mock context
69
- const { ctx } = createMockContext({
70
- stores: {
71
- global: {
72
- kind: 'filesystem',
73
- categoryMode: 'free',
74
- categories: {},
75
- properties: { path: '/existing/path' },
76
- },
77
- },
78
- });
79
-
80
- await expectCommanderError(
81
- () => handleAdd(ctx, 'global', '/new/path'),
82
- 'STORE_ALREADY_EXISTS',
83
- 'already registered',
84
- );
85
- });
86
-
87
- it('should throw CommanderError with store name in the error message', async () => {
88
- const { ctx } = createMockContext({
89
- stores: {
90
- 'my-store': {
91
- kind: 'filesystem',
92
- categoryMode: 'free',
93
- categories: {},
94
- properties: { path: '/some/path' },
95
- },
96
- },
97
- });
98
-
99
- await expectCommanderError(
100
- () => handleAdd(ctx, 'my-store', '/new/path'),
101
- 'STORE_ALREADY_EXISTS',
102
- 'my-store',
103
- );
104
- });
105
-
106
- it('should normalize the store name before checking for existence', async () => {
107
- // Slug.from('MY-STORE') → 'my-store', so 'MY-STORE' matches 'my-store'
108
- const { ctx } = createMockContext({
109
- stores: {
110
- 'my-store': {
111
- kind: 'filesystem',
112
- categoryMode: 'free',
113
- categories: {},
114
- properties: { path: '/some/path' },
115
- },
116
- },
117
- });
118
-
119
- await expectCommanderError(
120
- () => handleAdd(ctx, 'MY-STORE', '/new/path'),
121
- 'STORE_ALREADY_EXISTS',
122
- );
123
- });
124
- });
125
-
126
- // NOTE: The success path (steps 4–6) requires reading and writing the global
127
- // config file via Bun.file() / Bun.write(). These calls use the hardcoded
128
- // getDefaultConfigPath() with no injection point, so they cannot be tested
129
- // at the unit level without filesystem mocking (which is prohibited).
130
- // The success path should be covered by integration tests.
131
- });
@@ -1,231 +0,0 @@
1
- /**
2
- * Store add command for registering a new store.
3
- *
4
- * This command registers a new store in the global registry with a given
5
- * name and filesystem path. Paths are resolved relative to the current
6
- * working directory, with support for tilde expansion.
7
- *
8
- * @example
9
- * ```bash
10
- * # Register a store with an absolute path
11
- * cortex store add work /path/to/work/memories
12
- *
13
- * # Register a store with a relative path
14
- * cortex store add project ./cortex
15
- *
16
- * # Register a store with tilde expansion
17
- * cortex store add personal ~/memories
18
- * ```
19
- */
20
-
21
- import { mkdir } from 'node:fs/promises';
22
- import { dirname } from 'node:path';
23
- import { Command } from '@commander-js/extra-typings';
24
- import { throwCliError } from '../../errors.ts';
25
- import { getDefaultConfigPath } from '../../utils/paths.ts';
26
- import { serializeOutput, type OutputStore, type OutputFormat } from '../../output.ts';
27
- import { resolveUserPath } from '../../utils/paths.ts';
28
- import { Slug, parseConfig, type CortexContext, type ConfigStore } from '@yeseh/cortex-core';
29
- import { createCliCommandContext } from '../../context.ts';
30
-
31
- /**
32
- * Options for the add command.
33
- */
34
- export interface AddCommandOptions {
35
- /** Output format (yaml, json, toon) */
36
- format?: string;
37
- }
38
-
39
- /**
40
- * Dependencies for the add command handler.
41
- * Allows injection for testing.
42
- */
43
- export interface AddHandlerDeps {
44
- /** Output stream for writing results (defaults to process.stdout) */
45
- stdout?: NodeJS.WritableStream;
46
- }
47
-
48
- /**
49
- * Validates store name input.
50
- *
51
- * @param name - The raw store name input
52
- * @returns The validated, trimmed store name
53
- * @throws {InvalidArgumentError} When the store name is empty or invalid
54
- */
55
- function validateStoreName(name: string): string {
56
- const slugResult = Slug.from(name);
57
- if (!slugResult.ok()) {
58
- throwCliError({
59
- code: 'INVALID_STORE_NAME',
60
- message: 'Store name must be a lowercase slug (letters, numbers, hyphens).',
61
- });
62
- }
63
-
64
- return slugResult.value.toString();
65
- }
66
-
67
- /**
68
- * Validates and resolves store path input.
69
- *
70
- * @param storePath - The raw store path input
71
- * @param cwd - The current working directory for relative path resolution
72
- * @returns The resolved absolute path
73
- * @throws {InvalidArgumentError} When the store path is empty
74
- */
75
- function validateAndResolvePath(storePath: string, cwd: string): string {
76
- const trimmed = storePath.trim();
77
- if (!trimmed) {
78
- throwCliError({ code: 'INVALID_STORE_PATH', message: 'Store path is required.' });
79
- }
80
- return resolveUserPath(trimmed, cwd);
81
- }
82
-
83
- /**
84
- * Writes the serialized output to the output stream.
85
- *
86
- * @param output - The store output payload
87
- * @param format - The output format
88
- * @param stdout - The output stream
89
- */
90
- function writeOutput(
91
- output: OutputStore,
92
- format: OutputFormat,
93
- stdout: NodeJS.WritableStream,
94
- ): void {
95
- const serialized = serializeOutput({ kind: 'store', value: output }, format);
96
- if (!serialized.ok()) {
97
- throwCliError({ code: 'SERIALIZE_FAILED', message: serialized.error.message });
98
- }
99
- stdout.write(serialized.value + '\n');
100
- }
101
-
102
- /**
103
- * Handles the add command execution.
104
- *
105
- * This function:
106
- * 1. Validates the store name format
107
- * 2. Validates and resolves the store path
108
- * 3. Checks if store already exists in context
109
- * 4. Reads current config file
110
- * 5. Adds the store to the config and saves
111
- * 6. Outputs the result
112
- *
113
- * @param ctx - The CortexContext with loaded configuration
114
- * @param name - The store name to register
115
- * @param storePath - The filesystem path to the store
116
- * @param options - Command options (format)
117
- * @param deps - Optional dependencies for testing
118
- * @throws {InvalidArgumentError} When the store name or path is invalid
119
- * @throws {CommanderError} When the store already exists or config operations fail
120
- */
121
- export async function handleAdd(
122
- ctx: CortexContext,
123
- name: string,
124
- storePath: string,
125
- options: AddCommandOptions = {},
126
- deps: AddHandlerDeps = {},
127
- ): Promise<void> {
128
- const cwd = ctx.cwd ?? process.cwd();
129
- const stdout = deps.stdout ?? ctx.stdout ?? process.stdout;
130
- const configPath = getDefaultConfigPath();
131
-
132
- // 1. Validate inputs
133
- const trimmedName = validateStoreName(name);
134
- const resolvedPath = validateAndResolvePath(storePath, cwd);
135
-
136
- // 2. Check if store already exists in context
137
- if (ctx.stores[trimmedName]) {
138
- throwCliError({
139
- code: 'STORE_ALREADY_EXISTS',
140
- message: `Store '${trimmedName}' is already registered.`,
141
- });
142
- }
143
-
144
- // 3. Read current config file (or start with empty config if it doesn't exist)
145
- const configFile = Bun.file(configPath);
146
- let parsedConfig: { settings?: unknown; stores: Record<string, unknown> };
147
-
148
- if (await configFile.exists()) {
149
- let configContents: string;
150
- try {
151
- configContents = await configFile.text();
152
- }
153
- catch {
154
- throwCliError({
155
- code: 'CONFIG_READ_FAILED',
156
- message: `Failed to read config at ${configPath}`,
157
- });
158
- }
159
-
160
- const parsed = parseConfig(configContents!);
161
- if (!parsed.ok()) {
162
- throwCliError(parsed.error);
163
- }
164
- parsedConfig = parsed.value;
165
- }
166
- else {
167
- // Config doesn't exist yet — start with empty config
168
- parsedConfig = { settings: undefined, stores: {} };
169
- // Ensure config directory exists
170
- await mkdir(dirname(configPath), { recursive: true });
171
- }
172
-
173
- // 4. Add new store to config
174
- const newStore: ConfigStore = {
175
- kind: 'filesystem',
176
- categoryMode: 'free',
177
- categories: {},
178
- properties: { path: resolvedPath },
179
- };
180
-
181
- const updatedConfig = {
182
- ...parsedConfig,
183
- stores: {
184
- ...parsedConfig.stores,
185
- [trimmedName]: newStore,
186
- },
187
- };
188
-
189
- // 5. Write updated config
190
- const serialized = Bun.YAML.stringify(updatedConfig, null, 2);
191
- try {
192
- await Bun.write(configPath, serialized);
193
- }
194
- catch {
195
- throwCliError({
196
- code: 'CONFIG_WRITE_FAILED',
197
- message: `Failed to write config at ${configPath}`,
198
- });
199
- }
200
-
201
- // 6. Output result
202
- const output: OutputStore = { name: trimmedName, path: resolvedPath };
203
- const format: OutputFormat = (options.format as OutputFormat) ?? 'yaml';
204
- writeOutput(output, format, stdout);
205
- }
206
-
207
- /**
208
- * The `add` subcommand for registering a new store.
209
- *
210
- * Registers a store with the given name and filesystem path. The path is
211
- * resolved relative to the current working directory, with support for
212
- * tilde expansion.
213
- *
214
- * @example
215
- * ```bash
216
- * cortex store add work /path/to/work/memories
217
- * cortex store add project ./cortex --format json
218
- * ```
219
- */
220
- export const addCommand = new Command('add')
221
- .description('Register a new store')
222
- .argument('<name>', 'Store name (lowercase slug)')
223
- .argument('<path>', 'Filesystem path to the store')
224
- .option('-o, --format <format>', 'Output format (yaml, json, toon)', 'yaml')
225
- .action(async (name, path, options) => {
226
- const context = await createCliCommandContext();
227
- if (!context.ok()) {
228
- throwCliError(context.error);
229
- }
230
- await handleAdd(context.value, name, path, options);
231
- });