@yeseh/cortex-cli 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/program.js +1538 -5
- package/dist/program.js.map +32 -3
- package/dist/run.d.ts +0 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +3 -4
- package/dist/run.js.map +3 -3
- package/package.json +4 -6
- package/dist/chunk-dsfj4baj.js +0 -1543
- package/dist/chunk-dsfj4baj.js.map +0 -38
- package/src/category/commands/create.spec.ts +0 -139
- package/src/category/commands/create.ts +0 -119
- package/src/category/index.ts +0 -24
- package/src/commands/init.spec.ts +0 -203
- package/src/commands/init.ts +0 -301
- package/src/context.spec.ts +0 -60
- package/src/context.ts +0 -170
- package/src/errors.spec.ts +0 -264
- package/src/errors.ts +0 -105
- package/src/memory/commands/add.spec.ts +0 -169
- package/src/memory/commands/add.ts +0 -158
- package/src/memory/commands/definitions.spec.ts +0 -80
- package/src/memory/commands/list.spec.ts +0 -123
- package/src/memory/commands/list.ts +0 -269
- package/src/memory/commands/move.spec.ts +0 -85
- package/src/memory/commands/move.ts +0 -119
- package/src/memory/commands/remove.spec.ts +0 -79
- package/src/memory/commands/remove.ts +0 -108
- package/src/memory/commands/show.spec.ts +0 -71
- package/src/memory/commands/show.ts +0 -165
- package/src/memory/commands/test-helpers.spec.ts +0 -127
- package/src/memory/commands/update.spec.ts +0 -86
- package/src/memory/commands/update.ts +0 -230
- package/src/memory/index.spec.ts +0 -59
- package/src/memory/index.ts +0 -44
- package/src/memory/parsing.spec.ts +0 -105
- package/src/memory/parsing.ts +0 -22
- package/src/observability.spec.ts +0 -126
- package/src/observability.ts +0 -82
- package/src/output.spec.ts +0 -835
- package/src/output.ts +0 -119
- package/src/program.spec.ts +0 -46
- package/src/program.ts +0 -75
- package/src/run.spec.ts +0 -31
- package/src/run.ts +0 -9
- package/src/store/commands/add.spec.ts +0 -131
- package/src/store/commands/add.ts +0 -231
- package/src/store/commands/init.spec.ts +0 -220
- package/src/store/commands/init.ts +0 -272
- package/src/store/commands/list.spec.ts +0 -175
- package/src/store/commands/list.ts +0 -102
- package/src/store/commands/prune.spec.ts +0 -120
- package/src/store/commands/prune.ts +0 -152
- package/src/store/commands/reindexs.spec.ts +0 -94
- package/src/store/commands/reindexs.ts +0 -97
- package/src/store/commands/remove.spec.ts +0 -97
- package/src/store/commands/remove.ts +0 -189
- package/src/store/index.spec.ts +0 -60
- package/src/store/index.ts +0 -49
- package/src/store/utils/resolve-store-name.spec.ts +0 -62
- package/src/store/utils/resolve-store-name.ts +0 -79
- package/src/test-helpers.spec.ts +0 -430
- package/src/tests/cli.integration.spec.ts +0 -1306
- package/src/toon.spec.ts +0 -183
- package/src/toon.ts +0 -462
- package/src/utils/git.spec.ts +0 -95
- package/src/utils/git.ts +0 -51
- package/src/utils/input.spec.ts +0 -326
- package/src/utils/input.ts +0 -150
- package/src/utils/paths.spec.ts +0 -235
- package/src/utils/paths.ts +0 -75
- package/src/utils/prompts.spec.ts +0 -23
- package/src/utils/prompts.ts +0 -88
- package/src/utils/resolve-default-store.spec.ts +0 -135
- package/src/utils/resolve-default-store.ts +0 -74
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Store remove command for unregistering a store.
|
|
3
|
-
*
|
|
4
|
-
* This command removes a store from the global registry. Note that this
|
|
5
|
-
* only unregisters the store - it does not delete the actual data.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```bash
|
|
9
|
-
* # Remove a store from the registry
|
|
10
|
-
* cortex store remove work
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { Command } from '@commander-js/extra-typings';
|
|
15
|
-
import { throwCliError } from '../../errors.ts';
|
|
16
|
-
import { getDefaultConfigPath } from '../../utils/paths.ts';
|
|
17
|
-
import { serializeOutput, type OutputStore, type OutputFormat } from '../../output.ts';
|
|
18
|
-
import { Slug, parseConfig, type CortexContext } from '@yeseh/cortex-core';
|
|
19
|
-
import { createCliCommandContext } from '../../context.ts';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Options for the remove command.
|
|
23
|
-
*/
|
|
24
|
-
export interface RemoveCommandOptions {
|
|
25
|
-
/** Output format (yaml, json, toon) */
|
|
26
|
-
format?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Dependencies for the remove command handler.
|
|
31
|
-
* Allows injection for testing.
|
|
32
|
-
*/
|
|
33
|
-
export interface RemoveHandlerDeps {
|
|
34
|
-
/** Output stream for writing results (defaults to process.stdout) */
|
|
35
|
-
stdout?: NodeJS.WritableStream;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Validates store name input.
|
|
40
|
-
*
|
|
41
|
-
* @param name - The raw store name input
|
|
42
|
-
* @returns The validated, trimmed store name
|
|
43
|
-
* @throws {InvalidArgumentError} When the store name is empty or invalid
|
|
44
|
-
*/
|
|
45
|
-
function validateStoreName(name: string): string {
|
|
46
|
-
const slugResult = Slug.from(name);
|
|
47
|
-
if (!slugResult.ok()) {
|
|
48
|
-
throwCliError({
|
|
49
|
-
code: 'INVALID_STORE_NAME',
|
|
50
|
-
message: 'Store name must be a lowercase slug (letters, numbers, hyphens).',
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return slugResult.value.toString();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Writes the serialized output to the output stream.
|
|
59
|
-
*
|
|
60
|
-
* @param output - The store output payload
|
|
61
|
-
* @param format - The output format
|
|
62
|
-
* @param stdout - The output stream
|
|
63
|
-
*/
|
|
64
|
-
function writeOutput(
|
|
65
|
-
output: OutputStore,
|
|
66
|
-
format: OutputFormat,
|
|
67
|
-
stdout: NodeJS.WritableStream,
|
|
68
|
-
): void {
|
|
69
|
-
const serialized = serializeOutput({ kind: 'store', value: output }, format);
|
|
70
|
-
if (!serialized.ok()) {
|
|
71
|
-
throwCliError({ code: 'SERIALIZE_FAILED', message: serialized.error.message });
|
|
72
|
-
}
|
|
73
|
-
stdout.write(serialized.value + '\n');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Handles the remove command execution.
|
|
78
|
-
*
|
|
79
|
-
* This function:
|
|
80
|
-
* 1. Validates the store name format
|
|
81
|
-
* 2. Checks that the store exists in context
|
|
82
|
-
* 3. Reads current config file
|
|
83
|
-
* 4. Removes the store from the config and saves
|
|
84
|
-
* 5. Outputs the result
|
|
85
|
-
*
|
|
86
|
-
* @param ctx - The Cortex context
|
|
87
|
-
* @param name - The store name to unregister
|
|
88
|
-
* @param options - Command options (format)
|
|
89
|
-
* @param deps - Optional dependencies for testing
|
|
90
|
-
* @throws {InvalidArgumentError} When the store name is invalid
|
|
91
|
-
* @throws {CommanderError} When the store doesn't exist or config operations fail
|
|
92
|
-
*/
|
|
93
|
-
export async function handleRemove(
|
|
94
|
-
ctx: CortexContext,
|
|
95
|
-
name: string,
|
|
96
|
-
options: RemoveCommandOptions = {},
|
|
97
|
-
deps: RemoveHandlerDeps = {},
|
|
98
|
-
): Promise<void> {
|
|
99
|
-
const stdout = deps.stdout ?? ctx.stdout ?? process.stdout;
|
|
100
|
-
const configPath = getDefaultConfigPath();
|
|
101
|
-
|
|
102
|
-
// 1. Validate store name
|
|
103
|
-
const trimmedName = validateStoreName(name);
|
|
104
|
-
|
|
105
|
-
// 2. Check if store exists in context
|
|
106
|
-
const existingStore = ctx.stores[trimmedName];
|
|
107
|
-
if (!existingStore) {
|
|
108
|
-
throwCliError({
|
|
109
|
-
code: 'STORE_NOT_FOUND',
|
|
110
|
-
message: `Store '${trimmedName}' is not registered.`,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Get the path from the existing store before removing
|
|
115
|
-
const storePath = (existingStore.properties as { path: string }).path;
|
|
116
|
-
|
|
117
|
-
// 3. Read current config file
|
|
118
|
-
const configFile = Bun.file(configPath);
|
|
119
|
-
if (!(await configFile.exists())) {
|
|
120
|
-
throwCliError({
|
|
121
|
-
code: 'CONFIG_NOT_FOUND',
|
|
122
|
-
message: `Config file not found at ${configPath}. Cannot remove store.`,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
let configContents: string;
|
|
126
|
-
try {
|
|
127
|
-
configContents = await configFile.text();
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
throwCliError({
|
|
131
|
-
code: 'CONFIG_READ_FAILED',
|
|
132
|
-
message: `Failed to read config at ${configPath}`,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const configResult = parseConfig(configContents);
|
|
137
|
-
if (!configResult.ok()) {
|
|
138
|
-
throwCliError(configResult.error);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// 4. Remove store from config
|
|
142
|
-
const { [trimmedName]: _removed, ...remainingStores } = configResult.value.stores ?? {};
|
|
143
|
-
|
|
144
|
-
const updatedConfig = {
|
|
145
|
-
...configResult.value,
|
|
146
|
-
stores: remainingStores,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// 5. Write updated config
|
|
150
|
-
const serialized = Bun.YAML.stringify(updatedConfig, null, 2);
|
|
151
|
-
try {
|
|
152
|
-
await Bun.write(configPath, serialized);
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
throwCliError({
|
|
156
|
-
code: 'CONFIG_WRITE_FAILED',
|
|
157
|
-
message: `Failed to write config at ${configPath}`,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 6. Output result
|
|
162
|
-
const output: OutputStore = { name: trimmedName, path: storePath };
|
|
163
|
-
const format: OutputFormat = (options.format as OutputFormat) ?? 'yaml';
|
|
164
|
-
writeOutput(output, format, stdout);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* The `remove` subcommand for unregistering a store.
|
|
169
|
-
*
|
|
170
|
-
* Removes a store from the registry. This only unregisters the store
|
|
171
|
-
* from the global registry - it does not delete the actual data.
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```bash
|
|
175
|
-
* cortex store remove work
|
|
176
|
-
* cortex store remove project --format json
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
export const removeCommand = new Command('remove')
|
|
180
|
-
.description('Unregister a store from the registry')
|
|
181
|
-
.argument('<name>', 'Store name to remove')
|
|
182
|
-
.option('-o, --format <format>', 'Output format (yaml, json, toon)', 'yaml')
|
|
183
|
-
.action(async (name, options) => {
|
|
184
|
-
const context = await createCliCommandContext();
|
|
185
|
-
if (!context.ok()) {
|
|
186
|
-
throwCliError(context.error);
|
|
187
|
-
}
|
|
188
|
-
await handleRemove(context.value, name, options);
|
|
189
|
-
});
|
package/src/store/index.spec.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for store command group wiring.
|
|
3
|
-
*
|
|
4
|
-
* Verifies that the `store` command group is correctly configured.
|
|
5
|
-
*
|
|
6
|
-
* @module cli/store/index.spec
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect } from 'bun:test';
|
|
10
|
-
|
|
11
|
-
import { storeCommand } from './index.ts';
|
|
12
|
-
|
|
13
|
-
// ── storeCommand wiring ──────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
describe('storeCommand', () => {
|
|
16
|
-
it('should have name "store"', () => {
|
|
17
|
-
expect(storeCommand.name()).toBe('store');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should have description', () => {
|
|
21
|
-
expect(storeCommand.description()).toBeTruthy();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should have --store option', () => {
|
|
25
|
-
const storeOption = storeCommand.options.find((o) => o.long === '--store');
|
|
26
|
-
expect(storeOption).toBeDefined();
|
|
27
|
-
expect(storeOption?.short).toBe('-s');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should have "list" subcommand', () => {
|
|
31
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
32
|
-
expect(names).toContain('list');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should have "add" subcommand', () => {
|
|
36
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
37
|
-
expect(names).toContain('add');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should have "remove" subcommand', () => {
|
|
41
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
42
|
-
expect(names).toContain('remove');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should have "init" subcommand', () => {
|
|
46
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
47
|
-
expect(names).toContain('init');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should have "prune" subcommand', () => {
|
|
51
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
52
|
-
expect(names).toContain('prune');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should have "reindex" subcommand', () => {
|
|
56
|
-
const names = storeCommand.commands.map((c) => c.name());
|
|
57
|
-
expect(names).toContain('reindex');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
package/src/store/index.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Store command group for the Cortex CLI.
|
|
3
|
-
*
|
|
4
|
-
* Provides store management operations including listing, adding, removing,
|
|
5
|
-
* and initializing memory stores. Also includes maintenance commands for
|
|
6
|
-
* pruning expired memories and reindexing stores.
|
|
7
|
-
*
|
|
8
|
-
* The `--store` option is defined at the group level and inherited by all
|
|
9
|
-
* subcommands, allowing users to specify which store to operate on.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```bash
|
|
13
|
-
* cortex store list # List all registered stores
|
|
14
|
-
* cortex store add <name> <path> # Register a store
|
|
15
|
-
* cortex store remove <name> # Unregister a store
|
|
16
|
-
* cortex store init [path] # Initialize a new store
|
|
17
|
-
* cortex store prune # Remove expired memories
|
|
18
|
-
* cortex store reindex # Rebuild store indexes
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
import { Command } from '@commander-js/extra-typings';
|
|
22
|
-
|
|
23
|
-
import { listCommand } from './commands/list.ts';
|
|
24
|
-
import { addCommand } from './commands/add.ts';
|
|
25
|
-
import { removeCommand } from './commands/remove.ts';
|
|
26
|
-
import { initCommand } from './commands/init.ts';
|
|
27
|
-
import { pruneCommand } from './commands/prune.ts';
|
|
28
|
-
import { reindexCommand } from './commands/reindexs.ts';
|
|
29
|
-
|
|
30
|
-
// Re-export resolveStoreName from its own utility module so callers that
|
|
31
|
-
// previously imported it from this file continue to work.
|
|
32
|
-
export { resolveStoreName } from './utils/resolve-store-name.ts';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Store command group.
|
|
36
|
-
*
|
|
37
|
-
* The `--store` option is inherited by all subcommands, allowing operations
|
|
38
|
-
* to target a specific named store rather than using automatic store resolution.
|
|
39
|
-
*/
|
|
40
|
-
export const storeCommand = new Command('store')
|
|
41
|
-
.description('Store management')
|
|
42
|
-
.option('-s, --store <name>', 'Use a specific named store');
|
|
43
|
-
|
|
44
|
-
storeCommand.addCommand(listCommand);
|
|
45
|
-
storeCommand.addCommand(addCommand);
|
|
46
|
-
storeCommand.addCommand(removeCommand);
|
|
47
|
-
storeCommand.addCommand(initCommand);
|
|
48
|
-
storeCommand.addCommand(pruneCommand);
|
|
49
|
-
storeCommand.addCommand(reindexCommand);
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from 'bun:test';
|
|
2
|
-
import { InvalidArgumentError } from '@commander-js/extra-typings';
|
|
3
|
-
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { basename, join } from 'node:path';
|
|
6
|
-
|
|
7
|
-
import { runGitCommand } from '../../utils/git.ts';
|
|
8
|
-
import { resolveStoreName } from './resolve-store-name.ts';
|
|
9
|
-
|
|
10
|
-
const THIS_DIR = import.meta.dir;
|
|
11
|
-
|
|
12
|
-
describe('resolveStoreName', () => {
|
|
13
|
-
const createdDirs: string[] = [];
|
|
14
|
-
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
for (const dir of createdDirs.splice(0)) {
|
|
17
|
-
await rm(dir, { recursive: true, force: true });
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should prioritize explicit name when provided', async () => {
|
|
22
|
-
const name = await resolveStoreName('/any/path', 'My Project Store!');
|
|
23
|
-
expect(name).toBe('my-project-store');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should throw InvalidArgumentError for invalid explicit name', async () => {
|
|
27
|
-
await expect(resolveStoreName('/any/path', ' ')).rejects.toThrow(InvalidArgumentError);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should derive store name from git toplevel basename', async () => {
|
|
31
|
-
const toplevelResult = await runGitCommand([
|
|
32
|
-
'rev-parse', '--show-toplevel',
|
|
33
|
-
], THIS_DIR);
|
|
34
|
-
if (!toplevelResult.ok) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const expected = basename(toplevelResult.value)
|
|
39
|
-
.toLowerCase()
|
|
40
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
41
|
-
.replace(/^-|-$/g, '');
|
|
42
|
-
|
|
43
|
-
const resolved = await resolveStoreName(THIS_DIR);
|
|
44
|
-
expect(resolved).toBe(expected);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should fall back to folder basename when not in a git repository', async () => {
|
|
48
|
-
const dir = await mkdtemp(join(tmpdir(), 'Resolve Name Folder '));
|
|
49
|
-
createdDirs.push(dir);
|
|
50
|
-
|
|
51
|
-
const resolved = await resolveStoreName(dir);
|
|
52
|
-
expect(resolved).toMatch(/^resolve-name-folder-/);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should return an empty slug when folder basename normalizes to nothing', async () => {
|
|
56
|
-
const dir = join(tmpdir(), '!!!');
|
|
57
|
-
await mkdir(dir, { recursive: true });
|
|
58
|
-
createdDirs.push(dir);
|
|
59
|
-
|
|
60
|
-
await expect(resolveStoreName(dir)).resolves.toBe('');
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility for resolving the store name using a priority-based strategy.
|
|
3
|
-
*
|
|
4
|
-
* Extracted into its own module to avoid a circular dependency:
|
|
5
|
-
* store/commands/init.ts → store/index.ts → store/commands/init.ts
|
|
6
|
-
*
|
|
7
|
-
* @module cli/store/utils/resolve-store-name
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { Slug } from '@yeseh/cortex-core';
|
|
11
|
-
import { basename } from 'node:path';
|
|
12
|
-
import { detectGitRepoName } from '../../utils/git.ts';
|
|
13
|
-
import { throwCliError } from '../../errors.ts';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Resolves the store name using a priority-based strategy.
|
|
17
|
-
*
|
|
18
|
-
* Name resolution follows this precedence:
|
|
19
|
-
* 1. **Explicit name** - If `--name` option is provided, use it directly
|
|
20
|
-
* 2. **Git detection** - Auto-detect from git repository directory name
|
|
21
|
-
* 3. **Folder name** - Use the current folder name if git detection fails
|
|
22
|
-
* 4. **Error** - Fail with guidance to use `--name` flag
|
|
23
|
-
*
|
|
24
|
-
* Git repository names are normalized to valid store name format:
|
|
25
|
-
* - Converted to lowercase
|
|
26
|
-
* - Non-alphanumeric characters replaced with hyphens
|
|
27
|
-
* - Leading/trailing hyphens removed
|
|
28
|
-
*
|
|
29
|
-
* @param cwd - Current working directory
|
|
30
|
-
* @param explicitName - Optional explicit name provided via `--name` option
|
|
31
|
-
* @returns The validated store name
|
|
32
|
-
* @throws {InvalidArgumentError} When the name is invalid
|
|
33
|
-
* @throws {CommanderError} When git detection fails and no name provided
|
|
34
|
-
*/
|
|
35
|
-
export async function resolveStoreName(cwd: string, explicitName?: string): Promise<string> {
|
|
36
|
-
// 1. Use explicit name if provided
|
|
37
|
-
if (explicitName) {
|
|
38
|
-
const slugResult = Slug.from(explicitName);
|
|
39
|
-
if (!slugResult.ok()) {
|
|
40
|
-
throwCliError({
|
|
41
|
-
code: 'INVALID_STORE_NAME',
|
|
42
|
-
message: 'Store name must be a lowercase slug (letters, numbers, hyphens).',
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return slugResult.value.toString();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 2. Try git detection
|
|
50
|
-
const gitName = await detectGitRepoName(cwd);
|
|
51
|
-
if (gitName) {
|
|
52
|
-
// Convert to valid store name (lowercase slug)
|
|
53
|
-
const normalized = gitName
|
|
54
|
-
.toLowerCase()
|
|
55
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
56
|
-
.replace(/^-|-$/g, '');
|
|
57
|
-
const slugResult = Slug.from(normalized);
|
|
58
|
-
if (!slugResult.ok()) {
|
|
59
|
-
throwCliError({
|
|
60
|
-
code: 'INVALID_STORE_NAME',
|
|
61
|
-
message: 'Could not derive valid store name from git repo.',
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
return slugResult.value.toString();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 3. Use the leaf folder name if git detection fails
|
|
68
|
-
const folderName = basename(cwd);
|
|
69
|
-
const slugResult = Slug.from(folderName);
|
|
70
|
-
if (slugResult.ok()) {
|
|
71
|
-
return slugResult.value.toString();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 4. Error: require --name
|
|
75
|
-
throwCliError({
|
|
76
|
-
code: 'GIT_REPO_REQUIRED',
|
|
77
|
-
message: 'Not in a git repository. Use --name to specify the store name.',
|
|
78
|
-
});
|
|
79
|
-
}
|