byterover-cli 2.1.2 → 2.1.4

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.
@@ -112,7 +112,7 @@ export const LLM_REGISTRY = {
112
112
  supportedFileTypes: ['image', 'pdf'],
113
113
  },
114
114
  gemini: {
115
- defaultModel: 'gemini-3.1-flash-lite-preview',
115
+ defaultModel: 'gemini-3-flash-preview',
116
116
  models: [
117
117
  // Gemini 3.1 series
118
118
  {
@@ -125,7 +125,6 @@ export const LLM_REGISTRY = {
125
125
  supportsThinking: true,
126
126
  },
127
127
  charsPerToken: 4,
128
- default: true,
129
128
  displayName: 'Gemini 3.1 Flash Lite',
130
129
  maxInputTokens: 1_000_000,
131
130
  maxOutputTokens: 8192,
@@ -144,6 +143,7 @@ export const LLM_REGISTRY = {
144
143
  supportsThinking: true,
145
144
  },
146
145
  charsPerToken: 4,
146
+ default: true,
147
147
  displayName: 'Gemini 3 Flash (Preview)',
148
148
  maxInputTokens: 1_000_000,
149
149
  maxOutputTokens: 8192,
@@ -839,7 +839,7 @@ export class CipherAgent extends BaseAgent {
839
839
  countTokens: (text) => mapGenerator.estimateTokensSync(text),
840
840
  };
841
841
  // Compute registry-clamped maxContextTokens
842
- const mapModel = sessionLLMConfig.model ?? 'gemini-3.1-flash-lite-preview';
842
+ const mapModel = sessionLLMConfig.model ?? 'gemini-3-flash-preview';
843
843
  const mapRegistryProvider = resolveRegistryProvider(mapModel, mapProvider);
844
844
  const effectiveMaxContextTokens = getEffectiveMaxInputTokens(mapRegistryProvider, mapModel, sessionLLMConfig.maxInputTokens);
845
845
  const mapLogger = new EventBasedLogger(this._agentEventBus, 'MapTools');
@@ -270,7 +270,7 @@ async function executePrompt(prompt, agent, state, eventBus) {
270
270
  */
271
271
  export async function startInteractiveLoop(agent, options) {
272
272
  // Display welcome message
273
- displayWelcome(options?.sessionId ?? 'agent-session', options?.model ?? 'gemini-3.1-flash-lite-preview', options?.eventBus);
273
+ displayWelcome(options?.sessionId ?? 'agent-session', options?.model ?? 'gemini-3-flash-preview', options?.eventBus);
274
274
  // Create readline interface
275
275
  const rl = readline.createInterface({
276
276
  input: process.stdin,
@@ -163,7 +163,7 @@ export async function createCipherAgentServices(config, agentEventBus) {
163
163
  const messageStorageService = messageStorage;
164
164
  const historyStorage = new GranularHistoryStorage(messageStorage);
165
165
  // CompactionService for context overflow management
166
- const tokenizer = new GeminiTokenizer(config.model ?? 'gemini-3.1-flash-lite-preview');
166
+ const tokenizer = new GeminiTokenizer(config.model ?? 'gemini-3-flash-preview');
167
167
  const compactionService = new CompactionService(messageStorage, tokenizer, {
168
168
  overflowThreshold: 0.85, // 85% triggers compaction check
169
169
  protectedTurns: 2, // Protect last 2 user turns from pruning
@@ -241,13 +241,13 @@ export function createSessionServices(sessionId, sharedServices, httpConfig, llm
241
241
  });
242
242
  const escalatedStrategy = new EscalatedCompressionStrategy({
243
243
  generator: compactionGenerator,
244
- model: llmConfig.model ?? 'gemini-3.1-flash-lite-preview',
244
+ model: llmConfig.model ?? 'gemini-3-flash-preview',
245
245
  });
246
246
  return new AgentLLMService(sessionId, generator, {
247
247
  maxInputTokens: llmConfig.maxInputTokens,
248
248
  maxIterations: llmConfig.maxIterations ?? 50,
249
249
  maxTokens: llmConfig.maxTokens ?? 8192,
250
- model: llmConfig.model ?? 'gemini-3.1-flash-lite-preview',
250
+ model: llmConfig.model ?? 'gemini-3-flash-preview',
251
251
  provider,
252
252
  temperature: llmConfig.temperature ?? 0.7,
253
253
  verbose: llmConfig.verbose ?? false,
@@ -27,7 +27,7 @@ export const byteroverProvider = {
27
27
  temperature: config.temperature,
28
28
  });
29
29
  },
30
- defaultModel: 'gemini-3.1-flash-lite-preview',
30
+ defaultModel: 'gemini-3-flash-preview',
31
31
  description: 'Internal ByteRover LLM',
32
32
  envVars: [],
33
33
  id: 'byterover',
@@ -89,6 +89,7 @@ export default class Restart extends Command {
89
89
  */
90
90
  private static waitForPidToDie;
91
91
  protected cleanupAllDaemonFiles(dataDir: string): void;
92
+ protected exitProcess(code: number): void;
92
93
  protected killAllBrvProcesses(dataDir: string): Promise<void>;
93
94
  run(): Promise<void>;
94
95
  protected startDaemon(serverPath: string): Promise<EnsureDaemonResult>;
@@ -95,7 +95,7 @@ All open sessions and background processes are stopped before the fresh start.`;
95
95
  * When cwd is resolved: check only absolute patterns (precise, no false positives).
96
96
  * When cwd is unavailable: also check relative fallback patterns (./bin/dev.js).
97
97
  */
98
- static killByMacOsProcScan(patterns, excludePid) {
98
+ static killByMacOsProcScan(patterns, excludePids) {
99
99
  const psResult = spawnSync('ps', ['-A', '-o', 'pid,args'], { encoding: 'utf8' });
100
100
  if (!psResult.stdout)
101
101
  return;
@@ -106,7 +106,7 @@ All open sessions and background processes are stopped before the fresh start.`;
106
106
  continue;
107
107
  const pid = Number.parseInt(match[1], 10);
108
108
  const rawCmdline = match[2].trim();
109
- if (Number.isNaN(pid) || pid === excludePid)
109
+ if (Number.isNaN(pid) || excludePids.has(pid))
110
110
  continue;
111
111
  // Resolve relative .js path using cwd map to get an absolute path for matching.
112
112
  let cmdline = rawCmdline;
@@ -170,7 +170,7 @@ All open sessions and background processes are stopped before the fresh start.`;
170
170
  * Works on all Linux distros including Alpine — /proc is a kernel feature,
171
171
  * no userspace tools required.
172
172
  */
173
- static killByProcScan(patterns, excludePid) {
173
+ static killByProcScan(patterns, excludePids) {
174
174
  let entries;
175
175
  try {
176
176
  entries = readdirSync('/proc');
@@ -180,7 +180,7 @@ All open sessions and background processes are stopped before the fresh start.`;
180
180
  }
181
181
  for (const entry of entries) {
182
182
  const pid = Number.parseInt(entry, 10);
183
- if (Number.isNaN(pid) || pid === excludePid)
183
+ if (Number.isNaN(pid) || excludePids.has(pid))
184
184
  continue;
185
185
  try {
186
186
  const args = readFileSync(join('/proc', entry, 'cmdline'), 'utf8')
@@ -235,17 +235,20 @@ All open sessions and background processes are stopped before the fresh start.`;
235
235
  static patternKill() {
236
236
  const brvBinDir = dirname(process.argv[1]);
237
237
  const allPatterns = Restart.buildKillPatterns(brvBinDir, process.argv[1]);
238
+ // Exclude both the current node process and its parent shell wrapper (install.sh installs
239
+ // use a shell script that forks node — killing the wrapper garbles terminal output).
240
+ const excludePids = new Set([process.pid, process.ppid]);
238
241
  if (process.platform === 'win32') {
239
242
  const whereClause = allPatterns.map((p) => `$_.CommandLine -like '*${p}*'`).join(' -or ');
240
- const script = `Get-CimInstance Win32_Process | Where-Object { (${whereClause}) -and $_.ProcessId -ne ${process.pid} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }`;
243
+ const script = `Get-CimInstance Win32_Process | Where-Object { (${whereClause}) -and $_.ProcessId -ne ${process.pid} -and $_.ProcessId -ne ${process.ppid} } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }`;
241
244
  spawnSync('powershell', ['-Command', script], { stdio: 'ignore' });
242
245
  }
243
246
  else if (process.platform === 'linux') {
244
- Restart.killByProcScan(allPatterns, process.pid);
247
+ Restart.killByProcScan(allPatterns, excludePids);
245
248
  }
246
249
  else {
247
250
  // macOS (and other Unix): ps -A scan with lsof cwd resolution for relative paths
248
- Restart.killByMacOsProcScan(allPatterns, process.pid);
251
+ Restart.killByMacOsProcScan(allPatterns, excludePids);
249
252
  }
250
253
  }
251
254
  static sleep(ms) {
@@ -284,6 +287,10 @@ All open sessions and background processes are stopped before the fresh start.`;
284
287
  }
285
288
  }
286
289
  }
290
+ exitProcess(code) {
291
+ // eslint-disable-next-line n/no-process-exit, unicorn/no-process-exit
292
+ process.exit(code);
293
+ }
287
294
  async killAllBrvProcesses(dataDir) {
288
295
  this.log('Stopping processes...');
289
296
  // Read PID directly from daemon.json — no health-check filtering.
@@ -315,7 +322,7 @@ All open sessions and background processes are stopped before the fresh start.`;
315
322
  const result = await this.startDaemon(serverPath);
316
323
  if (result.success) {
317
324
  this.log(`Daemon started (PID ${result.info.pid}, port ${result.info.port})`);
318
- return;
325
+ break;
319
326
  }
320
327
  const detail = result.spawnError ? ` (${result.spawnError})` : '';
321
328
  if (attempt < MAX_ATTEMPTS) {
@@ -326,6 +333,11 @@ All open sessions and background processes are stopped before the fresh start.`;
326
333
  }
327
334
  }
328
335
  /* eslint-enable no-await-in-loop */
336
+ // Force exit — oclif does not call process.exit() after run() returns,
337
+ // relying on the event loop to drain. In production, third-party plugin
338
+ // hooks (e.g. @oclif/plugin-update) can leave open handles that prevent
339
+ // the process from exiting naturally. mcp.ts uses the same pattern.
340
+ this.exitProcess(0);
329
341
  }
330
342
  async startDaemon(serverPath) {
331
343
  return ensureDaemonRunning({ serverPath, timeoutMs: DAEMON_START_TIMEOUT_MS });
@@ -31,7 +31,7 @@ export declare const TRANSPORT_RECONNECTION_ATTEMPTS = 30;
31
31
  export declare const TRANSPORT_PING_INTERVAL_MS = 5000;
32
32
  export declare const TRANSPORT_PING_TIMEOUT_MS = 10000;
33
33
  export declare const TRANSPORT_DEFAULT_TRANSPORTS: ('polling' | 'websocket')[];
34
- export declare const DEFAULT_LLM_MODEL = "gemini-3.1-flash-lite-preview";
34
+ export declare const DEFAULT_LLM_MODEL = "gemini-3-flash-preview";
35
35
  export declare const PROJECT_ROOM_PREFIX = "project:";
36
36
  export declare const PROJECT_ROOM_SUFFIX = ":broadcast";
37
37
  export declare const GLOBAL_PROJECTS_DIR = "projects";
@@ -40,7 +40,7 @@ export const TRANSPORT_PING_TIMEOUT_MS = 10_000; // 10s timeout - avoid false di
40
40
  // HTTP polling may be blocked by IDE sandboxes causing "xhr poll error"
41
41
  export const TRANSPORT_DEFAULT_TRANSPORTS = ['websocket'];
42
42
  // LLM Model defaults
43
- export const DEFAULT_LLM_MODEL = 'gemini-3.1-flash-lite-preview';
43
+ export const DEFAULT_LLM_MODEL = 'gemini-3-flash-preview';
44
44
  // Project room naming convention
45
45
  export const PROJECT_ROOM_PREFIX = 'project:';
46
46
  export const PROJECT_ROOM_SUFFIX = ':broadcast';
@@ -81,7 +81,7 @@ export async function resolveProviderConfig(providerConfigStore, providerKeychai
81
81
  provider: activeProvider,
82
82
  providerApiKey: apiKey || undefined,
83
83
  providerBaseUrl: config.getBaseUrl(activeProvider) || undefined,
84
- providerKeyMissing: !apiKey,
84
+ providerKeyMissing: providerRequiresApiKey(activeProvider) && !apiKey,
85
85
  };
86
86
  }
87
87
  case 'openrouter': {
@@ -91,7 +91,7 @@ export async function resolveProviderConfig(providerConfigStore, providerKeychai
91
91
  maxInputTokens,
92
92
  openRouterApiKey: apiKey || undefined,
93
93
  provider: activeProvider,
94
- providerKeyMissing: !apiKey,
94
+ providerKeyMissing: providerRequiresApiKey(activeProvider) && !apiKey,
95
95
  };
96
96
  }
97
97
  default: {
@@ -105,7 +105,7 @@ export async function resolveProviderConfig(providerConfigStore, providerKeychai
105
105
  providerApiKey: apiKey || undefined,
106
106
  providerBaseUrl: providerDef?.baseUrl || undefined,
107
107
  providerHeaders: headers && Object.keys(headers).length > 0 ? { ...headers } : undefined,
108
- providerKeyMissing: !apiKey,
108
+ providerKeyMissing: providerRequiresApiKey(activeProvider) && !apiKey,
109
109
  };
110
110
  }
111
111
  }
@@ -997,6 +997,104 @@
997
997
  "switch.js"
998
998
  ]
999
999
  },
1000
+ "space:list": {
1001
+ "aliases": [],
1002
+ "args": {},
1003
+ "description": "List all teams and spaces",
1004
+ "examples": [
1005
+ "<%= config.bin %> space list",
1006
+ "<%= config.bin %> space list --format json"
1007
+ ],
1008
+ "flags": {
1009
+ "format": {
1010
+ "char": "f",
1011
+ "description": "Output format",
1012
+ "name": "format",
1013
+ "default": "text",
1014
+ "hasDynamicHelp": false,
1015
+ "multiple": false,
1016
+ "options": [
1017
+ "text",
1018
+ "json"
1019
+ ],
1020
+ "type": "option"
1021
+ }
1022
+ },
1023
+ "hasDynamicHelp": false,
1024
+ "hiddenAliases": [],
1025
+ "id": "space:list",
1026
+ "pluginAlias": "byterover-cli",
1027
+ "pluginName": "byterover-cli",
1028
+ "pluginType": "core",
1029
+ "strict": true,
1030
+ "enableJsonFlag": false,
1031
+ "isESM": true,
1032
+ "relativePath": [
1033
+ "dist",
1034
+ "oclif",
1035
+ "commands",
1036
+ "space",
1037
+ "list.js"
1038
+ ]
1039
+ },
1040
+ "space:switch": {
1041
+ "aliases": [],
1042
+ "args": {},
1043
+ "description": "Switch to a different space",
1044
+ "examples": [
1045
+ "<%= config.bin %> space switch --team acme --name my-space",
1046
+ "<%= config.bin %> space switch --team acme --name my-space --format json"
1047
+ ],
1048
+ "flags": {
1049
+ "format": {
1050
+ "char": "f",
1051
+ "description": "Output format",
1052
+ "name": "format",
1053
+ "default": "text",
1054
+ "hasDynamicHelp": false,
1055
+ "multiple": false,
1056
+ "options": [
1057
+ "text",
1058
+ "json"
1059
+ ],
1060
+ "type": "option"
1061
+ },
1062
+ "name": {
1063
+ "char": "n",
1064
+ "description": "Name of the space to switch to",
1065
+ "name": "name",
1066
+ "required": true,
1067
+ "hasDynamicHelp": false,
1068
+ "multiple": false,
1069
+ "type": "option"
1070
+ },
1071
+ "team": {
1072
+ "char": "t",
1073
+ "description": "Team name",
1074
+ "name": "team",
1075
+ "required": true,
1076
+ "hasDynamicHelp": false,
1077
+ "multiple": false,
1078
+ "type": "option"
1079
+ }
1080
+ },
1081
+ "hasDynamicHelp": false,
1082
+ "hiddenAliases": [],
1083
+ "id": "space:switch",
1084
+ "pluginAlias": "byterover-cli",
1085
+ "pluginName": "byterover-cli",
1086
+ "pluginType": "core",
1087
+ "strict": true,
1088
+ "enableJsonFlag": false,
1089
+ "isESM": true,
1090
+ "relativePath": [
1091
+ "dist",
1092
+ "oclif",
1093
+ "commands",
1094
+ "space",
1095
+ "switch.js"
1096
+ ]
1097
+ },
1000
1098
  "providers:connect": {
1001
1099
  "aliases": [],
1002
1100
  "args": {
@@ -1237,104 +1335,6 @@
1237
1335
  "switch.js"
1238
1336
  ]
1239
1337
  },
1240
- "space:list": {
1241
- "aliases": [],
1242
- "args": {},
1243
- "description": "List all teams and spaces",
1244
- "examples": [
1245
- "<%= config.bin %> space list",
1246
- "<%= config.bin %> space list --format json"
1247
- ],
1248
- "flags": {
1249
- "format": {
1250
- "char": "f",
1251
- "description": "Output format",
1252
- "name": "format",
1253
- "default": "text",
1254
- "hasDynamicHelp": false,
1255
- "multiple": false,
1256
- "options": [
1257
- "text",
1258
- "json"
1259
- ],
1260
- "type": "option"
1261
- }
1262
- },
1263
- "hasDynamicHelp": false,
1264
- "hiddenAliases": [],
1265
- "id": "space:list",
1266
- "pluginAlias": "byterover-cli",
1267
- "pluginName": "byterover-cli",
1268
- "pluginType": "core",
1269
- "strict": true,
1270
- "enableJsonFlag": false,
1271
- "isESM": true,
1272
- "relativePath": [
1273
- "dist",
1274
- "oclif",
1275
- "commands",
1276
- "space",
1277
- "list.js"
1278
- ]
1279
- },
1280
- "space:switch": {
1281
- "aliases": [],
1282
- "args": {},
1283
- "description": "Switch to a different space",
1284
- "examples": [
1285
- "<%= config.bin %> space switch --team acme --name my-space",
1286
- "<%= config.bin %> space switch --team acme --name my-space --format json"
1287
- ],
1288
- "flags": {
1289
- "format": {
1290
- "char": "f",
1291
- "description": "Output format",
1292
- "name": "format",
1293
- "default": "text",
1294
- "hasDynamicHelp": false,
1295
- "multiple": false,
1296
- "options": [
1297
- "text",
1298
- "json"
1299
- ],
1300
- "type": "option"
1301
- },
1302
- "name": {
1303
- "char": "n",
1304
- "description": "Name of the space to switch to",
1305
- "name": "name",
1306
- "required": true,
1307
- "hasDynamicHelp": false,
1308
- "multiple": false,
1309
- "type": "option"
1310
- },
1311
- "team": {
1312
- "char": "t",
1313
- "description": "Team name",
1314
- "name": "team",
1315
- "required": true,
1316
- "hasDynamicHelp": false,
1317
- "multiple": false,
1318
- "type": "option"
1319
- }
1320
- },
1321
- "hasDynamicHelp": false,
1322
- "hiddenAliases": [],
1323
- "id": "space:switch",
1324
- "pluginAlias": "byterover-cli",
1325
- "pluginName": "byterover-cli",
1326
- "pluginType": "core",
1327
- "strict": true,
1328
- "enableJsonFlag": false,
1329
- "isESM": true,
1330
- "relativePath": [
1331
- "dist",
1332
- "oclif",
1333
- "commands",
1334
- "space",
1335
- "switch.js"
1336
- ]
1337
- },
1338
1338
  "hub:registry:add": {
1339
1339
  "aliases": [],
1340
1340
  "args": {
@@ -1534,5 +1534,5 @@
1534
1534
  ]
1535
1535
  }
1536
1536
  },
1537
- "version": "2.1.2"
1537
+ "version": "2.1.4"
1538
1538
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "byterover-cli",
3
3
  "description": "ByteRover's CLI",
4
- "version": "2.1.2",
4
+ "version": "2.1.4",
5
5
  "author": "ByteRover",
6
6
  "bin": {
7
7
  "brv": "./bin/run.js"