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.
- package/README.md +19 -13
- package/dist/commands/hook-prompt-submit.d.ts +27 -0
- package/dist/commands/hook-prompt-submit.js +39 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +61 -0
- package/dist/commands/status.js +8 -3
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
- package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
- package/dist/core/domain/cipher/tools/constants.js +1 -0
- package/dist/core/domain/entities/agent.d.ts +16 -0
- package/dist/core/domain/entities/agent.js +78 -0
- package/dist/core/domain/entities/connector-type.d.ts +10 -0
- package/dist/core/domain/entities/connector-type.js +9 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +2 -0
- package/dist/core/domain/errors/task-error.d.ts +4 -0
- package/dist/core/domain/errors/task-error.js +7 -0
- package/dist/core/domain/transport/schemas.d.ts +40 -0
- package/dist/core/domain/transport/schemas.js +28 -0
- package/dist/core/interfaces/connectors/connector-types.d.ts +70 -0
- package/dist/core/interfaces/connectors/i-connector-manager.d.ts +72 -0
- package/dist/core/interfaces/connectors/i-connector-manager.js +1 -0
- package/dist/core/interfaces/connectors/i-connector.d.ts +54 -0
- package/dist/core/interfaces/connectors/i-connector.js +1 -0
- package/dist/core/interfaces/i-file-service.d.ts +7 -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/core/interfaces/usecase/i-connectors-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-connectors-use-case.js +1 -0
- package/dist/hooks/init/update-notifier.d.ts +1 -0
- package/dist/hooks/init/update-notifier.js +10 -1
- 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/file-system/binary-utils.d.ts +7 -12
- package/dist/infra/cipher/file-system/binary-utils.js +46 -31
- package/dist/infra/cipher/llm/context/context-manager.d.ts +10 -2
- package/dist/infra/cipher/llm/context/context-manager.js +39 -2
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +48 -9
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +40 -12
- 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/system-prompt/contributors/context-tree-structure-contributor.d.ts +6 -7
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +57 -18
- package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
- package/dist/infra/cipher/tools/implementations/curate-tool.js +380 -24
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +38 -17
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.d.ts +7 -0
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.js +303 -0
- package/dist/infra/cipher/tools/index.d.ts +1 -0
- package/dist/infra/cipher/tools/index.js +1 -0
- package/dist/infra/cipher/tools/tool-manager.js +1 -0
- package/dist/infra/cipher/tools/tool-registry.js +7 -0
- package/dist/infra/connectors/connector-manager.d.ts +32 -0
- package/dist/infra/connectors/connector-manager.js +158 -0
- package/dist/infra/connectors/hook/hook-connector-config.d.ts +52 -0
- package/dist/infra/connectors/hook/hook-connector-config.js +41 -0
- package/dist/infra/connectors/hook/hook-connector.d.ts +46 -0
- package/dist/infra/connectors/hook/hook-connector.js +231 -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/{rule → connectors/rules}/legacy-rule-detector.d.ts +2 -2
- package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.js +1 -1
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +95 -0
- package/dist/infra/{rule/agent-rule-config.js → connectors/rules/rules-connector-config.js} +10 -10
- package/dist/infra/connectors/rules/rules-connector.d.ts +34 -0
- package/dist/infra/connectors/rules/rules-connector.js +139 -0
- 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 +27 -0
- package/dist/infra/connectors/shared/template-service.js +125 -0
- package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +5 -2
- package/dist/infra/context-tree/file-context-tree-writer-service.js +20 -5
- package/dist/infra/core/executors/curate-executor.d.ts +2 -2
- package/dist/infra/core/executors/curate-executor.js +7 -7
- package/dist/infra/core/executors/query-executor.d.ts +12 -0
- package/dist/infra/core/executors/query-executor.js +62 -1
- package/dist/infra/file/fs-file-service.d.ts +7 -0
- package/dist/infra/file/fs-file-service.js +15 -1
- 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.d.ts +2 -2
- package/dist/infra/process/agent-worker.js +663 -142
- package/dist/infra/process/constants.d.ts +1 -1
- package/dist/infra/process/constants.js +1 -1
- package/dist/infra/process/ipc-types.d.ts +17 -4
- package/dist/infra/process/ipc-types.js +3 -3
- package/dist/infra/process/parent-heartbeat.d.ts +47 -0
- package/dist/infra/process/parent-heartbeat.js +118 -0
- package/dist/infra/process/process-manager.d.ts +79 -0
- package/dist/infra/process/process-manager.js +277 -3
- package/dist/infra/process/task-queue-manager.d.ts +13 -0
- package/dist/infra/process/task-queue-manager.js +19 -0
- package/dist/infra/process/transport-handlers.d.ts +3 -0
- package/dist/infra/process/transport-handlers.js +51 -5
- package/dist/infra/process/transport-worker.js +9 -69
- package/dist/infra/repl/commands/connectors-command.d.ts +8 -0
- package/dist/infra/repl/commands/{gen-rules-command.js → connectors-command.js} +21 -10
- package/dist/infra/repl/commands/curate-command.js +2 -2
- package/dist/infra/repl/commands/index.js +3 -2
- package/dist/infra/repl/commands/init-command.js +11 -7
- package/dist/infra/repl/commands/query-command.js +22 -2
- package/dist/infra/repl/commands/reset-command.js +1 -1
- package/dist/infra/transport/socket-io-transport-client.d.ts +75 -0
- package/dist/infra/transport/socket-io-transport-client.js +308 -7
- package/dist/infra/transport/socket-io-transport-server.js +4 -0
- package/dist/infra/usecase/connectors-use-case.d.ts +63 -0
- package/dist/infra/usecase/connectors-use-case.js +222 -0
- package/dist/infra/usecase/init-use-case.d.ts +8 -43
- package/dist/infra/usecase/init-use-case.js +27 -252
- package/dist/infra/usecase/logout-use-case.js +1 -1
- package/dist/infra/usecase/pull-use-case.js +5 -5
- package/dist/infra/usecase/push-use-case.js +4 -4
- package/dist/infra/usecase/reset-use-case.js +3 -4
- package/dist/infra/usecase/space-list-use-case.js +3 -3
- package/dist/infra/usecase/space-switch-use-case.js +3 -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 +114 -4
- package/dist/resources/prompts/explore.yml +34 -0
- package/dist/resources/prompts/query-orchestrator.yml +112 -0
- package/dist/resources/prompts/system-prompt.yml +12 -2
- package/dist/resources/tools/search_knowledge.txt +32 -0
- package/dist/templates/mcp-base.md +1 -0
- package/dist/templates/sections/brv-instructions.md +98 -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 +14 -11
- package/dist/tui/components/onboarding/welcome-box.js +1 -1
- package/dist/tui/components/suggestions.js +3 -3
- package/dist/tui/contexts/mode-context.js +6 -2
- package/dist/tui/contexts/onboarding-context.d.ts +4 -0
- package/dist/tui/contexts/onboarding-context.js +14 -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 +87 -98
- package/dist/tui/views/logs-view.d.ts +8 -0
- package/dist/tui/views/logs-view.js +55 -27
- package/dist/utils/file-validator.d.ts +1 -1
- package/dist/utils/file-validator.js +25 -28
- package/dist/utils/type-guards.d.ts +5 -0
- package/dist/utils/type-guards.js +7 -0
- package/oclif.manifest.json +55 -4
- package/package.json +12 -1
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +0 -3
- package/dist/infra/repl/commands/gen-rules-command.d.ts +0 -7
- package/dist/infra/rule/agent-rule-config.d.ts +0 -19
- package/dist/infra/rule/rule-template-service.d.ts +0 -18
- package/dist/infra/rule/rule-template-service.js +0 -88
- package/dist/infra/usecase/generate-rules-use-case.d.ts +0 -61
- package/dist/infra/usecase/generate-rules-use-case.js +0 -285
- /package/dist/core/interfaces/{usecase/i-generate-rules-use-case.js → connectors/connector-types.js} +0 -0
- /package/dist/infra/{rule → connectors/shared}/constants.d.ts +0 -0
- /package/dist/infra/{rule → connectors/shared}/constants.js +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boundary markers for managed ByteRover MCP sections in TOML files.
|
|
3
|
+
*/
|
|
4
|
+
export const BRV_MCP_TOML_MARKERS = {
|
|
5
|
+
END: '# END BYTEROVER MCP',
|
|
6
|
+
START: '# BEGIN BYTEROVER MCP',
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Convert a JavaScript object to TOML format for MCP server config.
|
|
10
|
+
* Only handles simple key-value pairs and arrays (no nested objects).
|
|
11
|
+
*/
|
|
12
|
+
function toTomlSection(sectionName, config) {
|
|
13
|
+
const lines = [`[${sectionName}]`];
|
|
14
|
+
for (const [key, value] of Object.entries(config)) {
|
|
15
|
+
if (value === undefined || value === null) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'string') {
|
|
19
|
+
lines.push(`${key} = "${value}"`);
|
|
20
|
+
}
|
|
21
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
22
|
+
lines.push(`${key} = ${value}`);
|
|
23
|
+
}
|
|
24
|
+
else if (Array.isArray(value)) {
|
|
25
|
+
const arrayStr = value.map((v) => (typeof v === 'string' ? `"${v}"` : String(v))).join(', ');
|
|
26
|
+
lines.push(`${key} = [${arrayStr}]`);
|
|
27
|
+
}
|
|
28
|
+
// Skip nested objects for simplicity
|
|
29
|
+
}
|
|
30
|
+
return lines.join('\n');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* MCP config writer for TOML format files.
|
|
34
|
+
* Uses marker-based insertion/replacement similar to rules connector.
|
|
35
|
+
*/
|
|
36
|
+
export class TomlMcpConfigWriter {
|
|
37
|
+
fileService;
|
|
38
|
+
serverName;
|
|
39
|
+
constructor(options) {
|
|
40
|
+
this.fileService = options.fileService;
|
|
41
|
+
this.serverName = options.serverName;
|
|
42
|
+
}
|
|
43
|
+
async exists(filePath) {
|
|
44
|
+
const fileExists = await this.fileService.exists(filePath);
|
|
45
|
+
if (!fileExists) {
|
|
46
|
+
return { fileExists: false, serverExists: false };
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const content = await this.fileService.read(filePath);
|
|
50
|
+
const hasMarkers = content.includes(BRV_MCP_TOML_MARKERS.START) && content.includes(BRV_MCP_TOML_MARKERS.END);
|
|
51
|
+
return {
|
|
52
|
+
fileExists: true,
|
|
53
|
+
serverExists: hasMarkers,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { fileExists: true, serverExists: false };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async remove(filePath) {
|
|
61
|
+
const fileExists = await this.fileService.exists(filePath);
|
|
62
|
+
if (!fileExists) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const content = await this.fileService.read(filePath);
|
|
66
|
+
const hasMarkers = content.includes(BRV_MCP_TOML_MARKERS.START) && content.includes(BRV_MCP_TOML_MARKERS.END);
|
|
67
|
+
if (!hasMarkers) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const newContent = this.removeMarkerSection(content);
|
|
71
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
72
|
+
if (newContent.trim() === '') {
|
|
73
|
+
await this.fileService.delete(filePath);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await this.fileService.write(newContent, filePath, 'overwrite');
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
async write(filePath, serverConfig) {
|
|
81
|
+
const fileExists = await this.fileService.exists(filePath);
|
|
82
|
+
const mcpSection = this.buildMcpSection(serverConfig);
|
|
83
|
+
if (!fileExists) {
|
|
84
|
+
await this.fileService.write(mcpSection, filePath, 'overwrite');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const content = await this.fileService.read(filePath);
|
|
88
|
+
const hasMarkers = content.includes(BRV_MCP_TOML_MARKERS.START) && content.includes(BRV_MCP_TOML_MARKERS.END);
|
|
89
|
+
if (hasMarkers) {
|
|
90
|
+
// Replace existing section
|
|
91
|
+
const newContent = this.replaceMarkerSection(content, mcpSection);
|
|
92
|
+
await this.fileService.write(newContent, filePath, 'overwrite');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Append to file
|
|
96
|
+
const newContent = content.trimEnd() + '\n\n' + mcpSection;
|
|
97
|
+
await this.fileService.write(newContent, filePath, 'overwrite');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build the MCP section with markers.
|
|
102
|
+
*/
|
|
103
|
+
buildMcpSection(serverConfig) {
|
|
104
|
+
const tomlContent = toTomlSection(`mcp_servers.${this.serverName}`, serverConfig);
|
|
105
|
+
return `${BRV_MCP_TOML_MARKERS.START}\n${tomlContent}\n${BRV_MCP_TOML_MARKERS.END}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Remove the section between markers (inclusive).
|
|
109
|
+
*/
|
|
110
|
+
removeMarkerSection(content) {
|
|
111
|
+
const startIndex = content.indexOf(BRV_MCP_TOML_MARKERS.START);
|
|
112
|
+
const endIndex = content.indexOf(BRV_MCP_TOML_MARKERS.END);
|
|
113
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
114
|
+
return content;
|
|
115
|
+
}
|
|
116
|
+
const before = content.slice(0, startIndex);
|
|
117
|
+
const after = content.slice(endIndex + BRV_MCP_TOML_MARKERS.END.length);
|
|
118
|
+
// Clean up extra newlines
|
|
119
|
+
return (before + after).replaceAll(/\n{3,}/g, '\n\n').trim();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Replace the section between markers with new content.
|
|
123
|
+
*/
|
|
124
|
+
replaceMarkerSection(content, newSection) {
|
|
125
|
+
const startIndex = content.indexOf(BRV_MCP_TOML_MARKERS.START);
|
|
126
|
+
const endIndex = content.indexOf(BRV_MCP_TOML_MARKERS.END);
|
|
127
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
128
|
+
return content;
|
|
129
|
+
}
|
|
130
|
+
const before = content.slice(0, startIndex);
|
|
131
|
+
const after = content.slice(endIndex + BRV_MCP_TOML_MARKERS.END.length);
|
|
132
|
+
return before + newSection + after;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Agent } from '
|
|
2
|
-
import type { ILegacyRuleDetector, LegacyRuleDetectionResult } from '
|
|
1
|
+
import type { Agent } from '../../../core/domain/entities/agent.js';
|
|
2
|
+
import type { ILegacyRuleDetector, LegacyRuleDetectionResult } from '../../../core/interfaces/i-legacy-rule-detector.js';
|
|
3
3
|
export declare class LegacyRuleDetector implements ILegacyRuleDetector {
|
|
4
4
|
private static readonly SECTION_SEPARATOR_PATTERN;
|
|
5
5
|
private static readonly WORKFLOW_HEADER_PATTERN;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { WriteMode } from '../../../core/interfaces/i-file-service.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for agent-specific rule files.
|
|
4
|
+
*/
|
|
5
|
+
export type RulesConnectorConfig = {
|
|
6
|
+
/**
|
|
7
|
+
* The file path where the agent's rules should be written.
|
|
8
|
+
*/
|
|
9
|
+
filePath: string;
|
|
10
|
+
/**
|
|
11
|
+
* The write mode to use when writing the rule file.
|
|
12
|
+
*/
|
|
13
|
+
writeMode: WriteMode;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Mapping of agents to their rule file configurations.
|
|
17
|
+
*/
|
|
18
|
+
export declare const RULES_CONNECTOR_CONFIGS: {
|
|
19
|
+
readonly Amp: {
|
|
20
|
+
readonly filePath: "AGENTS.md";
|
|
21
|
+
readonly writeMode: "append";
|
|
22
|
+
};
|
|
23
|
+
readonly 'Augment Code': {
|
|
24
|
+
readonly filePath: ".augment/rules/agent-context.md";
|
|
25
|
+
readonly writeMode: "overwrite";
|
|
26
|
+
};
|
|
27
|
+
readonly 'Claude Code': {
|
|
28
|
+
readonly filePath: "CLAUDE.md";
|
|
29
|
+
readonly writeMode: "append";
|
|
30
|
+
};
|
|
31
|
+
readonly Cline: {
|
|
32
|
+
readonly filePath: ".clinerules/agent-context.md";
|
|
33
|
+
readonly writeMode: "overwrite";
|
|
34
|
+
};
|
|
35
|
+
readonly Codex: {
|
|
36
|
+
readonly filePath: "AGENTS.md";
|
|
37
|
+
readonly writeMode: "append";
|
|
38
|
+
};
|
|
39
|
+
readonly Cursor: {
|
|
40
|
+
readonly filePath: ".cursor/rules/agent-context.mdc";
|
|
41
|
+
readonly writeMode: "overwrite";
|
|
42
|
+
};
|
|
43
|
+
readonly 'Gemini CLI': {
|
|
44
|
+
readonly filePath: "GEMINI.md";
|
|
45
|
+
readonly writeMode: "append";
|
|
46
|
+
};
|
|
47
|
+
readonly 'Github Copilot': {
|
|
48
|
+
readonly filePath: ".github/copilot-instructions.md";
|
|
49
|
+
readonly writeMode: "append";
|
|
50
|
+
};
|
|
51
|
+
readonly Junie: {
|
|
52
|
+
readonly filePath: ".junie/guidelines.md";
|
|
53
|
+
readonly writeMode: "append";
|
|
54
|
+
};
|
|
55
|
+
readonly 'Kilo Code': {
|
|
56
|
+
readonly filePath: ".kilocode/rules/agent-context.md";
|
|
57
|
+
readonly writeMode: "overwrite";
|
|
58
|
+
};
|
|
59
|
+
readonly Kiro: {
|
|
60
|
+
readonly filePath: ".kiro/steering/agent-context.md";
|
|
61
|
+
readonly writeMode: "overwrite";
|
|
62
|
+
};
|
|
63
|
+
readonly Qoder: {
|
|
64
|
+
readonly filePath: ".qoder/rules/agent-context.md";
|
|
65
|
+
readonly writeMode: "overwrite";
|
|
66
|
+
};
|
|
67
|
+
readonly 'Qwen Code': {
|
|
68
|
+
readonly filePath: "QWEN.md";
|
|
69
|
+
readonly writeMode: "append";
|
|
70
|
+
};
|
|
71
|
+
readonly 'Roo Code': {
|
|
72
|
+
readonly filePath: ".roo/rules/agent-context.md";
|
|
73
|
+
readonly writeMode: "overwrite";
|
|
74
|
+
};
|
|
75
|
+
readonly 'Trae.ai': {
|
|
76
|
+
readonly filePath: "project_rules.md";
|
|
77
|
+
readonly writeMode: "append";
|
|
78
|
+
};
|
|
79
|
+
readonly Warp: {
|
|
80
|
+
readonly filePath: "WARP.md";
|
|
81
|
+
readonly writeMode: "append";
|
|
82
|
+
};
|
|
83
|
+
readonly Windsurf: {
|
|
84
|
+
readonly filePath: ".windsurf/rules/agent-context.md";
|
|
85
|
+
readonly writeMode: "overwrite";
|
|
86
|
+
};
|
|
87
|
+
readonly Zed: {
|
|
88
|
+
readonly filePath: "agent-context.rules";
|
|
89
|
+
readonly writeMode: "overwrite";
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Type for agents that have rules connector configurations.
|
|
94
|
+
*/
|
|
95
|
+
export type RulesSupportedAgent = keyof typeof RULES_CONNECTOR_CONFIGS;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mapping of agents to their rule file configurations.
|
|
3
3
|
*/
|
|
4
|
-
export const
|
|
4
|
+
export const RULES_CONNECTOR_CONFIGS = {
|
|
5
5
|
Amp: {
|
|
6
6
|
filePath: 'AGENTS.md',
|
|
7
7
|
writeMode: 'append',
|
|
8
8
|
},
|
|
9
9
|
'Augment Code': {
|
|
10
|
-
filePath: '.augment/rules/agent-context
|
|
10
|
+
filePath: '.augment/rules/agent-context.md',
|
|
11
11
|
writeMode: 'overwrite',
|
|
12
12
|
},
|
|
13
13
|
'Claude Code': {
|
|
@@ -15,7 +15,7 @@ export const AGENT_RULE_CONFIGS = {
|
|
|
15
15
|
writeMode: 'append',
|
|
16
16
|
},
|
|
17
17
|
Cline: {
|
|
18
|
-
filePath: '.clinerules/agent-context
|
|
18
|
+
filePath: '.clinerules/agent-context.md',
|
|
19
19
|
writeMode: 'overwrite',
|
|
20
20
|
},
|
|
21
21
|
Codex: {
|
|
@@ -23,7 +23,7 @@ export const AGENT_RULE_CONFIGS = {
|
|
|
23
23
|
writeMode: 'append',
|
|
24
24
|
},
|
|
25
25
|
Cursor: {
|
|
26
|
-
filePath: '.cursor/rules/agent-context
|
|
26
|
+
filePath: '.cursor/rules/agent-context.mdc',
|
|
27
27
|
writeMode: 'overwrite',
|
|
28
28
|
},
|
|
29
29
|
'Gemini CLI': {
|
|
@@ -39,15 +39,15 @@ export const AGENT_RULE_CONFIGS = {
|
|
|
39
39
|
writeMode: 'append',
|
|
40
40
|
},
|
|
41
41
|
'Kilo Code': {
|
|
42
|
-
filePath: '.kilocode/rules/agent-context
|
|
42
|
+
filePath: '.kilocode/rules/agent-context.md',
|
|
43
43
|
writeMode: 'overwrite',
|
|
44
44
|
},
|
|
45
45
|
Kiro: {
|
|
46
|
-
filePath: '.kiro/steering/agent-context
|
|
46
|
+
filePath: '.kiro/steering/agent-context.md',
|
|
47
47
|
writeMode: 'overwrite',
|
|
48
48
|
},
|
|
49
49
|
Qoder: {
|
|
50
|
-
filePath: '.qoder/rules/agent-context
|
|
50
|
+
filePath: '.qoder/rules/agent-context.md',
|
|
51
51
|
writeMode: 'overwrite',
|
|
52
52
|
},
|
|
53
53
|
'Qwen Code': {
|
|
@@ -55,7 +55,7 @@ export const AGENT_RULE_CONFIGS = {
|
|
|
55
55
|
writeMode: 'append',
|
|
56
56
|
},
|
|
57
57
|
'Roo Code': {
|
|
58
|
-
filePath: '.roo/rules/agent-context
|
|
58
|
+
filePath: '.roo/rules/agent-context.md',
|
|
59
59
|
writeMode: 'overwrite',
|
|
60
60
|
},
|
|
61
61
|
'Trae.ai': {
|
|
@@ -67,11 +67,11 @@ export const AGENT_RULE_CONFIGS = {
|
|
|
67
67
|
writeMode: 'append',
|
|
68
68
|
},
|
|
69
69
|
Windsurf: {
|
|
70
|
-
filePath: '.windsurf/rules/agent-context
|
|
70
|
+
filePath: '.windsurf/rules/agent-context.md',
|
|
71
71
|
writeMode: 'overwrite',
|
|
72
72
|
},
|
|
73
73
|
Zed: {
|
|
74
|
-
filePath: 'agent-context
|
|
74
|
+
filePath: 'agent-context.rules',
|
|
75
75
|
writeMode: 'overwrite',
|
|
76
76
|
},
|
|
77
77
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Agent } from '../../../core/domain/entities/agent.js';
|
|
2
|
+
import type { ConnectorType } from '../../../core/domain/entities/connector-type.js';
|
|
3
|
+
import type { ConnectorInstallResult, ConnectorStatus, ConnectorUninstallResult } from '../../../core/interfaces/connectors/connector-types.js';
|
|
4
|
+
import type { IConnector } from '../../../core/interfaces/connectors/i-connector.js';
|
|
5
|
+
import type { IFileService } from '../../../core/interfaces/i-file-service.js';
|
|
6
|
+
import type { IRuleTemplateService } from '../../../core/interfaces/i-rule-template-service.js';
|
|
7
|
+
/**
|
|
8
|
+
* Options for constructing RulesConnector.
|
|
9
|
+
*/
|
|
10
|
+
type RulesConnectorOptions = {
|
|
11
|
+
fileService: IFileService;
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
templateService: IRuleTemplateService;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Connector that integrates BRV with coding agents via rule files.
|
|
17
|
+
* Manages the installation, uninstallation, and status of rule files.
|
|
18
|
+
*/
|
|
19
|
+
export declare class RulesConnector implements IConnector {
|
|
20
|
+
readonly type: ConnectorType;
|
|
21
|
+
private readonly fileService;
|
|
22
|
+
private readonly projectRoot;
|
|
23
|
+
private readonly ruleFileManager;
|
|
24
|
+
private readonly supportedAgents;
|
|
25
|
+
private readonly templateService;
|
|
26
|
+
constructor(options: RulesConnectorOptions);
|
|
27
|
+
getConfigPath(agent: Agent): string;
|
|
28
|
+
getSupportedAgents(): Agent[];
|
|
29
|
+
install(agent: Agent): Promise<ConnectorInstallResult>;
|
|
30
|
+
isSupported(agent: Agent): boolean;
|
|
31
|
+
status(agent: Agent): Promise<ConnectorStatus>;
|
|
32
|
+
uninstall(agent: Agent): Promise<ConnectorUninstallResult>;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { AGENT_CONNECTOR_CONFIG } from '../../../core/domain/entities/agent.js';
|
|
3
|
+
import { RuleFileManager } from '../shared/rule-file-manager.js';
|
|
4
|
+
import { RULES_CONNECTOR_CONFIGS } from './rules-connector-config.js';
|
|
5
|
+
/**
|
|
6
|
+
* Connector that integrates BRV with coding agents via rule files.
|
|
7
|
+
* Manages the installation, uninstallation, and status of rule files.
|
|
8
|
+
*/
|
|
9
|
+
export class RulesConnector {
|
|
10
|
+
type = 'rules';
|
|
11
|
+
fileService;
|
|
12
|
+
projectRoot;
|
|
13
|
+
ruleFileManager;
|
|
14
|
+
supportedAgents;
|
|
15
|
+
templateService;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.fileService = options.fileService;
|
|
18
|
+
this.projectRoot = options.projectRoot;
|
|
19
|
+
this.templateService = options.templateService;
|
|
20
|
+
this.ruleFileManager = new RuleFileManager({
|
|
21
|
+
fileService: options.fileService,
|
|
22
|
+
projectRoot: options.projectRoot,
|
|
23
|
+
});
|
|
24
|
+
this.supportedAgents = Object.entries(AGENT_CONNECTOR_CONFIG)
|
|
25
|
+
.filter(([_, config]) => config.supported.includes(this.type))
|
|
26
|
+
.map(([agent]) => agent);
|
|
27
|
+
}
|
|
28
|
+
getConfigPath(agent) {
|
|
29
|
+
return RULES_CONNECTOR_CONFIGS[agent].filePath;
|
|
30
|
+
}
|
|
31
|
+
getSupportedAgents() {
|
|
32
|
+
return this.supportedAgents;
|
|
33
|
+
}
|
|
34
|
+
async install(agent) {
|
|
35
|
+
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
36
|
+
try {
|
|
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);
|
|
40
|
+
return {
|
|
41
|
+
alreadyInstalled: false,
|
|
42
|
+
configPath: config.filePath,
|
|
43
|
+
message: `Rules connector installed for ${agent} (created ${config.filePath})`,
|
|
44
|
+
success: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
alreadyInstalled: false,
|
|
50
|
+
configPath: config.filePath,
|
|
51
|
+
message: `Failed to install rules connector for ${agent}: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
+
success: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
isSupported(agent) {
|
|
57
|
+
return AGENT_CONNECTOR_CONFIG[agent].supported.includes(this.type);
|
|
58
|
+
}
|
|
59
|
+
async status(agent) {
|
|
60
|
+
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
61
|
+
const fullPath = path.join(this.projectRoot, config.filePath);
|
|
62
|
+
try {
|
|
63
|
+
const { fileExists, hasMarkers } = await this.ruleFileManager.status(config.filePath);
|
|
64
|
+
if (!fileExists) {
|
|
65
|
+
return {
|
|
66
|
+
configExists: false,
|
|
67
|
+
configPath: config.filePath,
|
|
68
|
+
installed: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const content = await this.fileService.read(fullPath);
|
|
72
|
+
const hasMcpTools = content.includes('brv-query') || content.includes('brv-curate');
|
|
73
|
+
// For overwrite files, any BRV content means installed
|
|
74
|
+
// For append files, need both markers and agent tag
|
|
75
|
+
const installed = hasMarkers && !hasMcpTools;
|
|
76
|
+
return {
|
|
77
|
+
configExists: true,
|
|
78
|
+
configPath: config.filePath,
|
|
79
|
+
installed,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
configExists: true,
|
|
85
|
+
configPath: config.filePath,
|
|
86
|
+
error: error instanceof Error ? error.message : String(error),
|
|
87
|
+
installed: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async uninstall(agent) {
|
|
92
|
+
const config = RULES_CONNECTOR_CONFIGS[agent];
|
|
93
|
+
try {
|
|
94
|
+
const { fileExists, hasLegacyTag, hasMarkers } = await this.ruleFileManager.status(config.filePath);
|
|
95
|
+
if (!fileExists) {
|
|
96
|
+
return {
|
|
97
|
+
configPath: config.filePath,
|
|
98
|
+
message: `Rule file does not exist: ${config.filePath}`,
|
|
99
|
+
success: true,
|
|
100
|
+
wasInstalled: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (!hasMarkers) {
|
|
104
|
+
if (!hasLegacyTag) {
|
|
105
|
+
return {
|
|
106
|
+
configPath: config.filePath,
|
|
107
|
+
message: `Rules connector is not installed for ${agent}`,
|
|
108
|
+
success: true,
|
|
109
|
+
wasInstalled: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Legacy format detected - cannot safely uninstall
|
|
113
|
+
return {
|
|
114
|
+
configPath: config.filePath,
|
|
115
|
+
message: `Legacy rules detected for ${agent}. Please manually remove the old rules section.`,
|
|
116
|
+
success: false,
|
|
117
|
+
wasInstalled: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const result = await this.ruleFileManager.uninstall(config.filePath, config.writeMode);
|
|
121
|
+
return {
|
|
122
|
+
configPath: config.filePath,
|
|
123
|
+
message: result.wasInstalled
|
|
124
|
+
? `Rules connector uninstalled for ${agent}`
|
|
125
|
+
: `Rules connector is not installed for ${agent}`,
|
|
126
|
+
success: true,
|
|
127
|
+
wasInstalled: result.wasInstalled,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
configPath: config.filePath,
|
|
133
|
+
message: `Failed to uninstall rules connector for ${agent}: ${error instanceof Error ? error.message : String(error)}`,
|
|
134
|
+
success: false,
|
|
135
|
+
wasInstalled: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
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
|
+
}
|