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
|
@@ -10,21 +10,21 @@ export const mapToPushContexts = (params) => {
|
|
|
10
10
|
const addedContextFiles = params.addedFiles.map((file) => new CogitPushContext({
|
|
11
11
|
content: file.content,
|
|
12
12
|
operation: 'add',
|
|
13
|
-
path:
|
|
13
|
+
path: file.path,
|
|
14
14
|
tags: [],
|
|
15
15
|
title: file.title,
|
|
16
16
|
}));
|
|
17
17
|
const editedContextFiles = params.modifiedFiles.map((file) => new CogitPushContext({
|
|
18
18
|
content: file.content,
|
|
19
19
|
operation: 'edit',
|
|
20
|
-
path:
|
|
20
|
+
path: file.path,
|
|
21
21
|
tags: [],
|
|
22
22
|
title: file.title,
|
|
23
23
|
}));
|
|
24
|
-
const deletedContextFiles = params.deletedPaths.map((
|
|
24
|
+
const deletedContextFiles = params.deletedPaths.map((deletedPath) => new CogitPushContext({
|
|
25
25
|
content: '',
|
|
26
26
|
operation: 'delete',
|
|
27
|
-
path:
|
|
27
|
+
path: deletedPath,
|
|
28
28
|
tags: [],
|
|
29
29
|
title: '',
|
|
30
30
|
}));
|
|
@@ -40,7 +40,7 @@ export class HttpCogitPushService {
|
|
|
40
40
|
const response = await this.makeRequest({
|
|
41
41
|
accessToken: params.accessToken,
|
|
42
42
|
branch: params.branch,
|
|
43
|
-
currentSha: '',
|
|
43
|
+
currentSha: 'sha_placeholder',
|
|
44
44
|
memories,
|
|
45
45
|
sessionKey: params.sessionKey,
|
|
46
46
|
url,
|
|
@@ -79,8 +79,8 @@ export class HttpCogitPushService {
|
|
|
79
79
|
typeof error.response === 'object' &&
|
|
80
80
|
'data' in error.response) {
|
|
81
81
|
const errorResponse = error.response.data;
|
|
82
|
-
if (errorResponse.
|
|
83
|
-
return extractShaFromErrorDetails(errorResponse.
|
|
82
|
+
if (errorResponse.error) {
|
|
83
|
+
return extractShaFromErrorDetails(errorResponse.error);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
return undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { access, mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { CONTEXT_TREE_DOMAINS } from '../../config/context-tree-domains.js';
|
|
4
4
|
import { BRV_DIR, CONTEXT_FILE, CONTEXT_TREE_DIR } from '../../constants.js';
|
|
@@ -16,7 +16,6 @@ export class FileContextTreeService {
|
|
|
16
16
|
const contextTreeDir = join(baseDir, BRV_DIR, CONTEXT_TREE_DIR);
|
|
17
17
|
try {
|
|
18
18
|
// Check if context tree directory exists
|
|
19
|
-
const { access } = await import('node:fs/promises');
|
|
20
19
|
await access(contextTreeDir);
|
|
21
20
|
return true;
|
|
22
21
|
}
|
|
@@ -17,6 +17,8 @@ export declare class FileContextTreeWriterService implements IContextTreeWriterS
|
|
|
17
17
|
sync(params: SyncParams): Promise<SyncResult>;
|
|
18
18
|
/**
|
|
19
19
|
* Normalizes a file path by removing leading slashes.
|
|
20
|
+
* Retained for backwards compatibility with legacy API responses
|
|
21
|
+
* that may still include leading slashes in paths.
|
|
20
22
|
*/
|
|
21
23
|
private normalizePath;
|
|
22
24
|
}
|
|
@@ -54,6 +54,8 @@ export class FileContextTreeWriterService {
|
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Normalizes a file path by removing leading slashes.
|
|
57
|
+
* Retained for backwards compatibility with legacy API responses
|
|
58
|
+
* that may still include leading slashes in paths.
|
|
57
59
|
*/
|
|
58
60
|
normalizePath(path) {
|
|
59
61
|
return path.replace(/^\/+/, '');
|
|
@@ -3,6 +3,7 @@ import { type IFileService, type WriteMode } from '../../core/interfaces/i-file-
|
|
|
3
3
|
* File service implementation using Node.js fs module.
|
|
4
4
|
*/
|
|
5
5
|
export declare class FsFileService implements IFileService {
|
|
6
|
+
createBackup(filePath: string): Promise<string>;
|
|
6
7
|
/**
|
|
7
8
|
* Checks if a file exists at the specified path.
|
|
8
9
|
*
|
|
@@ -17,6 +18,7 @@ export declare class FsFileService implements IFileService {
|
|
|
17
18
|
* @returns A promise that resolves with the content of the file.
|
|
18
19
|
*/
|
|
19
20
|
read(filePath: string): Promise<string>;
|
|
21
|
+
replaceContent(filePath: string, oldContent: string, newContent: string): Promise<void>;
|
|
20
22
|
/**
|
|
21
23
|
* Writes content to the specified file.
|
|
22
24
|
* @param content The content to write.
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { access, appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { access, appendFile, copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* File service implementation using Node.js fs module.
|
|
5
5
|
*/
|
|
6
6
|
export class FsFileService {
|
|
7
|
+
async createBackup(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
// Timestamp format: YYYY-MM-DD-HH-MM-SS
|
|
10
|
+
const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('T').join('-').slice(0, -5);
|
|
11
|
+
const backupPath = `${filePath}.backup-${timestamp}`;
|
|
12
|
+
await copyFile(filePath, backupPath);
|
|
13
|
+
return backupPath;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw new Error(`Failed to create backup file '${filePath}': ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
7
19
|
/**
|
|
8
20
|
* Checks if a file exists at the specified path.
|
|
9
21
|
*
|
|
@@ -33,6 +45,16 @@ export class FsFileService {
|
|
|
33
45
|
throw new Error(`Failed to read content from file '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
|
|
34
46
|
}
|
|
35
47
|
}
|
|
48
|
+
async replaceContent(filePath, oldContent, newContent) {
|
|
49
|
+
try {
|
|
50
|
+
const currentContent = await this.read(filePath);
|
|
51
|
+
const updatedContent = currentContent.replace(oldContent, newContent);
|
|
52
|
+
await this.write(updatedContent, filePath, 'overwrite');
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`Failed to replace content in file '${filePath}': ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
36
58
|
/**
|
|
37
59
|
* Writes content to the specified file.
|
|
38
60
|
* @param content The content to write.
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ByteRover CLI generated rule template tag.
|
|
3
3
|
*/
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const BRV_RULE_TAG = "Generated by ByteRover CLI for";
|
|
5
|
+
/**
|
|
6
|
+
* Boundary markers for managed ByteRover rule sections.
|
|
7
|
+
* Used to identify and replace ByteRover-generated content in shared instruction files.
|
|
8
|
+
*/
|
|
9
|
+
export declare const BRV_RULE_MARKERS: {
|
|
10
|
+
readonly END: "<!-- END BYTEROVER RULES -->";
|
|
11
|
+
readonly START: "<!-- BEGIN BYTEROVER RULES -->";
|
|
12
|
+
};
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ByteRover CLI generated rule template tag.
|
|
3
3
|
*/
|
|
4
|
-
export const
|
|
4
|
+
export const BRV_RULE_TAG = 'Generated by ByteRover CLI for';
|
|
5
|
+
/**
|
|
6
|
+
* Boundary markers for managed ByteRover rule sections.
|
|
7
|
+
* Used to identify and replace ByteRover-generated content in shared instruction files.
|
|
8
|
+
*/
|
|
9
|
+
export const BRV_RULE_MARKERS = {
|
|
10
|
+
END: '<!-- END BYTEROVER RULES -->',
|
|
11
|
+
START: '<!-- BEGIN BYTEROVER RULES -->',
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Agent } from '../../core/domain/entities/agent.js';
|
|
2
|
+
import type { ILegacyRuleDetector, LegacyRuleDetectionResult } from '../../core/interfaces/i-legacy-rule-detector.js';
|
|
3
|
+
export declare class LegacyRuleDetector implements ILegacyRuleDetector {
|
|
4
|
+
private static readonly SECTION_SEPARATOR_PATTERN;
|
|
5
|
+
private static readonly WORKFLOW_HEADER_PATTERN;
|
|
6
|
+
detectLegacyRules(content: string, agentName: Agent): LegacyRuleDetectionResult;
|
|
7
|
+
/**
|
|
8
|
+
* Attempts to find the start of a ByteRover rule section by working backwards from the footer.
|
|
9
|
+
*
|
|
10
|
+
* Strategy 3 (Conservative Multi-Pattern Match):
|
|
11
|
+
* 1. Look backwards for "# Workflow Instruction" header (most reliable)
|
|
12
|
+
* 2. If not found, look backwards for "---" separator before command reference
|
|
13
|
+
* 3. If still not found, return undefined (uncertain)
|
|
14
|
+
*
|
|
15
|
+
* @param lines All lines in the file.
|
|
16
|
+
* @param footerIndex Index of the line containing the footer tag (0-indexed).
|
|
17
|
+
* @param usedLineRanges Line ranges already claimed by detected sections.
|
|
18
|
+
* @returns Index of the start line (0-indexed), or undefined if uncertain.
|
|
19
|
+
*/
|
|
20
|
+
private findSectionStart;
|
|
21
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { BRV_RULE_TAG } from './constants.js';
|
|
2
|
+
export class LegacyRuleDetector {
|
|
3
|
+
static SECTION_SEPARATOR_PATTERN = /^---\s*$/;
|
|
4
|
+
static WORKFLOW_HEADER_PATTERN = /^#\sWorkflow Instruction$/;
|
|
5
|
+
detectLegacyRules(content, agentName) {
|
|
6
|
+
// Normalize line endings to handle CRLF
|
|
7
|
+
const normalizedContent = content.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
|
|
8
|
+
const lines = normalizedContent.split('\n');
|
|
9
|
+
const footerTag = `${BRV_RULE_TAG} ${agentName}`;
|
|
10
|
+
const reliableMatches = [];
|
|
11
|
+
const uncertainMatches = [];
|
|
12
|
+
const usedLineRanges = [];
|
|
13
|
+
// Find all occurrences of the footer tag
|
|
14
|
+
for (const [index, line] of lines.entries()) {
|
|
15
|
+
// Use exact match to avoid matching partial agent names like "Github Copilot Extended"
|
|
16
|
+
if (line.trim() === footerTag) {
|
|
17
|
+
const footerIndex = index;
|
|
18
|
+
const footerLineNumber = footerIndex + 1;
|
|
19
|
+
// Try to find the start of this ByteRover section
|
|
20
|
+
const startIndex = this.findSectionStart(lines, footerIndex, usedLineRanges);
|
|
21
|
+
if (startIndex === undefined) {
|
|
22
|
+
// Uncertain match - couldn't reliably determine start
|
|
23
|
+
uncertainMatches.push({
|
|
24
|
+
footerLine: footerLineNumber,
|
|
25
|
+
reason: 'Could not reliably determine the start of the ByteRover rule section.',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Reliable match found
|
|
30
|
+
const startLineNumber = startIndex + 1;
|
|
31
|
+
const sectionContent = lines.slice(startIndex, footerIndex + 1).join('\n');
|
|
32
|
+
reliableMatches.push({
|
|
33
|
+
content: sectionContent,
|
|
34
|
+
endLine: footerLineNumber,
|
|
35
|
+
startLine: startLineNumber,
|
|
36
|
+
});
|
|
37
|
+
// Mark this range as used so it won't be reused by subsequent footers
|
|
38
|
+
usedLineRanges.push({ end: footerIndex, start: startIndex });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { reliableMatches, uncertainMatches };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Attempts to find the start of a ByteRover rule section by working backwards from the footer.
|
|
46
|
+
*
|
|
47
|
+
* Strategy 3 (Conservative Multi-Pattern Match):
|
|
48
|
+
* 1. Look backwards for "# Workflow Instruction" header (most reliable)
|
|
49
|
+
* 2. If not found, look backwards for "---" separator before command reference
|
|
50
|
+
* 3. If still not found, return undefined (uncertain)
|
|
51
|
+
*
|
|
52
|
+
* @param lines All lines in the file.
|
|
53
|
+
* @param footerIndex Index of the line containing the footer tag (0-indexed).
|
|
54
|
+
* @param usedLineRanges Line ranges already claimed by detected sections.
|
|
55
|
+
* @returns Index of the start line (0-indexed), or undefined if uncertain.
|
|
56
|
+
*/
|
|
57
|
+
findSectionStart(lines, footerIndex, usedLineRanges) {
|
|
58
|
+
// Strategy 1: Look for "# Workflow Instruction" header (most reliable)
|
|
59
|
+
for (let i = footerIndex - 1; i >= 0; i--) {
|
|
60
|
+
if (LegacyRuleDetector.WORKFLOW_HEADER_PATTERN.test(lines[i])) {
|
|
61
|
+
// Check if this header is within any already-detected section
|
|
62
|
+
const isWithinUsedRange = usedLineRanges.some((range) => i >= range.start && i <= range.end);
|
|
63
|
+
if (!isWithinUsedRange) {
|
|
64
|
+
return i;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Strategy 2: Look for "---" separator
|
|
69
|
+
// We need to find the section separator that appears before the command reference
|
|
70
|
+
// and after the workflow content. This is less reliable but still useful.
|
|
71
|
+
const separatorIndices = [];
|
|
72
|
+
for (let i = footerIndex - 1; i >= 0; i--) {
|
|
73
|
+
if (LegacyRuleDetector.SECTION_SEPARATOR_PATTERN.test(lines[i])) {
|
|
74
|
+
separatorIndices.push(i);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// If we found at least one separator, use it as a fallback
|
|
78
|
+
// We want the one closest to the footer but before the "---" that precedes the footer
|
|
79
|
+
if (separatorIndices.length > 0) {
|
|
80
|
+
// The footer line should be preceded by "---", so we skip that one
|
|
81
|
+
// and look for the next separator going backwards
|
|
82
|
+
let candidateIndex;
|
|
83
|
+
for (const sepIndex of separatorIndices) {
|
|
84
|
+
const linesBetween = footerIndex - sepIndex;
|
|
85
|
+
if (linesBetween === 1) {
|
|
86
|
+
// This is the separator right before the footer, skip it
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// Check if this separator is within any already-detected section
|
|
90
|
+
const isWithinUsedRange = usedLineRanges.some((range) => sepIndex >= range.start && sepIndex <= range.end);
|
|
91
|
+
if (isWithinUsedRange) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// This could be a section separator within the ByteRover content
|
|
95
|
+
// Use the first one we find (closest to footer)
|
|
96
|
+
candidateIndex = sepIndex + 1;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (candidateIndex !== undefined && candidateIndex < footerIndex) {
|
|
100
|
+
return candidateIndex;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Strategy 3: Could not reliably determine start
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BRV_RULE_MARKERS, BRV_RULE_TAG } from './constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps rule content with boundary markers for identification and replacement.
|
|
4
|
+
*
|
|
5
|
+
* @param content The rule content to wrap.
|
|
6
|
+
* @param agent The agent name for the footer tag.
|
|
7
|
+
* @param header Agent-specific header.
|
|
8
|
+
* @returns The wrapped content with boundary markers.
|
|
9
|
+
*/
|
|
10
|
+
const wrapContentWithBoundaryMarkers = (content, agent, header) => {
|
|
11
|
+
const parts = [BRV_RULE_MARKERS.START, header, content, '---', `${BRV_RULE_TAG} ${agent}`, BRV_RULE_MARKERS.END];
|
|
12
|
+
return parts.join('\n');
|
|
13
|
+
};
|
|
2
14
|
const guideHeaders = [
|
|
3
15
|
{
|
|
4
16
|
agent: 'Augment Code',
|
|
@@ -67,11 +79,7 @@ export class RuleTemplateService {
|
|
|
67
79
|
const content = this.templateLoader.substituteVariables(baseTemplate, context);
|
|
68
80
|
// Add agent-specific header if available (from develop branch)
|
|
69
81
|
const header = guideHeaders.find((h) => h.agent === agent)?.value || '';
|
|
70
|
-
return
|
|
71
|
-
${content}
|
|
72
|
-
---
|
|
73
|
-
${BR_RULE_TAG} ${agent}
|
|
74
|
-
`;
|
|
82
|
+
return wrapContentWithBoundaryMarkers(content, agent, header);
|
|
75
83
|
}
|
|
76
84
|
catch (error) {
|
|
77
85
|
throw new Error(`Failed to generate rule content for agent '${agent}': ${error instanceof Error ? error.message : String(error)}`);
|
package/oclif.manifest.json
CHANGED
|
@@ -55,39 +55,9 @@
|
|
|
55
55
|
"",
|
|
56
56
|
"# Autonomous mode - LLM auto-categorizes your context",
|
|
57
57
|
"<%= config.bin %> <%= command.id %> \"Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts\"",
|
|
58
|
-
""
|
|
59
|
-
"# Autonomous mode with OpenRouter (development only)",
|
|
60
|
-
"<%= config.bin %> <%= command.id %> -k YOUR_API_KEY \"React components follow atomic design in src/components/. Atoms in atoms/, molecules in molecules/, organisms in organisms/\"",
|
|
61
|
-
"",
|
|
62
|
-
"# Autonomous mode with custom model (development only)",
|
|
63
|
-
"<%= config.bin %> <%= command.id %> -k YOUR_API_KEY -m anthropic/claude-sonnet-4 \"API rate limit is 100 req/min per user. Implemented using Redis with sliding window in rateLimiter.ts\""
|
|
58
|
+
""
|
|
64
59
|
],
|
|
65
|
-
"flags": {
|
|
66
|
-
"apiKey": {
|
|
67
|
-
"char": "k",
|
|
68
|
-
"description": "OpenRouter API key (use OpenRouter instead of internal gRPC backend) [Development only]",
|
|
69
|
-
"env": "OPENROUTER_API_KEY",
|
|
70
|
-
"name": "apiKey",
|
|
71
|
-
"hasDynamicHelp": false,
|
|
72
|
-
"multiple": false,
|
|
73
|
-
"type": "option"
|
|
74
|
-
},
|
|
75
|
-
"model": {
|
|
76
|
-
"char": "m",
|
|
77
|
-
"description": "Model to use (default: google/gemini-2.5-pro for OpenRouter, gemini-2.5-pro for gRPC) [Development only]",
|
|
78
|
-
"name": "model",
|
|
79
|
-
"hasDynamicHelp": false,
|
|
80
|
-
"multiple": false,
|
|
81
|
-
"type": "option"
|
|
82
|
-
},
|
|
83
|
-
"verbose": {
|
|
84
|
-
"char": "v",
|
|
85
|
-
"description": "Enable verbose debug output [Development only]",
|
|
86
|
-
"name": "verbose",
|
|
87
|
-
"allowNo": false,
|
|
88
|
-
"type": "boolean"
|
|
89
|
-
}
|
|
90
|
-
},
|
|
60
|
+
"flags": {},
|
|
91
61
|
"hasDynamicHelp": false,
|
|
92
62
|
"hiddenAliases": [],
|
|
93
63
|
"id": "curate",
|
|
@@ -109,7 +79,7 @@
|
|
|
109
79
|
"description": "Purely for testing CodingAgentLogWatcher [Development only]",
|
|
110
80
|
"flags": {},
|
|
111
81
|
"hasDynamicHelp": false,
|
|
112
|
-
"hidden":
|
|
82
|
+
"hidden": true,
|
|
113
83
|
"hiddenAliases": [],
|
|
114
84
|
"id": "foo",
|
|
115
85
|
"pluginAlias": "byterover-cli",
|
|
@@ -337,42 +307,9 @@
|
|
|
337
307
|
"# Ask questions about patterns, decisions, or implementation details",
|
|
338
308
|
"<%= config.bin %> <%= command.id %> What are the coding standards?",
|
|
339
309
|
"<%= config.bin %> <%= command.id %> How is authentication implemented?",
|
|
340
|
-
""
|
|
341
|
-
"# Query with OpenRouter (development only)",
|
|
342
|
-
"<%= config.bin %> <%= command.id %> -k YOUR_API_KEY Show me all API endpoints",
|
|
343
|
-
"",
|
|
344
|
-
"# Query with custom model (development only)",
|
|
345
|
-
"<%= config.bin %> <%= command.id %> -k YOUR_API_KEY -m anthropic/claude-sonnet-4 Explain the database schema",
|
|
346
|
-
"",
|
|
347
|
-
"# Query with verbose output (development only)",
|
|
348
|
-
"<%= config.bin %> <%= command.id %> -v What testing strategies are used?"
|
|
310
|
+
""
|
|
349
311
|
],
|
|
350
|
-
"flags": {
|
|
351
|
-
"apiKey": {
|
|
352
|
-
"char": "k",
|
|
353
|
-
"description": "OpenRouter API key (use OpenRouter instead of internal gRPC backend) [Development only]",
|
|
354
|
-
"env": "OPENROUTER_API_KEY",
|
|
355
|
-
"name": "apiKey",
|
|
356
|
-
"hasDynamicHelp": false,
|
|
357
|
-
"multiple": false,
|
|
358
|
-
"type": "option"
|
|
359
|
-
},
|
|
360
|
-
"model": {
|
|
361
|
-
"char": "m",
|
|
362
|
-
"description": "Model to use (default: google/gemini-2.5-pro for OpenRouter, gemini-2.5-pro for gRPC) [Development only]",
|
|
363
|
-
"name": "model",
|
|
364
|
-
"hasDynamicHelp": false,
|
|
365
|
-
"multiple": false,
|
|
366
|
-
"type": "option"
|
|
367
|
-
},
|
|
368
|
-
"verbose": {
|
|
369
|
-
"char": "v",
|
|
370
|
-
"description": "Enable verbose debug output [Development only]",
|
|
371
|
-
"name": "verbose",
|
|
372
|
-
"allowNo": false,
|
|
373
|
-
"type": "boolean"
|
|
374
|
-
}
|
|
375
|
-
},
|
|
312
|
+
"flags": {},
|
|
376
313
|
"hasDynamicHelp": false,
|
|
377
314
|
"hiddenAliases": [],
|
|
378
315
|
"id": "query",
|
|
@@ -473,7 +410,7 @@
|
|
|
473
410
|
}
|
|
474
411
|
},
|
|
475
412
|
"hasDynamicHelp": false,
|
|
476
|
-
"hidden":
|
|
413
|
+
"hidden": true,
|
|
477
414
|
"hiddenAliases": [],
|
|
478
415
|
"id": "watch",
|
|
479
416
|
"pluginAlias": "byterover-cli",
|
|
@@ -527,30 +464,6 @@
|
|
|
527
464
|
"<%= config.bin %> <%= command.id %> --working-directory ~/myproject"
|
|
528
465
|
],
|
|
529
466
|
"flags": {
|
|
530
|
-
"apiKey": {
|
|
531
|
-
"char": "k",
|
|
532
|
-
"description": "OpenRouter API key (use OpenRouter instead of gRPC backend) [Development only]",
|
|
533
|
-
"env": "OPENROUTER_API_KEY",
|
|
534
|
-
"name": "apiKey",
|
|
535
|
-
"hasDynamicHelp": false,
|
|
536
|
-
"multiple": false,
|
|
537
|
-
"type": "option"
|
|
538
|
-
},
|
|
539
|
-
"model": {
|
|
540
|
-
"char": "m",
|
|
541
|
-
"description": "Model to use (default: google/gemini-2.5-pro for OpenRouter, gemini-2.5-pro for gRPC) [Development only]",
|
|
542
|
-
"name": "model",
|
|
543
|
-
"hasDynamicHelp": false,
|
|
544
|
-
"multiple": false,
|
|
545
|
-
"type": "option"
|
|
546
|
-
},
|
|
547
|
-
"verbose": {
|
|
548
|
-
"char": "v",
|
|
549
|
-
"description": "Enable verbose debug output for prompt loading and agent operations [Development only]",
|
|
550
|
-
"name": "verbose",
|
|
551
|
-
"allowNo": false,
|
|
552
|
-
"type": "boolean"
|
|
553
|
-
},
|
|
554
467
|
"continue": {
|
|
555
468
|
"char": "c",
|
|
556
469
|
"description": "Continue most recent session (requires prompt in headless mode)",
|
|
@@ -599,7 +512,7 @@
|
|
|
599
512
|
}
|
|
600
513
|
},
|
|
601
514
|
"hasDynamicHelp": false,
|
|
602
|
-
"hidden":
|
|
515
|
+
"hidden": true,
|
|
603
516
|
"hiddenAliases": [],
|
|
604
517
|
"id": "cipher-agent:run",
|
|
605
518
|
"pluginAlias": "byterover-cli",
|
|
@@ -631,7 +544,7 @@
|
|
|
631
544
|
],
|
|
632
545
|
"flags": {},
|
|
633
546
|
"hasDynamicHelp": false,
|
|
634
|
-
"hidden":
|
|
547
|
+
"hidden": true,
|
|
635
548
|
"hiddenAliases": [],
|
|
636
549
|
"id": "cipher-agent:set-prompt",
|
|
637
550
|
"pluginAlias": "byterover-cli",
|
|
@@ -656,7 +569,7 @@
|
|
|
656
569
|
],
|
|
657
570
|
"flags": {},
|
|
658
571
|
"hasDynamicHelp": false,
|
|
659
|
-
"hidden":
|
|
572
|
+
"hidden": true,
|
|
660
573
|
"hiddenAliases": [],
|
|
661
574
|
"id": "cipher-agent:show-prompt",
|
|
662
575
|
"pluginAlias": "byterover-cli",
|
|
@@ -759,5 +672,5 @@
|
|
|
759
672
|
]
|
|
760
673
|
}
|
|
761
674
|
},
|
|
762
|
-
"version": "0.3.
|
|
675
|
+
"version": "0.3.4"
|
|
763
676
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byterover-cli",
|
|
3
3
|
"description": "ByteRover's CLI",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.4",
|
|
5
5
|
"author": "ByteRover",
|
|
6
6
|
"bin": {
|
|
7
7
|
"brv": "./bin/run.js"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"@inquirer/prompts": "^7.9.0",
|
|
17
17
|
"@oclif/core": "^4",
|
|
18
18
|
"@oclif/plugin-help": "^6",
|
|
19
|
+
"@types/update-notifier": "^6.0.8",
|
|
19
20
|
"axios": "^1.12.2",
|
|
20
21
|
"better-sqlite3": "^11.5.0",
|
|
21
22
|
"chalk": "^5.6.2",
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"nanoid": "^5.1.6",
|
|
31
32
|
"open": "^10.2.0",
|
|
32
33
|
"openai": "^6.9.1",
|
|
34
|
+
"update-notifier": "^7.3.1",
|
|
33
35
|
"zod": "^3.25.76",
|
|
34
36
|
"zod-to-json-schema": "^3.24.6"
|
|
35
37
|
},
|
|
@@ -75,7 +77,10 @@
|
|
|
75
77
|
"dirname": "brv",
|
|
76
78
|
"commands": "./dist/commands",
|
|
77
79
|
"hooks": {
|
|
78
|
-
"init":
|
|
80
|
+
"init": [
|
|
81
|
+
"./dist/hooks/init/welcome",
|
|
82
|
+
"./dist/hooks/init/update-notifier"
|
|
83
|
+
],
|
|
79
84
|
"command_not_found": "./dist/hooks/command_not_found/handle-invalid-commands",
|
|
80
85
|
"error": "./dist/hooks/error/clean-errors",
|
|
81
86
|
"prerun": "./dist/hooks/prerun/validate-brv-config-version"
|
|
@@ -96,7 +101,7 @@
|
|
|
96
101
|
"lint": "eslint",
|
|
97
102
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
98
103
|
"posttest": "npm run lint",
|
|
99
|
-
"prepack": "npm run build && oclif manifest",
|
|
104
|
+
"prepack": "npm run build && BRV_ENV=production oclif manifest",
|
|
100
105
|
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
101
106
|
"version": "git add README.md"
|
|
102
107
|
},
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export class RuleError extends Error {
|
|
2
|
-
constructor(message) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = 'RuleError';
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
export class RuleExistsError extends RuleError {
|
|
8
|
-
constructor(message = 'Rule already exists') {
|
|
9
|
-
super(message);
|
|
10
|
-
this.name = 'RuleExistsError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Agent } from '../domain/entities/agent.js';
|
|
2
|
-
/**
|
|
3
|
-
* Interface for rule writer service operations.
|
|
4
|
-
*/
|
|
5
|
-
export interface IRuleWriterService {
|
|
6
|
-
/**
|
|
7
|
-
* Writes a rule for the given agent.
|
|
8
|
-
* @param agent The agent for which to write the rule.
|
|
9
|
-
* @param force Whether to force the rule to be written even if it already exists.
|
|
10
|
-
* @returns A promise that resolves when the rule has been written.
|
|
11
|
-
*/
|
|
12
|
-
writeRule: (agent: Agent, force: boolean) => Promise<void>;
|
|
13
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type Agent } from '../../core/domain/entities/agent.js';
|
|
2
|
-
import { type IFileService } from '../../core/interfaces/i-file-service.js';
|
|
3
|
-
import { IRuleTemplateService } from '../../core/interfaces/i-rule-template-service.js';
|
|
4
|
-
import { type IRuleWriterService } from '../../core/interfaces/i-rule-writer-service.js';
|
|
5
|
-
/**
|
|
6
|
-
* Service for writing agent-specific rule files.
|
|
7
|
-
* Uses IFileService to write files and RuleTemplateService to generate content.
|
|
8
|
-
*/
|
|
9
|
-
export declare class RuleWriterService implements IRuleWriterService {
|
|
10
|
-
private readonly fileService;
|
|
11
|
-
private readonly templateService;
|
|
12
|
-
/**
|
|
13
|
-
* Creates a new RuleWriterService.
|
|
14
|
-
* @param fileService The file service to use for writing files.
|
|
15
|
-
* @param templateService The template service to use for generating rule content.
|
|
16
|
-
*/
|
|
17
|
-
constructor(fileService: IFileService, templateService: IRuleTemplateService);
|
|
18
|
-
writeRule(agent: Agent, force: boolean): Promise<void>;
|
|
19
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { RuleExistsError } from '../../core/domain/errors/rule-error.js';
|
|
2
|
-
import { AGENT_RULE_CONFIGS } from './agent-rule-config.js';
|
|
3
|
-
import { BR_RULE_TAG } from './constants.js';
|
|
4
|
-
/**
|
|
5
|
-
* Service for writing agent-specific rule files.
|
|
6
|
-
* Uses IFileService to write files and RuleTemplateService to generate content.
|
|
7
|
-
*/
|
|
8
|
-
export class RuleWriterService {
|
|
9
|
-
fileService;
|
|
10
|
-
templateService;
|
|
11
|
-
/**
|
|
12
|
-
* Creates a new RuleWriterService.
|
|
13
|
-
* @param fileService The file service to use for writing files.
|
|
14
|
-
* @param templateService The template service to use for generating rule content.
|
|
15
|
-
*/
|
|
16
|
-
constructor(fileService, templateService) {
|
|
17
|
-
this.fileService = fileService;
|
|
18
|
-
this.templateService = templateService;
|
|
19
|
-
}
|
|
20
|
-
async writeRule(agent, force) {
|
|
21
|
-
const config = AGENT_RULE_CONFIGS[agent];
|
|
22
|
-
if (!config) {
|
|
23
|
-
throw new Error(`No configuration found for agent: ${agent}`);
|
|
24
|
-
}
|
|
25
|
-
const { filePath, writeMode } = config;
|
|
26
|
-
const fileExists = await this.fileService.exists(filePath);
|
|
27
|
-
if (writeMode === 'overwrite' && fileExists && !force) {
|
|
28
|
-
throw new RuleExistsError();
|
|
29
|
-
}
|
|
30
|
-
if (writeMode === 'append' && fileExists && !force) {
|
|
31
|
-
const content = await this.fileService.read(filePath);
|
|
32
|
-
if (content.includes(BR_RULE_TAG)) {
|
|
33
|
-
throw new RuleExistsError();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const ruleContent = await this.templateService.generateRuleContent(agent);
|
|
37
|
-
await this.fileService.write(ruleContent, filePath, writeMode);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
File without changes
|