byterover-cli 1.1.0 → 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.
- package/README.md +8 -4
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +61 -0
- package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
- package/dist/core/domain/entities/agent.js +72 -18
- package/dist/core/domain/entities/connector-type.d.ts +2 -1
- package/dist/core/domain/entities/connector-type.js +2 -1
- package/dist/core/interfaces/connectors/connector-types.d.ts +13 -0
- package/dist/core/interfaces/i-mcp-config-writer.d.ts +40 -0
- package/dist/core/interfaces/i-mcp-config-writer.js +1 -0
- package/dist/core/interfaces/i-rule-template-service.d.ts +4 -2
- package/dist/core/interfaces/transport/i-transport-client.d.ts +7 -0
- package/dist/infra/cipher/agent/cipher-agent.d.ts +8 -0
- package/dist/infra/cipher/agent/cipher-agent.js +16 -0
- package/dist/infra/cipher/llm/context/context-manager.d.ts +8 -0
- package/dist/infra/cipher/llm/context/context-manager.js +16 -0
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +38 -10
- package/dist/infra/cipher/session/chat-session.d.ts +3 -0
- package/dist/infra/cipher/session/chat-session.js +7 -1
- package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
- package/dist/infra/cipher/tools/implementations/curate-tool.js +360 -22
- package/dist/infra/connectors/connector-manager.js +2 -0
- package/dist/infra/connectors/mcp/index.d.ts +4 -0
- package/dist/infra/connectors/mcp/index.js +4 -0
- package/dist/infra/connectors/mcp/json-mcp-config-writer.d.ts +26 -0
- package/dist/infra/connectors/mcp/json-mcp-config-writer.js +71 -0
- package/dist/infra/connectors/mcp/mcp-connector-config.d.ts +229 -0
- package/dist/infra/connectors/mcp/mcp-connector-config.js +173 -0
- package/dist/infra/connectors/mcp/mcp-connector.d.ts +80 -0
- package/dist/infra/connectors/mcp/mcp-connector.js +324 -0
- package/dist/infra/connectors/mcp/toml-mcp-config-writer.d.ts +45 -0
- package/dist/infra/connectors/mcp/toml-mcp-config-writer.js +134 -0
- package/dist/infra/connectors/rules/rules-connector.d.ts +1 -8
- package/dist/infra/connectors/rules/rules-connector.js +20 -85
- package/dist/infra/connectors/shared/rule-file-manager.d.ts +72 -0
- package/dist/infra/connectors/shared/rule-file-manager.js +119 -0
- package/dist/infra/connectors/shared/template-service.d.ts +10 -1
- package/dist/infra/connectors/shared/template-service.js +53 -16
- package/dist/infra/mcp/index.d.ts +2 -0
- package/dist/infra/mcp/index.js +2 -0
- package/dist/infra/mcp/mcp-server.d.ts +58 -0
- package/dist/infra/mcp/mcp-server.js +178 -0
- package/dist/infra/mcp/tools/brv-curate-tool.d.ts +23 -0
- package/dist/infra/mcp/tools/brv-curate-tool.js +68 -0
- package/dist/infra/mcp/tools/brv-query-tool.d.ts +17 -0
- package/dist/infra/mcp/tools/brv-query-tool.js +68 -0
- package/dist/infra/mcp/tools/index.d.ts +3 -0
- package/dist/infra/mcp/tools/index.js +3 -0
- package/dist/infra/mcp/tools/task-result-waiter.d.ts +30 -0
- package/dist/infra/mcp/tools/task-result-waiter.js +56 -0
- package/dist/infra/process/agent-worker.js +37 -0
- package/dist/infra/repl/commands/curate-command.js +2 -2
- package/dist/infra/transport/socket-io-transport-client.d.ts +7 -0
- package/dist/infra/transport/socket-io-transport-client.js +25 -0
- package/dist/infra/transport/socket-io-transport-server.js +4 -0
- package/dist/infra/usecase/connectors-use-case.d.ts +4 -0
- package/dist/infra/usecase/connectors-use-case.js +29 -10
- package/dist/infra/usecase/init-use-case.js +2 -3
- package/dist/infra/usecase/status-use-case.d.ts +10 -0
- package/dist/infra/usecase/status-use-case.js +53 -0
- package/dist/resources/prompts/curate.yml +107 -4
- package/dist/templates/mcp-base.md +1 -0
- package/dist/templates/sections/mcp-workflow.md +13 -0
- package/dist/tui/app.js +4 -1
- package/dist/tui/components/command-details.js +1 -1
- package/dist/tui/components/execution/execution-changes.d.ts +2 -0
- package/dist/tui/components/execution/execution-changes.js +5 -1
- package/dist/tui/components/execution/execution-content.d.ts +2 -0
- package/dist/tui/components/execution/execution-content.js +8 -18
- package/dist/tui/components/execution/execution-input.d.ts +2 -0
- package/dist/tui/components/execution/execution-input.js +6 -4
- package/dist/tui/components/execution/execution-progress.d.ts +2 -0
- package/dist/tui/components/execution/execution-progress.js +6 -2
- package/dist/tui/components/execution/expanded-log-view.d.ts +20 -0
- package/dist/tui/components/execution/expanded-log-view.js +75 -0
- package/dist/tui/components/execution/expanded-message-view.d.ts +24 -0
- package/dist/tui/components/execution/expanded-message-view.js +68 -0
- package/dist/tui/components/execution/index.d.ts +2 -0
- package/dist/tui/components/execution/index.js +2 -0
- package/dist/tui/components/execution/log-item.d.ts +4 -0
- package/dist/tui/components/execution/log-item.js +2 -2
- package/dist/tui/components/footer.js +1 -1
- package/dist/tui/components/index.d.ts +2 -1
- package/dist/tui/components/index.js +2 -1
- package/dist/tui/components/init.js +2 -9
- package/dist/tui/components/logo.js +4 -3
- package/dist/tui/components/markdown.d.ts +13 -0
- package/dist/tui/components/markdown.js +88 -0
- package/dist/tui/components/message-item.js +1 -1
- package/dist/tui/components/onboarding/onboarding-flow.js +1 -1
- package/dist/tui/components/suggestions.js +3 -3
- package/dist/tui/contexts/mode-context.js +6 -2
- package/dist/tui/hooks/index.d.ts +1 -0
- package/dist/tui/hooks/index.js +1 -0
- package/dist/tui/hooks/use-is-latest-version.d.ts +6 -0
- package/dist/tui/hooks/use-is-latest-version.js +22 -0
- package/dist/tui/views/command-view.d.ts +1 -1
- package/dist/tui/views/command-view.js +83 -98
- package/dist/tui/views/logs-view.d.ts +8 -0
- package/dist/tui/views/logs-view.js +55 -27
- package/oclif.manifest.json +26 -1
- package/package.json +9 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { AGENT_CONNECTOR_CONFIG } from '../../../core/domain/entities/agent.js';
|
|
3
|
-
import {
|
|
3
|
+
import { RuleFileManager } from '../shared/rule-file-manager.js';
|
|
4
4
|
import { RULES_CONNECTOR_CONFIGS } from './rules-connector-config.js';
|
|
5
5
|
/**
|
|
6
6
|
* Connector that integrates BRV with coding agents via rule files.
|
|
@@ -10,12 +10,17 @@ export class RulesConnector {
|
|
|
10
10
|
type = 'rules';
|
|
11
11
|
fileService;
|
|
12
12
|
projectRoot;
|
|
13
|
+
ruleFileManager;
|
|
13
14
|
supportedAgents;
|
|
14
15
|
templateService;
|
|
15
16
|
constructor(options) {
|
|
16
17
|
this.fileService = options.fileService;
|
|
17
18
|
this.projectRoot = options.projectRoot;
|
|
18
19
|
this.templateService = options.templateService;
|
|
20
|
+
this.ruleFileManager = new RuleFileManager({
|
|
21
|
+
fileService: options.fileService,
|
|
22
|
+
projectRoot: options.projectRoot,
|
|
23
|
+
});
|
|
19
24
|
this.supportedAgents = Object.entries(AGENT_CONNECTOR_CONFIG)
|
|
20
25
|
.filter(([_, config]) => config.supported.includes(this.type))
|
|
21
26
|
.map(([agent]) => agent);
|
|
@@ -28,36 +33,10 @@ export class RulesConnector {
|
|
|
28
33
|
}
|
|
29
34
|
async install(agent) {
|
|
30
35
|
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
31
|
-
const fullPath = path.join(this.projectRoot, config.filePath);
|
|
32
36
|
try {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
37
|
-
// File exists but no BRV content or different agent - append or replace based on writeMode
|
|
38
|
-
const ruleContent = await this.templateService.generateRuleContent(agent);
|
|
39
|
-
if (config.writeMode === 'overwrite') {
|
|
40
|
-
await this.fileService.write(ruleContent, fullPath, 'overwrite');
|
|
41
|
-
}
|
|
42
|
-
else if (hasMarkers) {
|
|
43
|
-
// Replace existing markers section
|
|
44
|
-
const newContent = this.replaceMarkerSection(content, ruleContent);
|
|
45
|
-
await this.fileService.write(newContent, fullPath, 'overwrite');
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
// Append to file
|
|
49
|
-
await this.fileService.write(ruleContent, fullPath, 'append');
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
alreadyInstalled: false,
|
|
53
|
-
configPath: config.filePath,
|
|
54
|
-
message: `Rules connector installed for ${agent}`,
|
|
55
|
-
success: true,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
// File doesn't exist - create it
|
|
59
|
-
const ruleContent = await this.templateService.generateRuleContent(agent);
|
|
60
|
-
await this.fileService.write(ruleContent, fullPath, 'overwrite');
|
|
37
|
+
const ruleContent = await this.templateService.generateRuleContent(agent, this.type);
|
|
38
|
+
// Write the rule content to the file
|
|
39
|
+
await this.ruleFileManager.install(config.filePath, config.writeMode, ruleContent);
|
|
61
40
|
return {
|
|
62
41
|
alreadyInstalled: false,
|
|
63
42
|
configPath: config.filePath,
|
|
@@ -81,8 +60,8 @@ export class RulesConnector {
|
|
|
81
60
|
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
82
61
|
const fullPath = path.join(this.projectRoot, config.filePath);
|
|
83
62
|
try {
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
63
|
+
const { fileExists, hasMarkers } = await this.ruleFileManager.status(config.filePath);
|
|
64
|
+
if (!fileExists) {
|
|
86
65
|
return {
|
|
87
66
|
configExists: false,
|
|
88
67
|
configPath: config.filePath,
|
|
@@ -90,12 +69,10 @@ export class RulesConnector {
|
|
|
90
69
|
};
|
|
91
70
|
}
|
|
92
71
|
const content = await this.fileService.read(fullPath);
|
|
93
|
-
|
|
94
|
-
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
95
|
-
const hasAgentTag = content.includes(`${BRV_RULE_TAG} ${agent}`);
|
|
72
|
+
const hasMcpTools = content.includes('brv-query') || content.includes('brv-curate');
|
|
96
73
|
// For overwrite files, any BRV content means installed
|
|
97
74
|
// For append files, need both markers and agent tag
|
|
98
|
-
const installed =
|
|
75
|
+
const installed = hasMarkers && !hasMcpTools;
|
|
99
76
|
return {
|
|
100
77
|
configExists: true,
|
|
101
78
|
configPath: config.filePath,
|
|
@@ -113,10 +90,9 @@ export class RulesConnector {
|
|
|
113
90
|
}
|
|
114
91
|
async uninstall(agent) {
|
|
115
92
|
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
116
|
-
const fullPath = path.join(this.projectRoot, config.filePath);
|
|
117
93
|
try {
|
|
118
|
-
const
|
|
119
|
-
if (!
|
|
94
|
+
const { fileExists, hasLegacyTag, hasMarkers } = await this.ruleFileManager.status(config.filePath);
|
|
95
|
+
if (!fileExists) {
|
|
120
96
|
return {
|
|
121
97
|
configPath: config.filePath,
|
|
122
98
|
message: `Rule file does not exist: ${config.filePath}`,
|
|
@@ -124,11 +100,7 @@ export class RulesConnector {
|
|
|
124
100
|
wasInstalled: false,
|
|
125
101
|
};
|
|
126
102
|
}
|
|
127
|
-
const content = await this.fileService.read(fullPath);
|
|
128
|
-
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
129
103
|
if (!hasMarkers) {
|
|
130
|
-
// Check for legacy format (footer tag without markers)
|
|
131
|
-
const hasLegacyTag = content.includes(`${BRV_RULE_TAG} ${agent}`);
|
|
132
104
|
if (!hasLegacyTag) {
|
|
133
105
|
return {
|
|
134
106
|
configPath: config.filePath,
|
|
@@ -145,24 +117,14 @@ export class RulesConnector {
|
|
|
145
117
|
wasInstalled: true,
|
|
146
118
|
};
|
|
147
119
|
}
|
|
148
|
-
|
|
149
|
-
if (config.writeMode === 'overwrite') {
|
|
150
|
-
// For dedicated files, delete the entire file
|
|
151
|
-
await this.fileService.delete(fullPath);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
// For shared files, remove only the BRV section
|
|
155
|
-
const newContent = this.removeMarkerSection(content);
|
|
156
|
-
// If file would be empty, delete it; otherwise write the new content
|
|
157
|
-
await (newContent.trim() === ''
|
|
158
|
-
? this.fileService.delete(fullPath)
|
|
159
|
-
: this.fileService.write(newContent, fullPath, 'overwrite'));
|
|
160
|
-
}
|
|
120
|
+
const result = await this.ruleFileManager.uninstall(config.filePath, config.writeMode);
|
|
161
121
|
return {
|
|
162
122
|
configPath: config.filePath,
|
|
163
|
-
message:
|
|
123
|
+
message: result.wasInstalled
|
|
124
|
+
? `Rules connector uninstalled for ${agent}`
|
|
125
|
+
: `Rules connector is not installed for ${agent}`,
|
|
164
126
|
success: true,
|
|
165
|
-
wasInstalled:
|
|
127
|
+
wasInstalled: result.wasInstalled,
|
|
166
128
|
};
|
|
167
129
|
}
|
|
168
130
|
catch (error) {
|
|
@@ -174,31 +136,4 @@ export class RulesConnector {
|
|
|
174
136
|
};
|
|
175
137
|
}
|
|
176
138
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Removes the section between BRV markers (inclusive).
|
|
179
|
-
*/
|
|
180
|
-
removeMarkerSection(content) {
|
|
181
|
-
const startIndex = content.indexOf(BRV_RULE_MARKERS.START);
|
|
182
|
-
const endIndex = content.indexOf(BRV_RULE_MARKERS.END);
|
|
183
|
-
if (startIndex === -1 || endIndex === -1) {
|
|
184
|
-
return content;
|
|
185
|
-
}
|
|
186
|
-
const before = content.slice(0, startIndex);
|
|
187
|
-
const after = content.slice(endIndex + BRV_RULE_MARKERS.END.length);
|
|
188
|
-
// Clean up extra newlines
|
|
189
|
-
return (before + after).replaceAll(/\n{3,}/g, '\n\n').trim();
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Replaces the section between BRV markers with new content.
|
|
193
|
-
*/
|
|
194
|
-
replaceMarkerSection(content, newRuleContent) {
|
|
195
|
-
const startIndex = content.indexOf(BRV_RULE_MARKERS.START);
|
|
196
|
-
const endIndex = content.indexOf(BRV_RULE_MARKERS.END);
|
|
197
|
-
if (startIndex === -1 || endIndex === -1) {
|
|
198
|
-
return content;
|
|
199
|
-
}
|
|
200
|
-
const before = content.slice(0, startIndex);
|
|
201
|
-
const after = content.slice(endIndex + BRV_RULE_MARKERS.END.length);
|
|
202
|
-
return before + newRuleContent + after;
|
|
203
|
-
}
|
|
204
139
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { IFileService, WriteMode } from '../../../core/interfaces/i-file-service.js';
|
|
2
|
+
/**
|
|
3
|
+
* Result of a rule file installation operation.
|
|
4
|
+
*/
|
|
5
|
+
export type RuleFileInstallResult = {
|
|
6
|
+
/** Whether the content was newly installed (false if replaced existing) */
|
|
7
|
+
isNew: boolean;
|
|
8
|
+
/** Whether the operation succeeded */
|
|
9
|
+
success: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Result of a rule file uninstallation operation.
|
|
13
|
+
*/
|
|
14
|
+
export type RuleFileUninstallResult = {
|
|
15
|
+
/** Whether the operation succeeded */
|
|
16
|
+
success: boolean;
|
|
17
|
+
/** Whether there was content to remove */
|
|
18
|
+
wasInstalled: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Result of a rule file status check.
|
|
22
|
+
*/
|
|
23
|
+
export type RuleFileStatusResult = {
|
|
24
|
+
/** Whether the file exists */
|
|
25
|
+
fileExists: boolean;
|
|
26
|
+
/** Whether the file contains legacy BRV tag (without markers) */
|
|
27
|
+
hasLegacyTag: boolean;
|
|
28
|
+
/** Whether the file contains BRV markers */
|
|
29
|
+
hasMarkers: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Manages rule file operations including installation, uninstallation,
|
|
33
|
+
* and marker section manipulation.
|
|
34
|
+
*
|
|
35
|
+
* This class centralizes the logic for working with rule files that use
|
|
36
|
+
* BRV markers to delimit managed content sections.
|
|
37
|
+
*/
|
|
38
|
+
export declare class RuleFileManager {
|
|
39
|
+
private readonly fileService;
|
|
40
|
+
private readonly projectRoot;
|
|
41
|
+
constructor(options: {
|
|
42
|
+
fileService: IFileService;
|
|
43
|
+
projectRoot: string;
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Install rule content into a file.
|
|
47
|
+
*
|
|
48
|
+
* @param filePath - Relative path to the rule file
|
|
49
|
+
* @param writeMode - How to write the content ('overwrite' or 'append')
|
|
50
|
+
* @param ruleContent - The rule content to write (should include markers)
|
|
51
|
+
*/
|
|
52
|
+
install(filePath: string, writeMode: WriteMode, ruleContent: string): Promise<RuleFileInstallResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Removes the section between BRV markers (inclusive).
|
|
55
|
+
*/
|
|
56
|
+
removeMarkerSection(content: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Replaces the section between BRV markers with new content.
|
|
59
|
+
*/
|
|
60
|
+
replaceMarkerSection(content: string, newRuleContent: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Check the status of rule content in a file.
|
|
63
|
+
*/
|
|
64
|
+
status(filePath: string): Promise<RuleFileStatusResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Uninstall rule content from a file.
|
|
67
|
+
*
|
|
68
|
+
* @param filePath - Relative path to the rule file
|
|
69
|
+
* @param writeMode - How the content was written ('overwrite' or 'append')
|
|
70
|
+
*/
|
|
71
|
+
uninstall(filePath: string, writeMode: WriteMode): Promise<RuleFileUninstallResult>;
|
|
72
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { BRV_RULE_MARKERS, BRV_RULE_TAG } from './constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages rule file operations including installation, uninstallation,
|
|
5
|
+
* and marker section manipulation.
|
|
6
|
+
*
|
|
7
|
+
* This class centralizes the logic for working with rule files that use
|
|
8
|
+
* BRV markers to delimit managed content sections.
|
|
9
|
+
*/
|
|
10
|
+
export class RuleFileManager {
|
|
11
|
+
fileService;
|
|
12
|
+
projectRoot;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.fileService = options.fileService;
|
|
15
|
+
this.projectRoot = options.projectRoot;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Install rule content into a file.
|
|
19
|
+
*
|
|
20
|
+
* @param filePath - Relative path to the rule file
|
|
21
|
+
* @param writeMode - How to write the content ('overwrite' or 'append')
|
|
22
|
+
* @param ruleContent - The rule content to write (should include markers)
|
|
23
|
+
*/
|
|
24
|
+
async install(filePath, writeMode, ruleContent) {
|
|
25
|
+
const fullPath = path.join(this.projectRoot, filePath);
|
|
26
|
+
const exists = await this.fileService.exists(fullPath);
|
|
27
|
+
if (exists) {
|
|
28
|
+
const content = await this.fileService.read(fullPath);
|
|
29
|
+
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
30
|
+
if (writeMode === 'overwrite') {
|
|
31
|
+
await this.fileService.write(ruleContent, fullPath, 'overwrite');
|
|
32
|
+
}
|
|
33
|
+
else if (hasMarkers) {
|
|
34
|
+
// Replace existing markers section
|
|
35
|
+
const newContent = this.replaceMarkerSection(content, ruleContent);
|
|
36
|
+
await this.fileService.write(newContent, fullPath, 'overwrite');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Append to file
|
|
40
|
+
await this.fileService.write(ruleContent, fullPath, 'append');
|
|
41
|
+
}
|
|
42
|
+
return { isNew: !hasMarkers, success: true };
|
|
43
|
+
}
|
|
44
|
+
// File doesn't exist - create it
|
|
45
|
+
await this.fileService.write(ruleContent, fullPath, 'overwrite');
|
|
46
|
+
return { isNew: true, success: true };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Removes the section between BRV markers (inclusive).
|
|
50
|
+
*/
|
|
51
|
+
removeMarkerSection(content) {
|
|
52
|
+
const startIndex = content.indexOf(BRV_RULE_MARKERS.START);
|
|
53
|
+
const endIndex = content.indexOf(BRV_RULE_MARKERS.END);
|
|
54
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
55
|
+
return content;
|
|
56
|
+
}
|
|
57
|
+
const before = content.slice(0, startIndex);
|
|
58
|
+
const after = content.slice(endIndex + BRV_RULE_MARKERS.END.length);
|
|
59
|
+
// Clean up extra newlines
|
|
60
|
+
return (before + after).replaceAll(/\n{3,}/g, '\n\n').trim();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Replaces the section between BRV markers with new content.
|
|
64
|
+
*/
|
|
65
|
+
replaceMarkerSection(content, newRuleContent) {
|
|
66
|
+
const startIndex = content.indexOf(BRV_RULE_MARKERS.START);
|
|
67
|
+
const endIndex = content.indexOf(BRV_RULE_MARKERS.END);
|
|
68
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
69
|
+
return content;
|
|
70
|
+
}
|
|
71
|
+
const before = content.slice(0, startIndex);
|
|
72
|
+
const after = content.slice(endIndex + BRV_RULE_MARKERS.END.length);
|
|
73
|
+
return before + newRuleContent + after;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check the status of rule content in a file.
|
|
77
|
+
*/
|
|
78
|
+
async status(filePath) {
|
|
79
|
+
const fullPath = path.join(this.projectRoot, filePath);
|
|
80
|
+
const fileExists = await this.fileService.exists(fullPath);
|
|
81
|
+
if (!fileExists) {
|
|
82
|
+
return { fileExists: false, hasLegacyTag: false, hasMarkers: false };
|
|
83
|
+
}
|
|
84
|
+
const content = await this.fileService.read(fullPath);
|
|
85
|
+
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
86
|
+
const hasLegacyTag = content.includes(BRV_RULE_TAG);
|
|
87
|
+
return { fileExists: true, hasLegacyTag, hasMarkers };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Uninstall rule content from a file.
|
|
91
|
+
*
|
|
92
|
+
* @param filePath - Relative path to the rule file
|
|
93
|
+
* @param writeMode - How the content was written ('overwrite' or 'append')
|
|
94
|
+
*/
|
|
95
|
+
async uninstall(filePath, writeMode) {
|
|
96
|
+
const fullPath = path.join(this.projectRoot, filePath);
|
|
97
|
+
const exists = await this.fileService.exists(fullPath);
|
|
98
|
+
if (!exists) {
|
|
99
|
+
return { success: true, wasInstalled: false };
|
|
100
|
+
}
|
|
101
|
+
const content = await this.fileService.read(fullPath);
|
|
102
|
+
const hasMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
103
|
+
if (!hasMarkers) {
|
|
104
|
+
return { success: true, wasInstalled: false };
|
|
105
|
+
}
|
|
106
|
+
if (writeMode === 'overwrite') {
|
|
107
|
+
// For dedicated files, delete the entire file
|
|
108
|
+
await this.fileService.delete(fullPath);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// For shared files, remove only the BRV section
|
|
112
|
+
const newContent = this.removeMarkerSection(content);
|
|
113
|
+
await (newContent.trim() === ''
|
|
114
|
+
? this.fileService.delete(fullPath)
|
|
115
|
+
: this.fileService.write(newContent, fullPath, 'overwrite'));
|
|
116
|
+
}
|
|
117
|
+
return { success: true, wasInstalled: true };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Agent } from '../../../core/domain/entities/agent.js';
|
|
2
|
+
import type { ConnectorType } from '../../../core/domain/entities/connector-type.js';
|
|
2
3
|
import type { IRuleTemplateService } from '../../../core/interfaces/i-rule-template-service.js';
|
|
3
4
|
import type { ITemplateLoader } from '../../../core/interfaces/i-template-loader.js';
|
|
4
5
|
/**
|
|
@@ -14,5 +15,13 @@ export declare class RuleTemplateService implements IRuleTemplateService {
|
|
|
14
15
|
* @returns Promise resolving to the rule content as a string.
|
|
15
16
|
* @throws Error if templates cannot be loaded or assembled.
|
|
16
17
|
*/
|
|
17
|
-
generateRuleContent(agent: Agent): Promise<string>;
|
|
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;
|
|
18
27
|
}
|
|
@@ -60,29 +60,66 @@ export class RuleTemplateService {
|
|
|
60
60
|
* @returns Promise resolving to the rule content as a string.
|
|
61
61
|
* @throws Error if templates cannot be loaded or assembled.
|
|
62
62
|
*/
|
|
63
|
-
async generateRuleContent(agent) {
|
|
63
|
+
async generateRuleContent(agent, type) {
|
|
64
64
|
try {
|
|
65
65
|
// Load section templates
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
80
|
// Add agent-specific header if available
|
|
81
81
|
const header = guideHeaders.find((h) => h.agent === agent)?.value || '';
|
|
82
82
|
return wrapContentWithBoundaryMarkers(content, agent, header);
|
|
83
83
|
}
|
|
84
84
|
catch (error) {
|
|
85
|
-
throw new Error(`Failed to generate rule content for agent '${agent}': ${error instanceof Error ? error.message : String(error)}`);
|
|
85
|
+
throw new Error(`Failed to generate rule content for agent '${agent}' - type '${type}': ${error instanceof Error ? error.message : String(error)}`);
|
|
86
86
|
}
|
|
87
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
|
+
}
|
|
88
125
|
}
|
|
@@ -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
|
+
}
|