@yeseh/cortex-cli 0.6.0 → 0.6.3
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/package.json +3 -3
- package/src/category/commands/create.ts +7 -3
- package/src/context.ts +15 -20
- package/src/memory/commands/add.ts +2 -1
- package/src/memory/commands/list.ts +2 -1
- package/src/memory/commands/move.ts +6 -2
- package/src/memory/commands/remove.ts +6 -2
- package/src/memory/commands/show.ts +3 -2
- package/src/memory/commands/update.ts +2 -1
- package/src/run.ts +0 -0
- package/src/store/commands/init.spec.ts +62 -78
- package/src/store/commands/init.ts +31 -15
- package/src/store/commands/prune.ts +4 -4
- package/src/store/commands/reindexs.ts +4 -3
- package/src/tests/cli.integration.spec.ts +136 -0
- package/src/utils/input.ts +9 -4
- package/src/utils/resolve-default-store.spec.ts +135 -0
- package/src/utils/resolve-default-store.ts +74 -0
- package/dist/category/commands/create.d.ts +0 -44
- package/dist/category/commands/create.d.ts.map +0 -1
- package/dist/category/commands/create.spec.d.ts +0 -7
- package/dist/category/commands/create.spec.d.ts.map +0 -1
- package/dist/category/index.d.ts +0 -19
- package/dist/category/index.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -58
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.spec.d.ts +0 -2
- package/dist/commands/init.spec.d.ts.map +0 -1
- package/dist/context.d.ts +0 -18
- package/dist/context.d.ts.map +0 -1
- package/dist/context.spec.d.ts +0 -2
- package/dist/context.spec.d.ts.map +0 -1
- package/dist/create-cli-command.d.ts +0 -23
- package/dist/create-cli-command.d.ts.map +0 -1
- package/dist/create-cli-command.spec.d.ts +0 -10
- package/dist/create-cli-command.spec.d.ts.map +0 -1
- package/dist/errors.d.ts +0 -57
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.spec.d.ts +0 -2
- package/dist/errors.spec.d.ts.map +0 -1
- package/dist/input.d.ts +0 -42
- package/dist/input.d.ts.map +0 -1
- package/dist/input.spec.d.ts +0 -2
- package/dist/input.spec.d.ts.map +0 -1
- package/dist/memory/commands/add.d.ts +0 -62
- package/dist/memory/commands/add.d.ts.map +0 -1
- package/dist/memory/commands/add.spec.d.ts +0 -7
- package/dist/memory/commands/add.spec.d.ts.map +0 -1
- package/dist/memory/commands/definitions.spec.d.ts +0 -10
- package/dist/memory/commands/definitions.spec.d.ts.map +0 -1
- package/dist/memory/commands/handlers.spec.d.ts +0 -2
- package/dist/memory/commands/handlers.spec.d.ts.map +0 -1
- package/dist/memory/commands/list.d.ts +0 -119
- package/dist/memory/commands/list.d.ts.map +0 -1
- package/dist/memory/commands/list.spec.d.ts +0 -2
- package/dist/memory/commands/list.spec.d.ts.map +0 -1
- package/dist/memory/commands/move.d.ts +0 -42
- package/dist/memory/commands/move.d.ts.map +0 -1
- package/dist/memory/commands/move.spec.d.ts +0 -2
- package/dist/memory/commands/move.spec.d.ts.map +0 -1
- package/dist/memory/commands/remove.d.ts +0 -41
- package/dist/memory/commands/remove.d.ts.map +0 -1
- package/dist/memory/commands/remove.spec.d.ts +0 -2
- package/dist/memory/commands/remove.spec.d.ts.map +0 -1
- package/dist/memory/commands/show.d.ts +0 -81
- package/dist/memory/commands/show.d.ts.map +0 -1
- package/dist/memory/commands/show.spec.d.ts +0 -2
- package/dist/memory/commands/show.spec.d.ts.map +0 -1
- package/dist/memory/commands/test-helpers.spec.d.ts +0 -19
- package/dist/memory/commands/test-helpers.spec.d.ts.map +0 -1
- package/dist/memory/commands/update.d.ts +0 -73
- package/dist/memory/commands/update.d.ts.map +0 -1
- package/dist/memory/commands/update.spec.d.ts +0 -2
- package/dist/memory/commands/update.spec.d.ts.map +0 -1
- package/dist/memory/index.d.ts +0 -29
- package/dist/memory/index.d.ts.map +0 -1
- package/dist/memory/index.spec.d.ts +0 -10
- package/dist/memory/index.spec.d.ts.map +0 -1
- package/dist/memory/parsing.d.ts +0 -3
- package/dist/memory/parsing.d.ts.map +0 -1
- package/dist/memory/parsing.spec.d.ts +0 -7
- package/dist/memory/parsing.spec.d.ts.map +0 -1
- package/dist/output.d.ts +0 -87
- package/dist/output.d.ts.map +0 -1
- package/dist/output.spec.d.ts +0 -2
- package/dist/output.spec.d.ts.map +0 -1
- package/dist/paths.d.ts +0 -27
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.spec.d.ts +0 -7
- package/dist/paths.spec.d.ts.map +0 -1
- package/dist/program.d.ts +0 -41
- package/dist/program.d.ts.map +0 -1
- package/dist/program.spec.d.ts +0 -11
- package/dist/program.spec.d.ts.map +0 -1
- package/dist/run.d.ts +0 -7
- package/dist/run.d.ts.map +0 -1
- package/dist/run.spec.d.ts +0 -12
- package/dist/run.spec.d.ts.map +0 -1
- package/dist/store/commands/add.d.ts +0 -73
- package/dist/store/commands/add.d.ts.map +0 -1
- package/dist/store/commands/add.spec.d.ts +0 -17
- package/dist/store/commands/add.spec.d.ts.map +0 -1
- package/dist/store/commands/init.d.ts +0 -75
- package/dist/store/commands/init.d.ts.map +0 -1
- package/dist/store/commands/init.spec.d.ts +0 -7
- package/dist/store/commands/init.spec.d.ts.map +0 -1
- package/dist/store/commands/list.d.ts +0 -62
- package/dist/store/commands/list.d.ts.map +0 -1
- package/dist/store/commands/list.spec.d.ts +0 -7
- package/dist/store/commands/list.spec.d.ts.map +0 -1
- package/dist/store/commands/prune.d.ts +0 -92
- package/dist/store/commands/prune.d.ts.map +0 -1
- package/dist/store/commands/prune.spec.d.ts +0 -7
- package/dist/store/commands/prune.spec.d.ts.map +0 -1
- package/dist/store/commands/reindexs.d.ts +0 -54
- package/dist/store/commands/reindexs.d.ts.map +0 -1
- package/dist/store/commands/reindexs.spec.d.ts +0 -7
- package/dist/store/commands/reindexs.spec.d.ts.map +0 -1
- package/dist/store/commands/remove.d.ts +0 -63
- package/dist/store/commands/remove.d.ts.map +0 -1
- package/dist/store/commands/remove.spec.d.ts +0 -17
- package/dist/store/commands/remove.spec.d.ts.map +0 -1
- package/dist/store/index.d.ts +0 -32
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/index.spec.d.ts +0 -9
- package/dist/store/index.spec.d.ts.map +0 -1
- package/dist/store/utils/resolve-store-name.d.ts +0 -30
- package/dist/store/utils/resolve-store-name.d.ts.map +0 -1
- package/dist/store/utils/resolve-store-name.spec.d.ts +0 -2
- package/dist/store/utils/resolve-store-name.spec.d.ts.map +0 -1
- package/dist/test-helpers.spec.d.ts +0 -224
- package/dist/test-helpers.spec.d.ts.map +0 -1
- package/dist/tests/cli.integration.spec.d.ts +0 -11
- package/dist/tests/cli.integration.spec.d.ts.map +0 -1
- package/dist/toon.d.ts +0 -197
- package/dist/toon.d.ts.map +0 -1
- package/dist/toon.spec.d.ts +0 -9
- package/dist/toon.spec.d.ts.map +0 -1
- package/dist/utils/git.d.ts +0 -20
- package/dist/utils/git.d.ts.map +0 -1
- package/dist/utils/git.spec.d.ts +0 -7
- package/dist/utils/git.spec.d.ts.map +0 -1
|
@@ -23,6 +23,7 @@ import { Command } from '@commander-js/extra-typings';
|
|
|
23
23
|
import { throwCliError } from '../../errors.ts';
|
|
24
24
|
import { type CortexContext } from '@yeseh/cortex-core';
|
|
25
25
|
import { createCliCommandContext } from '../../context.ts';
|
|
26
|
+
import { resolveDefaultStore } from '../../utils/resolve-default-store.ts';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Options for the prune command.
|
|
@@ -83,13 +84,13 @@ export async function handlePrune(
|
|
|
83
84
|
ctx: CortexContext,
|
|
84
85
|
storeName: string | undefined,
|
|
85
86
|
options: PruneCommandOptions,
|
|
86
|
-
deps: PruneHandlerDeps = {}
|
|
87
|
+
deps: PruneHandlerDeps = {}
|
|
87
88
|
): Promise<void> {
|
|
88
89
|
const now = deps.now ?? ctx.now();
|
|
89
90
|
const stdout = deps.stdout ?? ctx.stdout ?? process.stdout;
|
|
90
91
|
|
|
91
92
|
// Get store through Cortex client
|
|
92
|
-
const storeResult = ctx.cortex.getStore(storeName
|
|
93
|
+
const storeResult = ctx.cortex.getStore(resolveDefaultStore(ctx, storeName));
|
|
93
94
|
if (!storeResult.ok()) {
|
|
94
95
|
throwCliError(storeResult.error);
|
|
95
96
|
}
|
|
@@ -121,8 +122,7 @@ export async function handlePrune(
|
|
|
121
122
|
const paths = pruned.map((entry) => entry.path).join('\n ');
|
|
122
123
|
if (options.dryRun) {
|
|
123
124
|
stdout.write(`Would prune ${pruned.length} expired memories:\n ${paths}\n`);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
125
|
+
} else {
|
|
126
126
|
stdout.write(`Pruned ${pruned.length} expired memories:\n ${paths}\n`);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -18,6 +18,7 @@ import { Command } from '@commander-js/extra-typings';
|
|
|
18
18
|
import { throwCliError } from '../../errors.ts';
|
|
19
19
|
import { type CortexContext } from '@yeseh/cortex-core';
|
|
20
20
|
import { createCliCommandContext } from '../../context.ts';
|
|
21
|
+
import { resolveDefaultStore } from '../../utils/resolve-default-store.ts';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Dependencies for the reindex command handler.
|
|
@@ -45,12 +46,12 @@ export interface ReindexHandlerDeps {
|
|
|
45
46
|
export async function handleReindex(
|
|
46
47
|
ctx: CortexContext,
|
|
47
48
|
storeName: string | undefined,
|
|
48
|
-
deps: ReindexHandlerDeps = {}
|
|
49
|
+
deps: ReindexHandlerDeps = {}
|
|
49
50
|
): Promise<void> {
|
|
50
51
|
const stdout = deps.stdout ?? ctx.stdout ?? process.stdout;
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
// Get store through Cortex client
|
|
53
|
-
const effectiveStoreName = storeName
|
|
54
|
+
const effectiveStoreName = resolveDefaultStore(ctx, storeName);
|
|
54
55
|
const storeResult = ctx.cortex.getStore(effectiveStoreName);
|
|
55
56
|
if (!storeResult.ok()) {
|
|
56
57
|
throwCliError(storeResult.error);
|
|
@@ -1167,4 +1167,140 @@ describe('Cortex CLI Integration Tests', () => {
|
|
|
1167
1167
|
expect(result.stdout).toContain('reindex');
|
|
1168
1168
|
});
|
|
1169
1169
|
});
|
|
1170
|
+
|
|
1171
|
+
describe('store init command', () => {
|
|
1172
|
+
it('should initialize a new store when no store is configured', async () => {
|
|
1173
|
+
// Create an isolated directory with no pre-configured stores — the
|
|
1174
|
+
// config.yaml exists but has no store entries, simulating a fresh install.
|
|
1175
|
+
const freshDir = await fs.mkdtemp(join(tmpdir(), 'cortex-store-init-'));
|
|
1176
|
+
const configDir = join(freshDir, '.config', 'cortex');
|
|
1177
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
1178
|
+
await fs.writeFile(join(configDir, 'config.yaml'), 'stores: {}\n', 'utf8');
|
|
1179
|
+
|
|
1180
|
+
const storePath = join(freshDir, '.cortex', 'memory');
|
|
1181
|
+
|
|
1182
|
+
try {
|
|
1183
|
+
const result = await runCortexCli(
|
|
1184
|
+
[
|
|
1185
|
+
'store',
|
|
1186
|
+
'init',
|
|
1187
|
+
storePath,
|
|
1188
|
+
'--name',
|
|
1189
|
+
'my-project',
|
|
1190
|
+
'--format',
|
|
1191
|
+
'json',
|
|
1192
|
+
],
|
|
1193
|
+
{
|
|
1194
|
+
cwd: freshDir,
|
|
1195
|
+
env: { CORTEX_CONFIG_DIR: configDir },
|
|
1196
|
+
},
|
|
1197
|
+
);
|
|
1198
|
+
|
|
1199
|
+
expectCliOk([
|
|
1200
|
+
'store',
|
|
1201
|
+
'init',
|
|
1202
|
+
storePath,
|
|
1203
|
+
'--name',
|
|
1204
|
+
'my-project',
|
|
1205
|
+
], result);
|
|
1206
|
+
expect(result.exitCode).toBe(0);
|
|
1207
|
+
|
|
1208
|
+
const parsed = JSON.parse(result.stdout) as {
|
|
1209
|
+
value: { name: string; path: string };
|
|
1210
|
+
};
|
|
1211
|
+
expect(parsed.value.name).toBe('my-project');
|
|
1212
|
+
expect(parsed.value.path).toBe(storePath);
|
|
1213
|
+
|
|
1214
|
+
// Verify the store was registered in config.yaml
|
|
1215
|
+
const configContent = await fs.readFile(join(configDir, 'config.yaml'), 'utf8');
|
|
1216
|
+
expect(configContent).toContain('my-project');
|
|
1217
|
+
expect(configContent).toContain(storePath);
|
|
1218
|
+
}
|
|
1219
|
+
finally {
|
|
1220
|
+
await fs.rm(freshDir, { recursive: true, force: true });
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
it('should create the store directory when it does not yet exist', async () => {
|
|
1225
|
+
const freshDir = await fs.mkdtemp(join(tmpdir(), 'cortex-store-init-newdir-'));
|
|
1226
|
+
const configDir = join(freshDir, '.config', 'cortex');
|
|
1227
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
1228
|
+
await fs.writeFile(join(configDir, 'config.yaml'), 'stores: {}\n', 'utf8');
|
|
1229
|
+
|
|
1230
|
+
// Point at a path that does NOT yet exist on disk
|
|
1231
|
+
const storePath = join(freshDir, 'brand-new-store');
|
|
1232
|
+
|
|
1233
|
+
try {
|
|
1234
|
+
const result = await runCortexCli(
|
|
1235
|
+
[
|
|
1236
|
+
'store',
|
|
1237
|
+
'init',
|
|
1238
|
+
storePath,
|
|
1239
|
+
'--name',
|
|
1240
|
+
'new-dir-store',
|
|
1241
|
+
'--format',
|
|
1242
|
+
'json',
|
|
1243
|
+
],
|
|
1244
|
+
{
|
|
1245
|
+
cwd: freshDir,
|
|
1246
|
+
env: { CORTEX_CONFIG_DIR: configDir },
|
|
1247
|
+
},
|
|
1248
|
+
);
|
|
1249
|
+
|
|
1250
|
+
expectCliOk([
|
|
1251
|
+
'store',
|
|
1252
|
+
'init',
|
|
1253
|
+
storePath,
|
|
1254
|
+
'--name',
|
|
1255
|
+
'new-dir-store',
|
|
1256
|
+
], result);
|
|
1257
|
+
|
|
1258
|
+
// The directory must have been created by the init command
|
|
1259
|
+
const dirStat = await fs.stat(storePath);
|
|
1260
|
+
expect(dirStat.isDirectory()).toBe(true);
|
|
1261
|
+
}
|
|
1262
|
+
finally {
|
|
1263
|
+
await fs.rm(freshDir, { recursive: true, force: true });
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
it('should fail when the store name already exists', async () => {
|
|
1268
|
+
// Register a store first using the existing testProject setup
|
|
1269
|
+
const storePath = join(testProject, '.cortex', 'memory2');
|
|
1270
|
+
const firstInit = await runCortexCli(
|
|
1271
|
+
[
|
|
1272
|
+
'store',
|
|
1273
|
+
'init',
|
|
1274
|
+
storePath,
|
|
1275
|
+
'--name',
|
|
1276
|
+
'existing-store',
|
|
1277
|
+
'--format',
|
|
1278
|
+
'json',
|
|
1279
|
+
],
|
|
1280
|
+
{ cwd: testProject },
|
|
1281
|
+
);
|
|
1282
|
+
expectCliOk([
|
|
1283
|
+
'store',
|
|
1284
|
+
'init',
|
|
1285
|
+
storePath,
|
|
1286
|
+
'--name',
|
|
1287
|
+
'existing-store',
|
|
1288
|
+
], firstInit);
|
|
1289
|
+
|
|
1290
|
+
// Attempt to init the same name again — should fail
|
|
1291
|
+
const secondInit = await runCortexCli(
|
|
1292
|
+
[
|
|
1293
|
+
'store',
|
|
1294
|
+
'init',
|
|
1295
|
+
join(testProject, '.cortex', 'memory3'),
|
|
1296
|
+
'--name',
|
|
1297
|
+
'existing-store',
|
|
1298
|
+
],
|
|
1299
|
+
{ cwd: testProject },
|
|
1300
|
+
);
|
|
1301
|
+
|
|
1302
|
+
expect(secondInit.exitCode).toBe(1);
|
|
1303
|
+
expect(secondInit.stderr.toLowerCase()).toMatch(/already exists/);
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1170
1306
|
});
|
package/src/utils/input.ts
CHANGED
|
@@ -49,7 +49,7 @@ type InputResult = Result<InputContent, InputError>;
|
|
|
49
49
|
type OptionalContentResult = Result<InputContent | null, InputError>;
|
|
50
50
|
|
|
51
51
|
export const readContentFromFile = async (
|
|
52
|
-
filePath: string | undefined
|
|
52
|
+
filePath: string | undefined,
|
|
53
53
|
): Promise<OptionalContentResult> => {
|
|
54
54
|
if (filePath === undefined) {
|
|
55
55
|
return ok(null);
|
|
@@ -64,7 +64,8 @@ export const readContentFromFile = async (
|
|
|
64
64
|
try {
|
|
65
65
|
const content = await readFile(trimmed, 'utf8');
|
|
66
66
|
return ok({ content, source: 'file' });
|
|
67
|
-
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
68
69
|
return err({
|
|
69
70
|
code: 'FILE_READ_FAILED',
|
|
70
71
|
message: `Failed to read content file: ${trimmed}.`,
|
|
@@ -75,7 +76,7 @@ export const readContentFromFile = async (
|
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
export const readContentFromStream = async (
|
|
78
|
-
stream: NodeJS.ReadableStream
|
|
79
|
+
stream: NodeJS.ReadableStream,
|
|
79
80
|
): Promise<OptionalContentResult> => {
|
|
80
81
|
const isTty = 'isTTY' in stream ? Boolean(stream.isTTY) : false;
|
|
81
82
|
if (isTty) {
|
|
@@ -110,7 +111,11 @@ export const resolveInput = async (source: InputSource): Promise<InputResult> =>
|
|
|
110
111
|
source.stream !== undefined &&
|
|
111
112
|
!('isTTY' in source.stream && Boolean((source.stream as { isTTY?: boolean }).isTTY));
|
|
112
113
|
|
|
113
|
-
const requestedSources = [
|
|
114
|
+
const requestedSources = [
|
|
115
|
+
contentProvided,
|
|
116
|
+
fileProvided,
|
|
117
|
+
streamRequested,
|
|
118
|
+
].filter(Boolean);
|
|
114
119
|
|
|
115
120
|
if (requestedSources.length > 1) {
|
|
116
121
|
return err({
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for resolveDefaultStore utility.
|
|
3
|
+
*
|
|
4
|
+
* @module cli/utils/resolve-default-store.spec
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'bun:test';
|
|
8
|
+
import { resolveDefaultStore } from './resolve-default-store.ts';
|
|
9
|
+
import type { CortexContext } from '@yeseh/cortex-core';
|
|
10
|
+
|
|
11
|
+
// Minimal mock context factory
|
|
12
|
+
function makeCtx(
|
|
13
|
+
overrides: {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
stores?: Record<string, { properties?: Record<string, unknown> }>;
|
|
16
|
+
defaultStore?: string;
|
|
17
|
+
} = {},
|
|
18
|
+
): CortexContext {
|
|
19
|
+
return {
|
|
20
|
+
cwd: overrides.cwd ?? '/home/user/myproject',
|
|
21
|
+
stores: (overrides.stores ?? {}) as CortexContext['stores'],
|
|
22
|
+
settings: { defaultStore: overrides.defaultStore },
|
|
23
|
+
cortex: {} as CortexContext['cortex'],
|
|
24
|
+
config: {} as CortexContext['config'],
|
|
25
|
+
now: () => new Date(),
|
|
26
|
+
stdin: process.stdin,
|
|
27
|
+
stdout: process.stdout,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('resolveDefaultStore', () => {
|
|
32
|
+
it('should return explicit store when provided', () => {
|
|
33
|
+
const ctx = makeCtx({ stores: { mystore: { properties: { path: '/some/path' } } } });
|
|
34
|
+
expect(resolveDefaultStore(ctx, 'mystore')).toBe('mystore');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return explicit store even when a local store exists', () => {
|
|
38
|
+
const ctx = makeCtx({
|
|
39
|
+
cwd: '/home/user/myproject',
|
|
40
|
+
stores: {
|
|
41
|
+
local: { properties: { path: '/home/user/myproject/.cortex' } },
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
expect(resolveDefaultStore(ctx, 'explicit')).toBe('explicit');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should auto-detect local store whose path matches <cwd>/.cortex', () => {
|
|
48
|
+
const ctx = makeCtx({
|
|
49
|
+
cwd: '/home/user/myproject',
|
|
50
|
+
stores: {
|
|
51
|
+
global: { properties: { path: '/home/user/.config/cortex/memory' } },
|
|
52
|
+
myproject: { properties: { path: '/home/user/myproject/.cortex' } },
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('myproject');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should auto-detect local store whose path matches <cwd>/.cortex/memory', () => {
|
|
59
|
+
const ctx = makeCtx({
|
|
60
|
+
cwd: '/home/user/myproject',
|
|
61
|
+
stores: {
|
|
62
|
+
global: { properties: { path: '/home/user/.config/cortex/memory' } },
|
|
63
|
+
myproject: { properties: { path: '/home/user/myproject/.cortex/memory' } },
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('myproject');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should not match a store whose path only starts with cwd (not an exact .cortex path)', () => {
|
|
70
|
+
// e.g. cwd is /home/user/proj and a store has /home/user/proj-other/.cortex
|
|
71
|
+
const ctx = makeCtx({
|
|
72
|
+
cwd: '/home/user/proj',
|
|
73
|
+
stores: {
|
|
74
|
+
other: { properties: { path: '/home/user/proj-other/.cortex' } },
|
|
75
|
+
global: { properties: { path: '/home/user/.config/cortex/memory' } },
|
|
76
|
+
},
|
|
77
|
+
defaultStore: 'global',
|
|
78
|
+
});
|
|
79
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('global');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should fall back to settings.defaultStore when no local store matches', () => {
|
|
83
|
+
const ctx = makeCtx({
|
|
84
|
+
cwd: '/home/user/myproject',
|
|
85
|
+
stores: {
|
|
86
|
+
custom: { properties: { path: '/data/custom' } },
|
|
87
|
+
},
|
|
88
|
+
defaultStore: 'custom',
|
|
89
|
+
});
|
|
90
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('custom');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should fall back to "global" when no local store and no defaultStore', () => {
|
|
94
|
+
const ctx = makeCtx({
|
|
95
|
+
cwd: '/home/user/myproject',
|
|
96
|
+
stores: {
|
|
97
|
+
other: { properties: { path: '/data/other' } },
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('global');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should fall back to "global" when stores is empty', () => {
|
|
104
|
+
const ctx = makeCtx({ cwd: '/home/user/myproject', stores: {} });
|
|
105
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('global');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should handle stores with missing properties gracefully', () => {
|
|
109
|
+
const ctx = makeCtx({
|
|
110
|
+
cwd: '/home/user/myproject',
|
|
111
|
+
stores: {
|
|
112
|
+
broken: {} as { properties?: Record<string, unknown> },
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('global');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should use process.cwd() when ctx.cwd is not set', () => {
|
|
119
|
+
// Build a ctx with no cwd — store path matches process.cwd()
|
|
120
|
+
const cwd = process.cwd();
|
|
121
|
+
const ctx = {
|
|
122
|
+
stores: {
|
|
123
|
+
local: { properties: { path: `${cwd}/.cortex` } },
|
|
124
|
+
} as unknown as CortexContext['stores'],
|
|
125
|
+
settings: {},
|
|
126
|
+
cortex: {} as CortexContext['cortex'],
|
|
127
|
+
config: {} as CortexContext['config'],
|
|
128
|
+
now: () => new Date(),
|
|
129
|
+
stdin: process.stdin,
|
|
130
|
+
stdout: process.stdout,
|
|
131
|
+
// cwd intentionally omitted
|
|
132
|
+
} as CortexContext;
|
|
133
|
+
expect(resolveDefaultStore(ctx, undefined)).toBe('local');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for resolving which store to target when no `--store` flag is given.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (first match wins):
|
|
5
|
+
* 1. Explicit store name provided by the caller (from `--store` flag)
|
|
6
|
+
* 2. Local store – a registered store whose path starts with `<cwd>/.cortex`
|
|
7
|
+
* 3. `settings.defaultStore` from the Cortex config file
|
|
8
|
+
* 4. Hard-coded fallback: `"global"`
|
|
9
|
+
*
|
|
10
|
+
* @module cli/utils/resolve-default-store
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In a command handler:
|
|
15
|
+
* const storeName = resolveDefaultStore(ctx, parentOpts?.store);
|
|
16
|
+
* const storeResult = ctx.cortex.getStore(storeName);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import type { CortexContext } from '@yeseh/cortex-core';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the effective store name for a command invocation.
|
|
25
|
+
*
|
|
26
|
+
* When a user runs a command without `--store` in a project directory where
|
|
27
|
+
* they have run `cortex store init`, the local store registered for that
|
|
28
|
+
* directory is automatically selected so they don't have to type `--store`
|
|
29
|
+
* on every command.
|
|
30
|
+
*
|
|
31
|
+
* @param ctx - The current Cortex context (provides `stores`, `settings`, `cwd`)
|
|
32
|
+
* @param explicit - Store name from the `--store` CLI flag (may be undefined)
|
|
33
|
+
* @returns The resolved store name to use
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // No --store flag, inside /home/user/my-project with a .cortex store:
|
|
38
|
+
* resolveDefaultStore(ctx, undefined);
|
|
39
|
+
* // → "my-project" (the store registered at /home/user/my-project/.cortex)
|
|
40
|
+
*
|
|
41
|
+
* // Explicit flag always wins:
|
|
42
|
+
* resolveDefaultStore(ctx, "global");
|
|
43
|
+
* // → "global"
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function resolveDefaultStore(ctx: CortexContext, explicit: string | undefined): string {
|
|
47
|
+
// 1. Explicit --store flag wins
|
|
48
|
+
if (explicit) return explicit;
|
|
49
|
+
|
|
50
|
+
const cwd = ctx.cwd ?? process.cwd();
|
|
51
|
+
const stores = ctx.stores ?? {};
|
|
52
|
+
|
|
53
|
+
// 2. Local store – registered store whose path is the `.cortex` dir in cwd
|
|
54
|
+
// Both `.cortex` and `.cortex/memory` are accepted to handle both
|
|
55
|
+
// naming conventions in use across the project.
|
|
56
|
+
const localPaths = [
|
|
57
|
+
join(cwd, '.cortex'), join(cwd, '.cortex', 'memory'),
|
|
58
|
+
];
|
|
59
|
+
for (const [
|
|
60
|
+
name, store,
|
|
61
|
+
] of Object.entries(stores)) {
|
|
62
|
+
const storePath = store.properties?.path as string | undefined;
|
|
63
|
+
if (storePath && localPaths.includes(storePath)) {
|
|
64
|
+
return name;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3. settings.defaultStore from config
|
|
69
|
+
const defaultStore = ctx.settings?.defaultStore;
|
|
70
|
+
if (defaultStore) return defaultStore;
|
|
71
|
+
|
|
72
|
+
// 4. Hard-coded fallback
|
|
73
|
+
return 'global';
|
|
74
|
+
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Category create command.
|
|
3
|
-
*
|
|
4
|
-
* Creates a category at the specified path, including any missing ancestors.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```bash
|
|
8
|
-
* # Create a category in default store
|
|
9
|
-
* cortex category create standards/typescript
|
|
10
|
-
*
|
|
11
|
-
* # Create a category in a specific store
|
|
12
|
-
* cortex category --store my-store create standards/typescript
|
|
13
|
-
*
|
|
14
|
-
* # Serialize output as JSON
|
|
15
|
-
* cortex category create standards --format json
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
import { Command } from '@commander-js/extra-typings';
|
|
19
|
-
import { type CortexContext } from '@yeseh/cortex-core';
|
|
20
|
-
/** Options parsed by Commander for the create command */
|
|
21
|
-
export interface CreateCommandOptions {
|
|
22
|
-
description?: string;
|
|
23
|
-
format?: string;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Handler for the category create command.
|
|
27
|
-
* Exported for direct testing without Commander parsing.
|
|
28
|
-
*
|
|
29
|
-
* @param ctx - CLI execution context
|
|
30
|
-
* @param storeName - Optional store name from parent command
|
|
31
|
-
* @param path - Category path (e.g., "standards/typescript")
|
|
32
|
-
* @param options - Command options from Commander
|
|
33
|
-
*/
|
|
34
|
-
export declare function handleCreate(ctx: CortexContext, storeName: string | undefined, path: string, options?: CreateCommandOptions): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* The `category create` subcommand.
|
|
37
|
-
*
|
|
38
|
-
* Creates a category and any missing ancestors.
|
|
39
|
-
*/
|
|
40
|
-
export declare const createCommand: Command<[string], {
|
|
41
|
-
description?: string | undefined;
|
|
42
|
-
format?: string | undefined;
|
|
43
|
-
}, {}>;
|
|
44
|
-
//# sourceMappingURL=create.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/category/commands/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAMxD,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAC9B,GAAG,EAAE,aAAa,EAClB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,oBAAyB,GACnC,OAAO,CAAC,IAAI,CAAC,CAuCf;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;MAapB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create.spec.d.ts","sourceRoot":"","sources":["../../../src/category/commands/create.spec.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/dist/category/index.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Category command group for the CLI.
|
|
3
|
-
*
|
|
4
|
-
* This module defines the `category` command group, which provides operations
|
|
5
|
-
* for managing categories in the Cortex memory system. The `--store` option
|
|
6
|
-
* is defined at the group level and inherited by all subcommands.
|
|
7
|
-
*/
|
|
8
|
-
import { Command } from '@commander-js/extra-typings';
|
|
9
|
-
/**
|
|
10
|
-
* The `category` command group.
|
|
11
|
-
*
|
|
12
|
-
* Provides category management operations. The `--store` option allows
|
|
13
|
-
* targeting a specific named store instead of the default store.
|
|
14
|
-
* This option is inherited by all subcommands.
|
|
15
|
-
*/
|
|
16
|
-
export declare const categoryCommand: Command<[], {
|
|
17
|
-
store?: string | undefined;
|
|
18
|
-
}, {}>;
|
|
19
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/category/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAItD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;MAEmC,CAAC"}
|
package/dist/commands/init.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Init command for initializing the global cortex configuration store.
|
|
3
|
-
*
|
|
4
|
-
* Creates the global config store at ~/.config/cortex/ with:
|
|
5
|
-
* - config.yaml: Global configuration with default settings
|
|
6
|
-
* - stores.yaml: Store registry with a 'default' store pointing to the memory directory
|
|
7
|
-
* - memory/: Default store with 'global' and 'projects' categories
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```bash
|
|
11
|
-
* # Initialize global cortex configuration
|
|
12
|
-
* cortex init
|
|
13
|
-
*
|
|
14
|
-
* # Reinitialize even if already initialized
|
|
15
|
-
* cortex init --force
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
import { Command } from '@commander-js/extra-typings';
|
|
19
|
-
import { type CortexContext } from '@yeseh/cortex-core';
|
|
20
|
-
/**
|
|
21
|
-
* Options for the init command.
|
|
22
|
-
*/
|
|
23
|
-
export interface InitCommandOptions {
|
|
24
|
-
/** Reinitialize even if already initialized */
|
|
25
|
-
force?: boolean;
|
|
26
|
-
/** Output format (yaml, json, toon) */
|
|
27
|
-
format?: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The `init` command for initializing the global cortex configuration.
|
|
31
|
-
*
|
|
32
|
-
* Creates the global config store at ~/.config/cortex/ with default settings
|
|
33
|
-
* and store registry.
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```bash
|
|
37
|
-
* cortex init # Initialize global config
|
|
38
|
-
* cortex init --force # Reinitialize even if exists
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare const initCommand: Command<[], {
|
|
42
|
-
force?: true | undefined;
|
|
43
|
-
format: string;
|
|
44
|
-
}, {}>;
|
|
45
|
-
/**
|
|
46
|
-
* Handles the init command execution.
|
|
47
|
-
*
|
|
48
|
-
* This function:
|
|
49
|
-
* 1. Initializes the global cortex config store
|
|
50
|
-
* 2. Creates default categories
|
|
51
|
-
* 3. Outputs the result
|
|
52
|
-
*
|
|
53
|
-
* @param options - Command options (force, format)
|
|
54
|
-
* @throws {InvalidArgumentError} When arguments are invalid
|
|
55
|
-
* @throws {CommanderError} When initialization fails
|
|
56
|
-
*/
|
|
57
|
-
export declare function handleInit(ctx: CortexContext, options?: InitCommandOptions): Promise<void>;
|
|
58
|
-
//# sourceMappingURL=init.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAStD,OAAO,EAIH,KAAK,aAAa,EAErB,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW;;;MAalB,CAAC;AAEP;;;;;;;;;;;GAWG;AAIH,wBAAsB,UAAU,CAC5B,GAAG,EAAE,aAAa,EAClB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init.spec.d.ts","sourceRoot":"","sources":["../../src/commands/init.spec.ts"],"names":[],"mappings":""}
|
package/dist/context.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Store resolution utilities for CLI commands.
|
|
3
|
-
*
|
|
4
|
-
* This module provides functions to resolve the store context (root directory)
|
|
5
|
-
* based on command-line options, current working directory, and the global
|
|
6
|
-
* store configuration.
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Default path to the global store.
|
|
10
|
-
*/
|
|
11
|
-
export declare const getDefaultGlobalStorePath: () => string;
|
|
12
|
-
/**
|
|
13
|
-
* Default path to the store configuration file.
|
|
14
|
-
* Respects CORTEX_CONFIG (config file path) and CORTEX_CONFIG_DIR (config directory)
|
|
15
|
-
* environment variables when set.
|
|
16
|
-
*/
|
|
17
|
-
export declare const getDefaultConfigPath: () => string;
|
|
18
|
-
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAO,MACQ,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAWvC,CAAC"}
|
package/dist/context.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.spec.d.ts","sourceRoot":"","sources":["../src/context.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { getDefaultSettings, type ConfigValidationError, type CortexContext, type Result } from '@yeseh/cortex-core';
|
|
2
|
-
import { FilesystemStorageAdapter, FilesystemConfigAdapter } from '@yeseh/cortex-storage-fs';
|
|
3
|
-
export declare const validateStorePath: (storePath: string, storeName: string) => Result<void, ConfigValidationError>;
|
|
4
|
-
export interface ConfigLoadOptions {
|
|
5
|
-
cwd?: string;
|
|
6
|
-
globalConfigPath?: string;
|
|
7
|
-
localConfigPath?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface CliContextOptions {
|
|
10
|
-
configDir?: string;
|
|
11
|
-
configCwd?: string;
|
|
12
|
-
}
|
|
13
|
-
export interface CliConfigContext {
|
|
14
|
-
configAdapter: FilesystemConfigAdapter;
|
|
15
|
-
stores: Record<string, any>;
|
|
16
|
-
settings: ReturnType<typeof getDefaultSettings>;
|
|
17
|
-
effectiveCwd: string;
|
|
18
|
-
}
|
|
19
|
-
export declare const createCliConfigAdapter: (configPath: string) => FilesystemConfigAdapter;
|
|
20
|
-
export declare const createCliAdapterFactory: (configAdapter: FilesystemConfigAdapter) => (storeName: string) => FilesystemStorageAdapter;
|
|
21
|
-
export declare const createCliConfigContext: (options?: CliContextOptions) => Promise<Result<CliConfigContext, any>>;
|
|
22
|
-
export declare const createCliCommandContext: (configDir?: string) => Promise<Result<CortexContext, any>>;
|
|
23
|
-
//# sourceMappingURL=create-cli-command.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-cli-command.d.ts","sourceRoot":"","sources":["../src/create-cli-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,kBAAkB,EAElB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,MAAM,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAY7F,eAAO,MAAM,iBAAiB,GAC1B,WAAW,MAAM,EACjB,WAAW,MAAM,KAClB,MAAM,CAAC,IAAI,EAAE,qBAAqB,CAWpC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,aAAa,EAAE,uBAAuB,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,QAAQ,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IAChD,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,sBAAsB,GAAI,YAAY,MAAM,KAAG,uBAE3D,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,eAAe,uBAAuB,MAClE,WAAW,MAAM,6BAkB5B,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAC/B,UAAS,iBAAsB,KAChC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,GAAG,CAAC,CA0CvC,CAAC;AAKF,eAAO,MAAM,uBAAuB,GAChC,YAAY,MAAM,KACnB,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,CAAC,CAoCpC,CAAC"}
|