byterover-cli 0.2.0 → 0.2.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.
@@ -6,10 +6,16 @@ import type { IProjectConfigStore } from '../core/interfaces/i-project-config-st
6
6
  import type { ISpaceService } from '../core/interfaces/i-space-service.js';
7
7
  import type { ITeamService } from '../core/interfaces/i-team-service.js';
8
8
  import type { ITokenStore } from '../core/interfaces/i-token-store.js';
9
+ import { BrvConfig } from '../core/domain/entities/brv-config.js';
9
10
  import { ITrackingService } from '../core/interfaces/i-tracking-service.js';
10
11
  export default class Init extends Command {
11
12
  static description: string;
12
13
  static examples: string[];
14
+ static flags: {
15
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ protected cleanupBeforeReInitialization(): Promise<void>;
18
+ protected confirmReInitialization(config: BrvConfig): Promise<boolean>;
13
19
  protected createServices(): {
14
20
  playbookService: IPlaybookService;
15
21
  projectConfigStore: IProjectConfigStore;
@@ -1,5 +1,7 @@
1
- import { select } from '@inquirer/prompts';
2
- import { Command, ux } from '@oclif/core';
1
+ import { confirm, select } from '@inquirer/prompts';
2
+ import { Command, Flags, ux } from '@oclif/core';
3
+ import { rm } from 'node:fs/promises';
4
+ import { join } from 'node:path';
3
5
  import { getCurrentConfig } from '../config/environment.js';
4
6
  import { BRV_DIR, PROJECT_CONFIG_FILE } from '../constants.js';
5
7
  import { BrvConfig } from '../core/domain/entities/brv-config.js';
@@ -16,6 +18,41 @@ export default class Init extends Command {
16
18
  '# Re-initialize if config exists (will show current config and exit):\n<%= config.bin %> <%= command.id %>',
17
19
  '# Full workflow: login then initialize:\n<%= config.bin %> login\n<%= config.bin %> <%= command.id %>',
18
20
  ];
21
+ static flags = {
22
+ force: Flags.boolean({
23
+ char: 'f',
24
+ default: false,
25
+ description: 'Force re-initialization without confirmation prompt',
26
+ }),
27
+ };
28
+ async cleanupBeforeReInitialization() {
29
+ const brvDir = join(process.cwd(), BRV_DIR);
30
+ this.log('\n Cleaning up existing ByteRover directory...');
31
+ ux.action.start(` Removing ${BRV_DIR}/`);
32
+ try {
33
+ await rm(brvDir, { force: true, recursive: true });
34
+ ux.action.stop('✓');
35
+ }
36
+ catch (error) {
37
+ ux.action.stop('✗');
38
+ this.error(`Failed to remove ${BRV_DIR}/: ${error instanceof Error ? error.message : 'Unknown error'}`);
39
+ }
40
+ }
41
+ async confirmReInitialization(config) {
42
+ this.log('\n Project is already initialized');
43
+ this.log(` Team: ${config.teamName}`);
44
+ this.log(` Space: ${config.spaceName}`);
45
+ this.log(` Config: ${join(process.cwd(), BRV_DIR, PROJECT_CONFIG_FILE)}`);
46
+ this.log('\n Re-initializing will:');
47
+ this.log(` - Remove the entire ${BRV_DIR}/ directory and all its contents`);
48
+ this.log(' - Allow you to select a new team/space');
49
+ this.log(' - Create a fresh configuration and ACE playbook');
50
+ this.log(' - Regenerate rule instructions\n');
51
+ return confirm({
52
+ default: false,
53
+ message: 'Continue with re-initialization?',
54
+ });
55
+ }
19
56
  createServices() {
20
57
  const envConfig = getCurrentConfig();
21
58
  const tokenStore = new KeychainTokenStore();
@@ -62,18 +99,31 @@ export default class Init extends Command {
62
99
  return selectedTeam;
63
100
  }
64
101
  async run() {
102
+ const { flags } = await this.parse(Init);
65
103
  try {
66
104
  const { playbookService, projectConfigStore, spaceService, teamService, tokenStore, trackingService } = this.createServices();
67
- // 1. Check if already initialized
68
- const isInitialized = await projectConfigStore.exists();
69
- if (isInitialized) {
70
- this.log('Project is already initialized with ByteRover.');
71
- const existingProjectConfig = await projectConfigStore.read();
72
- this.log(`Your space for this project is: ${existingProjectConfig?.teamName}/${existingProjectConfig?.spaceName}`);
73
- return;
105
+ const alreadyInitialized = await projectConfigStore.exists();
106
+ if (alreadyInitialized) {
107
+ const currentConfig = await projectConfigStore.read();
108
+ if (currentConfig === undefined) {
109
+ this.error('Configuration file exists but cannot be read. Please check .brv/config.json');
110
+ }
111
+ if (!flags.force) {
112
+ const confirmed = await this.confirmReInitialization(currentConfig);
113
+ if (!confirmed) {
114
+ this.log('\nCancelled. Project configuration unchanged.');
115
+ return;
116
+ }
117
+ }
118
+ try {
119
+ await this.cleanupBeforeReInitialization();
120
+ }
121
+ catch (error) {
122
+ this.error(`Failed to clean up existing data: ${error instanceof Error ? error.message : 'Unknown error'}`);
123
+ }
124
+ this.log('\n'); // Spacing before continuing with init flow
74
125
  }
75
126
  this.log('Initializing ByteRover project...\n');
76
- // 2. Load and validate authentication token
77
127
  const token = await tokenStore.load();
78
128
  if (token === undefined) {
79
129
  this.error('Not authenticated. Please run "brv login" first.');
@@ -81,7 +131,6 @@ export default class Init extends Command {
81
131
  if (!token.isValid()) {
82
132
  this.error('Authentication token expired. Please run "brv login" again.');
83
133
  }
84
- // 3. Fetch all teams with spinner
85
134
  ux.action.start('Fetching all teams');
86
135
  const teamResult = await teamService.getTeams(token.accessToken, token.sessionKey, { fetchAll: true });
87
136
  ux.action.stop();
@@ -91,10 +140,8 @@ export default class Init extends Command {
91
140
  this.log(`Please visit ${getCurrentConfig().webAppUrl} to create your first team.`);
92
141
  return;
93
142
  }
94
- // 4. Prompt for team selection
95
143
  this.log();
96
144
  const selectedTeam = await this.promptForTeamSelection(teams);
97
- // 5. Fetch all spaces for the selected team with spinner
98
145
  ux.action.start('Fetching all spaces');
99
146
  const spaceResult = await spaceService.getSpaces(token.accessToken, token.sessionKey, selectedTeam.id, {
100
147
  fetchAll: true,
@@ -106,13 +153,10 @@ export default class Init extends Command {
106
153
  this.log(`Please visit ${getCurrentConfig().webAppUrl} to create your first space for ${selectedTeam.getDisplayName()}.`);
107
154
  return;
108
155
  }
109
- // 6. Prompt for space selection
110
156
  this.log();
111
157
  const selectedSpace = await this.promptForSpaceSelection(spaces);
112
- // 7. Create and save configuration
113
158
  const config = BrvConfig.fromSpace(selectedSpace);
114
159
  await projectConfigStore.write(config);
115
- // 8. Initialize ACE playbook
116
160
  this.log('\nInitializing ACE context...');
117
161
  try {
118
162
  const playbookPath = await playbookService.initialize();
@@ -122,13 +166,10 @@ export default class Init extends Command {
122
166
  // Warn but don't fail if ACE init fails
123
167
  this.warn(`ACE initialization skipped: ${error instanceof Error ? error.message : 'Unknown error'}`);
124
168
  }
125
- // 9. Generate rules
126
169
  this.log(`\nGenerate rule instructions for coding agents to work with ByteRover correctly`);
127
170
  this.log();
128
171
  await this.config.runCommand('gen-rules');
129
- // Track space initialization
130
172
  await trackingService.track('space:init');
131
- // 10. Display success
132
173
  this.log(`\n✓ Project initialized successfully!`);
133
174
  this.log(`✓ Connected to space: ${selectedSpace.getDisplayName()}`);
134
175
  this.log(`✓ Configuration saved to: ${BRV_DIR}/${PROJECT_CONFIG_FILE}`);
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { ITokenStore } from '../core/interfaces/i-token-store.js';
3
+ import type { ITrackingService } from '../core/interfaces/i-tracking-service.js';
4
+ export default class Logout extends Command {
5
+ static description: string;
6
+ static examples: string[];
7
+ static flags: {
8
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ protected confirmLogout(userEmail: string): Promise<boolean>;
11
+ protected createServices(): {
12
+ tokenStore: ITokenStore;
13
+ trackingService: ITrackingService;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,61 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { Command, Flags } from '@oclif/core';
3
+ import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
4
+ import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
5
+ export default class Logout extends Command {
6
+ static description = 'Log out of ByteRover CLI and clear authentication (does not affect local project files)';
7
+ static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --yes'];
8
+ static flags = {
9
+ yes: Flags.boolean({
10
+ char: 'y',
11
+ default: false,
12
+ description: 'Skip confirmation prompt',
13
+ }),
14
+ };
15
+ async confirmLogout(userEmail) {
16
+ return confirm({
17
+ // Pressing 'Enter' = Yes
18
+ default: true,
19
+ message: `Logging out ${userEmail}. Are you sure?`,
20
+ });
21
+ }
22
+ createServices() {
23
+ const tokenStore = new KeychainTokenStore();
24
+ const trackingService = new MixpanelTrackingService(tokenStore);
25
+ return {
26
+ tokenStore,
27
+ trackingService,
28
+ };
29
+ }
30
+ async run() {
31
+ const { flags } = await this.parse(Logout);
32
+ const { tokenStore, trackingService } = this.createServices();
33
+ try {
34
+ const token = await tokenStore.load();
35
+ if (token === undefined) {
36
+ this.log('You are not currently logged in.');
37
+ return;
38
+ }
39
+ if (!flags.yes) {
40
+ const confirmed = await this.confirmLogout(token.userEmail);
41
+ if (!confirmed) {
42
+ this.log('Logout cancelled.');
43
+ return;
44
+ }
45
+ }
46
+ try {
47
+ await trackingService.track('auth:signed_out');
48
+ }
49
+ catch { }
50
+ await tokenStore.clear();
51
+ this.log('Successfully logged out.');
52
+ this.log("Run 'brv login' to authenticate again.");
53
+ }
54
+ catch (error) {
55
+ if (error instanceof Error && error.message.includes('keychain')) {
56
+ this.error('Unable to access system keychain. Please check your system permissions and try again.');
57
+ }
58
+ this.error(error instanceof Error ? error.message : 'Logout failed');
59
+ }
60
+ }
61
+ }
@@ -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", "space:init", "space:changed", "rule:generate", "ace:add_bullet", "ace:update_bullet", "ace:remove_bullet", "ace:view_status", "mem:push", "mem:retrieve"];
4
+ export declare const EVENT_VALUES: readonly ["auth:signed_in", "auth:signed_out", "space:init", "space:changed", "rule:generate", "ace:add_bullet", "ace:update_bullet", "ace:remove_bullet", "ace:view_status", "mem:push", "mem:retrieve"];
5
5
  export type EventName = (typeof EVENT_VALUES)[number];
6
6
  export interface PropertyDict {
7
7
  [key: string]: any;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  export const EVENT_VALUES = [
5
5
  'auth:signed_in',
6
+ 'auth:signed_out',
6
7
  'space:init',
7
8
  'space:changed',
8
9
  'rule:generate',
@@ -1,8 +1,4 @@
1
1
  import { Agent } from '../domain/entities/agent.js';
2
- /**
3
- * ByteRover CLI generated rule template tag.
4
- */
5
- export declare const BR_RULE_TAG = "Generated by ByteRover CLI for";
6
2
  /**
7
3
  * Interface for rule template service operations.
8
4
  */
@@ -1,4 +1 @@
1
- /**
2
- * ByteRover CLI generated rule template tag.
3
- */
4
- export const BR_RULE_TAG = 'Generated by ByteRover CLI for';
1
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * ByteRover CLI generated rule template tag.
3
+ */
4
+ export declare const BR_RULE_TAG = "Generated by ByteRover CLI for";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * ByteRover CLI generated rule template tag.
3
+ */
4
+ export const BR_RULE_TAG = 'Generated by ByteRover CLI for';
@@ -1,4 +1,4 @@
1
- import { BR_RULE_TAG } from '../../core/interfaces/i-rule-template-service.js';
1
+ import { BR_RULE_TAG } from './constants.js';
2
2
  const guideHeaders = [
3
3
  {
4
4
  agent: 'Augment Code',
@@ -1,6 +1,6 @@
1
1
  import { RuleExistsError } from '../../core/domain/errors/rule-error.js';
2
- import { BR_RULE_TAG } from '../../core/interfaces/i-rule-template-service.js';
3
2
  import { AGENT_RULE_CONFIGS } from './agent-rule-config.js';
3
+ import { BR_RULE_TAG } from './constants.js';
4
4
  /**
5
5
  * Service for writing agent-specific rule files.
6
6
  * Uses IFileService to write files and RuleTemplateService to generate content.
@@ -24,20 +24,16 @@ export class RuleWriterService {
24
24
  }
25
25
  const { filePath, writeMode } = config;
26
26
  const fileExists = await this.fileService.exists(filePath);
27
- // Throw an error if the file exists and force is not set
28
27
  if (writeMode === 'overwrite' && fileExists && !force) {
29
28
  throw new RuleExistsError();
30
29
  }
31
30
  if (writeMode === 'append' && fileExists && !force) {
32
31
  const content = await this.fileService.read(filePath);
33
- // Throw an error if the rule already exists
34
32
  if (content.includes(BR_RULE_TAG)) {
35
33
  throw new RuleExistsError();
36
34
  }
37
35
  }
38
- // Generate rule content
39
36
  const ruleContent = await this.templateService.generateRuleContent(agent);
40
- // Write the rule file
41
37
  await this.fileService.write(ruleContent, filePath, writeMode);
42
38
  }
43
39
  }
@@ -72,6 +72,7 @@ brv retrieve -q "database connection issues"
72
72
  **Flags:**
73
73
 
74
74
  - `-b, --branch <string>`: ByteRover branch name (default: "main", NOT git branch)
75
+ - `-y, --yes`: Skip confirmation prompt
75
76
 
76
77
  **Examples:**
77
78
 
@@ -120,7 +121,24 @@ brv complete "auth-update" "Improved error handling" "Better errors" --tool-usag
120
121
 
121
122
  ---
122
123
 
123
- ### `brv show`
124
+ ### `brv status`
125
+
126
+ **Description**: Show CLI status and project information. Display local ACE context (ACE playbook) managed by ByteRover CLI.
127
+
128
+ **Arguments:**
129
+
130
+ - `DIRECTORY`:Project directory (defaults to current directory).
131
+
132
+ **Flags:**
133
+
134
+ - `-f, --format=<option>`: [default: table] Output format. <options: table|json>
135
+
136
+ **Examples:**
137
+
138
+ ```bash
139
+ brv status
140
+ brv status --format json
141
+ ```
124
142
 
125
143
  ## Best Practices
126
144
 
@@ -19,19 +19,19 @@ Once the user chooses "Auto-run", apply that preference to all subsequent `brv`
19
19
 
20
20
  ## Daily Flow
21
21
 
22
- 1.Retrieve context
22
+ 1. Always attempt to retrieve relevant context from ByteRover first by running `brv retrieve`.
23
23
 
24
- 2.Do the work with minimal code reads
24
+ 2. Do the work with minimal code reads
25
25
 
26
- 3.Record new or refined knowledge in the playbook
26
+ 3. Record new or refined knowledge in the playbook
27
27
 
28
- - Add only specific, actionable, contextual items; avoid secrets. Redact when in doubt.
28
+ - Add only specific, actionable, contextual items; avoid secrets. Redact when in doubt.
29
29
 
30
- 4.Sync policy (manual by default)
30
+ 4. Sync policy (manual by default)
31
31
 
32
- - Do not auto-push.
33
- - When the playbook has meaningful updates, prompt the user to run the command
34
- - If the user has explicitly allowed auto-push, proceed non-interactively.
32
+ - Do not auto-push.
33
+ - When the playbook has meaningful updates, prompt the user to run the command
34
+ - If the user has explicitly allowed auto-push, proceed non-interactively by running `brv push -y`.
35
35
 
36
36
  ## Playbook Guideline
37
37
 
@@ -210,7 +210,15 @@
210
210
  "# Re-initialize if config exists (will show current config and exit):\n<%= config.bin %> <%= command.id %>",
211
211
  "# Full workflow: login then initialize:\n<%= config.bin %> login\n<%= config.bin %> <%= command.id %>"
212
212
  ],
213
- "flags": {},
213
+ "flags": {
214
+ "force": {
215
+ "char": "f",
216
+ "description": "Force re-initialization without confirmation prompt",
217
+ "name": "force",
218
+ "allowNo": false,
219
+ "type": "boolean"
220
+ }
221
+ },
214
222
  "hasDynamicHelp": false,
215
223
  "hiddenAliases": [],
216
224
  "id": "init",
@@ -251,6 +259,38 @@
251
259
  "login.js"
252
260
  ]
253
261
  },
262
+ "logout": {
263
+ "aliases": [],
264
+ "args": {},
265
+ "description": "Log out of ByteRover CLI and clear authentication (does not affect local project files)",
266
+ "examples": [
267
+ "<%= config.bin %> <%= command.id %>",
268
+ "<%= config.bin %> <%= command.id %> --yes"
269
+ ],
270
+ "flags": {
271
+ "yes": {
272
+ "char": "y",
273
+ "description": "Skip confirmation prompt",
274
+ "name": "yes",
275
+ "allowNo": false,
276
+ "type": "boolean"
277
+ }
278
+ },
279
+ "hasDynamicHelp": false,
280
+ "hiddenAliases": [],
281
+ "id": "logout",
282
+ "pluginAlias": "byterover-cli",
283
+ "pluginName": "byterover-cli",
284
+ "pluginType": "core",
285
+ "strict": true,
286
+ "enableJsonFlag": false,
287
+ "isESM": true,
288
+ "relativePath": [
289
+ "dist",
290
+ "commands",
291
+ "logout.js"
292
+ ]
293
+ },
254
294
  "push": {
255
295
  "aliases": [],
256
296
  "args": {},
@@ -479,5 +519,5 @@
479
519
  ]
480
520
  }
481
521
  },
482
- "version": "0.2.0"
522
+ "version": "0.2.1"
483
523
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "byterover-cli",
3
3
  "description": "ByteRover's CLI",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "author": "ByteRover",
6
6
  "bin": {
7
7
  "brv": "./bin/run.js"
@@ -47,10 +47,8 @@
47
47
  "./dist",
48
48
  "./oclif.manifest.json"
49
49
  ],
50
- "homepage": "https://github.com/campfirein/byterover-cli",
51
- "keywords": [
52
- "oclif"
53
- ],
50
+ "homepage": "https://www.byterover.dev/",
51
+ "keywords": [],
54
52
  "license": "UNLICENSED",
55
53
  "main": "dist/index.js",
56
54
  "type": "module",