byterover-cli 1.0.5 → 1.2.0

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 (204) hide show
  1. package/README.md +19 -13
  2. package/dist/commands/hook-prompt-submit.d.ts +27 -0
  3. package/dist/commands/hook-prompt-submit.js +39 -0
  4. package/dist/commands/mcp.d.ts +13 -0
  5. package/dist/commands/mcp.js +61 -0
  6. package/dist/commands/status.js +8 -3
  7. package/dist/constants.d.ts +1 -1
  8. package/dist/constants.js +1 -1
  9. package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
  10. package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
  11. package/dist/core/domain/cipher/tools/constants.js +1 -0
  12. package/dist/core/domain/entities/agent.d.ts +16 -0
  13. package/dist/core/domain/entities/agent.js +78 -0
  14. package/dist/core/domain/entities/connector-type.d.ts +10 -0
  15. package/dist/core/domain/entities/connector-type.js +9 -0
  16. package/dist/core/domain/entities/event.d.ts +1 -1
  17. package/dist/core/domain/entities/event.js +2 -0
  18. package/dist/core/domain/errors/task-error.d.ts +4 -0
  19. package/dist/core/domain/errors/task-error.js +7 -0
  20. package/dist/core/domain/transport/schemas.d.ts +40 -0
  21. package/dist/core/domain/transport/schemas.js +28 -0
  22. package/dist/core/interfaces/connectors/connector-types.d.ts +70 -0
  23. package/dist/core/interfaces/connectors/i-connector-manager.d.ts +72 -0
  24. package/dist/core/interfaces/connectors/i-connector-manager.js +1 -0
  25. package/dist/core/interfaces/connectors/i-connector.d.ts +54 -0
  26. package/dist/core/interfaces/connectors/i-connector.js +1 -0
  27. package/dist/core/interfaces/i-file-service.d.ts +7 -0
  28. package/dist/core/interfaces/i-mcp-config-writer.d.ts +40 -0
  29. package/dist/core/interfaces/i-mcp-config-writer.js +1 -0
  30. package/dist/core/interfaces/i-rule-template-service.d.ts +4 -2
  31. package/dist/core/interfaces/transport/i-transport-client.d.ts +7 -0
  32. package/dist/core/interfaces/usecase/i-connectors-use-case.d.ts +3 -0
  33. package/dist/core/interfaces/usecase/i-connectors-use-case.js +1 -0
  34. package/dist/hooks/init/update-notifier.d.ts +1 -0
  35. package/dist/hooks/init/update-notifier.js +10 -1
  36. package/dist/infra/cipher/agent/cipher-agent.d.ts +8 -0
  37. package/dist/infra/cipher/agent/cipher-agent.js +16 -0
  38. package/dist/infra/cipher/file-system/binary-utils.d.ts +7 -12
  39. package/dist/infra/cipher/file-system/binary-utils.js +46 -31
  40. package/dist/infra/cipher/llm/context/context-manager.d.ts +10 -2
  41. package/dist/infra/cipher/llm/context/context-manager.js +39 -2
  42. package/dist/infra/cipher/llm/formatters/gemini-formatter.js +48 -9
  43. package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
  44. package/dist/infra/cipher/llm/internal-llm-service.js +40 -12
  45. package/dist/infra/cipher/session/chat-session.d.ts +3 -0
  46. package/dist/infra/cipher/session/chat-session.js +7 -1
  47. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.d.ts +6 -7
  48. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +57 -18
  49. package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
  50. package/dist/infra/cipher/tools/implementations/curate-tool.js +380 -24
  51. package/dist/infra/cipher/tools/implementations/read-file-tool.js +38 -17
  52. package/dist/infra/cipher/tools/implementations/search-knowledge-tool.d.ts +7 -0
  53. package/dist/infra/cipher/tools/implementations/search-knowledge-tool.js +303 -0
  54. package/dist/infra/cipher/tools/index.d.ts +1 -0
  55. package/dist/infra/cipher/tools/index.js +1 -0
  56. package/dist/infra/cipher/tools/tool-manager.js +1 -0
  57. package/dist/infra/cipher/tools/tool-registry.js +7 -0
  58. package/dist/infra/connectors/connector-manager.d.ts +32 -0
  59. package/dist/infra/connectors/connector-manager.js +158 -0
  60. package/dist/infra/connectors/hook/hook-connector-config.d.ts +52 -0
  61. package/dist/infra/connectors/hook/hook-connector-config.js +41 -0
  62. package/dist/infra/connectors/hook/hook-connector.d.ts +46 -0
  63. package/dist/infra/connectors/hook/hook-connector.js +231 -0
  64. package/dist/infra/connectors/mcp/index.d.ts +4 -0
  65. package/dist/infra/connectors/mcp/index.js +4 -0
  66. package/dist/infra/connectors/mcp/json-mcp-config-writer.d.ts +26 -0
  67. package/dist/infra/connectors/mcp/json-mcp-config-writer.js +71 -0
  68. package/dist/infra/connectors/mcp/mcp-connector-config.d.ts +229 -0
  69. package/dist/infra/connectors/mcp/mcp-connector-config.js +173 -0
  70. package/dist/infra/connectors/mcp/mcp-connector.d.ts +80 -0
  71. package/dist/infra/connectors/mcp/mcp-connector.js +324 -0
  72. package/dist/infra/connectors/mcp/toml-mcp-config-writer.d.ts +45 -0
  73. package/dist/infra/connectors/mcp/toml-mcp-config-writer.js +134 -0
  74. package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.d.ts +2 -2
  75. package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.js +1 -1
  76. package/dist/infra/connectors/rules/rules-connector-config.d.ts +95 -0
  77. package/dist/infra/{rule/agent-rule-config.js → connectors/rules/rules-connector-config.js} +10 -10
  78. package/dist/infra/connectors/rules/rules-connector.d.ts +34 -0
  79. package/dist/infra/connectors/rules/rules-connector.js +139 -0
  80. package/dist/infra/connectors/shared/rule-file-manager.d.ts +72 -0
  81. package/dist/infra/connectors/shared/rule-file-manager.js +119 -0
  82. package/dist/infra/connectors/shared/template-service.d.ts +27 -0
  83. package/dist/infra/connectors/shared/template-service.js +125 -0
  84. package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +5 -2
  85. package/dist/infra/context-tree/file-context-tree-writer-service.js +20 -5
  86. package/dist/infra/core/executors/curate-executor.d.ts +2 -2
  87. package/dist/infra/core/executors/curate-executor.js +7 -7
  88. package/dist/infra/core/executors/query-executor.d.ts +12 -0
  89. package/dist/infra/core/executors/query-executor.js +62 -1
  90. package/dist/infra/file/fs-file-service.d.ts +7 -0
  91. package/dist/infra/file/fs-file-service.js +15 -1
  92. package/dist/infra/mcp/index.d.ts +2 -0
  93. package/dist/infra/mcp/index.js +2 -0
  94. package/dist/infra/mcp/mcp-server.d.ts +58 -0
  95. package/dist/infra/mcp/mcp-server.js +178 -0
  96. package/dist/infra/mcp/tools/brv-curate-tool.d.ts +23 -0
  97. package/dist/infra/mcp/tools/brv-curate-tool.js +68 -0
  98. package/dist/infra/mcp/tools/brv-query-tool.d.ts +17 -0
  99. package/dist/infra/mcp/tools/brv-query-tool.js +68 -0
  100. package/dist/infra/mcp/tools/index.d.ts +3 -0
  101. package/dist/infra/mcp/tools/index.js +3 -0
  102. package/dist/infra/mcp/tools/task-result-waiter.d.ts +30 -0
  103. package/dist/infra/mcp/tools/task-result-waiter.js +56 -0
  104. package/dist/infra/process/agent-worker.d.ts +2 -2
  105. package/dist/infra/process/agent-worker.js +663 -142
  106. package/dist/infra/process/constants.d.ts +1 -1
  107. package/dist/infra/process/constants.js +1 -1
  108. package/dist/infra/process/ipc-types.d.ts +17 -4
  109. package/dist/infra/process/ipc-types.js +3 -3
  110. package/dist/infra/process/parent-heartbeat.d.ts +47 -0
  111. package/dist/infra/process/parent-heartbeat.js +118 -0
  112. package/dist/infra/process/process-manager.d.ts +79 -0
  113. package/dist/infra/process/process-manager.js +277 -3
  114. package/dist/infra/process/task-queue-manager.d.ts +13 -0
  115. package/dist/infra/process/task-queue-manager.js +19 -0
  116. package/dist/infra/process/transport-handlers.d.ts +3 -0
  117. package/dist/infra/process/transport-handlers.js +51 -5
  118. package/dist/infra/process/transport-worker.js +9 -69
  119. package/dist/infra/repl/commands/connectors-command.d.ts +8 -0
  120. package/dist/infra/repl/commands/{gen-rules-command.js → connectors-command.js} +21 -10
  121. package/dist/infra/repl/commands/curate-command.js +2 -2
  122. package/dist/infra/repl/commands/index.js +3 -2
  123. package/dist/infra/repl/commands/init-command.js +11 -7
  124. package/dist/infra/repl/commands/query-command.js +22 -2
  125. package/dist/infra/repl/commands/reset-command.js +1 -1
  126. package/dist/infra/transport/socket-io-transport-client.d.ts +75 -0
  127. package/dist/infra/transport/socket-io-transport-client.js +308 -7
  128. package/dist/infra/transport/socket-io-transport-server.js +4 -0
  129. package/dist/infra/usecase/connectors-use-case.d.ts +63 -0
  130. package/dist/infra/usecase/connectors-use-case.js +222 -0
  131. package/dist/infra/usecase/init-use-case.d.ts +8 -43
  132. package/dist/infra/usecase/init-use-case.js +27 -252
  133. package/dist/infra/usecase/logout-use-case.js +1 -1
  134. package/dist/infra/usecase/pull-use-case.js +5 -5
  135. package/dist/infra/usecase/push-use-case.js +4 -4
  136. package/dist/infra/usecase/reset-use-case.js +3 -4
  137. package/dist/infra/usecase/space-list-use-case.js +3 -3
  138. package/dist/infra/usecase/space-switch-use-case.js +3 -3
  139. package/dist/infra/usecase/status-use-case.d.ts +10 -0
  140. package/dist/infra/usecase/status-use-case.js +53 -0
  141. package/dist/resources/prompts/curate.yml +114 -4
  142. package/dist/resources/prompts/explore.yml +34 -0
  143. package/dist/resources/prompts/query-orchestrator.yml +112 -0
  144. package/dist/resources/prompts/system-prompt.yml +12 -2
  145. package/dist/resources/tools/search_knowledge.txt +32 -0
  146. package/dist/templates/mcp-base.md +1 -0
  147. package/dist/templates/sections/brv-instructions.md +98 -0
  148. package/dist/templates/sections/mcp-workflow.md +13 -0
  149. package/dist/tui/app.js +4 -1
  150. package/dist/tui/components/command-details.js +1 -1
  151. package/dist/tui/components/execution/execution-changes.d.ts +2 -0
  152. package/dist/tui/components/execution/execution-changes.js +5 -1
  153. package/dist/tui/components/execution/execution-content.d.ts +2 -0
  154. package/dist/tui/components/execution/execution-content.js +8 -18
  155. package/dist/tui/components/execution/execution-input.d.ts +2 -0
  156. package/dist/tui/components/execution/execution-input.js +6 -4
  157. package/dist/tui/components/execution/execution-progress.d.ts +2 -0
  158. package/dist/tui/components/execution/execution-progress.js +6 -2
  159. package/dist/tui/components/execution/expanded-log-view.d.ts +20 -0
  160. package/dist/tui/components/execution/expanded-log-view.js +75 -0
  161. package/dist/tui/components/execution/expanded-message-view.d.ts +24 -0
  162. package/dist/tui/components/execution/expanded-message-view.js +68 -0
  163. package/dist/tui/components/execution/index.d.ts +2 -0
  164. package/dist/tui/components/execution/index.js +2 -0
  165. package/dist/tui/components/execution/log-item.d.ts +4 -0
  166. package/dist/tui/components/execution/log-item.js +2 -2
  167. package/dist/tui/components/footer.js +1 -1
  168. package/dist/tui/components/index.d.ts +2 -1
  169. package/dist/tui/components/index.js +2 -1
  170. package/dist/tui/components/init.js +2 -9
  171. package/dist/tui/components/logo.js +4 -3
  172. package/dist/tui/components/markdown.d.ts +13 -0
  173. package/dist/tui/components/markdown.js +88 -0
  174. package/dist/tui/components/message-item.js +1 -1
  175. package/dist/tui/components/onboarding/onboarding-flow.js +14 -11
  176. package/dist/tui/components/onboarding/welcome-box.js +1 -1
  177. package/dist/tui/components/suggestions.js +3 -3
  178. package/dist/tui/contexts/mode-context.js +6 -2
  179. package/dist/tui/contexts/onboarding-context.d.ts +4 -0
  180. package/dist/tui/contexts/onboarding-context.js +14 -2
  181. package/dist/tui/hooks/index.d.ts +1 -0
  182. package/dist/tui/hooks/index.js +1 -0
  183. package/dist/tui/hooks/use-is-latest-version.d.ts +6 -0
  184. package/dist/tui/hooks/use-is-latest-version.js +22 -0
  185. package/dist/tui/views/command-view.d.ts +1 -1
  186. package/dist/tui/views/command-view.js +87 -98
  187. package/dist/tui/views/logs-view.d.ts +8 -0
  188. package/dist/tui/views/logs-view.js +55 -27
  189. package/dist/utils/file-validator.d.ts +1 -1
  190. package/dist/utils/file-validator.js +25 -28
  191. package/dist/utils/type-guards.d.ts +5 -0
  192. package/dist/utils/type-guards.js +7 -0
  193. package/oclif.manifest.json +55 -4
  194. package/package.json +12 -1
  195. package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +0 -3
  196. package/dist/infra/repl/commands/gen-rules-command.d.ts +0 -7
  197. package/dist/infra/rule/agent-rule-config.d.ts +0 -19
  198. package/dist/infra/rule/rule-template-service.d.ts +0 -18
  199. package/dist/infra/rule/rule-template-service.js +0 -88
  200. package/dist/infra/usecase/generate-rules-use-case.d.ts +0 -61
  201. package/dist/infra/usecase/generate-rules-use-case.js +0 -285
  202. /package/dist/core/interfaces/{usecase/i-generate-rules-use-case.js → connectors/connector-types.js} +0 -0
  203. /package/dist/infra/{rule → connectors/shared}/constants.d.ts +0 -0
  204. /package/dist/infra/{rule → connectors/shared}/constants.js +0 -0
@@ -0,0 +1,27 @@
1
+ import type { Agent } from '../../../core/domain/entities/agent.js';
2
+ import type { ConnectorType } from '../../../core/domain/entities/connector-type.js';
3
+ import type { IRuleTemplateService } from '../../../core/interfaces/i-rule-template-service.js';
4
+ import type { ITemplateLoader } from '../../../core/interfaces/i-template-loader.js';
5
+ /**
6
+ * Service for generating rule templates for different agents.
7
+ * Loads templates from external files and assembles them with agent-specific context.
8
+ */
9
+ export declare class RuleTemplateService implements IRuleTemplateService {
10
+ private readonly templateLoader;
11
+ constructor(templateLoader: ITemplateLoader);
12
+ /**
13
+ * Generates rule content for the specified agent by loading and assembling templates.
14
+ * @param agent The agent for which to generate rules.
15
+ * @returns Promise resolving to the rule content as a string.
16
+ * @throws Error if templates cannot be loaded or assembled.
17
+ */
18
+ generateRuleContent(agent: Agent, type?: ConnectorType): Promise<string>;
19
+ /**
20
+ * Generates CLI mode content with full workflow and command reference.
21
+ */
22
+ private generateCliContent;
23
+ /**
24
+ * Generates MCP mode content with concise tool-focused instructions.
25
+ */
26
+ private generateMcpContent;
27
+ }
@@ -0,0 +1,125 @@
1
+ import { BRV_RULE_MARKERS, BRV_RULE_TAG } from './constants.js';
2
+ /**
3
+ * Wraps rule content with boundary markers for identification and replacement.
4
+ *
5
+ * @param content The rule content to wrap.
6
+ * @param agent The agent name for the footer tag.
7
+ * @param header Agent-specific header.
8
+ * @returns The wrapped content with boundary markers.
9
+ */
10
+ const wrapContentWithBoundaryMarkers = (content, agent, header) => {
11
+ const parts = [BRV_RULE_MARKERS.START, header, content, '---', `${BRV_RULE_TAG} ${agent}`, BRV_RULE_MARKERS.END];
12
+ return parts.join('\n');
13
+ };
14
+ const guideHeaders = [
15
+ {
16
+ agent: 'Augment Code',
17
+ value: `---
18
+ type: "always_apply"
19
+ ---`,
20
+ },
21
+ {
22
+ agent: 'Cursor',
23
+ value: `---
24
+ description: ByteRover CLI Rules
25
+ alwaysApply: true
26
+ ---`,
27
+ },
28
+ {
29
+ agent: 'Kiro',
30
+ value: `---
31
+ inclusion: always
32
+ ---`,
33
+ },
34
+ {
35
+ agent: 'Qoder',
36
+ value: `---
37
+ trigger: always_on
38
+ alwaysApply: true
39
+ ---`,
40
+ },
41
+ {
42
+ agent: 'Windsurf',
43
+ value: `---
44
+ trigger: always_on
45
+ ---`,
46
+ },
47
+ ];
48
+ /**
49
+ * Service for generating rule templates for different agents.
50
+ * Loads templates from external files and assembles them with agent-specific context.
51
+ */
52
+ export class RuleTemplateService {
53
+ templateLoader;
54
+ constructor(templateLoader) {
55
+ this.templateLoader = templateLoader;
56
+ }
57
+ /**
58
+ * Generates rule content for the specified agent by loading and assembling templates.
59
+ * @param agent The agent for which to generate rules.
60
+ * @returns Promise resolving to the rule content as a string.
61
+ * @throws Error if templates cannot be loaded or assembled.
62
+ */
63
+ async generateRuleContent(agent, type) {
64
+ try {
65
+ // Load section templates
66
+ let content;
67
+ switch (type) {
68
+ case 'mcp': {
69
+ content = await this.generateMcpContent();
70
+ break;
71
+ }
72
+ case 'rules': {
73
+ content = await this.generateCliContent(agent);
74
+ break;
75
+ }
76
+ default: {
77
+ throw new Error(`Unsupported connector type: ${type}`);
78
+ }
79
+ }
80
+ // Add agent-specific header if available
81
+ const header = guideHeaders.find((h) => h.agent === agent)?.value || '';
82
+ return wrapContentWithBoundaryMarkers(content, agent, header);
83
+ }
84
+ catch (error) {
85
+ throw new Error(`Failed to generate rule content for agent '${agent}' - type '${type}': ${error instanceof Error ? error.message : String(error)}`);
86
+ }
87
+ }
88
+ /**
89
+ * Generates CLI mode content with full workflow and command reference.
90
+ */
91
+ async generateCliContent(agent) {
92
+ // Load section templates
93
+ const workflow = await this.templateLoader.loadSection('workflow');
94
+ const commandReference = await this.templateLoader.loadSection('command-reference');
95
+ // Load base template
96
+ const baseTemplate = await this.templateLoader.loadTemplate('base.md');
97
+ // Assemble context for variable substitution
98
+ const context = {
99
+ /* eslint-disable camelcase */
100
+ agent_name: agent,
101
+ command_reference: commandReference,
102
+ workflow,
103
+ /* eslint-enable camelcase */
104
+ };
105
+ // Substitute variables and get content
106
+ return this.templateLoader.substituteVariables(baseTemplate, context);
107
+ }
108
+ /**
109
+ * Generates MCP mode content with concise tool-focused instructions.
110
+ */
111
+ async generateMcpContent() {
112
+ // Load MCP-specific section
113
+ const mcpWorkflow = await this.templateLoader.loadSection('mcp-workflow');
114
+ // Load MCP base template
115
+ const mcpBaseTemplate = await this.templateLoader.loadTemplate('mcp-base.md');
116
+ // Assemble context for variable substitution
117
+ const context = {
118
+ /* eslint-disable camelcase */
119
+ mcp_workflow: mcpWorkflow,
120
+ /* eslint-enable camelcase */
121
+ };
122
+ // Substitute variables and get content
123
+ return this.templateLoader.substituteVariables(mcpBaseTemplate, context);
124
+ }
125
+ }
@@ -15,11 +15,14 @@ export declare class FileContextTreeWriterService implements IContextTreeWriterS
15
15
  private readonly snapshotService;
16
16
  constructor(dependencies: ContextTreeWriterServiceDependencies, config?: ContextTreeWriterServiceConfig);
17
17
  sync(params: SyncParams): Promise<SyncResult>;
18
+ /**
19
+ * Builds a map of remote files with normalized paths.
20
+ * Filters out README.md at root level to match getCurrentState behavior.
21
+ */
22
+ private buildRemoteFilesMap;
18
23
  /**
19
24
  * Normalizes a file path by converting backslashes to forward slashes
20
25
  * and removing leading slashes.
21
- * Ensures cross-platform compatibility and handles legacy API responses
22
- * that may include leading slashes or Windows-style backslashes.
23
26
  */
24
27
  private normalizePath;
25
28
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join } from 'node:path';
4
- import { BRV_DIR, CONTEXT_TREE_DIR } from '../../constants.js';
4
+ import { BRV_DIR, CONTEXT_TREE_DIR, README_FILE } from '../../constants.js';
5
5
  import { toUnixPath } from './path-utils.js';
6
6
  /**
7
7
  * File-based implementation of IContextTreeWriterService.
@@ -19,8 +19,9 @@ export class FileContextTreeWriterService {
19
19
  const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
20
20
  // Get current local state
21
21
  const localState = await this.snapshotService.getCurrentState(params.directory);
22
- // Build map of remote files (normalize paths - remove leading /)
23
- const remoteFiles = new Map(params.files.map((file) => [this.normalizePath(file.path), file]));
22
+ // Build map of remote files, filtering out README.md at root level
23
+ // This matches getCurrentState behavior which also excludes root README.md
24
+ const remoteFiles = this.buildRemoteFilesMap(params.files);
24
25
  const added = [];
25
26
  const edited = [];
26
27
  const deleted = [];
@@ -53,11 +54,25 @@ export class FileContextTreeWriterService {
53
54
  }
54
55
  return { added, deleted, edited };
55
56
  }
57
+ /**
58
+ * Builds a map of remote files with normalized paths.
59
+ * Filters out README.md at root level to match getCurrentState behavior.
60
+ */
61
+ buildRemoteFilesMap(files) {
62
+ const result = new Map();
63
+ for (const file of files) {
64
+ const normalizedPath = this.normalizePath(file.path);
65
+ // Skip README.md at root level (matches getCurrentState behavior)
66
+ if (normalizedPath === README_FILE) {
67
+ continue;
68
+ }
69
+ result.set(normalizedPath, file);
70
+ }
71
+ return result;
72
+ }
56
73
  /**
57
74
  * Normalizes a file path by converting backslashes to forward slashes
58
75
  * and removing leading slashes.
59
- * Ensures cross-platform compatibility and handles legacy API responses
60
- * that may include leading slashes or Windows-style backslashes.
61
76
  */
62
77
  normalizePath(path) {
63
78
  return toUnixPath(path).replace(/^\/+/, '');
@@ -23,8 +23,8 @@ export declare class CurateExecutor implements ICurateExecutor {
23
23
  *
24
24
  * @param filePaths - Array of file paths (relative or absolute)
25
25
  * @param clientCwd - Client's working directory for file validation (optional, defaults to process.cwd())
26
- * @returns Formatted instructions for the agent to read the specified files,
27
- * or undefined if validation fails
26
+ * @returns Formatted instructions for the agent to read the specified files
27
+ * @throws {FileValidationError} If any file validation fails
28
28
  */
29
29
  private processFileReferences;
30
30
  }
@@ -19,10 +19,8 @@ export class CurateExecutor {
19
19
  static MAX_FILES = 5;
20
20
  async executeWithAgent(agent, options) {
21
21
  const { clientCwd, content, files, taskId } = options;
22
+ // Process file references (throws FileValidationError if validation fails)
22
23
  const fileReferenceInstructions = this.processFileReferences(files ?? [], clientCwd);
23
- if (fileReferenceInstructions === undefined) {
24
- throw new FileValidationError();
25
- }
26
24
  // Build prompt with optional file reference instructions
27
25
  const prompt = fileReferenceInstructions ? `${content}\n${fileReferenceInstructions}` : content;
28
26
  // Execute with curate commandType
@@ -39,8 +37,8 @@ export class CurateExecutor {
39
37
  *
40
38
  * @param filePaths - Array of file paths (relative or absolute)
41
39
  * @param clientCwd - Client's working directory for file validation (optional, defaults to process.cwd())
42
- * @returns Formatted instructions for the agent to read the specified files,
43
- * or undefined if validation fails
40
+ * @returns Formatted instructions for the agent to read the specified files
41
+ * @throws {FileValidationError} If any file validation fails
44
42
  */
45
43
  processFileReferences(filePaths, clientCwd) {
46
44
  if (!filePaths || filePaths.length === 0) {
@@ -64,9 +62,10 @@ export class CurateExecutor {
64
62
  errors.push(result.error ?? `Invalid file: ${filePath}`);
65
63
  }
66
64
  }
67
- // If there are any validation errors, return undefined
65
+ // If there are any validation errors, throw with specific messages
68
66
  if (errors.length > 0) {
69
- return undefined;
67
+ const errorMessage = `File validation failed:\n${errors.map((e) => ` - ${e}`).join('\n')}`;
68
+ throw new FileValidationError(errorMessage);
70
69
  }
71
70
  // Format instructions for the agent
72
71
  const instructions = [
@@ -80,6 +79,7 @@ export class CurateExecutor {
80
79
  '- You MUST use the `read_file` tool to read ALL of these files IN PARALLEL (in a single iteration) before proceeding to create knowledge topics',
81
80
  '- These files contain essential context that will help you create comprehensive and accurate knowledge topics',
82
81
  '- Read them in parallel to maximize efficiency - they do not depend on each other',
82
+ '- **CRITICAL:** Only curate files where `read_file` returns `success: true`. Files that return `success: false` are error messages and should not be curated.',
83
83
  '- After reading all files, proceed with the normal workflow: detect domains, find existing knowledge, and create/update topics',
84
84
  '',
85
85
  ];
@@ -11,7 +11,19 @@ import type { IQueryExecutor, QueryExecuteOptions } from '../../../core/interfac
11
11
  * - Event streaming is handled by agent-worker (subscribes to agentEventBus)
12
12
  * - Transport handles task lifecycle (task:started, task:completed, task:error)
13
13
  * - Executor focuses solely on query execution
14
+ *
15
+ * Enhanced with multi-perspective search strategy for comprehensive results.
14
16
  */
15
17
  export declare class QueryExecutor implements IQueryExecutor {
16
18
  executeWithAgent(agent: ICipherAgent, options: QueryExecuteOptions): Promise<string>;
19
+ /**
20
+ * Build an enhanced query prompt with multi-perspective search instructions.
21
+ *
22
+ * The prompt instructs the agent to:
23
+ * 1. Analyze the query to understand intent
24
+ * 2. Generate 2-3 search perspectives for comprehensive coverage
25
+ * 3. Execute searches using the task tool with explore subagents
26
+ * 4. Synthesize results into a cohesive answer with citations
27
+ */
28
+ private buildQueryPrompt;
17
29
  }
@@ -9,6 +9,8 @@
9
9
  * - Event streaming is handled by agent-worker (subscribes to agentEventBus)
10
10
  * - Transport handles task lifecycle (task:started, task:completed, task:error)
11
11
  * - Executor focuses solely on query execution
12
+ *
13
+ * Enhanced with multi-perspective search strategy for comprehensive results.
12
14
  */
13
15
  export class QueryExecutor {
14
16
  async executeWithAgent(agent, options) {
@@ -16,11 +18,70 @@ export class QueryExecutor {
16
18
  // Execute with query commandType
17
19
  // Agent uses its default session (created during start())
18
20
  // Task lifecycle is managed by Transport (task:started, task:completed, task:error)
19
- const prompt = `Search the context tree for: ${query}`;
21
+ const prompt = this.buildQueryPrompt(query);
20
22
  const response = await agent.execute(prompt, {
21
23
  executionContext: { commandType: 'query' },
22
24
  taskId,
23
25
  });
24
26
  return response;
25
27
  }
28
+ /**
29
+ * Build an enhanced query prompt with multi-perspective search instructions.
30
+ *
31
+ * The prompt instructs the agent to:
32
+ * 1. Analyze the query to understand intent
33
+ * 2. Generate 2-3 search perspectives for comprehensive coverage
34
+ * 3. Execute searches using the task tool with explore subagents
35
+ * 4. Synthesize results into a cohesive answer with citations
36
+ */
37
+ buildQueryPrompt(query) {
38
+ return `## User Query
39
+ ${query}
40
+
41
+ ## Query Processing Instructions
42
+
43
+ You are searching the context tree to answer the user's question. Follow this strategy for comprehensive results:
44
+
45
+ ### Step 1: Query Analysis
46
+ First, analyze the query:
47
+ - What is the user trying to learn? (core intent)
48
+ - What are the key concepts mentioned?
49
+ - Is this a factual, analytical, or exploratory question?
50
+
51
+ ### Step 2: Multi-Perspective Search
52
+ Generate 2-3 complementary search perspectives:
53
+
54
+ 1. **Direct Search**: Use exact terms from the query
55
+ 2. **Related Concepts**: Search for synonyms, related terms, technical jargon
56
+ 3. **Implementation Patterns**: Search for how things are used or implemented
57
+
58
+ ### Step 3: Execute Parallel Searches
59
+ Use the \`task\` tool to spawn explore subagents for each perspective:
60
+ - Each subagent focuses on ONE search angle
61
+ - Use \`contextTreeOnly=true\` to search only the context tree
62
+ - Provide clear, specific search instructions
63
+
64
+ Example:
65
+ \`\`\`
66
+ task(
67
+ subagentType="explore",
68
+ description="Search [perspective]",
69
+ prompt="Search for [specific terms]. Look in .brv/context-tree/ for [what to find].",
70
+ contextTreeOnly=true
71
+ )
72
+ \`\`\`
73
+
74
+ ### Step 4: Synthesize Results
75
+ After gathering information:
76
+ 1. Identify overlapping findings (likely most relevant)
77
+ 2. Combine unique insights from each perspective
78
+ 3. Provide a clear, organized answer
79
+ 4. Cite sources with file paths
80
+
81
+ ### Response Format
82
+ - **Summary**: Brief answer (2-3 sentences)
83
+ - **Details**: Expanded explanation with findings
84
+ - **Sources**: File paths for referenced information
85
+ - **Gaps**: Note any aspects that couldn't be answered`;
86
+ }
26
87
  }
@@ -4,6 +4,13 @@ import { type IFileService, type WriteMode } from '../../core/interfaces/i-file-
4
4
  */
5
5
  export declare class FsFileService implements IFileService {
6
6
  createBackup(filePath: string): Promise<string>;
7
+ /**
8
+ * Deletes a file at the specified path.
9
+ *
10
+ * @param filePath The path to the file to delete.
11
+ * @returns A promise that resolves when the file has been deleted.
12
+ */
13
+ delete(filePath: string): Promise<void>;
7
14
  /**
8
15
  * Checks if a file exists at the specified path.
9
16
  *
@@ -1,4 +1,4 @@
1
- import { access, appendFile, copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { access, appendFile, copyFile, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
2
2
  import { dirname } from 'node:path';
3
3
  /**
4
4
  * File service implementation using Node.js fs module.
@@ -16,6 +16,20 @@ export class FsFileService {
16
16
  throw new Error(`Failed to create backup file '${filePath}': ${error instanceof Error ? error.message : 'Unknown error'}`);
17
17
  }
18
18
  }
19
+ /**
20
+ * Deletes a file at the specified path.
21
+ *
22
+ * @param filePath The path to the file to delete.
23
+ * @returns A promise that resolves when the file has been deleted.
24
+ */
25
+ async delete(filePath) {
26
+ try {
27
+ await unlink(filePath);
28
+ }
29
+ catch (error) {
30
+ throw new Error(`Failed to delete file '${filePath}': ${error instanceof Error ? error.message : 'Unknown error'}`);
31
+ }
32
+ }
19
33
  /**
20
34
  * Checks if a file exists at the specified path.
21
35
  *
@@ -0,0 +1,2 @@
1
+ export { ByteRoverMcpServer, type McpServerConfig } from './mcp-server.js';
2
+ export { registerBrvCurateTool, registerBrvQueryTool, waitForTaskResult } from './tools/index.js';
@@ -0,0 +1,2 @@
1
+ export { ByteRoverMcpServer } from './mcp-server.js';
2
+ export { registerBrvCurateTool, registerBrvQueryTool, waitForTaskResult } from './tools/index.js';
@@ -0,0 +1,58 @@
1
+ export interface McpServerConfig {
2
+ /** CLI version for MCP server identification */
3
+ version: string;
4
+ /** Working directory for file operations */
5
+ workingDirectory: string;
6
+ }
7
+ /**
8
+ * ByteRover MCP Server.
9
+ *
10
+ * Exposes brv-query and brv-curate as MCP tools for coding agents.
11
+ * Connects to a running brv instance via Socket.IO transport.
12
+ *
13
+ * Architecture:
14
+ * - Coding agent spawns `brv mcp` process
15
+ * - MCP server connects to running brv instance via Socket.IO
16
+ * - MCP tools create tasks via transport
17
+ * - Tasks are executed by the existing agent process
18
+ */
19
+ export declare class ByteRoverMcpServer {
20
+ private client;
21
+ private readonly config;
22
+ private currentReconnectDelay;
23
+ private isReconnecting;
24
+ private reconnectTimer;
25
+ private readonly server;
26
+ private transport;
27
+ constructor(config: McpServerConfig);
28
+ /**
29
+ * Starts the MCP server.
30
+ *
31
+ * 1. Connects to running brv instance via Socket.IO
32
+ * 2. Starts MCP server with stdio transport
33
+ *
34
+ * @throws NoInstanceRunningError - No brv instance is running
35
+ * @throws ConnectionFailedError - Failed to connect to brv instance
36
+ */
37
+ start(): Promise<void>;
38
+ /**
39
+ * Stops the MCP server.
40
+ *
41
+ * Disconnects from the brv instance.
42
+ */
43
+ stop(): Promise<void>;
44
+ /**
45
+ * Attempts to reconnect to the brv instance.
46
+ * Uses exponential backoff for retry delays.
47
+ */
48
+ private attemptReconnect;
49
+ /**
50
+ * Log to stderr (stdout is reserved for MCP protocol).
51
+ */
52
+ private log;
53
+ /**
54
+ * Sets up the state change handler for a client.
55
+ * Handles disconnection by triggering auto-reconnect.
56
+ */
57
+ private setupStateChangeHandler;
58
+ }
@@ -0,0 +1,178 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createTransportClientFactory } from '../transport/transport-client-factory.js';
4
+ import { registerBrvCurateTool, registerBrvQueryTool } from './tools/index.js';
5
+ /** Reconnection configuration */
6
+ const RECONNECT_DELAY_MS = 1000;
7
+ const RECONNECT_MAX_DELAY_MS = 30_000;
8
+ const RECONNECT_BACKOFF_MULTIPLIER = 1.5;
9
+ /**
10
+ * ByteRover MCP Server.
11
+ *
12
+ * Exposes brv-query and brv-curate as MCP tools for coding agents.
13
+ * Connects to a running brv instance via Socket.IO transport.
14
+ *
15
+ * Architecture:
16
+ * - Coding agent spawns `brv mcp` process
17
+ * - MCP server connects to running brv instance via Socket.IO
18
+ * - MCP tools create tasks via transport
19
+ * - Tasks are executed by the existing agent process
20
+ */
21
+ export class ByteRoverMcpServer {
22
+ client;
23
+ config;
24
+ currentReconnectDelay = RECONNECT_DELAY_MS;
25
+ isReconnecting = false;
26
+ reconnectTimer;
27
+ server;
28
+ transport;
29
+ constructor(config) {
30
+ this.config = config;
31
+ this.server = new McpServer({
32
+ name: 'byterover',
33
+ version: config.version,
34
+ });
35
+ // Register tools with lazy client getter
36
+ // Client will be set when start() is called
37
+ registerBrvQueryTool(this.server, () => this.client, () => this.config.workingDirectory);
38
+ registerBrvCurateTool(this.server, () => this.client, () => this.config.workingDirectory);
39
+ }
40
+ /**
41
+ * Starts the MCP server.
42
+ *
43
+ * 1. Connects to running brv instance via Socket.IO
44
+ * 2. Starts MCP server with stdio transport
45
+ *
46
+ * @throws NoInstanceRunningError - No brv instance is running
47
+ * @throws ConnectionFailedError - Failed to connect to brv instance
48
+ */
49
+ async start() {
50
+ this.log('Starting MCP server...');
51
+ this.log(`Working directory: ${this.config.workingDirectory}`);
52
+ // Connect to running brv instance
53
+ const factory = createTransportClientFactory();
54
+ this.log('Connecting to brv instance...');
55
+ let connectionResult;
56
+ try {
57
+ connectionResult = await factory.connect(this.config.workingDirectory);
58
+ }
59
+ catch (error) {
60
+ this.log(`Connection failed: ${error instanceof Error ? error.message : String(error)}`);
61
+ throw error;
62
+ }
63
+ const { client, projectRoot } = connectionResult;
64
+ this.client = client;
65
+ this.log(`Connected to brv instance at ${projectRoot}`);
66
+ this.log(`Client ID: ${client.getClientId()}`);
67
+ this.log(`Initial connection state: ${client.getState()}`);
68
+ // Monitor connection state changes and handle reconnection
69
+ this.setupStateChangeHandler(client);
70
+ // Start MCP server with stdio transport
71
+ this.transport = new StdioServerTransport();
72
+ await this.server.connect(this.transport);
73
+ this.log('MCP server started and ready for tool calls');
74
+ // Log client state periodically to debug connection issues
75
+ setInterval(() => {
76
+ if (this.client) {
77
+ this.log(`[heartbeat] Client state: ${this.client.getState()}, ID: ${this.client.getClientId()}`);
78
+ }
79
+ else {
80
+ this.log('[heartbeat] Client is undefined!');
81
+ }
82
+ }, 10_000);
83
+ }
84
+ /**
85
+ * Stops the MCP server.
86
+ *
87
+ * Disconnects from the brv instance.
88
+ */
89
+ async stop() {
90
+ // Clear any pending reconnection timer
91
+ if (this.reconnectTimer) {
92
+ clearTimeout(this.reconnectTimer);
93
+ this.reconnectTimer = undefined;
94
+ }
95
+ this.isReconnecting = false;
96
+ if (this.client) {
97
+ await this.client.disconnect();
98
+ this.client = undefined;
99
+ }
100
+ }
101
+ /**
102
+ * Attempts to reconnect to the brv instance.
103
+ * Uses exponential backoff for retry delays.
104
+ */
105
+ async attemptReconnect() {
106
+ if (this.isReconnecting) {
107
+ this.log('Reconnection already in progress, skipping...');
108
+ return;
109
+ }
110
+ this.isReconnecting = true;
111
+ this.log(`Attempting to reconnect in ${this.currentReconnectDelay}ms...`);
112
+ this.reconnectTimer = setTimeout(async () => {
113
+ try {
114
+ const factory = createTransportClientFactory();
115
+ const result = await factory.connect(this.config.workingDirectory);
116
+ // Disconnect old client if it exists
117
+ if (this.client) {
118
+ try {
119
+ await this.client.disconnect();
120
+ }
121
+ catch {
122
+ // Ignore disconnect errors on old client
123
+ }
124
+ }
125
+ this.client = result.client;
126
+ this.log(`Reconnected successfully! Client ID: ${result.client.getClientId()}`);
127
+ // Reset backoff delay on successful connection
128
+ this.currentReconnectDelay = RECONNECT_DELAY_MS;
129
+ this.isReconnecting = false;
130
+ // Set up state change handler for the new client
131
+ this.setupStateChangeHandler(result.client);
132
+ }
133
+ catch (error) {
134
+ this.log(`Reconnection failed: ${error instanceof Error ? error.message : String(error)}`);
135
+ // Increase delay with exponential backoff (capped at max)
136
+ this.currentReconnectDelay = Math.min(this.currentReconnectDelay * RECONNECT_BACKOFF_MULTIPLIER, RECONNECT_MAX_DELAY_MS);
137
+ this.isReconnecting = false;
138
+ // Schedule next reconnection attempt
139
+ this.attemptReconnect();
140
+ }
141
+ }, this.currentReconnectDelay);
142
+ }
143
+ /**
144
+ * Log to stderr (stdout is reserved for MCP protocol).
145
+ */
146
+ log(msg) {
147
+ process.stderr.write(`[brv-mcp] ${msg}\n`);
148
+ }
149
+ /**
150
+ * Sets up the state change handler for a client.
151
+ * Handles disconnection by triggering auto-reconnect.
152
+ */
153
+ setupStateChangeHandler(client) {
154
+ client.onStateChange((state) => {
155
+ const timestamp = new Date().toISOString();
156
+ this.log(`[${timestamp}] Connection state changed: ${state}`);
157
+ switch (state) {
158
+ case 'connected': {
159
+ this.log(`[${timestamp}] Connected to brv instance.`);
160
+ // Reset backoff delay on successful connection
161
+ this.currentReconnectDelay = RECONNECT_DELAY_MS;
162
+ this.isReconnecting = false;
163
+ break;
164
+ }
165
+ case 'disconnected': {
166
+ this.log(`[${timestamp}] Socket disconnected from brv instance. Initiating reconnection...`);
167
+ // Trigger auto-reconnect
168
+ this.attemptReconnect();
169
+ break;
170
+ }
171
+ case 'reconnecting': {
172
+ this.log(`[${timestamp}] Socket.IO attempting to reconnect...`);
173
+ break;
174
+ }
175
+ }
176
+ });
177
+ }
178
+ }