byterover-cli 1.1.0 → 1.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.
Files changed (107) hide show
  1. package/README.md +24 -5
  2. package/dist/commands/mcp.d.ts +13 -0
  3. package/dist/commands/mcp.js +61 -0
  4. package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
  5. package/dist/core/domain/entities/agent.js +72 -18
  6. package/dist/core/domain/entities/connector-type.d.ts +2 -1
  7. package/dist/core/domain/entities/connector-type.js +2 -1
  8. package/dist/core/interfaces/cipher/i-chat-session.d.ts +3 -1
  9. package/dist/core/interfaces/connectors/connector-types.d.ts +13 -0
  10. package/dist/core/interfaces/i-mcp-config-writer.d.ts +40 -0
  11. package/dist/core/interfaces/i-mcp-config-writer.js +1 -0
  12. package/dist/core/interfaces/i-rule-template-service.d.ts +4 -2
  13. package/dist/core/interfaces/transport/i-transport-client.d.ts +7 -0
  14. package/dist/infra/cipher/agent/cipher-agent.d.ts +8 -0
  15. package/dist/infra/cipher/agent/cipher-agent.js +16 -0
  16. package/dist/infra/cipher/llm/context/context-manager.d.ts +8 -0
  17. package/dist/infra/cipher/llm/context/context-manager.js +16 -0
  18. package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
  19. package/dist/infra/cipher/llm/internal-llm-service.js +38 -10
  20. package/dist/infra/cipher/session/chat-session.d.ts +6 -1
  21. package/dist/infra/cipher/session/chat-session.js +12 -4
  22. package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
  23. package/dist/infra/cipher/tools/implementations/curate-tool.js +360 -22
  24. package/dist/infra/cipher/tools/implementations/task-tool.js +3 -3
  25. package/dist/infra/connectors/connector-manager.js +2 -0
  26. package/dist/infra/connectors/mcp/index.d.ts +4 -0
  27. package/dist/infra/connectors/mcp/index.js +4 -0
  28. package/dist/infra/connectors/mcp/json-mcp-config-writer.d.ts +26 -0
  29. package/dist/infra/connectors/mcp/json-mcp-config-writer.js +71 -0
  30. package/dist/infra/connectors/mcp/mcp-connector-config.d.ts +229 -0
  31. package/dist/infra/connectors/mcp/mcp-connector-config.js +173 -0
  32. package/dist/infra/connectors/mcp/mcp-connector.d.ts +80 -0
  33. package/dist/infra/connectors/mcp/mcp-connector.js +324 -0
  34. package/dist/infra/connectors/mcp/toml-mcp-config-writer.d.ts +45 -0
  35. package/dist/infra/connectors/mcp/toml-mcp-config-writer.js +134 -0
  36. package/dist/infra/connectors/rules/rules-connector.d.ts +1 -8
  37. package/dist/infra/connectors/rules/rules-connector.js +20 -85
  38. package/dist/infra/connectors/shared/rule-file-manager.d.ts +72 -0
  39. package/dist/infra/connectors/shared/rule-file-manager.js +119 -0
  40. package/dist/infra/connectors/shared/template-service.d.ts +10 -1
  41. package/dist/infra/connectors/shared/template-service.js +53 -16
  42. package/dist/infra/mcp/index.d.ts +2 -0
  43. package/dist/infra/mcp/index.js +2 -0
  44. package/dist/infra/mcp/mcp-server.d.ts +58 -0
  45. package/dist/infra/mcp/mcp-server.js +178 -0
  46. package/dist/infra/mcp/tools/brv-curate-tool.d.ts +23 -0
  47. package/dist/infra/mcp/tools/brv-curate-tool.js +68 -0
  48. package/dist/infra/mcp/tools/brv-query-tool.d.ts +17 -0
  49. package/dist/infra/mcp/tools/brv-query-tool.js +68 -0
  50. package/dist/infra/mcp/tools/index.d.ts +3 -0
  51. package/dist/infra/mcp/tools/index.js +3 -0
  52. package/dist/infra/mcp/tools/task-result-waiter.d.ts +30 -0
  53. package/dist/infra/mcp/tools/task-result-waiter.js +56 -0
  54. package/dist/infra/process/agent-worker.js +37 -0
  55. package/dist/infra/repl/commands/curate-command.js +2 -2
  56. package/dist/infra/transport/socket-io-transport-client.d.ts +16 -0
  57. package/dist/infra/transport/socket-io-transport-client.js +46 -2
  58. package/dist/infra/transport/socket-io-transport-server.js +4 -0
  59. package/dist/infra/usecase/connectors-use-case.d.ts +4 -0
  60. package/dist/infra/usecase/connectors-use-case.js +29 -10
  61. package/dist/infra/usecase/init-use-case.js +2 -3
  62. package/dist/infra/usecase/status-use-case.d.ts +10 -0
  63. package/dist/infra/usecase/status-use-case.js +53 -0
  64. package/dist/resources/prompts/curate.yml +107 -4
  65. package/dist/templates/mcp-base.md +1 -0
  66. package/dist/templates/sections/command-reference.md +5 -96
  67. package/dist/templates/sections/mcp-workflow.md +13 -0
  68. package/dist/templates/sections/workflow.md +21 -16
  69. package/dist/tui/app.js +4 -1
  70. package/dist/tui/components/command-details.js +1 -1
  71. package/dist/tui/components/execution/execution-changes.d.ts +2 -0
  72. package/dist/tui/components/execution/execution-changes.js +5 -1
  73. package/dist/tui/components/execution/execution-content.d.ts +2 -0
  74. package/dist/tui/components/execution/execution-content.js +8 -18
  75. package/dist/tui/components/execution/execution-input.d.ts +2 -0
  76. package/dist/tui/components/execution/execution-input.js +6 -4
  77. package/dist/tui/components/execution/execution-progress.d.ts +2 -0
  78. package/dist/tui/components/execution/execution-progress.js +6 -2
  79. package/dist/tui/components/execution/expanded-log-view.d.ts +20 -0
  80. package/dist/tui/components/execution/expanded-log-view.js +75 -0
  81. package/dist/tui/components/execution/expanded-message-view.d.ts +24 -0
  82. package/dist/tui/components/execution/expanded-message-view.js +68 -0
  83. package/dist/tui/components/execution/index.d.ts +2 -0
  84. package/dist/tui/components/execution/index.js +2 -0
  85. package/dist/tui/components/execution/log-item.d.ts +4 -0
  86. package/dist/tui/components/execution/log-item.js +2 -2
  87. package/dist/tui/components/footer.js +1 -1
  88. package/dist/tui/components/index.d.ts +2 -1
  89. package/dist/tui/components/index.js +2 -1
  90. package/dist/tui/components/init.js +2 -9
  91. package/dist/tui/components/logo.js +4 -3
  92. package/dist/tui/components/markdown.d.ts +13 -0
  93. package/dist/tui/components/markdown.js +88 -0
  94. package/dist/tui/components/message-item.js +1 -1
  95. package/dist/tui/components/onboarding/onboarding-flow.js +1 -1
  96. package/dist/tui/components/suggestions.js +3 -3
  97. package/dist/tui/contexts/mode-context.js +6 -2
  98. package/dist/tui/hooks/index.d.ts +1 -0
  99. package/dist/tui/hooks/index.js +1 -0
  100. package/dist/tui/hooks/use-is-latest-version.d.ts +6 -0
  101. package/dist/tui/hooks/use-is-latest-version.js +22 -0
  102. package/dist/tui/views/command-view.d.ts +1 -1
  103. package/dist/tui/views/command-view.js +83 -98
  104. package/dist/tui/views/logs-view.d.ts +8 -0
  105. package/dist/tui/views/logs-view.js +55 -27
  106. package/oclif.manifest.json +26 -1
  107. package/package.json +9 -1
@@ -46,6 +46,20 @@ export class ConnectorsUseCase {
46
46
  // Step 6: Install the selected connector
47
47
  await this.installConnector(selectedAgent, selectedType);
48
48
  }
49
+ /**
50
+ * Display manual setup instructions for MCP configuration.
51
+ */
52
+ displayManualInstructions(agent, instructions) {
53
+ this.terminal.log(`\nManual setup required for ${agent}`);
54
+ this.terminal.log('');
55
+ this.terminal.log('Add this configuration to your MCP settings:');
56
+ this.terminal.log('');
57
+ this.terminal.log(instructions.configContent);
58
+ this.terminal.log('');
59
+ if (instructions.guide) {
60
+ this.terminal.log(`\nFor detailed instructions, see: ${instructions.guide}`);
61
+ }
62
+ }
49
63
  /**
50
64
  * Gets a description for a connector type.
51
65
  */
@@ -54,12 +68,12 @@ export class ConnectorsUseCase {
54
68
  case 'hook': {
55
69
  return `Instructions injected on each prompt (${configPath})`;
56
70
  }
71
+ case 'mcp': {
72
+ return `Agent connects via MCP protocol ${configPath ? `(${configPath})` : ''}`;
73
+ }
57
74
  case 'rules': {
58
75
  return `Agent reads instructions from rule file (${configPath})`;
59
76
  }
60
- default: {
61
- return configPath;
62
- }
63
77
  }
64
78
  }
65
79
  /**
@@ -70,12 +84,12 @@ export class ConnectorsUseCase {
70
84
  case 'hook': {
71
85
  return 'Hook';
72
86
  }
87
+ case 'mcp': {
88
+ return 'MCP';
89
+ }
73
90
  case 'rules': {
74
91
  return 'Rules';
75
92
  }
76
- default: {
77
- return type;
78
- }
79
93
  }
80
94
  }
81
95
  /**
@@ -101,8 +115,13 @@ export class ConnectorsUseCase {
101
115
  async installConnector(agent, connectorType) {
102
116
  const result = await this.connectorManager.switchConnector(agent, connectorType);
103
117
  if (result.success) {
118
+ // Handle manual setup instructions
119
+ if (result.installResult.requiresManualSetup && result.installResult.manualInstructions) {
120
+ this.displayManualInstructions(agent, result.installResult.manualInstructions);
121
+ return;
122
+ }
104
123
  if (result.fromType && result.fromType !== result.toType) {
105
- this.terminal.log(`${agent} switched from ${result.fromType} to ${result.toType}`);
124
+ this.terminal.log(`${agent} switched from ${this.getConnectorLabel(result.fromType)} to ${this.getConnectorLabel(result.toType)}`);
106
125
  if (result.uninstallResult?.wasInstalled) {
107
126
  this.terminal.log(` Uninstalled: ${result.uninstallResult.configPath}`);
108
127
  }
@@ -117,8 +136,8 @@ export class ConnectorsUseCase {
117
136
  this.terminal.log(` Installed: ${result.installResult.configPath}`);
118
137
  }
119
138
  // Show restart message for hook connector
120
- if (result.toType === 'hook' && !result.installResult.alreadyInstalled) {
121
- this.terminal.warn(`\nPlease restart ${agent} to apply the new hooks.`);
139
+ if ((result.toType === 'hook' || result.toType === 'mcp') && !result.installResult.alreadyInstalled) {
140
+ this.terminal.warn(`\nPlease restart ${agent} to apply the new ${result.toType}.`);
122
141
  }
123
142
  }
124
143
  else {
@@ -194,7 +213,7 @@ export class ConnectorsUseCase {
194
213
  async promptForSwitchConfirmation(agent, fromType, toType) {
195
214
  const fromConnector = this.connectorManager.getConnector(fromType);
196
215
  const fromPath = fromConnector.getConfigPath(agent);
197
- this.terminal.warn(`${agent} is currently connected via ${fromType} (${fromPath})`);
216
+ this.terminal.warn(`${agent} is currently connected via ${this.getConnectorLabel(fromType)} ${fromPath ? `(${fromPath})` : ''}`);
198
217
  return this.terminal.confirm({
199
218
  default: true,
200
219
  message: `Switch to ${toType}? This will uninstall the current connector.`,
@@ -178,8 +178,8 @@ export class InitUseCase {
178
178
  this.terminal.log(`${selectedAgent} connected via ${defaultType}`);
179
179
  this.terminal.log(` Installed: ${result.configPath}`);
180
180
  // Show restart message for hook connector
181
- if (defaultType === 'hook') {
182
- this.terminal.warn(`\n⚠️ Please restart ${selectedAgent} to apply the new hooks.`);
181
+ if (defaultType === 'hook' || defaultType === 'mcp') {
182
+ this.terminal.warn(`\n⚠️ Please restart ${selectedAgent} to apply the new ${defaultType}.`);
183
183
  }
184
184
  }
185
185
  }
@@ -351,7 +351,6 @@ export class InitUseCase {
351
351
  this.terminal.log('✓ Context tree initialized');
352
352
  }
353
353
  else {
354
- // Remote has real data - sync it to local
355
354
  await this.contextTreeWriterService.sync({ files: [...coGitSnapshot.files] });
356
355
  await this.contextTreeSnapshotService.saveSnapshot();
357
356
  this.terminal.log(`✓ Synced ${coGitSnapshot.files.length} context files from remote`);
@@ -4,10 +4,12 @@ import type { IProjectConfigStore } from '../../core/interfaces/i-project-config
4
4
  import type { ITerminal } from '../../core/interfaces/i-terminal.js';
5
5
  import type { ITokenStore } from '../../core/interfaces/i-token-store.js';
6
6
  import type { ITrackingService } from '../../core/interfaces/i-tracking-service.js';
7
+ import type { IInstanceDiscovery } from '../../core/interfaces/instance/i-instance-discovery.js';
7
8
  import type { IStatusUseCase } from '../../core/interfaces/usecase/i-status-use-case.js';
8
9
  export interface StatusUseCaseOptions {
9
10
  contextTreeService: IContextTreeService;
10
11
  contextTreeSnapshotService: IContextTreeSnapshotService;
12
+ instanceDiscovery?: IInstanceDiscovery;
11
13
  projectConfigStore: IProjectConfigStore;
12
14
  terminal: ITerminal;
13
15
  tokenStore: ITokenStore;
@@ -16,6 +18,7 @@ export interface StatusUseCaseOptions {
16
18
  export declare class StatusUseCase implements IStatusUseCase {
17
19
  private readonly contextTreeService;
18
20
  private readonly contextTreeSnapshotService;
21
+ private readonly instanceDiscovery;
19
22
  private readonly projectConfigStore;
20
23
  private readonly terminal;
21
24
  private readonly tokenStore;
@@ -24,4 +27,11 @@ export declare class StatusUseCase implements IStatusUseCase {
24
27
  run(options: {
25
28
  cliVersion: string;
26
29
  }): Promise<void>;
30
+ /**
31
+ * Checks the MCP connection status by:
32
+ * 1. Discovering running brv instance
33
+ * 2. Connecting to it via Socket.IO
34
+ * 3. Verifying bidirectional communication with ping
35
+ */
36
+ private checkMcpStatus;
27
37
  }
@@ -2,9 +2,12 @@ import chalk from 'chalk';
2
2
  import { join } from 'node:path';
3
3
  import { BRV_DIR, CONTEXT_TREE_DIR } from '../../constants.js';
4
4
  import { getErrorMessage } from '../../utils/error-helpers.js';
5
+ import { FileInstanceDiscovery } from '../instance/file-instance-discovery.js';
6
+ import { SocketIOTransportClient } from '../transport/socket-io-transport-client.js';
5
7
  export class StatusUseCase {
6
8
  contextTreeService;
7
9
  contextTreeSnapshotService;
10
+ instanceDiscovery;
8
11
  projectConfigStore;
9
12
  terminal;
10
13
  tokenStore;
@@ -12,6 +15,7 @@ export class StatusUseCase {
12
15
  constructor(options) {
13
16
  this.contextTreeService = options.contextTreeService;
14
17
  this.contextTreeSnapshotService = options.contextTreeSnapshotService;
18
+ this.instanceDiscovery = options.instanceDiscovery ?? new FileInstanceDiscovery();
15
19
  this.projectConfigStore = options.projectConfigStore;
16
20
  this.terminal = options.terminal;
17
21
  this.tokenStore = options.tokenStore;
@@ -56,6 +60,8 @@ export class StatusUseCase {
56
60
  this.terminal.log('Project Status: Unable to read project configuration');
57
61
  this.terminal.warn(`Warning: ${getErrorMessage(error)}`);
58
62
  }
63
+ // MCP connection status
64
+ await this.checkMcpStatus();
59
65
  // Context tree status
60
66
  try {
61
67
  const contextTreeExists = await this.contextTreeService.exists();
@@ -94,4 +100,51 @@ export class StatusUseCase {
94
100
  this.terminal.warn(`Warning: ${error instanceof Error ? error.message : 'Context Tree unable to check status'}`);
95
101
  }
96
102
  }
103
+ /**
104
+ * Checks the MCP connection status by:
105
+ * 1. Discovering running brv instance
106
+ * 2. Connecting to it via Socket.IO
107
+ * 3. Verifying bidirectional communication with ping
108
+ */
109
+ async checkMcpStatus() {
110
+ try {
111
+ // Step 1: Discover running instance
112
+ const discoveryResult = await this.instanceDiscovery.discover(process.cwd());
113
+ if (!discoveryResult.found) {
114
+ if (discoveryResult.reason === 'instance_crashed') {
115
+ this.terminal.log(`MCP Status: ${chalk.red('Instance crashed')} (stale instance file found)`);
116
+ }
117
+ else {
118
+ this.terminal.log(`MCP Status: ${chalk.yellow('No instance running')}`);
119
+ }
120
+ return;
121
+ }
122
+ const { instance, projectRoot } = discoveryResult;
123
+ this.terminal.log(`MCP Status: Instance found (PID: ${instance.pid}, Port: ${instance.port})`);
124
+ // Step 2: Connect to instance
125
+ const client = new SocketIOTransportClient();
126
+ const url = instance.getTransportUrl();
127
+ try {
128
+ await client.connect(url);
129
+ }
130
+ catch (connectError) {
131
+ this.terminal.log(`MCP Status: ${chalk.red('Connection failed')} - ${getErrorMessage(connectError)}`);
132
+ return;
133
+ }
134
+ // Step 3: Verify bidirectional communication with ping
135
+ const isResponsive = await client.isConnected(2000);
136
+ if (isResponsive) {
137
+ this.terminal.log(`MCP Status: ${chalk.green('Connected and responsive')} (${projectRoot})`);
138
+ }
139
+ else {
140
+ this.terminal.log(`MCP Status: ${chalk.yellow('Connected but not responsive')} (ping timeout)`);
141
+ }
142
+ // Clean up
143
+ await client.disconnect();
144
+ }
145
+ catch (error) {
146
+ this.terminal.log(`MCP Status: ${chalk.red('Error checking status')}`);
147
+ this.terminal.warn(`Warning: ${getErrorMessage(error)}`);
148
+ }
149
+ }
97
150
  }
@@ -51,7 +51,110 @@ prompt: |
51
51
  - **Before creating a new domain**: Check if existing domains could accommodate the content
52
52
  - **Consolidate related concepts**: Group similar topics under the same domain for better organization
53
53
 
54
- 5. **Two-Part Context Model (REQUIRED)**: When creating context using the `curate` tool, you MUST use the structured format with `rawConcept` and `narrative`:
54
+ 5. **Domain Context (REQUIRED for new domains)**: When creating content in a NEW domain (one that doesn't exist yet), you MUST provide the `domainContext` field:
55
+
56
+ **domainContext** - Describes the domain's purpose and scope:
57
+ - `purpose` (required): What this domain represents and why it exists
58
+ - `scope.included` (required): Array of what belongs in this domain
59
+ - `scope.excluded` (optional): Array of what does NOT belong in this domain
60
+ - `ownership` (optional): Which team/system owns this domain
61
+ - `usage` (optional): How this domain should be used
62
+
63
+ **Example with domainContext for a new domain:**
64
+ ```json
65
+ {
66
+ "type": "ADD",
67
+ "path": "authentication/jwt",
68
+ "title": "Token Handling",
69
+ "content": { ... },
70
+ "domainContext": {
71
+ "purpose": "Contains all knowledge related to user and service authentication mechanisms used across the platform. Documents how identities are verified, credentials issued/validated, and authentication flows implemented.",
72
+ "scope": {
73
+ "included": [
74
+ "Login and signup authentication flows",
75
+ "Token-based authentication (JWT, refresh tokens)",
76
+ "Session handling",
77
+ "OAuth and third-party identity providers",
78
+ "Service-to-service authentication"
79
+ ],
80
+ "excluded": [
81
+ "Authorization and permission models (belongs in authorization)",
82
+ "User profile management",
83
+ "Account lifecycle management"
84
+ ]
85
+ },
86
+ "ownership": "Platform Security Team",
87
+ "usage": "Use this domain for documenting authentication flows, token handling, identity verification. Backend engineers implementing login/token validation, security engineers auditing auth flows."
88
+ },
89
+ "reason": "Documenting JWT token handling in new authentication domain"
90
+ }
91
+ ```
92
+
93
+ **When to provide domainContext:**
94
+ - ALWAYS when the domain doesn't exist yet (check with `list_directory` or `glob_files` first)
95
+ - NOT needed when adding to an existing domain (context.md already exists)
96
+
97
+ 6. **Topic Context (REQUIRED for new topics)**: When creating content in a NEW topic (one that doesn't exist yet), you MUST provide the `topicContext` field:
98
+
99
+ **topicContext** - Describes what the topic covers:
100
+ - `overview` (required): What this topic covers and its main focus
101
+ - `keyConcepts` (optional): Array of key concepts covered in this topic
102
+ - `relatedTopics` (optional): Array of related topics and how they connect
103
+
104
+ **Example with topicContext for a new topic:**
105
+ ```json
106
+ {
107
+ "type": "ADD",
108
+ "path": "authentication/jwt",
109
+ "title": "Token Handling",
110
+ "content": { ... },
111
+ "topicContext": {
112
+ "overview": "Covers all aspects of JWT-based authentication including token generation, validation, and refresh mechanisms used across the platform.",
113
+ "keyConcepts": [
114
+ "JWT tokens and their structure",
115
+ "Refresh token rotation",
116
+ "Token blacklisting for revocation",
117
+ "Token validation middleware"
118
+ ],
119
+ "relatedTopics": [
120
+ "authentication/session - for session-based alternatives",
121
+ "security/encryption - for token signing mechanisms"
122
+ ]
123
+ },
124
+ "reason": "Documenting JWT token handling in new jwt topic"
125
+ }
126
+ ```
127
+
128
+ **When to provide topicContext:**
129
+ - ALWAYS when the topic doesn't exist yet (check with `list_directory` or `glob_files` first)
130
+ - NOT needed when adding to an existing topic (topic/context.md already exists)
131
+
132
+ 7. **Subtopic Context (REQUIRED for new subtopics)**: When creating content in a NEW subtopic (one that doesn't exist yet), you MUST provide the `subtopicContext` field:
133
+
134
+ **subtopicContext** - Describes the subtopic's specific focus:
135
+ - `focus` (required): The specific focus of this subtopic
136
+ - `parentRelation` (optional): How this subtopic relates to its parent topic
137
+
138
+ **Example with subtopicContext for a new subtopic:**
139
+ ```json
140
+ {
141
+ "type": "ADD",
142
+ "path": "authentication/jwt/refresh_tokens",
143
+ "title": "Rotation Strategy",
144
+ "content": { ... },
145
+ "subtopicContext": {
146
+ "focus": "Focuses on refresh token rotation strategy and invalidation mechanisms to prevent token reuse attacks.",
147
+ "parentRelation": "Handles the token refresh aspect of JWT authentication, specifically how old tokens are invalidated when new ones are issued."
148
+ },
149
+ "reason": "Documenting refresh token rotation in new subtopic"
150
+ }
151
+ ```
152
+
153
+ **When to provide subtopicContext:**
154
+ - ALWAYS when the subtopic doesn't exist yet (check with `list_directory` or `glob_files` first)
155
+ - NOT needed when adding to an existing subtopic (subtopic/context.md already exists)
156
+
157
+ 8. **Two-Part Context Model (REQUIRED)**: When creating context using the `curate` tool, you MUST use the structured format with `rawConcept` and `narrative`:
55
158
 
56
159
  **rawConcept** - Captures essential metadata and technical footprint:
57
160
  - `task`: What is the task/feature being documented (required - always include this)
@@ -98,7 +201,7 @@ prompt: |
98
201
  }
99
202
  ```
100
203
 
101
- 6. **Context Quality Requirements**: Each context MUST:
204
+ 9. **Context Quality Requirements**: Each context MUST:
102
205
  - Include a clear `task` in rawConcept describing what the concept is about
103
206
  - Provide at least one of: `changes`, `files`, or `flow` in rawConcept
104
207
  - Include at least one narrative field (`structure`, `dependencies`, or `features`)
@@ -123,11 +226,11 @@ prompt: |
123
226
  code, message, context object, and optional cause. Use `ErrorHandler.wrap(fn)` for consistent error boundaries across
124
227
  async operations."
125
228
 
126
- 7. **Tool Execution Efficiency**:
229
+ 10. **Tool Execution Efficiency**:
127
230
  - When multiple tools don't depend on each other's results, execute them in parallel with `batch`
128
231
  - Example: `glob_files`, `list_directory`, `grep_content` and `read_file` operations for different files can run together
129
232
 
130
- 8. **Response Format**:
233
+ 11. **Response Format**:
131
234
  - Your final response must be a brief summary (1-2 sentences) describing what knowledge was curated
132
235
  - Always mention the topic name, and include the subtopic name if a subtopic was created
133
236
  - Do NOT include any file paths, directory paths, or specific location details in your response
@@ -0,0 +1 @@
1
+ {{mcp_workflow}}
@@ -1,100 +1,9 @@
1
1
  # ByteRover CLI Command Reference
2
2
 
3
- ## Memory Commands
3
+ ## Available Commands
4
4
 
5
- ### `brv curate`
5
+ - `brv curate` - Curate context to the context tree
6
+ - `brv query` - Query and retrieve information from the context tree
7
+ - `brv status` - Show CLI status and project information
6
8
 
7
- **Description:** Curate context to the context tree (interactive or autonomous mode)
8
-
9
- **Arguments:**
10
-
11
- - `CONTEXT`: Knowledge context: patterns, decisions, errors, or insights (triggers autonomous mode, optional)
12
-
13
- **Flags:**
14
-
15
- - `--files`, `-f`: Include file paths for critical context (max 5 files). Only text/code files from the current project directory are allowed. **CONTEXT argument must come BEFORE this flag.**
16
-
17
- **Good examples of context:**
18
-
19
- - "Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts"
20
- - "API rate limit is 100 req/min per user. Implemented using Redis with sliding window in rateLimiter.ts"
21
-
22
- **Bad examples:**
23
-
24
- - "Authentication" or "JWT tokens" (too vague, lacks context)
25
- - "Rate limiting" (no implementation details or file references)
26
-
27
- **Examples:**
28
-
29
- ```bash
30
- # Interactive mode (manually choose domain/topic)
31
- brv curate
32
-
33
- # Autonomous mode - LLM auto-categorizes your context
34
- brv curate "Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts"
35
-
36
- # Include files (CONTEXT must come before --files)
37
- # Single file
38
- brv curate "Authentication middleware validates JWT tokens" -f src/middleware/auth.ts
39
-
40
- # Multiple files - repeat --files flag for each file
41
- brv curate "JWT authentication implementation with refresh token rotation" --files src/auth/jwt.ts --files docs/auth.md
42
- ```
43
-
44
- **Behavior:**
45
-
46
- - Interactive mode: Navigate context tree, create topic folder, edit context.md
47
- - Autonomous mode: LLM automatically categorizes and places context in appropriate location
48
- - When `--files` is provided, agent reads files in parallel before creating knowledge topics
49
-
50
- **Requirements:** Project must be initialized (`brv init`) and authenticated (`brv login`)
51
-
52
- ---
53
-
54
- ### `brv query`
55
-
56
- **Description:** Query and retrieve information from the context tree
57
-
58
- **Arguments:**
59
-
60
- - `QUERY`: Natural language question about your codebase or project knowledge (required)
61
-
62
- **Good examples of queries:**
63
-
64
- - "How is user authentication implemented?"
65
- - "What are the API rate limits and where are they enforced?"
66
-
67
- **Bad examples:**
68
-
69
- - "auth" or "authentication" (too vague, not a question)
70
- - "show me code" (not specific about what information is needed)
71
-
72
- **Examples:**
73
-
74
- ```bash
75
- # Ask questions about patterns, decisions, or implementation details
76
- brv query What are the coding standards?
77
- brv query How is authentication implemented?
78
- ```
79
-
80
- **Behavior:**
81
-
82
- - Uses AI agent to search and answer questions about the context tree
83
- - Accepts natural language questions (not just keywords)
84
- - Displays tool execution progress in real-time
85
-
86
- **Requirements:** Project must be initialized (`brv init`) and authenticated (`brv login`)
87
-
88
- ---
89
-
90
- ## Best Practices
91
-
92
- ### Efficient Workflow
93
-
94
- 1. **Read only what's needed:** Check context tree with `brv status` to see changes before reading full content with `brv query`
95
- 2. **Update precisely:** Use `brv curate` to add/update specific context in context tree
96
- 3. **Push when appropriate:** Prompt user to run `brv push` after completing significant work
97
-
98
- ### Context tree Management
99
-
100
- - Use `brv curate` to directly add/update context in the context tree
9
+ Run `brv query --help` for query instruction and `brv curate --help` for curation instruction.
@@ -0,0 +1,13 @@
1
+ # Workflow Instruction
2
+
3
+ You are a coding agent integrated with ByteRover via MCP (Model Context Protocol).
4
+
5
+ ## Core Rules
6
+
7
+ 1. **Query First**: Automatically call the mcp tool `brv-query` when you need to query the context for the task and you do not have the context.
8
+ 2. **Curate Later**: After finishing the task, call `brv-curate` to store back the knowledge if it is very important.
9
+
10
+ ## Tool Usage
11
+
12
+ - `brv-query`: Query the context tree.
13
+ - `brv-curate`: Store context to the context tree.
@@ -1,27 +1,32 @@
1
1
  # Workflow Instruction
2
2
 
3
3
  You are a coding agent focused on one codebase. Use the brv CLI to manage working context.
4
- Core Rules:
5
4
 
6
- - Start from memory. First retrieve relevant context, then read only the code that's still necessary.
7
- - Keep a local context tree. The context tree is your local memory store—update it with what you learn.
5
+ ## Core Rules
8
6
 
9
- ## Context Tree Guideline
10
-
11
- - Be specific ("Use React Query for data fetching in web modules").
12
- - Be actionable (clear instruction a future agent/dev can apply).
13
- - Be contextual (mention module/service, constraints, links to source).
14
- - Include source (file + lines or commit) when possible.
7
+ - **Start from memory.** First retrieve relevant context with `brv query`, then read only the code that's still necessary.
8
+ - **Keep a local context tree.** The context tree is your local memory store—update it with `brv curate` when you learn something valuable.
15
9
 
16
- ## Using `brv curate` with Files
10
+ ## When to Query
17
11
 
18
- When adding complex implementations, use `--files` to include relevant source files (max 5). Only text/code files from the current project directory are allowed. **CONTEXT argument must come BEFORE --files flag.** For multiple files, repeat the `--files` (or `-f`) flag for each file.
12
+ Use `brv query` **before** starting any code task that requires understanding the codebase:
13
+ - Writing, editing, or modifying code
14
+ - Understanding how something works
15
+ - Debugging or troubleshooting issues
16
+ - Making architectural decisions
19
17
 
20
- Examples:
18
+ ## When to Curate
21
19
 
22
- - Single file: `brv curate "JWT authentication with refresh token rotation" -f src/auth.ts`
23
- - Multiple files: `brv curate "Authentication system" --files src/auth/jwt.ts --files src/auth/middleware.ts --files docs/auth.md`
20
+ Use `brv curate` **after** you learn or create something valuable:
21
+ - Wrote or modified code
22
+ - Discovered how something works
23
+ - Made architectural/design decisions
24
+ - Found a bug root cause or fix pattern
24
25
 
25
- ## CLI Usage Notes
26
+ ## Context Tree Guideline
26
27
 
27
- - Use --help on any command to discover flags. Provide exact arguments for the scenario.
28
+ Good context is:
29
+ - **Specific** ("Use React Query for data fetching in web modules")
30
+ - **Actionable** (clear instruction a future agent/dev can apply)
31
+ - **Contextual** (mention module/service, constraints, links to source)
32
+ - **Sourced** (include file + lines or commit when possible)
package/dist/tui/app.js CHANGED
@@ -7,6 +7,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  * - Authorized: Show main app with tabs
8
8
  */
9
9
  import { Box } from 'ink';
10
+ import { useState } from 'react';
10
11
  import { Footer, Header, TabBar } from './components/index.js';
11
12
  import { useAuth, useTasks } from './contexts/index.js';
12
13
  import { useTabNavigation, useTerminalBreakpoint, useUIHeights } from './hooks/index.js';
@@ -17,6 +18,8 @@ export const App = () => {
17
18
  const { isAuthorized } = useAuth();
18
19
  const { activeTab, tabs } = useTabNavigation();
19
20
  const { stats: taskStats } = useTasks();
21
+ const [selectedLogIndex, setSelectedLogIndex] = useState(0);
22
+ const [expandedViewLogId, setExpandedViewLogId] = useState(null);
20
23
  const contentHeight = Math.max(1, terminalHeight - header - tab - footer);
21
- return (_jsxs(Box, { flexDirection: "column", height: terminalHeight, paddingBottom: appBottomPadding, width: terminalWidth, children: [_jsx(Box, { flexShrink: 0, children: _jsx(Header, { compact: isAuthorized, showStatusLine: isAuthorized, taskStats: taskStats }) }), isAuthorized ? (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(TabBar, { activeTab: activeTab, tabs: tabs }) }), _jsxs(Box, { flexGrow: 1, paddingX: 1, children: [_jsx(Box, { display: activeTab === 'activity' ? 'flex' : 'none', height: "100%", width: "100%", children: _jsx(LogsView, { availableHeight: contentHeight }) }), _jsx(Box, { display: activeTab === 'console' ? 'flex' : 'none', height: "100%", width: "100%", children: _jsx(CommandView, { availableHeight: contentHeight }) })] }), _jsx(Box, { flexShrink: 0, children: _jsx(Footer, {}) })] })) : (_jsx(Box, { flexGrow: 1, paddingX: 1, children: _jsx(LoginView, {}) }))] }));
24
+ return (_jsxs(Box, { flexDirection: "column", height: terminalHeight, paddingBottom: appBottomPadding, width: terminalWidth, children: [_jsx(Box, { flexShrink: 0, children: _jsx(Header, { compact: isAuthorized, showStatusLine: isAuthorized, taskStats: taskStats }) }), isAuthorized ? (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(TabBar, { activeTab: activeTab, tabs: tabs }) }), _jsxs(Box, { flexGrow: 1, paddingX: 1, children: [activeTab === 'activity' && (_jsx(LogsView, { availableHeight: contentHeight, expandedViewLogId: expandedViewLogId, selectedLogIndex: selectedLogIndex, setExpandedViewLogId: setExpandedViewLogId, setSelectedLogIndex: setSelectedLogIndex })), _jsx(Box, { display: activeTab === 'console' ? 'flex' : 'none', height: "100%", width: "100%", children: _jsx(CommandView, { availableHeight: contentHeight }) })] }), _jsx(Box, { flexShrink: 0, children: _jsx(Footer, {}) })] })) : (_jsx(Box, { flexGrow: 1, paddingX: 1, children: _jsx(LoginView, {}) }))] }));
22
25
  };
@@ -20,7 +20,7 @@ function formatUsage(label, args, flags, subCommands) {
20
20
  usage += ` ${argsStr}`;
21
21
  }
22
22
  if (flags?.length) {
23
- const flagsStr = flags.map((f) => `[--${f.name}]`).join(' ');
23
+ const flagsStr = flags.map((f) => (f.type === 'file' ? `[${f.char}${f.name}]` : `[--${f.name}]`)).join(' ');
24
24
  usage += ` ${flagsStr}`;
25
25
  }
26
26
  return usage;
@@ -7,6 +7,8 @@ import React from 'react';
7
7
  interface ExecutionChangesProps {
8
8
  /** List of created file paths */
9
9
  created: string[];
10
+ /** Whether content should be fully expanded (no truncation) */
11
+ isExpand?: boolean;
10
12
  /** Maximum changes configuration */
11
13
  maxChanges?: {
12
14
  created: number;
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { Box, Text } from 'ink';
8
8
  import { useTheme } from '../../hooks/index.js';
9
- export const ExecutionChanges = ({ created, updated, maxChanges = { created: Infinity, updated: Infinity }, }) => {
9
+ export const ExecutionChanges = ({ created, isExpand = false, updated, maxChanges = { created: Number.MAX_SAFE_INTEGER, updated: Number.MAX_SAFE_INTEGER }, }) => {
10
10
  const { theme: { colors }, } = useTheme();
11
11
  const totalChanges = created.length + updated.length;
12
12
  if (totalChanges === 0) {
@@ -14,6 +14,10 @@ export const ExecutionChanges = ({ created, updated, maxChanges = { created: Inf
14
14
  }
15
15
  const hasCreated = created.length > 0;
16
16
  const hasUpdated = updated.length > 0;
17
+ // In expand mode, show all changes without truncation
18
+ if (isExpand) {
19
+ return (_jsxs(Box, { flexDirection: "column", children: [hasCreated && (_jsxs(Box, { columnGap: 1, children: [_jsx(Text, { color: colors.secondary, children: "created at:" }), _jsx(Box, { flexDirection: "column", children: created.map((path) => (_jsx(Text, { children: path }, path))) })] })), hasUpdated && (_jsxs(Box, { columnGap: 1, children: [_jsx(Text, { color: colors.secondary, children: "updated at:" }), _jsx(Box, { flexDirection: "column", children: updated.map((path) => (_jsx(Text, { children: path }, path))) })] }))] }));
20
+ }
17
21
  // Calculate overflow for each section
18
22
  // maxChanges represents total lines (items + indicator if overflow)
19
23
  const createdOverflow = created.length > maxChanges.created;
@@ -20,6 +20,8 @@ interface ExecutionContentProps {
20
20
  content: string;
21
21
  /** Whether this is error content */
22
22
  isError?: boolean;
23
+ /** Whether content should be fully expanded (no truncation) */
24
+ isExpand?: boolean;
23
25
  /** Maximum number of lines (rows) this component can use, including the "more lines" indicator */
24
26
  maxLines?: number;
25
27
  }
@@ -7,6 +7,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  import { Box, Text, useStdout } from 'ink';
8
8
  import { useTheme } from '../../hooks/index.js';
9
9
  import { getVisualLineCount } from '../../utils/line.js';
10
+ import { Markdown } from '../markdown.js';
10
11
  const DEFAULT_MAX_LINES = 5;
11
12
  /**
12
13
  * Truncate content string to maxLines, returning truncated content and remaining line count.
@@ -42,39 +43,28 @@ export function truncateContent(content, maxLines, maxCharsPerLine) {
42
43
  break;
43
44
  }
44
45
  }
45
- // Replace last 3 characters of last line with "..." if truncated
46
- let finalContent = truncatedLines.join('\n');
47
- if (truncatedLines.length > 0 && totalVisualLines > visualLineCount) {
48
- const lastLineIndex = truncatedLines.length - 1;
49
- const lastLine = truncatedLines[lastLineIndex];
50
- if (lastLine.length >= 3) {
51
- truncatedLines[lastLineIndex] = lastLine.slice(0, -3) + '...';
52
- finalContent = truncatedLines.join('\n');
53
- }
54
- else if (lastLine.length > 0) {
55
- // If last line is shorter than 3 chars, just replace entirely with "..."
56
- truncatedLines[lastLineIndex] = '...';
57
- finalContent = truncatedLines.join('\n');
58
- }
59
- }
60
46
  return {
61
47
  remainingLines: totalVisualLines - visualLineCount,
62
48
  totalLines: totalVisualLines,
63
- truncatedContent: finalContent,
49
+ truncatedContent: truncatedLines.join('\n'),
64
50
  };
65
51
  }
66
- export const ExecutionContent = ({ bottomMargin = 1, content, isError = false, maxLines = DEFAULT_MAX_LINES, }) => {
52
+ export const ExecutionContent = ({ bottomMargin = 1, content, isError = false, isExpand = false, maxLines = DEFAULT_MAX_LINES, }) => {
67
53
  const { theme: { colors }, } = useTheme();
68
54
  const { stdout } = useStdout();
69
55
  const contentWidth = (stdout?.columns ?? 80) - 4; // 4 is for padding
70
56
  if (!content) {
71
57
  return null;
72
58
  }
59
+ // In expand mode, render full content without truncation
60
+ if (isExpand) {
61
+ return (_jsx(Box, { flexDirection: "column", marginY: 1, children: isError ? (_jsx(Text, { color: colors.errorText, children: content })) : (_jsx(Markdown, { children: content })) }));
62
+ }
73
63
  // First check if content would overflow
74
64
  const { totalLines } = truncateContent(content, maxLines, contentWidth);
75
65
  const hasOverflow = totalLines > maxLines;
76
66
  // If overflow, reserve 1 line for indicator, show (maxLines - 1) lines of content
77
67
  const effectiveMaxLines = hasOverflow ? maxLines - 1 : maxLines;
78
68
  const { remainingLines, truncatedContent } = truncateContent(content, effectiveMaxLines, contentWidth);
79
- return (_jsxs(Box, { flexDirection: "column", marginBottom: bottomMargin, children: [_jsx(Text, { color: isError ? colors.errorText : colors.text, children: truncatedContent }), remainingLines > 0 && _jsxs(Text, { color: colors.dimText, children: ["\u2195 ", remainingLines, " more lines"] })] }));
69
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: bottomMargin, children: [isError ? (_jsx(Text, { color: colors.errorText, children: truncatedContent })) : (_jsx(Markdown, { children: truncatedContent })), remainingLines > 0 && _jsxs(Text, { color: colors.dimText, children: [remainingLines, " more lines"] })] }));
80
70
  };