@soulcraft/brainy 4.11.2 → 5.1.0

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 (44) hide show
  1. package/CHANGELOG.md +271 -0
  2. package/README.md +38 -1
  3. package/dist/augmentations/brainyAugmentation.d.ts +76 -0
  4. package/dist/augmentations/brainyAugmentation.js +126 -0
  5. package/dist/augmentations/cacheAugmentation.js +9 -4
  6. package/dist/brainy.d.ts +248 -15
  7. package/dist/brainy.js +707 -17
  8. package/dist/cli/commands/cow.d.ts +60 -0
  9. package/dist/cli/commands/cow.js +444 -0
  10. package/dist/cli/commands/import.js +1 -1
  11. package/dist/cli/commands/vfs.js +24 -40
  12. package/dist/cli/index.js +50 -0
  13. package/dist/hnsw/hnswIndex.d.ts +41 -0
  14. package/dist/hnsw/hnswIndex.js +96 -1
  15. package/dist/hnsw/typeAwareHNSWIndex.d.ts +9 -0
  16. package/dist/hnsw/typeAwareHNSWIndex.js +22 -0
  17. package/dist/import/ImportHistory.js +3 -3
  18. package/dist/importers/VFSStructureGenerator.d.ts +1 -1
  19. package/dist/importers/VFSStructureGenerator.js +3 -3
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.js +10 -0
  22. package/dist/storage/adapters/memoryStorage.d.ts +6 -0
  23. package/dist/storage/adapters/memoryStorage.js +39 -14
  24. package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +31 -1
  25. package/dist/storage/adapters/typeAwareStorageAdapter.js +272 -43
  26. package/dist/storage/baseStorage.d.ts +64 -0
  27. package/dist/storage/baseStorage.js +252 -12
  28. package/dist/storage/cow/BlobStorage.d.ts +232 -0
  29. package/dist/storage/cow/BlobStorage.js +437 -0
  30. package/dist/storage/cow/CommitLog.d.ts +199 -0
  31. package/dist/storage/cow/CommitLog.js +363 -0
  32. package/dist/storage/cow/CommitObject.d.ts +276 -0
  33. package/dist/storage/cow/CommitObject.js +431 -0
  34. package/dist/storage/cow/RefManager.d.ts +213 -0
  35. package/dist/storage/cow/RefManager.js +409 -0
  36. package/dist/storage/cow/TreeObject.d.ts +177 -0
  37. package/dist/storage/cow/TreeObject.js +293 -0
  38. package/dist/storage/storageFactory.d.ts +6 -0
  39. package/dist/storage/storageFactory.js +92 -74
  40. package/dist/types/brainy.types.d.ts +1 -0
  41. package/dist/vfs/FSCompat.d.ts +1 -1
  42. package/dist/vfs/FSCompat.js +1 -1
  43. package/dist/vfs/VirtualFileSystem.js +5 -6
  44. package/package.json +1 -1
@@ -0,0 +1,60 @@
1
+ /**
2
+ * COW CLI Commands - Copy-on-Write Operations
3
+ *
4
+ * Fork, branch, merge, and migration operations for instant cloning
5
+ */
6
+ interface CoreOptions {
7
+ verbose?: boolean;
8
+ json?: boolean;
9
+ pretty?: boolean;
10
+ }
11
+ interface ForkOptions extends CoreOptions {
12
+ name?: string;
13
+ message?: string;
14
+ author?: string;
15
+ }
16
+ interface MergeOptions extends CoreOptions {
17
+ force?: boolean;
18
+ strategy?: 'last-write-wins' | 'custom';
19
+ }
20
+ interface MigrateOptions extends CoreOptions {
21
+ from?: string;
22
+ to?: string;
23
+ backup?: boolean;
24
+ dryRun?: boolean;
25
+ }
26
+ export declare const cowCommands: {
27
+ /**
28
+ * Fork the current brain (instant clone)
29
+ */
30
+ fork(name: string | undefined, options: ForkOptions): Promise<void>;
31
+ /**
32
+ * List all branches/forks
33
+ */
34
+ branchList(options: CoreOptions): Promise<void>;
35
+ /**
36
+ * Switch to a different branch
37
+ */
38
+ checkout(branch: string | undefined, options: CoreOptions): Promise<void>;
39
+ /**
40
+ * Delete a branch/fork
41
+ */
42
+ branchDelete(branch: string | undefined, options: CoreOptions & {
43
+ force?: boolean;
44
+ }): Promise<void>;
45
+ /**
46
+ * Merge a fork/branch into current branch
47
+ */
48
+ merge(source: string | undefined, target: string | undefined, options: MergeOptions): Promise<void>;
49
+ /**
50
+ * Get commit history
51
+ */
52
+ history(options: CoreOptions & {
53
+ limit?: string;
54
+ }): Promise<void>;
55
+ /**
56
+ * Migrate from v4.x to v5.0.0 (one-time)
57
+ */
58
+ migrate(options: MigrateOptions): Promise<void>;
59
+ };
60
+ export {};
@@ -0,0 +1,444 @@
1
+ /**
2
+ * COW CLI Commands - Copy-on-Write Operations
3
+ *
4
+ * Fork, branch, merge, and migration operations for instant cloning
5
+ */
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import inquirer from 'inquirer';
9
+ import { Brainy } from '../../brainy.js';
10
+ import { existsSync } from 'node:fs';
11
+ import { resolve } from 'node:path';
12
+ let brainyInstance = null;
13
+ const getBrainy = () => {
14
+ if (!brainyInstance) {
15
+ brainyInstance = new Brainy();
16
+ }
17
+ return brainyInstance;
18
+ };
19
+ const formatOutput = (data, options) => {
20
+ if (options.json) {
21
+ console.log(options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data));
22
+ }
23
+ };
24
+ export const cowCommands = {
25
+ /**
26
+ * Fork the current brain (instant clone)
27
+ */
28
+ async fork(name, options) {
29
+ let spinner = null;
30
+ try {
31
+ const brain = getBrainy();
32
+ await brain.init();
33
+ // Interactive mode if no name provided
34
+ if (!name) {
35
+ const answers = await inquirer.prompt([
36
+ {
37
+ type: 'input',
38
+ name: 'branchName',
39
+ message: 'Enter fork/branch name:',
40
+ default: `fork-${Date.now()}`,
41
+ validate: (input) => input.trim().length > 0 || 'Branch name cannot be empty'
42
+ },
43
+ {
44
+ type: 'input',
45
+ name: 'message',
46
+ message: 'Commit message (optional):',
47
+ default: 'Fork from main'
48
+ }
49
+ ]);
50
+ name = answers.branchName;
51
+ options.message = answers.message;
52
+ }
53
+ spinner = ora(`Forking brain to ${chalk.cyan(name)}...`).start();
54
+ const startTime = Date.now();
55
+ const fork = await brain.fork(name);
56
+ const elapsed = Date.now() - startTime;
57
+ spinner.succeed(`Fork created: ${chalk.green(name)} ${chalk.dim(`(${elapsed}ms)`)}`);
58
+ // Show stats
59
+ const stats = await fork.getStats();
60
+ console.log(`
61
+ ${chalk.cyan('Fork Statistics:')}
62
+ ${chalk.dim('Entities:')} ${stats.entities.total || 0}
63
+ ${chalk.dim('Relationships:')} ${stats.relationships.totalRelationships || 0}
64
+ ${chalk.dim('Time:')} ${elapsed}ms
65
+ ${chalk.dim('Storage overhead:')} ~10-20%
66
+ `.trim());
67
+ if (options.json) {
68
+ formatOutput({
69
+ branch: name,
70
+ time: elapsed,
71
+ stats
72
+ }, options);
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (spinner)
77
+ spinner.fail('Fork failed');
78
+ console.error(chalk.red('Error:'), error.message);
79
+ if (options.verbose)
80
+ console.error(error);
81
+ process.exit(1);
82
+ }
83
+ },
84
+ /**
85
+ * List all branches/forks
86
+ */
87
+ async branchList(options) {
88
+ try {
89
+ const brain = getBrainy();
90
+ await brain.init();
91
+ const branches = await brain.listBranches();
92
+ const currentBranch = await brain.getCurrentBranch();
93
+ console.log(chalk.cyan('\nBranches:'));
94
+ for (const branch of branches) {
95
+ const isCurrent = branch === currentBranch;
96
+ const marker = isCurrent ? chalk.green('*') : ' ';
97
+ const name = isCurrent ? chalk.green(branch) : branch;
98
+ // Get branch info
99
+ // TODO: Re-enable when COW is integrated into BaseStorage
100
+ // const ref = await brain.storage.refManager.getRef(branch)
101
+ // const age = ref ? formatAge(Date.now() - ref.updatedAt) : 'unknown'
102
+ console.log(` ${marker} ${name}`);
103
+ }
104
+ console.log();
105
+ if (options.json) {
106
+ formatOutput({
107
+ branches,
108
+ currentBranch
109
+ }, options);
110
+ }
111
+ }
112
+ catch (error) {
113
+ console.error(chalk.red('Error:'), error.message);
114
+ if (options.verbose)
115
+ console.error(error);
116
+ process.exit(1);
117
+ }
118
+ },
119
+ /**
120
+ * Switch to a different branch
121
+ */
122
+ async checkout(branch, options) {
123
+ let spinner = null;
124
+ try {
125
+ const brain = getBrainy();
126
+ await brain.init();
127
+ // Interactive mode if no branch provided
128
+ if (!branch) {
129
+ const branches = await brain.listBranches();
130
+ const currentBranch = await brain.getCurrentBranch();
131
+ const { selected } = await inquirer.prompt([
132
+ {
133
+ type: 'list',
134
+ name: 'selected',
135
+ message: 'Select branch:',
136
+ choices: branches.map(b => ({
137
+ name: b === currentBranch ? `${b} (current)` : b,
138
+ value: b
139
+ }))
140
+ }
141
+ ]);
142
+ branch = selected;
143
+ }
144
+ const currentBranch = await brain.getCurrentBranch();
145
+ if (branch === currentBranch) {
146
+ console.log(chalk.yellow(`Already on branch '${branch}'`));
147
+ return;
148
+ }
149
+ spinner = ora(`Switching to ${chalk.cyan(branch)}...`).start();
150
+ await brain.checkout(branch);
151
+ spinner.succeed(`Switched to branch ${chalk.green(branch)}`);
152
+ if (options.json) {
153
+ formatOutput({ branch }, options);
154
+ }
155
+ }
156
+ catch (error) {
157
+ if (spinner)
158
+ spinner.fail('Checkout failed');
159
+ console.error(chalk.red('Error:'), error.message);
160
+ if (options.verbose)
161
+ console.error(error);
162
+ process.exit(1);
163
+ }
164
+ },
165
+ /**
166
+ * Delete a branch/fork
167
+ */
168
+ async branchDelete(branch, options) {
169
+ try {
170
+ const brain = getBrainy();
171
+ await brain.init();
172
+ // Interactive mode if no branch provided
173
+ if (!branch) {
174
+ const branches = await brain.listBranches();
175
+ const currentBranch = await brain.getCurrentBranch();
176
+ const { selected } = await inquirer.prompt([
177
+ {
178
+ type: 'list',
179
+ name: 'selected',
180
+ message: 'Select branch to delete:',
181
+ choices: branches
182
+ .filter(b => b !== currentBranch) // Can't delete current
183
+ .map(b => ({ name: b, value: b }))
184
+ }
185
+ ]);
186
+ branch = selected;
187
+ }
188
+ // Confirm deletion
189
+ if (!options.force) {
190
+ const { confirm } = await inquirer.prompt([
191
+ {
192
+ type: 'confirm',
193
+ name: 'confirm',
194
+ message: `Delete branch '${branch}'? This cannot be undone.`,
195
+ default: false
196
+ }
197
+ ]);
198
+ if (!confirm) {
199
+ console.log(chalk.yellow('Deletion cancelled'));
200
+ return;
201
+ }
202
+ }
203
+ const spinner = ora(`Deleting branch ${chalk.red(branch)}...`).start();
204
+ await brain.deleteBranch(branch);
205
+ spinner.succeed(`Deleted branch ${chalk.red(branch)}`);
206
+ if (options.json) {
207
+ formatOutput({ deleted: branch }, options);
208
+ }
209
+ }
210
+ catch (error) {
211
+ console.error(chalk.red('Error:'), error.message);
212
+ if (options.verbose)
213
+ console.error(error);
214
+ process.exit(1);
215
+ }
216
+ },
217
+ /**
218
+ * Merge a fork/branch into current branch
219
+ */
220
+ async merge(source, target, options) {
221
+ let spinner = null;
222
+ try {
223
+ const brain = getBrainy();
224
+ await brain.init();
225
+ // Interactive mode if parameters missing
226
+ if (!source || !target) {
227
+ const branches = await brain.listBranches();
228
+ const currentBranch = await brain.getCurrentBranch();
229
+ const answers = await inquirer.prompt([
230
+ {
231
+ type: 'list',
232
+ name: 'source',
233
+ message: 'Merge FROM branch:',
234
+ choices: branches.map(b => ({ name: b, value: b })),
235
+ when: !source
236
+ },
237
+ {
238
+ type: 'list',
239
+ name: 'target',
240
+ message: 'Merge INTO branch:',
241
+ choices: branches.map(b => ({
242
+ name: b === currentBranch ? `${b} (current)` : b,
243
+ value: b
244
+ })),
245
+ default: currentBranch,
246
+ when: !target
247
+ }
248
+ ]);
249
+ source = source || answers.source;
250
+ target = target || answers.target;
251
+ }
252
+ spinner = ora(`Merging ${chalk.cyan(source)} → ${chalk.green(target)}...`).start();
253
+ const result = await brain.merge(source, target, {
254
+ strategy: options.strategy || 'last-write-wins'
255
+ });
256
+ spinner.succeed(`Merged ${chalk.cyan(source)} → ${chalk.green(target)}`);
257
+ console.log(`
258
+ ${chalk.cyan('Merge Summary:')}
259
+ ${chalk.green('Added:')} ${result.added} entities
260
+ ${chalk.yellow('Modified:')} ${result.modified} entities
261
+ ${chalk.red('Deleted:')} ${result.deleted} entities
262
+ ${chalk.magenta('Conflicts:')} ${result.conflicts} (resolved)
263
+ `.trim());
264
+ if (options.json) {
265
+ formatOutput(result, options);
266
+ }
267
+ }
268
+ catch (error) {
269
+ if (spinner)
270
+ spinner.fail('Merge failed');
271
+ console.error(chalk.red('Error:'), error.message);
272
+ if (options.verbose)
273
+ console.error(error);
274
+ process.exit(1);
275
+ }
276
+ },
277
+ /**
278
+ * Get commit history
279
+ */
280
+ async history(options) {
281
+ try {
282
+ const brain = getBrainy();
283
+ await brain.init();
284
+ const limit = options.limit ? parseInt(options.limit) : 10;
285
+ const history = await brain.getHistory({ limit });
286
+ console.log(chalk.cyan(`\nCommit History (last ${limit}):\n`));
287
+ for (const commit of history) {
288
+ const date = new Date(commit.timestamp);
289
+ const age = formatAge(Date.now() - commit.timestamp);
290
+ console.log(`${chalk.yellow(commit.hash.substring(0, 8))} ` +
291
+ `${chalk.dim(commit.message)} ` +
292
+ `${chalk.dim(`by ${commit.author} (${age} ago)`)}`);
293
+ }
294
+ console.log();
295
+ if (options.json) {
296
+ formatOutput(history, options);
297
+ }
298
+ }
299
+ catch (error) {
300
+ console.error(chalk.red('Error:'), error.message);
301
+ if (options.verbose)
302
+ console.error(error);
303
+ process.exit(1);
304
+ }
305
+ },
306
+ /**
307
+ * Migrate from v4.x to v5.0.0 (one-time)
308
+ */
309
+ async migrate(options) {
310
+ let spinner = null;
311
+ try {
312
+ // Interactive mode if paths not provided
313
+ let fromPath = options.from;
314
+ let toPath = options.to;
315
+ if (!fromPath || !toPath) {
316
+ const answers = await inquirer.prompt([
317
+ {
318
+ type: 'input',
319
+ name: 'from',
320
+ message: 'Old Brainy data path (v4.x):',
321
+ default: './brainy-data',
322
+ when: !fromPath
323
+ },
324
+ {
325
+ type: 'input',
326
+ name: 'to',
327
+ message: 'New Brainy data path (v5.0.0):',
328
+ default: './brainy-data-v5',
329
+ when: !toPath
330
+ },
331
+ {
332
+ type: 'confirm',
333
+ name: 'backup',
334
+ message: 'Create backup before migration?',
335
+ default: true,
336
+ when: options.backup === undefined
337
+ }
338
+ ]);
339
+ fromPath = fromPath || answers.from;
340
+ toPath = toPath || answers.to;
341
+ options.backup = options.backup ?? answers.backup;
342
+ }
343
+ // Verify old data exists
344
+ if (!existsSync(resolve(fromPath))) {
345
+ throw new Error(`Old data path not found: ${fromPath}`);
346
+ }
347
+ // Create backup if requested
348
+ if (options.backup) {
349
+ const backupPath = `${fromPath}.backup-${Date.now()}`;
350
+ spinner = ora(`Creating backup: ${backupPath}...`).start();
351
+ // TODO: Implement backup
352
+ // await copyDirectory(fromPath, backupPath)
353
+ spinner.succeed(`Backup created: ${chalk.green(backupPath)}`);
354
+ }
355
+ if (options.dryRun) {
356
+ console.log(chalk.yellow('\n[DRY RUN] Migration plan:'));
357
+ console.log(` From: ${fromPath}`);
358
+ console.log(` To: ${toPath}`);
359
+ console.log(` Backup: ${options.backup ? 'Yes' : 'No'}`);
360
+ console.log();
361
+ return;
362
+ }
363
+ spinner = ora('Migrating to v5.0.0 COW format...').start();
364
+ // Load old brain (v4.x)
365
+ const oldBrain = new Brainy({
366
+ storage: {
367
+ type: 'filesystem',
368
+ options: { path: fromPath }
369
+ }
370
+ });
371
+ await oldBrain.init();
372
+ // Create new brain (v5.0.0)
373
+ const newBrain = new Brainy({
374
+ storage: {
375
+ type: 'filesystem',
376
+ options: { path: toPath }
377
+ }
378
+ });
379
+ await newBrain.init();
380
+ // Migrate all entities
381
+ const entities = await oldBrain.find({});
382
+ let migrated = 0;
383
+ spinner.text = `Migrating entities (0/${entities.length})...`;
384
+ for (const result of entities) {
385
+ // Add entity with proper params
386
+ await newBrain.add({
387
+ type: result.entity.type,
388
+ data: result.entity.data
389
+ });
390
+ migrated++;
391
+ if (migrated % 100 === 0) {
392
+ spinner.text = `Migrating entities (${migrated}/${entities.length})...`;
393
+ }
394
+ }
395
+ // Create initial commit (will be available after COW integration)
396
+ // await newBrain.commit({
397
+ // message: `Migrated from v4.x (${entities.length} entities)`,
398
+ // author: 'migration-tool'
399
+ // })
400
+ spinner.succeed(`Migration complete: ${chalk.green(migrated)} entities`);
401
+ console.log(`
402
+ ${chalk.cyan('Migration Summary:')}
403
+ ${chalk.dim('Old path:')} ${fromPath}
404
+ ${chalk.dim('New path:')} ${toPath}
405
+ ${chalk.dim('Entities:')} ${migrated}
406
+ ${chalk.dim('Format:')} v5.0.0 COW
407
+ `.trim());
408
+ if (options.json) {
409
+ formatOutput({
410
+ from: fromPath,
411
+ to: toPath,
412
+ migrated
413
+ }, options);
414
+ }
415
+ await oldBrain.close();
416
+ await newBrain.close();
417
+ }
418
+ catch (error) {
419
+ if (spinner)
420
+ spinner.fail('Migration failed');
421
+ console.error(chalk.red('Error:'), error.message);
422
+ if (options.verbose)
423
+ console.error(error);
424
+ process.exit(1);
425
+ }
426
+ }
427
+ };
428
+ /**
429
+ * Format timestamp age
430
+ */
431
+ function formatAge(ms) {
432
+ const seconds = Math.floor(ms / 1000);
433
+ const minutes = Math.floor(seconds / 60);
434
+ const hours = Math.floor(minutes / 60);
435
+ const days = Math.floor(hours / 24);
436
+ if (days > 0)
437
+ return `${days}d`;
438
+ if (hours > 0)
439
+ return `${hours}h`;
440
+ if (minutes > 0)
441
+ return `${minutes}m`;
442
+ return `${seconds}s`;
443
+ }
444
+ //# sourceMappingURL=cow.js.map
@@ -416,7 +416,7 @@ export const importCommands = {
416
416
  spinner = ora('Initializing VFS import...').start();
417
417
  const brain = getBrainy();
418
418
  // Get VFS
419
- const vfs = await brain.vfs();
419
+ const vfs = await brain.vfs;
420
420
  // Load DirectoryImporter
421
421
  const { DirectoryImporter } = await import('../../vfs/importers/DirectoryImporter.js');
422
422
  const importer = new DirectoryImporter(vfs, brain);
@@ -9,9 +9,10 @@ import Table from 'cli-table3';
9
9
  import { readFileSync, writeFileSync } from 'node:fs';
10
10
  import { Brainy } from '../../brainy.js';
11
11
  let brainyInstance = null;
12
- const getBrainy = () => {
12
+ const getBrainy = async () => {
13
13
  if (!brainyInstance) {
14
14
  brainyInstance = new Brainy();
15
+ await brainyInstance.init(); // v5.0.1: Initialize brain (VFS auto-initialized here!)
15
16
  }
16
17
  return brainyInstance;
17
18
  };
@@ -38,10 +39,9 @@ export const vfsCommands = {
38
39
  async read(path, options) {
39
40
  const spinner = ora('Reading file...').start();
40
41
  try {
41
- const brain = getBrainy();
42
- const vfs = brain.vfs();
43
- await vfs.init();
44
- const buffer = await vfs.readFile(path, {
42
+ const brain = await getBrainy(); // v5.0.1: Await async getBrainy
43
+ // v5.0.1: VFS auto-initialized, no need for vfs.init()
44
+ const buffer = await brain.vfs.readFile(path, {
45
45
  encoding: options.encoding
46
46
  });
47
47
  spinner.succeed('File read successfully');
@@ -70,9 +70,7 @@ export const vfsCommands = {
70
70
  async write(path, options) {
71
71
  const spinner = ora('Writing file...').start();
72
72
  try {
73
- const brain = getBrainy();
74
- const vfs = brain.vfs();
75
- await vfs.init();
73
+ const brain = await getBrainy();
76
74
  let data;
77
75
  if (options.file) {
78
76
  // Read from local file
@@ -85,7 +83,7 @@ export const vfsCommands = {
85
83
  spinner.fail('Must provide --content or --file');
86
84
  process.exit(1);
87
85
  }
88
- await vfs.writeFile(path, data, {
86
+ await brain.vfs.writeFile(path, data, {
89
87
  encoding: options.encoding
90
88
  });
91
89
  spinner.succeed('File written successfully');
@@ -109,10 +107,8 @@ export const vfsCommands = {
109
107
  async ls(path, options) {
110
108
  const spinner = ora('Listing directory...').start();
111
109
  try {
112
- const brain = getBrainy();
113
- const vfs = brain.vfs();
114
- await vfs.init();
115
- const entries = await vfs.readdir(path, { withFileTypes: true });
110
+ const brain = await getBrainy();
111
+ const entries = await brain.vfs.readdir(path, { withFileTypes: true });
116
112
  spinner.succeed(`Found ${Array.isArray(entries) ? entries.length : 0} items`);
117
113
  if (!options.json) {
118
114
  if (!Array.isArray(entries) || entries.length === 0) {
@@ -128,7 +124,7 @@ export const vfsCommands = {
128
124
  for (const entry of entries) {
129
125
  if (!options.all && entry.name.startsWith('.'))
130
126
  continue;
131
- const stat = await vfs.stat(`${path}/${entry.name}`);
127
+ const stat = await brain.vfs.stat(`${path}/${entry.name}`);
132
128
  table.push([
133
129
  entry.isDirectory() ? chalk.blue('DIR') : 'FILE',
134
130
  entry.isDirectory() ? '-' : formatBytes(stat.size),
@@ -169,10 +165,8 @@ export const vfsCommands = {
169
165
  async stat(path, options) {
170
166
  const spinner = ora('Getting file stats...').start();
171
167
  try {
172
- const brain = getBrainy();
173
- const vfs = brain.vfs();
174
- await vfs.init();
175
- const stats = await vfs.stat(path);
168
+ const brain = await getBrainy();
169
+ const stats = await brain.vfs.stat(path);
176
170
  spinner.succeed('Stats retrieved');
177
171
  if (!options.json) {
178
172
  console.log(chalk.cyan('\nFile Statistics:'));
@@ -199,10 +193,8 @@ export const vfsCommands = {
199
193
  async mkdir(path, options) {
200
194
  const spinner = ora('Creating directory...').start();
201
195
  try {
202
- const brain = getBrainy();
203
- const vfs = brain.vfs();
204
- await vfs.init();
205
- await vfs.mkdir(path, { recursive: options.parents });
196
+ const brain = await getBrainy();
197
+ await brain.vfs.mkdir(path, { recursive: options.parents });
206
198
  spinner.succeed('Directory created');
207
199
  if (!options.json) {
208
200
  console.log(chalk.green(`✓ Created: ${path}`));
@@ -223,15 +215,13 @@ export const vfsCommands = {
223
215
  async rm(path, options) {
224
216
  const spinner = ora('Removing...').start();
225
217
  try {
226
- const brain = getBrainy();
227
- const vfs = brain.vfs();
228
- await vfs.init();
229
- const stats = await vfs.stat(path);
218
+ const brain = await getBrainy();
219
+ const stats = await brain.vfs.stat(path);
230
220
  if (stats.isDirectory()) {
231
- await vfs.rmdir(path, { recursive: options.recursive });
221
+ await brain.vfs.rmdir(path, { recursive: options.recursive });
232
222
  }
233
223
  else {
234
- await vfs.unlink(path);
224
+ await brain.vfs.unlink(path);
235
225
  }
236
226
  spinner.succeed('Removed successfully');
237
227
  if (!options.json) {
@@ -255,10 +245,8 @@ export const vfsCommands = {
255
245
  async search(query, options) {
256
246
  const spinner = ora('Searching files...').start();
257
247
  try {
258
- const brain = getBrainy();
259
- const vfs = brain.vfs();
260
- await vfs.init();
261
- const results = await vfs.search(query, {
248
+ const brain = await getBrainy();
249
+ const results = await brain.vfs.search(query, {
262
250
  path: options.path,
263
251
  limit: options.limit ? parseInt(options.limit) : 10
264
252
  });
@@ -297,10 +285,8 @@ export const vfsCommands = {
297
285
  async similar(path, options) {
298
286
  const spinner = ora('Finding similar files...').start();
299
287
  try {
300
- const brain = getBrainy();
301
- const vfs = brain.vfs();
302
- await vfs.init();
303
- const results = await vfs.findSimilar(path, {
288
+ const brain = await getBrainy();
289
+ const results = await brain.vfs.findSimilar(path, {
304
290
  limit: options.limit ? parseInt(options.limit) : 10,
305
291
  threshold: options.threshold ? parseFloat(options.threshold) : 0.7
306
292
  });
@@ -336,10 +322,8 @@ export const vfsCommands = {
336
322
  async tree(path, options) {
337
323
  const spinner = ora('Building tree...').start();
338
324
  try {
339
- const brain = getBrainy();
340
- const vfs = brain.vfs();
341
- await vfs.init();
342
- const tree = await vfs.getTreeStructure(path, {
325
+ const brain = await getBrainy();
326
+ const tree = await brain.vfs.getTreeStructure(path, {
343
327
  maxDepth: options.depth ? parseInt(options.depth) : 3
344
328
  });
345
329
  spinner.succeed('Tree built');