byterover-cli 0.3.2 → 0.3.4
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/dist/commands/cipher-agent/run.d.ts +3 -2
- package/dist/commands/gen-rules.d.ts +42 -4
- package/dist/commands/gen-rules.js +235 -39
- package/dist/commands/init.d.ts +43 -3
- package/dist/commands/init.js +262 -24
- package/dist/config/environment.js +1 -1
- package/dist/core/domain/entities/brv-config.js +15 -6
- package/dist/core/domain/knowledge/directory-manager.js +2 -0
- package/dist/core/interfaces/cipher/i-chat-session.d.ts +2 -1
- package/dist/core/interfaces/i-file-service.d.ts +16 -0
- package/dist/core/interfaces/i-legacy-rule-detector.d.ts +56 -0
- package/dist/hooks/init/update-notifier.d.ts +39 -0
- package/dist/hooks/init/update-notifier.js +47 -0
- package/dist/infra/cipher/blob/sqlite-blob-storage.js +1 -1
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +6 -0
- package/dist/infra/cogit/context-tree-to-push-context-mapper.js +4 -4
- package/dist/infra/cogit/http-cogit-push-service.js +3 -3
- package/dist/infra/context-tree/file-context-tree-service.js +1 -2
- package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +2 -0
- package/dist/infra/context-tree/file-context-tree-writer-service.js +2 -0
- package/dist/infra/file/fs-file-service.d.ts +2 -0
- package/dist/infra/file/fs-file-service.js +23 -1
- package/dist/infra/rule/constants.d.ts +9 -1
- package/dist/infra/rule/constants.js +9 -1
- package/dist/infra/rule/legacy-rule-detector.d.ts +21 -0
- package/dist/infra/rule/legacy-rule-detector.js +106 -0
- package/dist/infra/rule/rule-template-service.js +14 -6
- package/oclif.manifest.json +10 -97
- package/package.json +8 -3
- package/dist/core/domain/errors/rule-error.d.ts +0 -6
- package/dist/core/domain/errors/rule-error.js +0 -12
- package/dist/core/interfaces/i-rule-writer-service.d.ts +0 -13
- package/dist/infra/rule/rule-writer-service.d.ts +0 -19
- package/dist/infra/rule/rule-writer-service.js +0 -39
- /package/dist/core/interfaces/{i-rule-writer-service.js → i-legacy-rule-detector.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { IProjectConfigStore } from '../../core/interfaces/i-project-config-store.js';
|
|
3
|
+
import { CipherAgent } from '../../infra/cipher/cipher-agent.js';
|
|
3
4
|
export default class CipherAgentRun extends Command {
|
|
4
5
|
static args: {
|
|
5
6
|
prompt: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
@@ -41,7 +42,7 @@ export default class CipherAgentRun extends Command {
|
|
|
41
42
|
* @param agent - CipherAgent instance
|
|
42
43
|
* @returns Most recent session ID or undefined if no sessions found
|
|
43
44
|
*/
|
|
44
|
-
protected getMostRecentSessionId(agent:
|
|
45
|
+
protected getMostRecentSessionId(agent: CipherAgent): Promise<string | undefined>;
|
|
45
46
|
run(): Promise<void>;
|
|
46
47
|
/**
|
|
47
48
|
* Validate that a session exists in storage.
|
|
@@ -50,7 +51,7 @@ export default class CipherAgentRun extends Command {
|
|
|
50
51
|
* @param sessionId - Session ID to validate
|
|
51
52
|
* @returns True if session exists
|
|
52
53
|
*/
|
|
53
|
-
protected validateSessionExists(agent:
|
|
54
|
+
protected validateSessionExists(agent: CipherAgent, sessionId: string): Promise<boolean>;
|
|
54
55
|
/**
|
|
55
56
|
* Create LLM configuration from flags and environment
|
|
56
57
|
*
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import type { Agent } from '../core/domain/entities/agent.js';
|
|
3
|
+
import type { IFileService } from '../core/interfaces/i-file-service.js';
|
|
4
|
+
import type { IRuleTemplateService } from '../core/interfaces/i-rule-template-service.js';
|
|
5
|
+
import type { ITrackingService } from '../core/interfaces/i-tracking-service.js';
|
|
6
|
+
import { LegacyRuleDetector } from '../infra/rule/legacy-rule-detector.js';
|
|
7
|
+
type CleanupStrategy = 'automatic' | 'manual';
|
|
5
8
|
export default class GenRules extends Command {
|
|
6
9
|
static description: string;
|
|
7
10
|
static examples: string[];
|
|
@@ -9,7 +12,9 @@ export default class GenRules extends Command {
|
|
|
9
12
|
agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
13
|
};
|
|
11
14
|
protected createServices(): {
|
|
12
|
-
|
|
15
|
+
fileService: IFileService;
|
|
16
|
+
legacyRuleDetector: LegacyRuleDetector;
|
|
17
|
+
templateService: IRuleTemplateService;
|
|
13
18
|
trackingService: ITrackingService;
|
|
14
19
|
};
|
|
15
20
|
/**
|
|
@@ -18,6 +23,20 @@ export default class GenRules extends Command {
|
|
|
18
23
|
* @returns The selected agent
|
|
19
24
|
*/
|
|
20
25
|
protected promptForAgentSelection(): Promise<Agent>;
|
|
26
|
+
/**
|
|
27
|
+
* Prompts the user to choose cleanup strategy for legacy rules.
|
|
28
|
+
* This method is protected to allow test overrides.
|
|
29
|
+
* @returns The chosen cleanup strategy
|
|
30
|
+
*/
|
|
31
|
+
protected promptForCleanupStrategy(): Promise<CleanupStrategy>;
|
|
32
|
+
/**
|
|
33
|
+
* Prompts the user to create a new rule file.
|
|
34
|
+
* This method is protected to allow test overrides.
|
|
35
|
+
* @param agent The agent for which the rule file doesn't exist
|
|
36
|
+
* @param filePath The path where the file would be created
|
|
37
|
+
* @returns True if the user wants to create the file, false otherwise
|
|
38
|
+
*/
|
|
39
|
+
protected promptForFileCreation(agent: Agent, filePath: string): Promise<boolean>;
|
|
21
40
|
/**
|
|
22
41
|
* Prompts the user to confirm overwriting an existing rule file.
|
|
23
42
|
* This method is protected to allow test overrides.
|
|
@@ -26,4 +45,23 @@ export default class GenRules extends Command {
|
|
|
26
45
|
*/
|
|
27
46
|
protected promptForOverwriteConfirmation(agent: Agent): Promise<boolean>;
|
|
28
47
|
run(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Appends ByteRover rules to a file that has no ByteRover content.
|
|
50
|
+
*/
|
|
51
|
+
private appendRulesToFile;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new rule file with ByteRover rules.
|
|
54
|
+
*/
|
|
55
|
+
private createNewRuleFile;
|
|
56
|
+
/**
|
|
57
|
+
* Handles legacy rules cleanup with user choice of automatic or manual.
|
|
58
|
+
*/
|
|
59
|
+
private handleLegacyRulesCleanup;
|
|
60
|
+
private performAutomaticCleanup;
|
|
61
|
+
private performManualCleanup;
|
|
62
|
+
/**
|
|
63
|
+
* Replaces existing ByteRover rules (with boundary markers) with new rules.
|
|
64
|
+
*/
|
|
65
|
+
private replaceExistingRules;
|
|
29
66
|
}
|
|
67
|
+
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { confirm, search } from '@inquirer/prompts';
|
|
1
|
+
import { confirm, search, select } from '@inquirer/prompts';
|
|
2
2
|
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import { AGENT_VALUES } from '../core/domain/entities/agent.js';
|
|
4
|
-
import { RuleExistsError } from '../core/domain/errors/rule-error.js';
|
|
5
4
|
import { FsFileService } from '../infra/file/fs-file-service.js';
|
|
5
|
+
import { AGENT_RULE_CONFIGS } from '../infra/rule/agent-rule-config.js';
|
|
6
|
+
import { BRV_RULE_MARKERS, BRV_RULE_TAG } from '../infra/rule/constants.js';
|
|
7
|
+
import { LegacyRuleDetector } from '../infra/rule/legacy-rule-detector.js';
|
|
6
8
|
import { RuleTemplateService } from '../infra/rule/rule-template-service.js';
|
|
7
|
-
import { RuleWriterService } from '../infra/rule/rule-writer-service.js';
|
|
8
9
|
import { KeychainTokenStore } from '../infra/storage/keychain-token-store.js';
|
|
9
10
|
import { FsTemplateLoader } from '../infra/template/fs-template-loader.js';
|
|
10
11
|
import { MixpanelTrackingService } from '../infra/tracking/mixpanel-tracking-service.js';
|
|
@@ -30,7 +31,9 @@ export default class GenRules extends Command {
|
|
|
30
31
|
const templateLoader = new FsTemplateLoader(fileService);
|
|
31
32
|
const templateService = new RuleTemplateService(templateLoader);
|
|
32
33
|
return {
|
|
33
|
-
|
|
34
|
+
fileService,
|
|
35
|
+
legacyRuleDetector: new LegacyRuleDetector(),
|
|
36
|
+
templateService,
|
|
34
37
|
trackingService: new MixpanelTrackingService(new KeychainTokenStore()),
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -51,6 +54,41 @@ export default class GenRules extends Command {
|
|
|
51
54
|
});
|
|
52
55
|
return answer;
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Prompts the user to choose cleanup strategy for legacy rules.
|
|
59
|
+
* This method is protected to allow test overrides.
|
|
60
|
+
* @returns The chosen cleanup strategy
|
|
61
|
+
*/
|
|
62
|
+
async promptForCleanupStrategy() {
|
|
63
|
+
return select({
|
|
64
|
+
choices: [
|
|
65
|
+
{
|
|
66
|
+
description: 'New rules will be added with boundary markers. You manually remove old sections at your convenience.',
|
|
67
|
+
name: 'Manual cleanup (recommended)',
|
|
68
|
+
value: 'manual',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
description: '⚠️ We will remove all detected old sections. May cause content loss if detection is imperfect. A backup will be created.',
|
|
72
|
+
name: 'Automatic cleanup',
|
|
73
|
+
value: 'automatic',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
message: 'How would you like to proceed?',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Prompts the user to create a new rule file.
|
|
81
|
+
* This method is protected to allow test overrides.
|
|
82
|
+
* @param agent The agent for which the rule file doesn't exist
|
|
83
|
+
* @param filePath The path where the file would be created
|
|
84
|
+
* @returns True if the user wants to create the file, false otherwise
|
|
85
|
+
*/
|
|
86
|
+
async promptForFileCreation(agent, filePath) {
|
|
87
|
+
return confirm({
|
|
88
|
+
default: true,
|
|
89
|
+
message: `Rule file '${filePath}' doesn't exist. Create it with ByteRover rules?`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
54
92
|
/**
|
|
55
93
|
* Prompts the user to confirm overwriting an existing rule file.
|
|
56
94
|
* This method is protected to allow test overrides.
|
|
@@ -64,45 +102,203 @@ export default class GenRules extends Command {
|
|
|
64
102
|
});
|
|
65
103
|
}
|
|
66
104
|
async run() {
|
|
67
|
-
const {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (error instanceof RuleExistsError) {
|
|
81
|
-
const overwrite = await this.promptForOverwriteConfirmation(answer);
|
|
82
|
-
if (overwrite) {
|
|
83
|
-
// Retry with forced=true
|
|
84
|
-
await ruleWriterService.writeRule(answer, true);
|
|
85
|
-
this.log(`✅ Successfully generated rule file for ${answer}`);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
this.log(`Skipping rule file generation for ${answer}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
// Non-recoverable error - throw to let oclif handle display
|
|
93
|
-
this.error(`Failed to generate rule file: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
-
}
|
|
105
|
+
const { fileService, legacyRuleDetector, templateService, trackingService } = this.createServices();
|
|
106
|
+
await trackingService.track('rule:generate');
|
|
107
|
+
const selectedAgent = await this.promptForAgentSelection();
|
|
108
|
+
const { filePath, writeMode } = AGENT_RULE_CONFIGS[selectedAgent];
|
|
109
|
+
this.log(`Generating rules for: ${selectedAgent}`);
|
|
110
|
+
// STEP 1: Check if file exists
|
|
111
|
+
const fileExists = await fileService.exists(filePath);
|
|
112
|
+
if (!fileExists) {
|
|
113
|
+
// Scenario A: File doesn't exist
|
|
114
|
+
const shouldCreate = await this.promptForFileCreation(selectedAgent, filePath);
|
|
115
|
+
if (!shouldCreate) {
|
|
116
|
+
this.log(`Skipped rule file creation for ${selectedAgent}`);
|
|
117
|
+
return;
|
|
95
118
|
}
|
|
119
|
+
await this.createNewRuleFile({
|
|
120
|
+
agent: selectedAgent,
|
|
121
|
+
filePath,
|
|
122
|
+
fileService,
|
|
123
|
+
templateService,
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// STEP 2: File exists - read content
|
|
128
|
+
const content = await fileService.read(filePath);
|
|
129
|
+
// STEP 3: Check for LEGACY rules (priority: clean these up first)
|
|
130
|
+
const hasFooterTag = content.includes(`${BRV_RULE_TAG} ${selectedAgent}`);
|
|
131
|
+
const hasBoundaryMarkers = content.includes(BRV_RULE_MARKERS.START) && content.includes(BRV_RULE_MARKERS.END);
|
|
132
|
+
const hasLegacyRules = hasFooterTag && !hasBoundaryMarkers;
|
|
133
|
+
if (hasLegacyRules) {
|
|
134
|
+
// Scenario B: Legacy rules detected - handle cleanup
|
|
135
|
+
await this.handleLegacyRulesCleanup({
|
|
136
|
+
agent: selectedAgent,
|
|
137
|
+
content,
|
|
138
|
+
filePath,
|
|
139
|
+
fileService,
|
|
140
|
+
legacyRuleDetector,
|
|
141
|
+
templateService,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
96
144
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
// STEP 4: Check for NEW rules (boundary markers)
|
|
146
|
+
if (hasBoundaryMarkers) {
|
|
147
|
+
// Scenario C: New rules exist - prompt for overwrite
|
|
148
|
+
const shouldOverwrite = await this.promptForOverwriteConfirmation(selectedAgent);
|
|
149
|
+
if (!shouldOverwrite) {
|
|
150
|
+
this.log(`Skipped rule file update for ${selectedAgent}`);
|
|
102
151
|
return;
|
|
103
152
|
}
|
|
104
|
-
|
|
105
|
-
|
|
153
|
+
await this.replaceExistingRules({
|
|
154
|
+
agent: selectedAgent,
|
|
155
|
+
content,
|
|
156
|
+
filePath,
|
|
157
|
+
fileService,
|
|
158
|
+
templateService,
|
|
159
|
+
writeMode,
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// STEP 5: No ByteRover content - append rules
|
|
164
|
+
await this.appendRulesToFile({
|
|
165
|
+
agent: selectedAgent,
|
|
166
|
+
filePath,
|
|
167
|
+
fileService,
|
|
168
|
+
templateService,
|
|
169
|
+
writeMode,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Appends ByteRover rules to a file that has no ByteRover content.
|
|
174
|
+
*/
|
|
175
|
+
async appendRulesToFile(params) {
|
|
176
|
+
const { agent, filePath, fileService, templateService, writeMode } = params;
|
|
177
|
+
const ruleContent = await templateService.generateRuleContent(agent);
|
|
178
|
+
// For dedicated ByteRover files, overwrite; for shared instruction files, append
|
|
179
|
+
const mode = writeMode === 'overwrite' ? 'overwrite' : 'append';
|
|
180
|
+
await fileService.write(ruleContent, filePath, mode);
|
|
181
|
+
this.log(`✅ Successfully added rule file for ${agent}`);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Creates a new rule file with ByteRover rules.
|
|
185
|
+
*/
|
|
186
|
+
async createNewRuleFile(params) {
|
|
187
|
+
const { agent, filePath, fileService, templateService } = params;
|
|
188
|
+
const ruleContent = await templateService.generateRuleContent(agent);
|
|
189
|
+
await fileService.write(ruleContent, filePath, 'overwrite');
|
|
190
|
+
this.log(`✅ Successfully created rule file for ${agent} at ${filePath}`);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Handles legacy rules cleanup with user choice of automatic or manual.
|
|
194
|
+
*/
|
|
195
|
+
async handleLegacyRulesCleanup(params) {
|
|
196
|
+
const { agent, content, filePath, fileService, legacyRuleDetector, templateService } = params;
|
|
197
|
+
const detectionResult = legacyRuleDetector.detectLegacyRules(content, agent);
|
|
198
|
+
const { reliableMatches, uncertainMatches } = detectionResult;
|
|
199
|
+
this.log(`\n⚠️ Detected ${reliableMatches.length + uncertainMatches.length} old ByteRover rule section(s) in ${filePath}:\n`);
|
|
200
|
+
if (reliableMatches.length > 0) {
|
|
201
|
+
this.log('Reliable matches:');
|
|
202
|
+
for (const [index, match] of reliableMatches.entries()) {
|
|
203
|
+
this.log(` Section ${index + 1}: lines ${match.startLine}-${match.endLine}`);
|
|
204
|
+
}
|
|
205
|
+
this.log();
|
|
206
|
+
}
|
|
207
|
+
if (uncertainMatches.length > 0) {
|
|
208
|
+
this.log(' ⚠️ Uncertain matches (cannot determine start):');
|
|
209
|
+
for (const match of uncertainMatches) {
|
|
210
|
+
this.log(` Footer found at line ${match.footerLine}`);
|
|
211
|
+
this.log(` Reason: ${match.reason}`);
|
|
212
|
+
}
|
|
213
|
+
this.log();
|
|
214
|
+
this.log('⚠️ Due to uncertain matches, only manual cleanup is available.\n');
|
|
215
|
+
await this.performManualCleanup({
|
|
216
|
+
agent,
|
|
217
|
+
filePath,
|
|
218
|
+
fileService,
|
|
219
|
+
reliableMatches,
|
|
220
|
+
templateService,
|
|
221
|
+
uncertainMatches,
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const selectedStrategy = await this.promptForCleanupStrategy();
|
|
226
|
+
await (selectedStrategy === 'manual'
|
|
227
|
+
? this.performManualCleanup({
|
|
228
|
+
agent,
|
|
229
|
+
filePath,
|
|
230
|
+
fileService,
|
|
231
|
+
reliableMatches,
|
|
232
|
+
templateService,
|
|
233
|
+
uncertainMatches,
|
|
234
|
+
})
|
|
235
|
+
: this.performAutomaticCleanup({
|
|
236
|
+
agent,
|
|
237
|
+
filePath,
|
|
238
|
+
fileService,
|
|
239
|
+
reliableMatches,
|
|
240
|
+
templateService,
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
async performAutomaticCleanup(params) {
|
|
244
|
+
const { agent, filePath, fileService, reliableMatches, templateService } = params;
|
|
245
|
+
const backupPath = await fileService.createBackup(filePath);
|
|
246
|
+
this.log(`📦 Backup created: ${backupPath}`);
|
|
247
|
+
let content = await fileService.read(filePath);
|
|
248
|
+
// Remove all reliable matches (in reverse order to preserve line numbers)
|
|
249
|
+
const sortedMatches = [...reliableMatches].sort((a, b) => b.startLine - a.startLine);
|
|
250
|
+
for (const match of sortedMatches) {
|
|
251
|
+
content = content.replace(match.content, '');
|
|
252
|
+
}
|
|
253
|
+
// Write cleaned content
|
|
254
|
+
await fileService.write(content, filePath, 'overwrite');
|
|
255
|
+
// Append new rules
|
|
256
|
+
const ruleContent = await templateService.generateRuleContent(agent);
|
|
257
|
+
await fileService.write(ruleContent, filePath, 'append');
|
|
258
|
+
this.log(`✅ Removed ${reliableMatches.length} old ByteRover section(s)`);
|
|
259
|
+
this.log(`✅ Added new rules with boundary markers`);
|
|
260
|
+
this.log(`\nYou can safely delete the backup file once verified.`);
|
|
261
|
+
}
|
|
262
|
+
async performManualCleanup(params) {
|
|
263
|
+
const { agent, filePath, fileService, reliableMatches, templateService, uncertainMatches } = params;
|
|
264
|
+
const ruleContent = await templateService.generateRuleContent(agent);
|
|
265
|
+
await fileService.write(ruleContent, filePath, 'append');
|
|
266
|
+
this.log(`✅ New ByteRover rules added with boundary markers\n`);
|
|
267
|
+
this.log('Please manually remove old sections:');
|
|
268
|
+
for (const [index, match] of reliableMatches.entries()) {
|
|
269
|
+
this.log(` - Section ${index + 1}: lines ${match.startLine}-${match.endLine} in ${filePath}`);
|
|
270
|
+
}
|
|
271
|
+
for (const match of uncertainMatches) {
|
|
272
|
+
this.log(` - Section ending at line ${match.footerLine} in ${filePath}`);
|
|
273
|
+
}
|
|
274
|
+
this.log('\nKeep only the section between:');
|
|
275
|
+
this.log(' <!-- BEGIN BYTEROVER RULES -->');
|
|
276
|
+
this.log(' <!-- END BYTEROVER RULES -->');
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Replaces existing ByteRover rules (with boundary markers) with new rules.
|
|
280
|
+
*/
|
|
281
|
+
async replaceExistingRules(params) {
|
|
282
|
+
const { agent, content, filePath, fileService, templateService, writeMode } = params;
|
|
283
|
+
const ruleContent = await templateService.generateRuleContent(agent);
|
|
284
|
+
if (writeMode === 'overwrite') {
|
|
285
|
+
// For dedicated ByteRover files, just overwrite the entire file
|
|
286
|
+
await fileService.write(ruleContent, filePath, 'overwrite');
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
// For shared instruction files, replace the section between markers
|
|
290
|
+
const startMarker = BRV_RULE_MARKERS.START;
|
|
291
|
+
const endMarker = BRV_RULE_MARKERS.END;
|
|
292
|
+
const startIndex = content.indexOf(startMarker);
|
|
293
|
+
const endIndex = content.indexOf(endMarker, startIndex);
|
|
294
|
+
if (startIndex === -1 || endIndex === -1) {
|
|
295
|
+
this.error('Could not find boundary markers in the file');
|
|
296
|
+
}
|
|
297
|
+
const before = content.slice(0, startIndex);
|
|
298
|
+
const after = content.slice(endIndex + endMarker.length);
|
|
299
|
+
const newContent = before + ruleContent + after;
|
|
300
|
+
await fileService.write(newContent, filePath, 'overwrite');
|
|
106
301
|
}
|
|
302
|
+
this.log(`✅ Successfully updated rule file for ${agent}`);
|
|
107
303
|
}
|
|
108
304
|
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -7,13 +7,16 @@ import type { IContextTreeService } from '../core/interfaces/i-context-tree-serv
|
|
|
7
7
|
import type { IContextTreeSnapshotService } from '../core/interfaces/i-context-tree-snapshot-service.js';
|
|
8
8
|
import type { IContextTreeWriterService } from '../core/interfaces/i-context-tree-writer-service.js';
|
|
9
9
|
import type { IProjectConfigStore } from '../core/interfaces/i-project-config-store.js';
|
|
10
|
-
import type { IRuleWriterService } from '../core/interfaces/i-rule-writer-service.js';
|
|
11
10
|
import type { ISpaceService } from '../core/interfaces/i-space-service.js';
|
|
12
11
|
import type { ITeamService } from '../core/interfaces/i-team-service.js';
|
|
13
12
|
import type { ITokenStore } from '../core/interfaces/i-token-store.js';
|
|
14
13
|
import { type Agent } from '../core/domain/entities/agent.js';
|
|
15
14
|
import { BrvConfig } from '../core/domain/entities/brv-config.js';
|
|
15
|
+
import { IFileService } from '../core/interfaces/i-file-service.js';
|
|
16
|
+
import { ILegacyRuleDetector } from '../core/interfaces/i-legacy-rule-detector.js';
|
|
17
|
+
import { IRuleTemplateService } from '../core/interfaces/i-rule-template-service.js';
|
|
16
18
|
import { ITrackingService } from '../core/interfaces/i-tracking-service.js';
|
|
19
|
+
type CleanupStrategy = 'automatic' | 'manual';
|
|
17
20
|
/**
|
|
18
21
|
* Represents a legacy config that exists but has version issues.
|
|
19
22
|
* Used to display config info during re-initialization prompt.
|
|
@@ -41,10 +44,12 @@ export default class Init extends Command {
|
|
|
41
44
|
contextTreeService: IContextTreeService;
|
|
42
45
|
contextTreeSnapshotService: IContextTreeSnapshotService;
|
|
43
46
|
contextTreeWriterService: IContextTreeWriterService;
|
|
47
|
+
fileService: IFileService;
|
|
48
|
+
legacyRuleDetector: ILegacyRuleDetector;
|
|
44
49
|
projectConfigStore: IProjectConfigStore;
|
|
45
|
-
ruleWriterService: IRuleWriterService;
|
|
46
50
|
spaceService: ISpaceService;
|
|
47
51
|
teamService: ITeamService;
|
|
52
|
+
templateService: IRuleTemplateService;
|
|
48
53
|
tokenStore: ITokenStore;
|
|
49
54
|
trackingService: ITrackingService;
|
|
50
55
|
};
|
|
@@ -55,10 +60,15 @@ export default class Init extends Command {
|
|
|
55
60
|
protected ensureAuthenticated(tokenStore: ITokenStore): Promise<AuthToken>;
|
|
56
61
|
protected fetchAndSelectSpace(spaceService: ISpaceService, token: AuthToken, team: Team): Promise<Space | undefined>;
|
|
57
62
|
protected fetchAndSelectTeam(teamService: ITeamService, token: AuthToken): Promise<Team | undefined>;
|
|
58
|
-
protected generateRulesForAgent(
|
|
63
|
+
protected generateRulesForAgent(selectedAgent: Agent, fileService: IFileService, templateService: IRuleTemplateService, legacyRuleDetector: ILegacyRuleDetector): Promise<void>;
|
|
59
64
|
protected getExistingConfig(projectConfigStore: IProjectConfigStore): Promise<BrvConfig | LegacyProjectConfigInfo | undefined>;
|
|
60
65
|
protected initializeMemoryContextDir(name: string, initFn: () => Promise<string>): Promise<void>;
|
|
61
66
|
protected isLegacyProjectConfig(config: BrvConfig | LegacyProjectConfigInfo): config is LegacyProjectConfigInfo;
|
|
67
|
+
/**
|
|
68
|
+
* Checks if the given path represents a README.md placeholder file.
|
|
69
|
+
* Handles both legacy paths with leading slash and new paths without.
|
|
70
|
+
*/
|
|
71
|
+
protected isReadmePlaceholder(path: string): boolean;
|
|
62
72
|
protected promptAceDeprecationRemoval(): Promise<boolean>;
|
|
63
73
|
/**
|
|
64
74
|
* Prompts the user to select an agent.
|
|
@@ -66,6 +76,20 @@ export default class Init extends Command {
|
|
|
66
76
|
* @returns The selected agent
|
|
67
77
|
*/
|
|
68
78
|
protected promptForAgentSelection(): Promise<Agent>;
|
|
79
|
+
/**
|
|
80
|
+
* Prompts the user to choose cleanup strategy for legacy rules.
|
|
81
|
+
* This method is protected to allow test overrides.
|
|
82
|
+
* @returns The chosen cleanup strategy
|
|
83
|
+
*/
|
|
84
|
+
protected promptForCleanupStrategy(): Promise<CleanupStrategy>;
|
|
85
|
+
/**
|
|
86
|
+
* Prompts the user to create a new rule file.
|
|
87
|
+
* This method is protected to allow test overrides.
|
|
88
|
+
* @param agent The agent for which the rule file doesn't exist
|
|
89
|
+
* @param filePath The path where the file would be created
|
|
90
|
+
* @returns True if the user wants to create the file, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
protected promptForFileCreation(agent: Agent, filePath: string): Promise<boolean>;
|
|
69
93
|
/**
|
|
70
94
|
* Prompts the user to confirm overwriting an existing rule file.
|
|
71
95
|
* This method is protected to allow test overrides.
|
|
@@ -86,5 +110,21 @@ export default class Init extends Command {
|
|
|
86
110
|
};
|
|
87
111
|
token: AuthToken;
|
|
88
112
|
}): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Appends ByteRover rules to a file that has no ByteRover content.
|
|
115
|
+
*/
|
|
116
|
+
private appendRulesToFile;
|
|
117
|
+
/**
|
|
118
|
+
* Creates a new rule file with ByteRover rules.
|
|
119
|
+
*/
|
|
120
|
+
private createNewRuleFile;
|
|
121
|
+
private handleLegacyRulesCleanup;
|
|
89
122
|
private logSuccess;
|
|
123
|
+
private performAutomaticCleanup;
|
|
124
|
+
private performManualCleanup;
|
|
125
|
+
/**
|
|
126
|
+
* Replaces existing ByteRover rules (with boundary markers) with new rules.
|
|
127
|
+
*/
|
|
128
|
+
private replaceExistingRules;
|
|
90
129
|
}
|
|
130
|
+
export {};
|