byterover-cli 1.0.3 → 1.0.5

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 (176) hide show
  1. package/README.md +75 -12
  2. package/dist/commands/curate.js +3 -3
  3. package/dist/commands/main.d.ts +13 -0
  4. package/dist/commands/main.js +55 -4
  5. package/dist/commands/query.js +3 -3
  6. package/dist/commands/status.js +2 -2
  7. package/dist/constants.d.ts +2 -1
  8. package/dist/constants.js +4 -1
  9. package/dist/core/domain/cipher/file-system/types.d.ts +2 -0
  10. package/dist/core/domain/cipher/llm/registry.js +53 -2
  11. package/dist/core/domain/cipher/llm/types.d.ts +2 -0
  12. package/dist/core/domain/cipher/process/types.d.ts +7 -0
  13. package/dist/core/domain/cipher/session/session-metadata.d.ts +178 -0
  14. package/dist/core/domain/cipher/session/session-metadata.js +147 -0
  15. package/dist/core/domain/entities/auth-token.js +6 -3
  16. package/dist/core/domain/entities/event.d.ts +1 -1
  17. package/dist/core/domain/entities/event.js +2 -1
  18. package/dist/core/domain/knowledge/markdown-writer.d.ts +15 -18
  19. package/dist/core/domain/knowledge/markdown-writer.js +232 -34
  20. package/dist/core/domain/knowledge/relation-parser.d.ts +37 -36
  21. package/dist/core/domain/knowledge/relation-parser.js +53 -58
  22. package/dist/core/domain/transport/schemas.d.ts +52 -1
  23. package/dist/core/domain/transport/schemas.js +30 -1
  24. package/dist/core/interfaces/cipher/i-blob-storage.d.ts +6 -0
  25. package/dist/core/interfaces/cipher/i-session-persistence.d.ts +133 -0
  26. package/dist/core/interfaces/cipher/i-session-persistence.js +7 -0
  27. package/dist/core/interfaces/cipher/index.d.ts +0 -1
  28. package/dist/core/interfaces/cipher/message-types.d.ts +6 -0
  29. package/dist/core/interfaces/executor/i-curate-executor.d.ts +2 -0
  30. package/dist/core/interfaces/i-context-file-reader.d.ts +3 -0
  31. package/dist/core/interfaces/usecase/{i-clear-use-case.d.ts → i-reset-use-case.d.ts} +1 -1
  32. package/dist/infra/cipher/agent/agent-schemas.d.ts +6 -6
  33. package/dist/infra/cipher/agent/cipher-agent.js +4 -0
  34. package/dist/infra/cipher/agent/service-initializer.js +4 -4
  35. package/dist/infra/cipher/file-system/context-tree-file-system-factory.js +3 -2
  36. package/dist/infra/cipher/file-system/file-system-service.d.ts +4 -0
  37. package/dist/infra/cipher/file-system/file-system-service.js +6 -0
  38. package/dist/infra/cipher/http/internal-llm-http-service.js +3 -5
  39. package/dist/infra/cipher/interactive-loop.js +3 -1
  40. package/dist/infra/cipher/llm/context/context-manager.js +40 -16
  41. package/dist/infra/cipher/llm/formatters/gemini-formatter.d.ts +13 -0
  42. package/dist/infra/cipher/llm/formatters/gemini-formatter.js +98 -6
  43. package/dist/infra/cipher/llm/generators/byterover-content-generator.js +6 -2
  44. package/dist/infra/cipher/llm/thought-parser.d.ts +21 -0
  45. package/dist/infra/cipher/llm/thought-parser.js +27 -0
  46. package/dist/infra/cipher/llm/tool-output-processor.d.ts +10 -0
  47. package/dist/infra/cipher/llm/tool-output-processor.js +80 -7
  48. package/dist/infra/cipher/process/process-service.js +11 -3
  49. package/dist/infra/cipher/session/chat-session.d.ts +7 -2
  50. package/dist/infra/cipher/session/chat-session.js +90 -52
  51. package/dist/infra/cipher/session/session-metadata-store.d.ts +52 -0
  52. package/dist/infra/cipher/session/session-metadata-store.js +406 -0
  53. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +4 -2
  54. package/dist/infra/cipher/tools/implementations/create-knowledge-topic-tool.js +24 -17
  55. package/dist/infra/cipher/tools/implementations/curate-tool.js +138 -65
  56. package/dist/infra/cipher/tools/implementations/read-file-tool.js +3 -12
  57. package/dist/infra/cipher/tools/implementations/spec-analyze-tool.js +18 -15
  58. package/dist/infra/cipher/tools/implementations/task-tool.js +54 -7
  59. package/dist/infra/context-tree/file-context-file-reader.js +4 -0
  60. package/dist/infra/context-tree/file-context-tree-service.js +4 -15
  61. package/dist/infra/core/executors/curate-executor.d.ts +2 -7
  62. package/dist/infra/core/executors/curate-executor.js +18 -53
  63. package/dist/infra/core/executors/query-executor.d.ts +1 -7
  64. package/dist/infra/core/executors/query-executor.js +10 -35
  65. package/dist/infra/core/task-processor.d.ts +2 -0
  66. package/dist/infra/core/task-processor.js +1 -0
  67. package/dist/infra/http/authenticated-http-client.js +5 -0
  68. package/dist/infra/process/agent-worker.js +113 -6
  69. package/dist/infra/process/constants.d.ts +1 -0
  70. package/dist/infra/process/constants.js +1 -0
  71. package/dist/infra/process/process-manager.d.ts +10 -1
  72. package/dist/infra/process/process-manager.js +16 -6
  73. package/dist/infra/process/task-queue-manager.js +2 -1
  74. package/dist/infra/process/transport-handlers.js +35 -0
  75. package/dist/infra/process/transport-worker.js +89 -1
  76. package/dist/infra/repl/commands/curate-command.js +2 -2
  77. package/dist/infra/repl/commands/gen-rules-command.js +2 -2
  78. package/dist/infra/repl/commands/index.js +5 -2
  79. package/dist/infra/repl/commands/init-command.js +2 -2
  80. package/dist/infra/repl/commands/login-command.js +2 -2
  81. package/dist/infra/repl/commands/logout-command.js +2 -2
  82. package/dist/infra/repl/commands/new-command.d.ts +14 -0
  83. package/dist/infra/repl/commands/new-command.js +61 -0
  84. package/dist/infra/repl/commands/pull-command.js +2 -2
  85. package/dist/infra/repl/commands/push-command.js +2 -2
  86. package/dist/infra/repl/commands/query-command.js +2 -2
  87. package/dist/infra/repl/commands/{clear-command.d.ts → reset-command.d.ts} +2 -2
  88. package/dist/infra/repl/commands/{clear-command.js → reset-command.js} +10 -10
  89. package/dist/infra/repl/commands/space/list-command.js +2 -2
  90. package/dist/infra/repl/commands/space/switch-command.js +2 -2
  91. package/dist/infra/repl/commands/status-command.js +2 -2
  92. package/dist/infra/repl/repl-startup.js +0 -2
  93. package/dist/infra/storage/file-token-store.d.ts +31 -0
  94. package/dist/infra/storage/file-token-store.js +98 -0
  95. package/dist/infra/storage/keychain-token-store.d.ts +4 -1
  96. package/dist/infra/storage/keychain-token-store.js +6 -4
  97. package/dist/infra/storage/token-store.d.ts +10 -0
  98. package/dist/infra/storage/token-store.js +14 -0
  99. package/dist/infra/usecase/curate-use-case.js +1 -1
  100. package/dist/infra/usecase/generate-rules-use-case.js +2 -2
  101. package/dist/infra/usecase/init-use-case.js +4 -4
  102. package/dist/infra/usecase/logout-use-case.js +1 -1
  103. package/dist/infra/usecase/push-use-case.js +1 -1
  104. package/dist/infra/usecase/{clear-use-case.d.ts → reset-use-case.d.ts} +5 -5
  105. package/dist/infra/usecase/{clear-use-case.js → reset-use-case.js} +5 -5
  106. package/dist/infra/user/http-user-service.js +6 -11
  107. package/dist/resources/prompts/curate.yml +79 -15
  108. package/dist/resources/prompts/plan.yml +6 -0
  109. package/dist/resources/tools/curate.txt +60 -15
  110. package/dist/tui/app.js +1 -1
  111. package/dist/tui/components/execution/log-item.js +2 -5
  112. package/dist/tui/components/header.d.ts +1 -1
  113. package/dist/tui/components/header.js +25 -4
  114. package/dist/tui/components/index.d.ts +5 -1
  115. package/dist/tui/components/index.js +3 -1
  116. package/dist/tui/components/init.d.ts +33 -0
  117. package/dist/tui/components/init.js +253 -0
  118. package/dist/tui/components/inline-prompts/inline-confirm.js +2 -2
  119. package/dist/tui/components/onboarding/index.d.ts +1 -0
  120. package/dist/tui/components/onboarding/index.js +1 -0
  121. package/dist/tui/components/onboarding/onboarding-flow.d.ts +2 -0
  122. package/dist/tui/components/onboarding/onboarding-flow.js +9 -229
  123. package/dist/tui/components/onboarding/onboarding-step.js +1 -1
  124. package/dist/tui/components/onboarding/welcome-box.d.ts +14 -0
  125. package/dist/tui/components/onboarding/welcome-box.js +23 -0
  126. package/dist/tui/components/status-badge.d.ts +22 -0
  127. package/dist/tui/components/status-badge.js +32 -0
  128. package/dist/tui/contexts/auth-context.js +2 -1
  129. package/dist/tui/contexts/index.d.ts +1 -0
  130. package/dist/tui/contexts/index.js +1 -0
  131. package/dist/tui/contexts/onboarding-context.d.ts +14 -0
  132. package/dist/tui/contexts/onboarding-context.js +17 -22
  133. package/dist/tui/contexts/status-context.d.ts +33 -0
  134. package/dist/tui/contexts/status-context.js +159 -0
  135. package/dist/tui/hooks/use-auth-polling.d.ts +4 -1
  136. package/dist/tui/hooks/use-auth-polling.js +21 -7
  137. package/dist/tui/hooks/use-tab-navigation.js +0 -2
  138. package/dist/tui/providers/app-providers.js +2 -2
  139. package/dist/tui/types/index.d.ts +2 -0
  140. package/dist/tui/types/index.js +2 -0
  141. package/dist/tui/types/status.d.ts +46 -0
  142. package/dist/tui/types/status.js +13 -0
  143. package/dist/tui/utils/index.d.ts +6 -0
  144. package/dist/tui/utils/index.js +6 -0
  145. package/dist/tui/utils/time.d.ts +10 -0
  146. package/dist/tui/utils/time.js +15 -0
  147. package/dist/tui/views/command-view.js +15 -2
  148. package/dist/tui/views/index.d.ts +1 -0
  149. package/dist/tui/views/index.js +1 -0
  150. package/dist/tui/views/init-view.d.ts +15 -0
  151. package/dist/tui/views/init-view.js +29 -0
  152. package/dist/tui/views/logs-view.js +22 -8
  153. package/dist/utils/environment-detector.d.ts +5 -0
  154. package/dist/utils/environment-detector.js +31 -0
  155. package/dist/utils/file-validator.js +9 -7
  156. package/dist/utils/global-data-path.d.ts +11 -0
  157. package/dist/utils/global-data-path.js +32 -0
  158. package/oclif.manifest.json +3 -3
  159. package/package.json +1 -1
  160. package/dist/config/context-tree-domains.d.ts +0 -17
  161. package/dist/config/context-tree-domains.js +0 -34
  162. package/dist/core/interfaces/cipher/i-agent-storage.d.ts +0 -152
  163. package/dist/core/interfaces/usecase/i-clear-use-case.js +0 -1
  164. package/dist/infra/cipher/consumer/consumer-lock.d.ts +0 -20
  165. package/dist/infra/cipher/consumer/consumer-lock.js +0 -41
  166. package/dist/infra/cipher/consumer/consumer-service.d.ts +0 -99
  167. package/dist/infra/cipher/consumer/consumer-service.js +0 -166
  168. package/dist/infra/cipher/consumer/execution-consumer.d.ts +0 -126
  169. package/dist/infra/cipher/consumer/execution-consumer.js +0 -561
  170. package/dist/infra/cipher/consumer/index.d.ts +0 -33
  171. package/dist/infra/cipher/consumer/index.js +0 -34
  172. package/dist/infra/cipher/consumer/queue-polling-service.d.ts +0 -120
  173. package/dist/infra/cipher/consumer/queue-polling-service.js +0 -249
  174. package/dist/infra/cipher/storage/agent-storage.d.ts +0 -246
  175. package/dist/infra/cipher/storage/agent-storage.js +0 -956
  176. /package/dist/core/interfaces/{cipher/i-agent-storage.js → usecase/i-reset-use-case.js} +0 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ByteRover CLI
2
2
 
3
- Command-line interface for ByteRover, featuring an interactive REPL with a modern terminal UI for managing your project's context tree and memory storage.
3
+ Command-line interface for ByteRover, featuring an interactive REPL with a modern React/Ink terminal UI for managing your project's context tree and knowledge storage. Seamlessly integrate with AI coding agents like Claude Code, Cursor, Windsurf, and GitHub Copilot.
4
4
 
5
5
  [![Version](https://img.shields.io/npm/v/byterover-cli.svg)](https://npmjs.org/package/byterover-cli)
6
6
  [![Downloads/week](https://img.shields.io/npm/dw/byterover-cli.svg)](https://npmjs.org/package/byterover-cli)
@@ -15,6 +15,7 @@ Command-line interface for ByteRover, featuring an interactive REPL with a moder
15
15
  * [Slash Commands Reference](#slash-commands-reference)
16
16
  * [Authentication](#authentication)
17
17
  * [Configuration](#configuration)
18
+ * [Troubleshooting](#troubleshooting)
18
19
  * [Getting Help](#getting-help)
19
20
 
20
21
  ## Installation
@@ -90,6 +91,16 @@ brv
90
91
 
91
92
  Running `brv` without arguments starts the interactive REPL. The REPL requires an interactive terminal (TTY).
92
93
 
94
+ ### TUI Features
95
+
96
+ The terminal UI includes:
97
+
98
+ - **Tab Navigation**: Switch between Chat and Activity views using `Tab`
99
+ - **Command Completion**: Type `/` to see available commands with auto-completion
100
+ - **Activity Log**: Real-time task status and execution progress
101
+ - **Streaming Output**: Live responses from AI-powered operations
102
+ - **File References**: Type `@` in curate mode to browse and attach files
103
+
93
104
  ### Using Commands
94
105
 
95
106
  In the REPL, use slash commands (commands prefixed with `/`) to interact with ByteRover:
@@ -104,24 +115,33 @@ Commands support tab completion for quick navigation.
104
115
 
105
116
  ## What is Context Tree?
106
117
 
107
- The **Context Tree** is ByteRover's structured memory system that helps you and your coding agents organize, store, and retrieve project knowledge efficiently.
118
+ The **Context Tree** is ByteRover's structured knowledge system that helps you and your AI coding agents organize, store, and retrieve project context efficiently.
108
119
 
109
120
  ### Why Use Context Tree?
110
121
 
111
122
  - **Organized Knowledge**: Structure your project knowledge by domain and topic
112
123
  - **Easy Retrieval**: Find relevant context quickly when you need it
113
124
  - **Persistent Memory**: Maintain project-specific knowledge across sessions
114
- - **Agent-Friendly**: Works seamlessly with coding agents like Claude Code, Cursor, and others
115
- - **Version Control**: Push and sync your context to ByteRover's cloud storage
125
+ - **Agent-Friendly**: Works seamlessly with AI coding agents like Claude Code, Cursor, Windsurf, and GitHub Copilot
126
+ - **Cloud Sync**: Push and sync your context tree to ByteRover's cloud storage for backup and team collaboration
127
+ - **Dynamic Domains**: Automatically creates new domains as your knowledge grows
116
128
 
117
129
  ### How It Works
118
130
 
119
131
  The context tree organizes knowledge into:
120
- - **Domains**: High-level categories (e.g., Architecture, API, Frontend)
132
+ - **Domains**: High-level categories (e.g., Architecture, API, Frontend) — created automatically or manually
121
133
  - **Topics**: Specific subjects within domains (e.g., Authentication, Components)
122
134
  - **Context Files**: Markdown files containing your actual knowledge
123
135
 
124
- For comprehensive instructions for coding agents, use `/gen-rules` to generate rule files.
136
+ ### Integrating with Coding Agents
137
+
138
+ Use `/gen-rules` to generate rule instruction files that help your AI coding agents understand and work with ByteRover:
139
+
140
+ ```
141
+ /gen-rules
142
+ ```
143
+
144
+ This creates agent-specific rule files (e.g., `CLAUDE.md`, `.cursorrules`) that instruct the agent how to read from and contribute to your context tree.
125
145
 
126
146
  ## Slash Commands Reference
127
147
 
@@ -150,8 +170,8 @@ For comprehensive instructions for coding agents, use `/gen-rules` to generate r
150
170
 
151
171
  | Command | Description |
152
172
  |---------|-------------|
153
- | `/push [-b branch] [-y]` | Push context tree to ByteRover memory storage |
154
- | `/pull [-b branch]` | Pull context tree from ByteRover memory storage |
173
+ | `/push [-b branch] [-y]` | Push context tree to ByteRover cloud storage |
174
+ | `/pull [-b branch]` | Pull context tree from ByteRover cloud storage |
155
175
 
156
176
  **Options:**
157
177
  - `-b, --branch <name>`: ByteRover branch name (default: `main`)
@@ -182,12 +202,23 @@ For comprehensive instructions for coding agents, use `/gen-rules` to generate r
182
202
 
183
203
  | Command | Description |
184
204
  |---------|-------------|
185
- | `/gen-rules` | Generate rule instructions for coding agents |
186
- | `/clear [-y] [directory]` | Reset context tree to default domains |
205
+ | `/gen-rules` | Generate rule files for AI coding agents (Claude Code, Cursor, etc.) |
206
+ | `/reset [-y] [directory]` | Reset context tree to default domains |
187
207
 
188
- **Clear options:**
208
+ **Reset options:**
209
+ - `-y, --yes`: Skip confirmation prompt
210
+
211
+ ### Session Management
212
+
213
+ | Command | Description |
214
+ |---------|-------------|
215
+ | `/new [-y]` | Start a fresh session (ends current session, clears conversation) |
216
+
217
+ **Options:**
189
218
  - `-y, --yes`: Skip confirmation prompt
190
219
 
220
+ **Note:** This command does NOT affect the context tree—it only clears the conversation history and starts a new session.
221
+
191
222
  ### Project Setup
192
223
 
193
224
  | Command | Description |
@@ -232,6 +263,16 @@ When you run `/init`, a configuration file is created at `.brv/config.json` in y
232
263
  - **User information**: Your user ID and email
233
264
  - **Project settings**: Project-specific configuration
234
265
 
266
+ ### Global Configuration
267
+
268
+ User-level configuration is stored at `~/.config/brv/`:
269
+
270
+ ```
271
+ ~/.config/brv/
272
+ ├── config.json # Global settings and device ID
273
+ └── logs/ # Session logs for debugging
274
+ ```
275
+
235
276
  ### Context Tree Structure
236
277
 
237
278
  The context tree is stored in `.brv/context-tree/`:
@@ -255,7 +296,29 @@ The context tree is stored in `.brv/context-tree/`:
255
296
  └── context.md
256
297
  ```
257
298
 
258
- **Note**: When you run `/push`, your context tree is uploaded to ByteRover's memory storage for version control and team collaboration.
299
+ **Note**: When you run `/push`, your context tree is uploaded to ByteRover's cloud storage for version control and team collaboration.
300
+
301
+ ## Troubleshooting
302
+
303
+ ### Session Logs
304
+
305
+ If you encounter issues, session logs are stored at `~/.config/brv/logs/`. Each session creates a timestamped log file (e.g., `brv-2024-01-15T10-30-00.log`) that can help diagnose problems.
306
+
307
+ ### Instance Lock
308
+
309
+ ByteRover CLI ensures only one instance runs per project folder. If you see an "instance already running" message:
310
+
311
+ 1. Check for another terminal with `brv` running in the same directory
312
+ 2. If no other instance is visible, the lock file may be stale — it will auto-release on the next start
313
+
314
+ ### Common Issues
315
+
316
+ | Issue | Solution |
317
+ |-------|----------|
318
+ | "REPL requires an interactive terminal" | Run `brv` directly in a terminal, not through piped commands |
319
+ | Authentication expires frequently | Run `/login` to refresh your session |
320
+ | Context tree not syncing | Check `/status` for sync status, then try `/push` or `/pull` |
321
+ | Rule files not generated | Ensure you're in a project directory with `.brv/` initialized |
259
322
 
260
323
  ## Getting Help
261
324
 
@@ -1,7 +1,7 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { isDevelopment } from '../config/environment.js';
3
3
  import { FileGlobalConfigStore } from '../infra/storage/file-global-config-store.js';
4
- import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
4
+ import { createTokenStore } from '../infra/storage/token-store.js';
5
5
  import { OclifTerminal } from '../infra/terminal/oclif-terminal.js';
6
6
  import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
7
7
  import { CurateUseCase } from '../infra/usecase/curate-use-case.js';
@@ -14,7 +14,7 @@ export default class Curate extends Command {
14
14
  };
15
15
  static description = `Curate context to the context tree (connects to running brv instance)
16
16
 
17
- Requires a running brv instance. Start one with: brv start
17
+ Requires a running brv instance. Start one with: brv
18
18
 
19
19
  Good examples:
20
20
  - "Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts"
@@ -49,7 +49,7 @@ Bad examples:
49
49
  : {}),
50
50
  };
51
51
  createUseCase() {
52
- const tokenStore = new KeychainTokenStore();
52
+ const tokenStore = createTokenStore();
53
53
  const globalConfigStore = new FileGlobalConfigStore();
54
54
  const terminal = new OclifTerminal(this);
55
55
  const trackingService = new MixpanelTrackingService({ globalConfigStore, tokenStore });
@@ -14,4 +14,17 @@ export default class Main extends Command {
14
14
  */
15
15
  static hidden: boolean;
16
16
  run(): Promise<void>;
17
+ /**
18
+ * Resolve session ID for the agent.
19
+ *
20
+ * Strategy:
21
+ * 1. Check for active session in .brv/sessions/active.json
22
+ * 2. If active session exists and is valid (not stale), resume it
23
+ * 3. If stale (process crashed), mark as interrupted and create new
24
+ * 4. If no active session, create new session
25
+ * 5. Run session cleanup on startup
26
+ *
27
+ * @returns Session ID to use
28
+ */
29
+ private resolveSessionId;
17
30
  }
@@ -1,12 +1,15 @@
1
1
  import { Command } from '@oclif/core';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { DEFAULT_SESSION_RETENTION } from '../core/domain/cipher/session/session-metadata.js';
4
+ import { SessionMetadataStore } from '../infra/cipher/session/session-metadata-store.js';
2
5
  import { ProjectConfigStore } from '../infra/config/file-config-store.js';
3
6
  import { getProcessManager } from '../infra/process/index.js';
4
7
  import { startRepl } from '../infra/repl/repl-startup.js';
5
8
  import { FileGlobalConfigStore } from '../infra/storage/file-global-config-store.js';
6
9
  import { FileOnboardingPreferenceStore } from '../infra/storage/file-onboarding-preference-store.js';
7
- import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
10
+ import { createTokenStore } from '../infra/storage/token-store.js';
8
11
  import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
9
- import { initSessionLog } from '../utils/process-logger.js';
12
+ import { initSessionLog, processManagerLog } from '../utils/process-logger.js';
10
13
  /**
11
14
  * Main command - Entry point for ByteRover CLI.
12
15
  *
@@ -30,10 +33,14 @@ export default class Main extends Command {
30
33
  this.log("Run 'brv --help' for available commands.");
31
34
  return;
32
35
  }
36
+ // Resolve session ID (auto-resume or create new)
37
+ const sessionId = await this.resolveSessionId();
38
+ processManagerLog(`Session ID resolved: ${sessionId}`);
33
39
  // Start Transport and Agent processes (v0.5.0 architecture)
40
+ // Pass session ID to Agent via environment variable
34
41
  const processManager = getProcessManager();
35
- await processManager.start();
36
- const tokenStore = new KeychainTokenStore();
42
+ await processManager.start({ sessionId });
43
+ const tokenStore = createTokenStore();
37
44
  const globalConfigStore = new FileGlobalConfigStore();
38
45
  const trackingService = new MixpanelTrackingService({ globalConfigStore, tokenStore });
39
46
  const onboardingPreferenceStore = new FileOnboardingPreferenceStore();
@@ -52,4 +59,48 @@ export default class Main extends Command {
52
59
  await processManager.stop();
53
60
  }
54
61
  }
62
+ /**
63
+ * Resolve session ID for the agent.
64
+ *
65
+ * Strategy:
66
+ * 1. Check for active session in .brv/sessions/active.json
67
+ * 2. If active session exists and is valid (not stale), resume it
68
+ * 3. If stale (process crashed), mark as interrupted and create new
69
+ * 4. If no active session, create new session
70
+ * 5. Run session cleanup on startup
71
+ *
72
+ * @returns Session ID to use
73
+ */
74
+ async resolveSessionId() {
75
+ const sessionStore = new SessionMetadataStore();
76
+ // Run cleanup on startup (async, don't wait)
77
+ sessionStore.cleanupSessions(DEFAULT_SESSION_RETENTION).catch((error) => {
78
+ processManagerLog(`Session cleanup failed: ${error}`);
79
+ });
80
+ // Check for active session
81
+ const activeSession = await sessionStore.getActiveSession();
82
+ if (activeSession) {
83
+ // Check if the active session is stale (process not running)
84
+ const isStale = await sessionStore.isActiveSessionStale();
85
+ if (isStale) {
86
+ // Mark the old session as interrupted
87
+ processManagerLog(`Active session ${activeSession.sessionId} is stale, marking as interrupted`);
88
+ await sessionStore.markSessionInterrupted(activeSession.sessionId);
89
+ }
90
+ else {
91
+ // Valid active session - resume it
92
+ processManagerLog(`Resuming active session: ${activeSession.sessionId}`);
93
+ return activeSession.sessionId;
94
+ }
95
+ }
96
+ // Create new session
97
+ const newSessionId = `agent-session-${randomUUID()}`;
98
+ processManagerLog(`Creating new session: ${newSessionId}`);
99
+ // Save session metadata
100
+ const metadata = sessionStore.createSessionMetadata(newSessionId);
101
+ await sessionStore.saveSession(metadata);
102
+ // Set as active session
103
+ await sessionStore.setActiveSession(newSessionId);
104
+ return newSessionId;
105
+ }
55
106
  }
@@ -1,7 +1,7 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { isDevelopment } from '../config/environment.js';
3
3
  import { FileGlobalConfigStore } from '../infra/storage/file-global-config-store.js';
4
- import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
4
+ import { createTokenStore } from '../infra/storage/token-store.js';
5
5
  import { OclifTerminal } from '../infra/terminal/oclif-terminal.js';
6
6
  import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
7
7
  import { QueryUseCase } from '../infra/usecase/query-use-case.js';
@@ -14,7 +14,7 @@ export default class Query extends Command {
14
14
  };
15
15
  static description = `Query and retrieve information from the context tree (connects to running brv instance)
16
16
 
17
- Requires a running brv instance. Start one with: brv start
17
+ Requires a running brv instance. Start one with: brv
18
18
 
19
19
  Good:
20
20
  - "How is user authentication implemented?"
@@ -40,7 +40,7 @@ Bad:
40
40
  };
41
41
  static strict = false;
42
42
  createUseCase() {
43
- const tokenStore = new KeychainTokenStore();
43
+ const tokenStore = createTokenStore();
44
44
  const globalConfigStore = new FileGlobalConfigStore();
45
45
  const trackingService = new MixpanelTrackingService({ globalConfigStore, tokenStore });
46
46
  return new QueryUseCase({
@@ -3,7 +3,7 @@ import { ProjectConfigStore } from '../infra/config/file-config-store.js';
3
3
  import { FileContextTreeService } from '../infra/context-tree/file-context-tree-service.js';
4
4
  import { FileContextTreeSnapshotService } from '../infra/context-tree/file-context-tree-snapshot-service.js';
5
5
  import { FileGlobalConfigStore } from '../infra/storage/file-global-config-store.js';
6
- import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
6
+ import { createTokenStore } from '../infra/storage/token-store.js';
7
7
  import { OclifTerminal } from '../infra/terminal/oclif-terminal.js';
8
8
  import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
9
9
  import { StatusUseCase } from '../infra/usecase/status-use-case.js';
@@ -29,7 +29,7 @@ export default class Status extends Command {
29
29
  }),
30
30
  };
31
31
  createUseCase() {
32
- const tokenStore = new KeychainTokenStore();
32
+ const tokenStore = createTokenStore();
33
33
  const globalConfigStore = new FileGlobalConfigStore();
34
34
  const trackingService = new MixpanelTrackingService({ globalConfigStore, tokenStore });
35
35
  const contextTreeSnapshotService = new FileContextTreeSnapshotService();
@@ -6,6 +6,7 @@ export declare const BRV_CONFIG_VERSION = "0.0.1";
6
6
  export declare const GLOBAL_CONFIG_DIR = "brv";
7
7
  export declare const GLOBAL_CONFIG_FILE = "config.json";
8
8
  export declare const GLOBAL_CONFIG_VERSION = "0.0.1";
9
+ export declare const GLOBAL_DATA_DIR = "brv";
9
10
  export declare const ACE_DIR = "ace";
10
11
  export declare const PROJECT = "byterover";
11
12
  export declare const CONTEXT_TREE_DIR = "context-tree";
@@ -33,4 +34,4 @@ export declare const TRANSPORT_RECONNECTION_ATTEMPTS = 30;
33
34
  export declare const TRANSPORT_PING_INTERVAL_MS = 5000;
34
35
  export declare const TRANSPORT_PING_TIMEOUT_MS = 10000;
35
36
  export declare const TRANSPORT_DEFAULT_TRANSPORTS: ('polling' | 'websocket')[];
36
- export declare const DEFAULT_LLM_MODEL = "gemini-2.5-pro";
37
+ export declare const DEFAULT_LLM_MODEL = "gemini-3-flash-preview";
package/dist/constants.js CHANGED
@@ -7,6 +7,9 @@ export const BRV_CONFIG_VERSION = '0.0.1';
7
7
  export const GLOBAL_CONFIG_DIR = 'brv';
8
8
  export const GLOBAL_CONFIG_FILE = 'config.json';
9
9
  export const GLOBAL_CONFIG_VERSION = '0.0.1';
10
+ // Global data directory name (for XDG_DATA_HOME - secrets, credentials, cache)
11
+ // Same value as GLOBAL_CONFIG_DIR but different semantic purpose
12
+ export const GLOBAL_DATA_DIR = 'brv';
10
13
  // ACE directory structure constants
11
14
  export const ACE_DIR = 'ace';
12
15
  export const PROJECT = 'byterover';
@@ -40,4 +43,4 @@ export const TRANSPORT_PING_TIMEOUT_MS = 10_000; // 10s timeout - avoid false di
40
43
  // HTTP polling may be blocked by IDE sandboxes causing "xhr poll error"
41
44
  export const TRANSPORT_DEFAULT_TRANSPORTS = ['websocket'];
42
45
  // LLM Model defaults
43
- export const DEFAULT_LLM_MODEL = 'gemini-2.5-pro';
46
+ export const DEFAULT_LLM_MODEL = 'gemini-3-flash-preview';
@@ -146,6 +146,8 @@ export interface FileContent {
146
146
  totalLines: number;
147
147
  /** Whether content was truncated due to size/line limits */
148
148
  truncated: boolean;
149
+ /** Number of lines that were truncated due to excessive length */
150
+ truncatedLineCount?: number;
149
151
  }
150
152
  /**
151
153
  * Result of a file write operation.
@@ -27,19 +27,54 @@ export const LLM_REGISTRY = {
27
27
  supportedFileTypes: [],
28
28
  },
29
29
  gemini: {
30
- defaultModel: 'gemini-2.5-flash',
30
+ defaultModel: 'gemini-3-flash-preview',
31
31
  models: [
32
- // Gemini 2.5 series
32
+ // Gemini 3 series (Preview)
33
33
  {
34
34
  capabilities: {
35
35
  supportsAudio: true,
36
36
  supportsImages: true,
37
+ supportsMultimodalFunctionResponse: true,
37
38
  supportsPdf: true,
38
39
  supportsStreaming: true,
39
40
  supportsThinking: true,
40
41
  },
41
42
  charsPerToken: 4,
42
43
  default: true,
44
+ displayName: 'Gemini 3 Flash (Preview)',
45
+ maxInputTokens: 1_000_000,
46
+ maxOutputTokens: 8192,
47
+ name: 'gemini-3-flash-preview',
48
+ pricing: { inputPerM: 0.075, outputPerM: 0.3 },
49
+ supportedFileTypes: ['image', 'pdf', 'audio'],
50
+ },
51
+ {
52
+ capabilities: {
53
+ supportsAudio: true,
54
+ supportsImages: true,
55
+ supportsMultimodalFunctionResponse: true,
56
+ supportsPdf: true,
57
+ supportsStreaming: true,
58
+ supportsThinking: true,
59
+ },
60
+ charsPerToken: 4,
61
+ displayName: 'Gemini 3 Pro (Preview)',
62
+ maxInputTokens: 1_000_000,
63
+ maxOutputTokens: 8192,
64
+ name: 'gemini-3-pro-preview',
65
+ pricing: { inputPerM: 1.25, outputPerM: 5 },
66
+ supportedFileTypes: ['image', 'pdf', 'audio'],
67
+ },
68
+ // Gemini 2.5 series
69
+ {
70
+ capabilities: {
71
+ supportsAudio: true,
72
+ supportsImages: true,
73
+ supportsPdf: true,
74
+ supportsStreaming: true,
75
+ supportsThinking: true,
76
+ },
77
+ charsPerToken: 4,
43
78
  displayName: 'Gemini 2.5 Flash',
44
79
  maxInputTokens: 1_000_000,
45
80
  maxOutputTokens: 8192,
@@ -47,6 +82,22 @@ export const LLM_REGISTRY = {
47
82
  pricing: { inputPerM: 0.075, outputPerM: 0.3 },
48
83
  supportedFileTypes: ['image', 'pdf', 'audio'],
49
84
  },
85
+ {
86
+ capabilities: {
87
+ supportsAudio: true,
88
+ supportsImages: true,
89
+ supportsPdf: true,
90
+ supportsStreaming: true,
91
+ supportsThinking: true,
92
+ },
93
+ charsPerToken: 4,
94
+ displayName: 'Gemini 2.5 Pro',
95
+ maxInputTokens: 1_000_000,
96
+ maxOutputTokens: 8192,
97
+ name: 'gemini-2.5-pro',
98
+ pricing: { inputPerM: 1.25, outputPerM: 5 },
99
+ supportedFileTypes: ['image', 'pdf', 'audio'],
100
+ },
50
101
  // Gemini 1.5 series
51
102
  {
52
103
  capabilities: {
@@ -28,6 +28,8 @@ export interface ModelCapabilities {
28
28
  supportsAudio: boolean;
29
29
  /** Whether the model supports image input */
30
30
  supportsImages: boolean;
31
+ /** Whether the model supports multimodal data in function responses (Gemini 3+) */
32
+ supportsMultimodalFunctionResponse?: boolean;
31
33
  /** Whether the model supports PDF input */
32
34
  supportsPdf: boolean;
33
35
  /** Whether the model supports streaming responses */
@@ -17,6 +17,13 @@ export interface ProcessConfig {
17
17
  * Custom environment variables for all commands.
18
18
  */
19
19
  environment: Record<string, string>;
20
+ /**
21
+ * Grace period in milliseconds to wait after SIGTERM before sending SIGKILL.
22
+ * Allows processes time to clean up gracefully.
23
+ *
24
+ * @default 5000 (5 seconds)
25
+ */
26
+ killGracePeriod: number;
20
27
  /**
21
28
  * Maximum number of concurrent background processes.
22
29
  * @default 5
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Session Metadata Types and Schemas
3
+ *
4
+ * Defines the data structures for persistent session management.
5
+ * Sessions are stored in .brv/sessions/ directory as JSON files.
6
+ *
7
+ * Design adapted from gemini-cli's ChatRecordingService pattern.
8
+ */
9
+ import { z } from 'zod';
10
+ /**
11
+ * Session status indicating lifecycle state.
12
+ */
13
+ export type SessionStatus = 'active' | 'ended' | 'interrupted';
14
+ /**
15
+ * Active session pointer stored in .brv/sessions/active.json
16
+ * Points to the currently active session for auto-resume.
17
+ */
18
+ export interface ActiveSessionPointer {
19
+ /** ISO timestamp when this session became active */
20
+ activatedAt: string;
21
+ /** PID of the process that activated this session (for stale detection) */
22
+ pid: number;
23
+ /**
24
+ * Unique token identifying this specific process instance.
25
+ * Used to detect PID reuse: if the PID exists but token differs,
26
+ * it's a different process that got the same PID after the original crashed.
27
+ * Optional for backward compatibility with existing session files.
28
+ */
29
+ processToken?: string;
30
+ /** Session ID of the currently active session */
31
+ sessionId: string;
32
+ }
33
+ /**
34
+ * Session metadata stored in .brv/sessions/session-*.json
35
+ * Contains metadata about a session for listing and management.
36
+ */
37
+ export interface SessionMetadata {
38
+ /** ISO timestamp when session was created */
39
+ createdAt: string;
40
+ /** ISO timestamp of last activity */
41
+ lastUpdated: string;
42
+ /** Number of messages in session (cached for quick display) */
43
+ messageCount: number;
44
+ /** Unique session identifier (UUID) */
45
+ sessionId: string;
46
+ /** Session lifecycle status */
47
+ status: SessionStatus;
48
+ /** Optional AI-generated summary */
49
+ summary?: string;
50
+ /** Session title (generated from first user message) */
51
+ title?: string;
52
+ /** Project working directory (for validation) */
53
+ workingDirectory: string;
54
+ }
55
+ /**
56
+ * Session info for display purposes (extends metadata with computed fields).
57
+ */
58
+ export interface SessionInfo extends SessionMetadata {
59
+ /** Filename without extension */
60
+ file: string;
61
+ /** Full filename including .json extension */
62
+ fileName: string;
63
+ /** First user message content (cleaned) */
64
+ firstUserMessage?: string;
65
+ /** Display index in the list (1-based) */
66
+ index: number;
67
+ /** Whether this is the currently active session */
68
+ isCurrentSession: boolean;
69
+ }
70
+ /**
71
+ * Result of resolving a session selection.
72
+ */
73
+ export interface SessionSelectionResult {
74
+ /** Display info string for user feedback */
75
+ displayInfo: string;
76
+ /** Loaded session metadata */
77
+ sessionData: SessionMetadata;
78
+ /** Full path to session metadata file */
79
+ sessionPath: string;
80
+ }
81
+ /**
82
+ * Schema for ActiveSessionPointer validation.
83
+ */
84
+ export declare const ActiveSessionPointerSchema: z.ZodObject<{
85
+ activatedAt: z.ZodUnion<[z.ZodString, z.ZodString]>;
86
+ pid: z.ZodNumber;
87
+ processToken: z.ZodOptional<z.ZodString>;
88
+ sessionId: z.ZodString;
89
+ }, "strip", z.ZodTypeAny, {
90
+ sessionId: string;
91
+ pid: number;
92
+ activatedAt: string;
93
+ processToken?: string | undefined;
94
+ }, {
95
+ sessionId: string;
96
+ pid: number;
97
+ activatedAt: string;
98
+ processToken?: string | undefined;
99
+ }>;
100
+ /**
101
+ * Schema for SessionMetadata validation.
102
+ */
103
+ export declare const SessionMetadataSchema: z.ZodObject<{
104
+ createdAt: z.ZodUnion<[z.ZodString, z.ZodString]>;
105
+ lastUpdated: z.ZodUnion<[z.ZodString, z.ZodString]>;
106
+ messageCount: z.ZodNumber;
107
+ sessionId: z.ZodString;
108
+ status: z.ZodEnum<["active", "ended", "interrupted"]>;
109
+ summary: z.ZodOptional<z.ZodString>;
110
+ title: z.ZodOptional<z.ZodString>;
111
+ workingDirectory: z.ZodString;
112
+ }, "strip", z.ZodTypeAny, {
113
+ status: "active" | "ended" | "interrupted";
114
+ sessionId: string;
115
+ createdAt: string;
116
+ lastUpdated: string;
117
+ messageCount: number;
118
+ workingDirectory: string;
119
+ summary?: string | undefined;
120
+ title?: string | undefined;
121
+ }, {
122
+ status: "active" | "ended" | "interrupted";
123
+ sessionId: string;
124
+ createdAt: string;
125
+ lastUpdated: string;
126
+ messageCount: number;
127
+ workingDirectory: string;
128
+ summary?: string | undefined;
129
+ title?: string | undefined;
130
+ }>;
131
+ /** Prefix for session metadata files */
132
+ export declare const SESSION_FILE_PREFIX = "session-";
133
+ /** Directory name for session storage */
134
+ export declare const SESSIONS_DIR = "sessions";
135
+ /** Filename for active session pointer */
136
+ export declare const ACTIVE_SESSION_FILE = "active.json";
137
+ /** Default session retention config */
138
+ export declare const DEFAULT_SESSION_RETENTION: {
139
+ /** Maximum age in days before auto-cleanup */
140
+ maxAgeDays: number;
141
+ /** Maximum number of sessions to keep */
142
+ maxCount: number;
143
+ /** Run cleanup on startup */
144
+ runOnStartup: boolean;
145
+ };
146
+ /**
147
+ * Generate a session filename from timestamp and session ID.
148
+ *
149
+ * @param sessionId - The session UUID
150
+ * @returns Filename in format: session-YYYY-MM-DDTHH-MM-SS-<uuid-prefix>.json
151
+ */
152
+ export declare function generateSessionFilename(sessionId: string): string;
153
+ /**
154
+ * Parse a session filename to extract timestamp and UUID prefix.
155
+ *
156
+ * @param filename - The session filename
157
+ * @returns Parsed components or null if invalid format
158
+ */
159
+ export declare function parseSessionFilename(filename: string): null | {
160
+ timestamp: string;
161
+ uuidPrefix: string;
162
+ };
163
+ /**
164
+ * Format a timestamp as relative time.
165
+ *
166
+ * @param timestamp - ISO timestamp string
167
+ * @param style - 'long' (e.g., "2 hours ago") or 'short' (e.g., "2h")
168
+ * @returns Formatted relative time string
169
+ */
170
+ export declare function formatRelativeTime(timestamp: string, style?: 'long' | 'short'): string;
171
+ /**
172
+ * Clean and sanitize message content for display.
173
+ * Converts newlines to spaces, collapses whitespace, removes non-printable chars.
174
+ *
175
+ * @param message - Raw message content
176
+ * @returns Cleaned message suitable for display
177
+ */
178
+ export declare function cleanMessageForTitle(message: string): string;