byterover-cli 0.4.0 → 0.4.1

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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Array of all supported Events.
3
3
  */
4
- export declare const EVENT_VALUES: readonly ["auth:signed_in", "auth:signed_out", "space:init", "space:changed", "rule:generate", "mem:status", "mem:curate", "mem:pull", "mem:push", "mem:query", "onboarding:init_completed", "onboarding:curate_completed", "onboarding:query_completed", "onboarding:skipped", "onboarding:completed"];
4
+ export declare const EVENT_VALUES: readonly ["repl", "auth:sign_in", "auth:signed_out", "space:init", "space:changed", "rule:generate", "mem:status", "mem:curate", "mem:pull", "mem:push", "mem:query", "onboarding:init_completed", "onboarding:curate_completed", "onboarding:query_completed", "onboarding:skipped", "onboarding:completed", "init"];
5
5
  export type EventName = (typeof EVENT_VALUES)[number];
6
6
  export interface PropertyDict {
7
7
  [key: string]: any;
@@ -2,7 +2,8 @@
2
2
  * Array of all supported Events.
3
3
  */
4
4
  export const EVENT_VALUES = [
5
- 'auth:signed_in',
5
+ 'repl',
6
+ 'auth:sign_in',
6
7
  'auth:signed_out',
7
8
  'space:init',
8
9
  'space:changed',
@@ -17,4 +18,5 @@ export const EVENT_VALUES = [
17
18
  'onboarding:query_completed',
18
19
  'onboarding:skipped',
19
20
  'onboarding:completed',
21
+ 'init'
20
22
  ];
@@ -16,11 +16,11 @@ function checkNodeVersion() {
16
16
  const [, major, minor] = versionMatch;
17
17
  const majorVersion = Number.parseInt(major, 10);
18
18
  const minorVersion = Number.parseInt(minor, 10);
19
- // Minimum supported version: Node 18
20
- if (majorVersion < 18) {
19
+ // Minimum supported version: Node 20
20
+ if (majorVersion < 20) {
21
21
  return {
22
22
  message: `Node.js ${majorVersion}.${minorVersion} is not supported.\n` +
23
- 'ByteRover CLI requires Node.js 18 or higher.\n' +
23
+ 'ByteRover CLI requires Node.js 20 or higher.\n' +
24
24
  `Current version: ${nodeVersion}\n\n` +
25
25
  'Please upgrade Node.js:\n' +
26
26
  ' - Using nvm: nvm install 22 && nvm use 22\n' +
@@ -28,21 +28,21 @@ function checkNodeVersion() {
28
28
  type: 'error',
29
29
  };
30
30
  }
31
- // Recommended versions: Node 18, 20, 22
32
- // Node 24+ may have compatibility issues with native modules (better-sqlite3, grpc)
31
+ // Recommended versions: Node 20, 22
32
+ // Node 24+ may have compatibility issues with native modules
33
33
  if (majorVersion >= 24) {
34
34
  return {
35
35
  message: `Node.js ${majorVersion}.${minorVersion} has not been fully tested with ByteRover CLI.\n` +
36
36
  `Current version: ${nodeVersion}\n` +
37
- 'Recommended versions: Node.js 18.x, 20.x, or 22.x (LTS)\n\n' +
38
- 'Some native dependencies (sqlite, grpc) may not work correctly.\n' +
37
+ 'Recommended versions: Node.js 20.x or 22.x\n\n' +
38
+ 'Some native dependencies may not work correctly.\n' +
39
39
  'If you encounter errors, please switch to a recommended version:\n' +
40
40
  ' - Using nvm: nvm install 22 && nvm use 22',
41
41
  type: 'warning',
42
42
  };
43
43
  }
44
44
  // All checks passed
45
- return null;
45
+ return undefined;
46
46
  }
47
47
  const hook = async function (options) {
48
48
  // Check Node.js version compatibility first
@@ -79,23 +79,8 @@ const hook = async function (options) {
79
79
  chalk.cyan(String.raw ` |____/ \__, |\__\___|_| \_\___/ \_/ \___|_| `),
80
80
  chalk.cyan(' |___/ '),
81
81
  ].join('\n');
82
- const onboardingText = [
83
- 'ByteRover CLI quick start:',
84
- '',
85
- ' 1. Authenticate with ByteRover:',
86
- ' brv login',
87
- ' 2. Link your workspace:',
88
- ' brv init',
89
- '',
90
- 'After setup, run `brv status` to confirm your connection.',
91
- '',
92
- ' Explore commands:',
93
- ' • `brv curate` - Organize and curate your context tree',
94
- ' • `brv query` - Search and query your context tree',
95
- ' • `brv push` - Push your context tree to ByteRover',
96
- ].join('\n');
97
82
  const docsLink = `For more information, run 'brv --help', 'brv [command] --help' or visit ${DOCS_URL}`;
98
- this.log(`\n${logo}\n\n${onboardingText}\n\n${docsLink}\n\n`);
83
+ this.log(`\n${logo}\n\n${docsLink}\n\n`);
99
84
  }
100
85
  };
101
86
  export default hook;
@@ -19,8 +19,10 @@ export async function startRepl(options) {
19
19
  brvConfig = await projectConfigStore.read();
20
20
  }
21
21
  }
22
+ await trackingService.track('repl', { status: 'started' });
22
23
  // Render the App with providers
23
24
  const { waitUntilExit } = render(_jsx(AppProviders, { initialAuthToken: isAuthorized ? authToken : undefined, initialBrvConfig: brvConfig, onboardingPreferenceStore: onboardingPreferenceStore, projectConfigStore: projectConfigStore, tokenStore: tokenStore, trackingService: trackingService, version: version, children: _jsx(App, {}) }));
24
25
  await waitUntilExit();
25
26
  stopQueuePollingService();
27
+ await trackingService.track('repl', { status: 'finished' });
26
28
  }
@@ -124,6 +124,7 @@ export class CurateUseCase {
124
124
  }
125
125
  }
126
126
  async run(options) {
127
+ await this.trackingService.track('mem:curate', { status: 'started' });
127
128
  // Determine mode: autonomous if context is provided
128
129
  return options.context ? this.runAutonomous(options.context, options) : this.runInteractive();
129
130
  }
@@ -131,7 +132,7 @@ export class CurateUseCase {
131
132
  * Handle workspace not initialized error
132
133
  */
133
134
  handleWorkspaceError(_error) {
134
- const message = 'Project not initialized. Please run "brv init" to select your team and workspace.';
135
+ const message = 'Project not initialized. Please run "/init" to select your team and workspace.';
135
136
  this.terminal.log(message);
136
137
  }
137
138
  /**
@@ -197,14 +198,14 @@ export class CurateUseCase {
197
198
  // Get authentication token
198
199
  const token = await this.tokenStore.load();
199
200
  if (!token) {
200
- this.terminal.log('Authentication required. Please run "brv login" first.');
201
+ this.terminal.log('Authentication required. Please run "/login" first.');
201
202
  return;
202
203
  }
203
204
  // Load project config
204
205
  const brvConfig = await this.projectConfigStore.read();
205
206
  // Validate workspace is initialized
206
207
  if (!brvConfig) {
207
- throw new WorkspaceNotInitializedError('Project not initialized. Please run "brv init" to select your team and workspace.', '.brv');
208
+ throw new WorkspaceNotInitializedError('Project not initialized. Please run "/init" to select your team and workspace.', '.brv');
208
209
  }
209
210
  // Process file references if provided (validates and creates instructions)
210
211
  const fileReferenceInstructions = this.processFileReferences(options.files ?? []);
@@ -223,7 +224,7 @@ export class CurateUseCase {
223
224
  // Simple output for agents - just confirm saved
224
225
  this.terminal.log('✓ Context queued for processing.');
225
226
  // Track the event
226
- await this.trackingService.track('mem:curate');
227
+ await this.trackingService.track('mem:curate', { status: 'finished' });
227
228
  }
228
229
  catch (error) {
229
230
  if (error instanceof WorkspaceNotInitializedError) {
@@ -231,7 +232,9 @@ export class CurateUseCase {
231
232
  return;
232
233
  }
233
234
  // Display error
234
- this.terminal.error(error instanceof Error ? error.message : 'Runtime error occurred');
235
+ const errMsg = error instanceof Error ? error.message : 'Runtime error occurred';
236
+ await this.trackingService.track('mem:curate', { message: errMsg, status: 'error' });
237
+ this.terminal.error(errMsg);
235
238
  }
236
239
  }
237
240
  /**
@@ -261,7 +264,9 @@ export class CurateUseCase {
261
264
  await this.openFile(contextFilePath);
262
265
  }
263
266
  catch (error) {
264
- this.terminal.error(error instanceof Error ? error.message : 'Unexpected error occurred');
267
+ const errMsg = error instanceof Error ? error.message : 'Unexpected error occurred';
268
+ await this.trackingService.track('mem:curate', { message: errMsg, status: 'error' });
269
+ this.terminal.error(errMsg);
265
270
  }
266
271
  }
267
272
  validateTopicName(value, targetPath) {
@@ -58,7 +58,9 @@ export class InitUseCase {
58
58
  }
59
59
  catch (error) {
60
60
  this.terminal.actionStop('✗');
61
- throw new Error(`Failed to remove ${BRV_DIR}/: ${error instanceof Error ? error.message : 'Unknown error'}`);
61
+ const brvDirRemovalErr = `Failed to remove ${BRV_DIR}/: ${error instanceof Error ? error.message : 'Unknown error'}`;
62
+ await this.trackingService.track('init', { message: brvDirRemovalErr, status: 'error' });
63
+ throw new Error(brvDirRemovalErr);
62
64
  }
63
65
  }
64
66
  async confirmReInitialization(config) {
@@ -93,11 +95,11 @@ export class InitUseCase {
93
95
  async ensureAuthenticated() {
94
96
  const token = await this.tokenStore.load();
95
97
  if (token === undefined) {
96
- this.terminal.log('Not authenticated. Please run "brv login" first.');
98
+ this.terminal.log('Not authenticated. Please run "/login" first.');
97
99
  return undefined;
98
100
  }
99
101
  if (!token.isValid()) {
100
- this.terminal.log('Authentication token expired. Please run "brv login" again.');
102
+ this.terminal.log('Authentication token expired. Please run "/login" again.');
101
103
  return undefined;
102
104
  }
103
105
  return token;
@@ -205,7 +207,9 @@ export class InitUseCase {
205
207
  try {
206
208
  const projectConfig = await this.projectConfigStore.read();
207
209
  if (projectConfig === undefined) {
208
- throw new Error('Configuration file exists but cannot be read. Please check .brv/config.json');
210
+ const corruptedConfigFileErr = 'Configuration file exists but cannot be read. Please check .brv/config.json';
211
+ this.trackingService.track('init', { message: corruptedConfigFileErr, status: 'error' });
212
+ throw new Error(corruptedConfigFileErr);
209
213
  }
210
214
  return projectConfig;
211
215
  }
@@ -362,6 +366,7 @@ export class InitUseCase {
362
366
  }
363
367
  async run(options) {
364
368
  try {
369
+ await this.trackingService.track('init', { status: 'started' });
365
370
  const authToken = await this.ensureAuthenticated();
366
371
  if (!authToken)
367
372
  return;
@@ -416,11 +421,14 @@ export class InitUseCase {
416
421
  await this.trackingService.track('rule:generate');
417
422
  await this.trackingService.track('space:init');
418
423
  this.logSuccess(selectedSpace);
424
+ await this.trackingService.track('init', { status: 'finished' });
419
425
  }
420
426
  catch (error) {
421
427
  // Stop action if it's in progress
422
428
  this.terminal.actionStop();
423
- this.terminal.error(`Initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
429
+ const initErr = `Initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`;
430
+ await this.trackingService.track('init', { message: initErr, status: 'error' });
431
+ this.terminal.error(initErr);
424
432
  }
425
433
  }
426
434
  async syncFromRemoteOrInitialize(params) {
@@ -452,7 +460,9 @@ export class InitUseCase {
452
460
  }
453
461
  }
454
462
  catch (error) {
455
- throw new Error(`Failed to sync from ByteRover: ${error instanceof Error ? error.message : 'Unknown error'}. Please try again.`);
463
+ const syncFailureErr = `Failed to sync from ByteRover: ${error instanceof Error ? error.message : 'Unknown error'}. Please try again.`;
464
+ await this.trackingService.track('init', { message: syncFailureErr, status: 'error' });
465
+ throw new Error(syncFailureErr);
456
466
  }
457
467
  }
458
468
  /**
@@ -19,13 +19,16 @@ export class LoginUseCase {
19
19
  }
20
20
  async run() {
21
21
  try {
22
+ await this.trackingService.track('auth:sign_in', { status: 'started' });
22
23
  this.terminal.log('Starting authentication process...');
23
24
  // Start callback server
24
25
  await this.callbackHandler.start();
25
26
  // Get port and build redirect URI
26
27
  const port = this.callbackHandler.getPort();
27
28
  if (!port) {
28
- throw new Error('Failed to get callback server port');
29
+ const getPortFailedErr = 'Failed to get callback server port';
30
+ await this.trackingService.track('auth:sign_in', { message: getPortFailedErr, status: 'error' });
31
+ throw new Error(getPortFailedErr);
29
32
  }
30
33
  const redirectUri = `http://localhost:${port}/callback`;
31
34
  // Initiate authorization (generates PKCE parameters and state internally)
@@ -38,6 +41,7 @@ export class LoginUseCase {
38
41
  }
39
42
  catch {
40
43
  // Browser launch failed, will return URL to user
44
+ await this.trackingService.track('auth:sign_in', { message: 'browser launch failed', status: 'error' });
41
45
  }
42
46
  // If browser failed to open, display the URL for manual copy
43
47
  if (!browserOpened) {
@@ -60,13 +64,13 @@ export class LoginUseCase {
60
64
  userId: user.id,
61
65
  });
62
66
  await this.tokenStore.save(authToken);
63
- // Track successful authentication
64
- await this.trackingService.track('auth:signed_in');
67
+ await this.trackingService.track('auth:sign_in', { status: 'finished' });
65
68
  this.terminal.log(`Logged in as ${user.email}`);
66
69
  }
67
70
  catch (error) {
68
71
  // Throw error to let oclif handle display
69
72
  const errorMessage = error instanceof Error ? error.message : 'Authentication failed';
73
+ await this.trackingService.track('auth:sign_in', { message: errorMessage, status: 'error' });
70
74
  this.terminal.error(errorMessage);
71
75
  }
72
76
  }
@@ -74,10 +78,12 @@ export class LoginUseCase {
74
78
  if (error instanceof DiscoveryError) {
75
79
  const errorMessage = `Failed to configure authentication: ${error.message}\n` +
76
80
  'Please check your network connection and try again.';
81
+ await this.trackingService.track('auth:sign_in', { message: errorMessage, status: 'error' });
77
82
  this.terminal.error(errorMessage);
78
83
  }
79
84
  // Throw error to let oclif handle display
80
85
  const errorMessage = error instanceof Error ? error.message : 'Authentication failed';
86
+ await this.trackingService.track('auth:sign_in', { message: errorMessage, status: 'error' });
81
87
  this.terminal.error(errorMessage);
82
88
  }
83
89
  finally {
@@ -32,6 +32,7 @@ export class QueryUseCase {
32
32
  return randomUUID();
33
33
  }
34
34
  async run(options) {
35
+ await this.trackingService.track('mem:query', { status: 'started' });
35
36
  // Initialize storage for tool call tracking (auto-detects .brv/blobs)
36
37
  const storage = await getAgentStorage();
37
38
  let executionId = null;
@@ -39,14 +40,14 @@ export class QueryUseCase {
39
40
  // Get authentication token
40
41
  const token = await this.tokenStore.load();
41
42
  if (!token) {
42
- this.terminal.log('Authentication required. Please run "brv login" first.');
43
+ this.terminal.log('Authentication required. Please run "/login" first.');
43
44
  return;
44
45
  }
45
46
  // Load project config
46
47
  const brvConfig = await this.projectConfigStore.read();
47
48
  // Validate workspace is initialized
48
49
  if (!brvConfig) {
49
- throw new WorkspaceNotInitializedError('Project not initialized. Please run "brv init" to select your team and workspace.', '.brv');
50
+ throw new WorkspaceNotInitializedError('Project not initialized. Please run "/init" to select your team and workspace.', '.brv');
50
51
  }
51
52
  // Create execution with status='running' (query runs synchronously)
52
53
  executionId = storage.createExecution('query', options.query);
@@ -87,8 +88,7 @@ export class QueryUseCase {
87
88
  storage.updateExecutionStatus(executionId, 'completed', response);
88
89
  this.terminal.log('\nQuery Results:');
89
90
  this.terminal.log(response);
90
- // Track query
91
- await this.trackingService.track('mem:query');
91
+ await this.trackingService.track('mem:query', { status: 'finished' });
92
92
  }
93
93
  finally {
94
94
  // Cleanup old executions
@@ -107,6 +107,7 @@ export class QueryUseCase {
107
107
  }
108
108
  // Display context on one line, error on separate line
109
109
  process.stderr.write('Failed to query context tree:\n');
110
+ await this.trackingService.track('mem:query', { message: formatError(error), status: 'error' });
110
111
  this.terminal.log(formatError(error));
111
112
  }
112
113
  }
@@ -19,9 +19,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
19
19
  import { createContext, useContext, useEffect, useMemo, useState } from 'react';
20
20
  import { load } from '../../infra/repl/commands/index.js';
21
21
  import { useSlashCommandProcessor } from '../hooks/index.js';
22
+ import { useServices } from './services-context.js';
22
23
  const CommandsContext = createContext(undefined);
23
24
  export function CommandsProvider({ children }) {
24
25
  const [commands, setCommands] = useState([]);
26
+ const { version } = useServices();
25
27
  useEffect(() => {
26
28
  const abortController = new AbortController();
27
29
  async function loadCommands() {
@@ -34,9 +36,8 @@ export function CommandsProvider({ children }) {
34
36
  }, []);
35
37
  const commandContext = useMemo(() => ({
36
38
  slashCommands: commands,
37
- // version:, -> need pass version here
38
- // syncService:, -> need pass sync service here
39
- }), [commands]);
39
+ version,
40
+ }), [commands, version]);
40
41
  const { handleSlashCommand } = useSlashCommandProcessor(commandContext, commands);
41
42
  const contextValue = useMemo(() => ({
42
43
  commands,
@@ -386,5 +386,5 @@
386
386
  ]
387
387
  }
388
388
  },
389
- "version": "0.4.0"
389
+ "version": "0.4.1"
390
390
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "byterover-cli",
3
3
  "description": "ByteRover's CLI",
4
- "version": "0.4.0",
4
+ "version": "0.4.1",
5
5
  "author": "ByteRover",
6
6
  "bin": {
7
7
  "brv": "./bin/run.js"