byterover-cli 1.2.1 → 1.4.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 +76 -8
- package/dist/constants.d.ts +0 -5
- package/dist/constants.js +0 -5
- package/dist/core/domain/cipher/agent/agent-info.d.ts +17 -17
- package/dist/core/domain/cipher/errors/file-system-error.d.ts +11 -0
- package/dist/core/domain/cipher/errors/file-system-error.js +17 -0
- package/dist/core/domain/cipher/file-system/types.d.ts +40 -6
- package/dist/core/domain/cipher/llm/schemas.d.ts +14 -14
- package/dist/core/domain/cipher/session/session-metadata.d.ts +2 -2
- package/dist/core/domain/entities/agent.d.ts +1 -1
- package/dist/core/domain/entities/agent.js +11 -6
- package/dist/core/domain/entities/connector-type.d.ts +2 -1
- package/dist/core/domain/entities/connector-type.js +2 -1
- package/dist/core/domain/transport/schemas.d.ts +66 -66
- package/dist/core/interfaces/cipher/cipher-services.d.ts +0 -3
- package/dist/core/interfaces/cipher/index.d.ts +0 -2
- package/dist/core/interfaces/connectors/i-connector.d.ts +2 -2
- package/dist/core/interfaces/i-file-service.d.ts +7 -0
- package/dist/infra/auth/oauth-service.d.ts +15 -0
- package/dist/infra/auth/oauth-service.js +38 -2
- package/dist/infra/cipher/agent/agent-schemas.d.ts +42 -42
- package/dist/infra/cipher/file-system/binary-utils.d.ts +15 -2
- package/dist/infra/cipher/file-system/binary-utils.js +26 -3
- package/dist/infra/cipher/file-system/file-system-service.d.ts +9 -0
- package/dist/infra/cipher/file-system/file-system-service.js +91 -8
- package/dist/infra/cipher/file-system/pdf-extractor.d.ts +100 -0
- package/dist/infra/cipher/file-system/pdf-extractor.js +226 -0
- package/dist/infra/cipher/llm/context/context-manager.js +7 -9
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +5 -1
- package/dist/infra/cipher/llm/internal-llm-service.js +57 -46
- package/dist/infra/cipher/system-prompt/contributor-schemas.d.ts +8 -8
- package/dist/infra/cipher/system-prompt/schemas.d.ts +5 -5
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +24 -4
- package/dist/infra/connectors/connector-manager.js +2 -0
- package/dist/infra/connectors/hook/hook-connector.d.ts +1 -1
- package/dist/infra/connectors/hook/hook-connector.js +3 -3
- package/dist/infra/connectors/mcp/mcp-connector.d.ts +1 -1
- package/dist/infra/connectors/mcp/mcp-connector.js +4 -4
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +4 -0
- package/dist/infra/connectors/rules/rules-connector-config.js +4 -0
- package/dist/infra/connectors/rules/rules-connector.d.ts +1 -1
- package/dist/infra/connectors/rules/rules-connector.js +4 -4
- package/dist/infra/connectors/shared/template-service.js +4 -0
- package/dist/infra/connectors/skill/index.d.ts +1 -0
- package/dist/infra/connectors/skill/index.js +1 -0
- package/dist/infra/connectors/skill/skill-connector-config.d.ts +45 -0
- package/dist/infra/connectors/skill/skill-connector-config.js +26 -0
- package/dist/infra/connectors/skill/skill-connector.d.ts +39 -0
- package/dist/infra/connectors/skill/skill-connector.js +160 -0
- package/dist/infra/connectors/skill/skill-content-loader.d.ts +18 -0
- package/dist/infra/connectors/skill/skill-content-loader.js +33 -0
- 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/tools/brv-curate-tool.d.ts +10 -4
- package/dist/infra/mcp/tools/brv-curate-tool.js +9 -4
- package/dist/infra/mcp/tools/task-result-waiter.js +8 -0
- package/dist/infra/process/agent-worker.js +30 -14
- package/dist/infra/process/task-queue-manager.d.ts +23 -34
- package/dist/infra/process/task-queue-manager.js +57 -118
- package/dist/infra/process/transport-handlers.js +1 -7
- package/dist/infra/repl/commands/connectors-command.js +1 -1
- package/dist/infra/repl/commands/space/switch-command.js +0 -2
- package/dist/infra/usecase/connectors-use-case.js +8 -2
- package/dist/infra/usecase/curate-use-case.js +10 -4
- package/dist/infra/usecase/init-use-case.js +1 -1
- package/dist/infra/usecase/reset-use-case.d.ts +1 -0
- package/dist/infra/usecase/reset-use-case.js +4 -1
- package/dist/infra/usecase/space-switch-use-case.d.ts +0 -10
- package/dist/infra/usecase/space-switch-use-case.js +7 -37
- package/dist/{commands → oclif/commands}/curate.d.ts +1 -1
- package/dist/{commands → oclif/commands}/curate.js +6 -6
- package/dist/{commands → oclif/commands}/hook-prompt-submit.d.ts +1 -1
- package/dist/{commands → oclif/commands}/hook-prompt-submit.js +3 -3
- package/dist/{commands → oclif/commands}/main.js +10 -10
- package/dist/{commands → oclif/commands}/mcp.js +2 -2
- package/dist/{commands → oclif/commands}/query.d.ts +1 -1
- package/dist/{commands → oclif/commands}/query.js +6 -6
- package/dist/{commands → oclif/commands}/status.d.ts +1 -1
- package/dist/{commands → oclif/commands}/status.js +8 -8
- package/dist/oclif/constants.d.ts +11 -0
- package/dist/oclif/constants.js +11 -0
- package/dist/{hooks → oclif/hooks}/init/welcome.js +4 -17
- package/dist/{hooks → oclif/hooks}/prerun/validate-brv-config-version.d.ts +1 -1
- package/dist/{hooks → oclif/hooks}/prerun/validate-brv-config-version.js +2 -2
- package/dist/resources/prompts/curate.yml +1 -0
- package/dist/resources/tools/read_file.txt +5 -2
- package/dist/templates/skill/SKILL.md +91 -0
- package/dist/templates/skill/TROUBLESHOOTING.md +50 -0
- package/dist/templates/skill/WORKFLOWS.md +229 -0
- package/dist/utils/file-validator.js +8 -4
- package/dist/utils/type-guards.d.ts +11 -0
- package/dist/utils/type-guards.js +13 -0
- package/oclif.manifest.json +7 -53
- package/package.json +12 -10
- package/dist/commands/watch.d.ts +0 -25
- package/dist/commands/watch.js +0 -175
- package/dist/core/interfaces/cipher/i-coding-agent-log-parser.d.ts +0 -20
- package/dist/core/interfaces/cipher/i-coding-agent-log-parser.js +0 -1
- package/dist/core/interfaces/cipher/i-coding-agent-log-watcher.d.ts +0 -31
- package/dist/core/interfaces/cipher/i-coding-agent-log-watcher.js +0 -1
- package/dist/core/interfaces/i-file-watcher-service.d.ts +0 -41
- package/dist/core/interfaces/i-file-watcher-service.js +0 -1
- package/dist/core/interfaces/parser/i-clean-parser-service.d.ts +0 -18
- package/dist/core/interfaces/parser/i-clean-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-raw-parser-service.d.ts +0 -17
- package/dist/core/interfaces/parser/i-raw-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-session-normalizer.d.ts +0 -56
- package/dist/core/interfaces/parser/i-session-normalizer.js +0 -1
- package/dist/infra/cipher/parsers/coding-agent-log-parser.d.ts +0 -24
- package/dist/infra/cipher/parsers/coding-agent-log-parser.js +0 -51
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.d.ts +0 -14
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.js +0 -55
- package/dist/infra/parsers/clean/clean-claude-service.d.ts +0 -111
- package/dist/infra/parsers/clean/clean-claude-service.js +0 -271
- package/dist/infra/parsers/clean/clean-codex-service.d.ts +0 -231
- package/dist/infra/parsers/clean/clean-codex-service.js +0 -534
- package/dist/infra/parsers/clean/clean-copilot-service.d.ts +0 -255
- package/dist/infra/parsers/clean/clean-copilot-service.js +0 -729
- package/dist/infra/parsers/clean/clean-cursor-service.d.ts +0 -161
- package/dist/infra/parsers/clean/clean-cursor-service.js +0 -432
- package/dist/infra/parsers/clean/clean-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/clean/clean-parser-service-factory.js +0 -80
- package/dist/infra/parsers/clean/shared.d.ts +0 -84
- package/dist/infra/parsers/clean/shared.js +0 -273
- package/dist/infra/parsers/raw/raw-claude-service.d.ts +0 -195
- package/dist/infra/parsers/raw/raw-claude-service.js +0 -548
- package/dist/infra/parsers/raw/raw-codex-service.d.ts +0 -313
- package/dist/infra/parsers/raw/raw-codex-service.js +0 -782
- package/dist/infra/parsers/raw/raw-copilot-service.d.ts +0 -196
- package/dist/infra/parsers/raw/raw-copilot-service.js +0 -558
- package/dist/infra/parsers/raw/raw-cursor-service.d.ts +0 -316
- package/dist/infra/parsers/raw/raw-cursor-service.js +0 -818
- package/dist/infra/parsers/raw/raw-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/raw/raw-parser-service-factory.js +0 -81
- package/dist/infra/process/constants.d.ts +0 -1
- package/dist/infra/process/constants.js +0 -1
- package/dist/infra/watcher/file-watcher-service.d.ts +0 -10
- package/dist/infra/watcher/file-watcher-service.js +0 -81
- /package/dist/{commands → oclif/commands}/main.d.ts +0 -0
- /package/dist/{commands → oclif/commands}/mcp.d.ts +0 -0
- /package/dist/{hooks → oclif/hooks}/command_not_found/handle-invalid-commands.d.ts +0 -0
- /package/dist/{hooks → oclif/hooks}/command_not_found/handle-invalid-commands.js +0 -0
- /package/dist/{hooks → oclif/hooks}/error/clean-errors.d.ts +0 -0
- /package/dist/{hooks → oclif/hooks}/error/clean-errors.js +0 -0
- /package/dist/{hooks → oclif/hooks}/init/update-notifier.d.ts +0 -0
- /package/dist/{hooks → oclif/hooks}/init/update-notifier.js +0 -0
- /package/dist/{hooks → oclif/hooks}/init/welcome.d.ts +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { getDocumentProxy, getMeta } from 'unpdf';
|
|
2
|
+
import { PdfExtractionError } from '../../../core/domain/cipher/errors/file-system-error.js';
|
|
3
|
+
/**
|
|
4
|
+
* PDF magic bytes: %PDF-
|
|
5
|
+
*/
|
|
6
|
+
const PDF_MAGIC_BYTES = [0x25, 0x50, 0x44, 0x46, 0x2d];
|
|
7
|
+
/**
|
|
8
|
+
* Default number of pages to extract when no limit specified.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_PAGE_LIMIT = 100;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of pages allowed per extraction.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_PAGE_LIMIT = 200;
|
|
15
|
+
/**
|
|
16
|
+
* PDF text extraction and metadata extraction utility.
|
|
17
|
+
* Provides page-by-page extraction with pagination support.
|
|
18
|
+
*
|
|
19
|
+
* Features:
|
|
20
|
+
* - Magic byte validation
|
|
21
|
+
* - Fast metadata-only extraction
|
|
22
|
+
* - Page-by-page text extraction with offset/limit
|
|
23
|
+
* - Default: 100 pages, max: 200 pages per extraction
|
|
24
|
+
*/
|
|
25
|
+
export class PdfExtractor {
|
|
26
|
+
/**
|
|
27
|
+
* Extracts metadata from a PDF buffer without extracting text.
|
|
28
|
+
* This is a fast path when you only need page count, title, author, etc.
|
|
29
|
+
*
|
|
30
|
+
* @param buffer - PDF file buffer
|
|
31
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
32
|
+
* @returns PDF metadata
|
|
33
|
+
*/
|
|
34
|
+
static async extractMetadata(buffer, filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const pdf = await getDocumentProxy(new Uint8Array(buffer));
|
|
37
|
+
const meta = await getMeta(pdf);
|
|
38
|
+
return PdfExtractor.buildMetadataFromInfo(pdf.numPages, meta.info);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw PdfExtractor.wrapExtractionError(error, filePath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extracts text from a PDF buffer with pagination support.
|
|
46
|
+
*
|
|
47
|
+
* @param buffer - PDF file buffer
|
|
48
|
+
* @param filePath - Path to the PDF file (for error messages)
|
|
49
|
+
* @param options - Extraction options (offset, limit)
|
|
50
|
+
* @returns Extraction result with pages, metadata, and continuation info
|
|
51
|
+
*/
|
|
52
|
+
static async extractText(buffer, filePath, options = {}) {
|
|
53
|
+
// Validate PDF magic bytes
|
|
54
|
+
if (!PdfExtractor.isValidPdf(buffer)) {
|
|
55
|
+
throw new PdfExtractionError(filePath, 'Invalid PDF file format (missing PDF header)');
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const pdf = await getDocumentProxy(new Uint8Array(buffer));
|
|
59
|
+
const meta = await getMeta(pdf);
|
|
60
|
+
const totalPages = pdf.numPages;
|
|
61
|
+
const metaInfo = meta.info;
|
|
62
|
+
// Calculate pagination
|
|
63
|
+
const offset = Math.max(1, options.offset ?? 1);
|
|
64
|
+
const limit = Math.min(options.limit ?? DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT);
|
|
65
|
+
// Return empty result if offset is beyond total pages
|
|
66
|
+
if (offset > totalPages) {
|
|
67
|
+
return {
|
|
68
|
+
hasMore: false,
|
|
69
|
+
metadata: PdfExtractor.buildMetadataFromInfo(totalPages, metaInfo),
|
|
70
|
+
pages: [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Calculate page range
|
|
74
|
+
const endPage = Math.min(offset + limit - 1, totalPages);
|
|
75
|
+
// Extract text only from requested pages using PDF.js page-level API
|
|
76
|
+
const pages = await PdfExtractor.extractPagesFromDocument(pdf, offset, endPage);
|
|
77
|
+
return {
|
|
78
|
+
hasMore: endPage < totalPages,
|
|
79
|
+
metadata: PdfExtractor.buildMetadataFromInfo(totalPages, metaInfo),
|
|
80
|
+
pages,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw PdfExtractor.wrapExtractionError(error, filePath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Checks if a buffer contains valid PDF magic bytes.
|
|
89
|
+
* @param buffer - Buffer to check
|
|
90
|
+
* @returns true if buffer starts with %PDF-
|
|
91
|
+
*/
|
|
92
|
+
static isValidPdf(buffer) {
|
|
93
|
+
if (buffer.length < PDF_MAGIC_BYTES.length) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
for (const [index, byte] of PDF_MAGIC_BYTES.entries()) {
|
|
97
|
+
if (buffer[index] !== byte) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Builds PdfMetadata from unpdf meta info object.
|
|
105
|
+
* @param pageCount - Total number of pages
|
|
106
|
+
* @param info - Optional info object from unpdf getMeta
|
|
107
|
+
* @returns PdfMetadata object
|
|
108
|
+
*/
|
|
109
|
+
static buildMetadataFromInfo(pageCount, info) {
|
|
110
|
+
const metadata = { pageCount };
|
|
111
|
+
if (!info) {
|
|
112
|
+
return metadata;
|
|
113
|
+
}
|
|
114
|
+
if (typeof info.Title === 'string' && info.Title.trim()) {
|
|
115
|
+
metadata.title = info.Title.trim();
|
|
116
|
+
}
|
|
117
|
+
if (typeof info.Author === 'string' && info.Author.trim()) {
|
|
118
|
+
metadata.author = info.Author.trim();
|
|
119
|
+
}
|
|
120
|
+
if (info.CreationDate) {
|
|
121
|
+
const parsed = PdfExtractor.parsePdfDate(info.CreationDate);
|
|
122
|
+
if (parsed) {
|
|
123
|
+
metadata.creationDate = parsed;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return metadata;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extracts text from specific pages of a PDF document.
|
|
130
|
+
* Uses PDF.js page-level API for efficient extraction of page ranges.
|
|
131
|
+
*
|
|
132
|
+
* @param pdf - PDF document proxy from unpdf
|
|
133
|
+
* @param startPage - Starting page number (1-based)
|
|
134
|
+
* @param endPage - Ending page number (1-based, inclusive)
|
|
135
|
+
* @returns Array of PdfPageContent with extracted text
|
|
136
|
+
*/
|
|
137
|
+
static async extractPagesFromDocument(pdf, startPage, endPage) {
|
|
138
|
+
// Build array of page numbers to extract
|
|
139
|
+
const pageNumbers = [];
|
|
140
|
+
for (let pageNum = startPage; pageNum <= endPage; pageNum++) {
|
|
141
|
+
pageNumbers.push(pageNum);
|
|
142
|
+
}
|
|
143
|
+
// Extract all requested pages in parallel
|
|
144
|
+
const pages = await Promise.all(pageNumbers.map(async (pageNum) => {
|
|
145
|
+
const page = await pdf.getPage(pageNum);
|
|
146
|
+
const textContent = await page.getTextContent();
|
|
147
|
+
// Extract text from text items, handling EOL markers
|
|
148
|
+
// TextContent.items contains TextItem and TextMarkedContent - we only want TextItem (has 'str')
|
|
149
|
+
const text = textContent.items
|
|
150
|
+
.filter((item) => typeof item === 'object' && item !== null && 'str' in item)
|
|
151
|
+
.map((item) => item.str + (item.hasEOL ? '\n' : ''))
|
|
152
|
+
.join('');
|
|
153
|
+
return {
|
|
154
|
+
pageNumber: pageNum,
|
|
155
|
+
text: text.trim(),
|
|
156
|
+
};
|
|
157
|
+
}));
|
|
158
|
+
return pages;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Extracts a meaningful error message from an unknown error.
|
|
162
|
+
*/
|
|
163
|
+
static getExtractionErrorMessage(error) {
|
|
164
|
+
if (error instanceof Error) {
|
|
165
|
+
return error.message;
|
|
166
|
+
}
|
|
167
|
+
if (typeof error === 'string') {
|
|
168
|
+
return error;
|
|
169
|
+
}
|
|
170
|
+
return 'Unknown PDF extraction error';
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Parses PDF date string format (D:YYYYMMDDHHmmSS) to Date object.
|
|
174
|
+
* @param dateStr - PDF date string
|
|
175
|
+
* @returns Parsed Date or undefined if invalid
|
|
176
|
+
*/
|
|
177
|
+
static parsePdfDate(dateStr) {
|
|
178
|
+
if (!dateStr) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
// PDF date format: D:YYYYMMDDHHmmSS+HH'mm' or variations
|
|
182
|
+
const match = dateStr.match(/D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?/);
|
|
183
|
+
if (!match) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const year = Number.parseInt(match[1], 10);
|
|
187
|
+
const month = match[2] ? Number.parseInt(match[2], 10) - 1 : 0;
|
|
188
|
+
const day = match[3] ? Number.parseInt(match[3], 10) : 1;
|
|
189
|
+
const hour = match[4] ? Number.parseInt(match[4], 10) : 0;
|
|
190
|
+
const minute = match[5] ? Number.parseInt(match[5], 10) : 0;
|
|
191
|
+
const second = match[6] ? Number.parseInt(match[6], 10) : 0;
|
|
192
|
+
return new Date(year, month, day, hour, minute, second);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Wraps extraction errors with appropriate PdfExtractionError.
|
|
196
|
+
* @param error - The caught error
|
|
197
|
+
* @param filePath - Path to the PDF file
|
|
198
|
+
* @returns PdfExtractionError with appropriate message
|
|
199
|
+
*/
|
|
200
|
+
static wrapExtractionError(error, filePath) {
|
|
201
|
+
const errorMessage = PdfExtractor.getExtractionErrorMessage(error);
|
|
202
|
+
const lowerMessage = errorMessage.toLowerCase();
|
|
203
|
+
if (lowerMessage.includes('password') || lowerMessage.includes('encrypted')) {
|
|
204
|
+
return new PdfExtractionError(filePath, 'PDF is password-protected or encrypted');
|
|
205
|
+
}
|
|
206
|
+
return new PdfExtractionError(filePath, errorMessage);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Formats extracted PDF pages into a readable string with page separators.
|
|
211
|
+
* @param pages - Array of extracted page contents
|
|
212
|
+
* @param metadata - PDF metadata
|
|
213
|
+
* @param hasMore - Whether there are more pages
|
|
214
|
+
* @param nextOffset - Next offset for continuation (if hasMore is true)
|
|
215
|
+
* @returns Formatted string with page separators
|
|
216
|
+
*/
|
|
217
|
+
export function formatPdfContent(pages, metadata, hasMore, nextOffset) {
|
|
218
|
+
if (pages.length === 0) {
|
|
219
|
+
return '';
|
|
220
|
+
}
|
|
221
|
+
const formattedPages = pages.map((page) => `--- Page ${page.pageNumber} ---\n${page.text}`).join('\n\n');
|
|
222
|
+
const truncationNote = hasMore
|
|
223
|
+
? `\n\n(PDF has more pages. Use offset=${nextOffset} to continue)`
|
|
224
|
+
: `\n\n(End of PDF - ${metadata.pageCount} pages)`;
|
|
225
|
+
return formattedPages + truncationNote;
|
|
226
|
+
}
|
|
@@ -200,15 +200,13 @@ export class ContextManager {
|
|
|
200
200
|
content,
|
|
201
201
|
role: 'user',
|
|
202
202
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
});
|
|
203
|
+
this.messages.push(message);
|
|
204
|
+
try {
|
|
205
|
+
await this.persistHistory();
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
this.logger.error('Failed to persist history after user message', { error, sessionId: this.sessionId });
|
|
209
|
+
}
|
|
212
210
|
}
|
|
213
211
|
/**
|
|
214
212
|
* Clear all messages from the conversation history.
|
|
@@ -72,6 +72,7 @@ export declare class ByteRoverLLMService implements ILLMService {
|
|
|
72
72
|
private readonly loopDetector;
|
|
73
73
|
private readonly memoryManager?;
|
|
74
74
|
private readonly metadataHandler;
|
|
75
|
+
private readonly mutex;
|
|
75
76
|
private readonly outputProcessor;
|
|
76
77
|
private readonly providerType;
|
|
77
78
|
private readonly sessionEventBus;
|
|
@@ -257,10 +258,13 @@ export declare class ByteRoverLLMService implements ILLMService {
|
|
|
257
258
|
* Execute a single iteration of the agentic loop.
|
|
258
259
|
*
|
|
259
260
|
* @param options - Iteration options
|
|
261
|
+
* @param options.executionContext - Optional execution context
|
|
262
|
+
* @param options.fileData - Optional file data (only used on first iteration)
|
|
263
|
+
* @param options.imageData - Optional image data (only used on first iteration)
|
|
260
264
|
* @param options.iterationCount - Current iteration number
|
|
261
265
|
* @param options.taskId - Task ID from usecase for billing tracking
|
|
266
|
+
* @param options.textInput - User input text (only used on first iteration)
|
|
262
267
|
* @param options.tools - Available tools for this iteration
|
|
263
|
-
* @param options.executionContext - Optional execution context
|
|
264
268
|
* @returns Final response string if complete, null if more iterations needed
|
|
265
269
|
*/
|
|
266
270
|
private executeAgenticIteration;
|
|
@@ -6,6 +6,7 @@ import { NoOpLogger } from '../../../core/interfaces/cipher/i-logger.js';
|
|
|
6
6
|
import { getErrorMessage } from '../../../utils/error-helpers.js';
|
|
7
7
|
import { EnvironmentContextBuilder } from '../system-prompt/environment-context-builder.js';
|
|
8
8
|
import { ToolMetadataHandler } from '../tools/streaming/metadata-handler.js';
|
|
9
|
+
import { AsyncMutex } from './context/async-mutex.js';
|
|
9
10
|
import { ContextManager } from './context/context-manager.js';
|
|
10
11
|
import { LoopDetector } from './context/loop-detector.js';
|
|
11
12
|
import { ClaudeMessageFormatter } from './formatters/claude-formatter.js';
|
|
@@ -44,6 +45,7 @@ export class ByteRoverLLMService {
|
|
|
44
45
|
loopDetector;
|
|
45
46
|
memoryManager;
|
|
46
47
|
metadataHandler;
|
|
48
|
+
mutex = new AsyncMutex();
|
|
47
49
|
outputProcessor;
|
|
48
50
|
providerType;
|
|
49
51
|
sessionEventBus;
|
|
@@ -147,8 +149,6 @@ export class ByteRoverLLMService {
|
|
|
147
149
|
async completeTask(textInput, options) {
|
|
148
150
|
// Extract options with defaults
|
|
149
151
|
const { executionContext, fileData, imageData, signal, taskId } = options ?? {};
|
|
150
|
-
// Add user message to context
|
|
151
|
-
await this.contextManager.addUserMessage(textInput, imageData, fileData);
|
|
152
152
|
// Get filtered tools based on command type (e.g., only read-only tools for 'query')
|
|
153
153
|
const toolSet = this.toolManager.getToolsForCommand(options?.executionContext?.commandType);
|
|
154
154
|
// Create state machine with configured limits
|
|
@@ -171,8 +171,11 @@ export class ByteRoverLLMService {
|
|
|
171
171
|
// eslint-disable-next-line no-await-in-loop -- Sequential iterations required for agentic loop
|
|
172
172
|
const result = await this.executeAgenticIteration({
|
|
173
173
|
executionContext,
|
|
174
|
+
fileData,
|
|
175
|
+
imageData,
|
|
174
176
|
iterationCount: stateMachine.getContext().turnCount,
|
|
175
177
|
taskId,
|
|
178
|
+
textInput,
|
|
176
179
|
tools: toolSet,
|
|
177
180
|
});
|
|
178
181
|
if (result !== null) {
|
|
@@ -477,14 +480,17 @@ export class ByteRoverLLMService {
|
|
|
477
480
|
* Execute a single iteration of the agentic loop.
|
|
478
481
|
*
|
|
479
482
|
* @param options - Iteration options
|
|
483
|
+
* @param options.executionContext - Optional execution context
|
|
484
|
+
* @param options.fileData - Optional file data (only used on first iteration)
|
|
485
|
+
* @param options.imageData - Optional image data (only used on first iteration)
|
|
480
486
|
* @param options.iterationCount - Current iteration number
|
|
481
487
|
* @param options.taskId - Task ID from usecase for billing tracking
|
|
488
|
+
* @param options.textInput - User input text (only used on first iteration)
|
|
482
489
|
* @param options.tools - Available tools for this iteration
|
|
483
|
-
* @param options.executionContext - Optional execution context
|
|
484
490
|
* @returns Final response string if complete, null if more iterations needed
|
|
485
491
|
*/
|
|
486
492
|
async executeAgenticIteration(options) {
|
|
487
|
-
const { executionContext, iterationCount, taskId, tools } = options;
|
|
493
|
+
const { executionContext, fileData, imageData, iterationCount, taskId, textInput, tools } = options;
|
|
488
494
|
// Build system prompt using SystemPromptManager (before compression for correct token accounting)
|
|
489
495
|
// Use filtered tool names based on command type (e.g., only read-only tools for 'query')
|
|
490
496
|
const availableTools = this.toolManager.getToolNamesForCommand(executionContext?.commandType);
|
|
@@ -533,54 +539,59 @@ export class ByteRoverLLMService {
|
|
|
533
539
|
reflectionType,
|
|
534
540
|
});
|
|
535
541
|
}
|
|
536
|
-
// Get token count for logging (using system prompt for token accounting)
|
|
537
|
-
const systemPromptTokens = this.generator.estimateTokensSync(systemPrompt);
|
|
538
|
-
const messages = this.contextManager.getMessages();
|
|
539
|
-
const messageTokenCounts = messages.map((msg) => this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)));
|
|
540
|
-
const maxMessageTokens = this.config.maxInputTokens - systemPromptTokens;
|
|
541
|
-
// Target utilization to leave headroom for response
|
|
542
|
-
const targetMessageTokens = Math.floor(maxMessageTokens * TARGET_MESSAGE_TOKEN_UTILIZATION);
|
|
543
|
-
this.contextManager.compressMessage(targetMessageTokens, messageTokenCounts);
|
|
544
|
-
// Calculate tokens after compression
|
|
545
|
-
const compressedMessagesTokens = this.contextManager
|
|
546
|
-
.getMessages()
|
|
547
|
-
.reduce((total, msg) => total +
|
|
548
|
-
this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
|
|
549
|
-
const tokensUsed = systemPromptTokens + compressedMessagesTokens;
|
|
550
|
-
// Verbose: Log messages that will be sent to LLM
|
|
551
|
-
if (this.config.verbose) {
|
|
552
|
-
console.log('\n========== MESSAGES (Sent to LLM) ==========');
|
|
553
|
-
console.log(JSON.stringify(this.contextManager.getMessages(), null, 2));
|
|
554
|
-
console.log('========== END MESSAGES ==========\n');
|
|
555
|
-
// Log token usage for monitoring compression behavior
|
|
556
|
-
console.log(`[ByteRoverLLMService] [Iter ${iterationCount + 1}/${this.config.maxIterations}] Sending to LLM: ${tokensUsed} tokens (max: ${this.config.maxInputTokens})`);
|
|
557
|
-
}
|
|
558
542
|
// Final iteration optimization for query: strip tools (reflection already added above)
|
|
559
543
|
let toolsForThisIteration = tools;
|
|
560
544
|
if (executionContext?.commandType === 'query' && iterationCount === this.config.maxIterations - 1) {
|
|
561
545
|
toolsForThisIteration = {}; // Empty toolset forces text response
|
|
562
546
|
}
|
|
563
|
-
//
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
//
|
|
547
|
+
// Get token count for logging (using system prompt for token accounting)
|
|
548
|
+
const systemPromptTokens = this.generator.estimateTokensSync(systemPrompt);
|
|
549
|
+
// Add user message and compress context within mutex lock
|
|
550
|
+
return this.mutex.withLock(async () => {
|
|
551
|
+
// Add user message to context only on the first iteration
|
|
552
|
+
await this.contextManager.addUserMessage(textInput, imageData, fileData);
|
|
553
|
+
const messages = this.contextManager.getMessages();
|
|
554
|
+
const messageTokenCounts = messages.map((msg) => this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)));
|
|
555
|
+
const maxMessageTokens = this.config.maxInputTokens - systemPromptTokens;
|
|
556
|
+
// Target utilization to leave headroom for response
|
|
557
|
+
const targetMessageTokens = Math.floor(maxMessageTokens * TARGET_MESSAGE_TOKEN_UTILIZATION);
|
|
558
|
+
this.contextManager.compressMessage(targetMessageTokens, messageTokenCounts);
|
|
559
|
+
// Calculate tokens after compression
|
|
560
|
+
const compressedMessagesTokens = this.contextManager
|
|
561
|
+
.getMessages()
|
|
562
|
+
.reduce((total, msg) => total +
|
|
563
|
+
this.generator.estimateTokensSync(typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)), 0);
|
|
564
|
+
const tokensUsed = systemPromptTokens + compressedMessagesTokens;
|
|
565
|
+
// Verbose: Log messages that will be sent to LLM
|
|
566
|
+
if (this.config.verbose) {
|
|
567
|
+
console.log('\n========== MESSAGES (Sent to LLM) ==========');
|
|
568
|
+
console.log(JSON.stringify(this.contextManager.getMessages(), null, 2));
|
|
569
|
+
console.log('========== END MESSAGES ==========\n');
|
|
570
|
+
// Log token usage for monitoring compression behavior
|
|
571
|
+
console.log(`[ByteRoverLLMService] [Iter ${iterationCount + 1}/${this.config.maxIterations}] Sending to LLM: ${tokensUsed} tokens (max: ${this.config.maxInputTokens})`);
|
|
572
|
+
}
|
|
573
|
+
// Build generation request
|
|
574
|
+
const request = this.buildGenerateContentRequest({
|
|
575
|
+
executionContext,
|
|
576
|
+
systemPrompt,
|
|
577
|
+
taskId,
|
|
578
|
+
tools: toolsForThisIteration,
|
|
579
|
+
});
|
|
580
|
+
// Call LLM via generator (retry + logging handled by decorators)
|
|
581
|
+
const lastMessage = await this.callLLMAndParseResponse(request);
|
|
582
|
+
// Check if there are tool calls
|
|
583
|
+
if (!lastMessage.toolCalls || lastMessage.toolCalls.length === 0) {
|
|
584
|
+
const response = await this.handleFinalResponse(lastMessage, taskId);
|
|
585
|
+
// Auto-compaction check after assistant response
|
|
586
|
+
await this.checkAndTriggerCompaction(taskId ?? '');
|
|
587
|
+
return response;
|
|
588
|
+
}
|
|
589
|
+
// Has tool calls - handle them (pass taskId for subagent billing)
|
|
590
|
+
await this.handleToolCalls(lastMessage, taskId);
|
|
591
|
+
// Auto-compaction check after tool execution batch
|
|
576
592
|
await this.checkAndTriggerCompaction(taskId ?? '');
|
|
577
|
-
return
|
|
578
|
-
}
|
|
579
|
-
// Has tool calls - handle them (pass taskId for subagent billing)
|
|
580
|
-
await this.handleToolCalls(lastMessage, taskId);
|
|
581
|
-
// Auto-compaction check after tool execution batch
|
|
582
|
-
await this.checkAndTriggerCompaction(taskId ?? '');
|
|
583
|
-
return null;
|
|
593
|
+
return null;
|
|
594
|
+
});
|
|
584
595
|
}
|
|
585
596
|
/**
|
|
586
597
|
* Execute a single tool call in parallel (without adding to context).
|
|
@@ -12,14 +12,14 @@ export declare const StaticContributorConfigSchema: z.ZodObject<{
|
|
|
12
12
|
type: z.ZodLiteral<"static">;
|
|
13
13
|
}, "strict", z.ZodTypeAny, {
|
|
14
14
|
type: "static";
|
|
15
|
-
content: string;
|
|
16
15
|
id: string;
|
|
16
|
+
content: string;
|
|
17
17
|
enabled: boolean;
|
|
18
18
|
priority: number;
|
|
19
19
|
}, {
|
|
20
20
|
type: "static";
|
|
21
|
-
content: string;
|
|
22
21
|
id: string;
|
|
22
|
+
content: string;
|
|
23
23
|
priority: number;
|
|
24
24
|
enabled?: boolean | undefined;
|
|
25
25
|
}>;
|
|
@@ -179,14 +179,14 @@ export declare const ContributorConfigSchema: z.ZodDiscriminatedUnion<"type", [z
|
|
|
179
179
|
type: z.ZodLiteral<"static">;
|
|
180
180
|
}, "strict", z.ZodTypeAny, {
|
|
181
181
|
type: "static";
|
|
182
|
-
content: string;
|
|
183
182
|
id: string;
|
|
183
|
+
content: string;
|
|
184
184
|
enabled: boolean;
|
|
185
185
|
priority: number;
|
|
186
186
|
}, {
|
|
187
187
|
type: "static";
|
|
188
|
-
content: string;
|
|
189
188
|
id: string;
|
|
189
|
+
content: string;
|
|
190
190
|
priority: number;
|
|
191
191
|
enabled?: boolean | undefined;
|
|
192
192
|
}>, z.ZodObject<{
|
|
@@ -311,14 +311,14 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
|
|
|
311
311
|
type: z.ZodLiteral<"static">;
|
|
312
312
|
}, "strict", z.ZodTypeAny, {
|
|
313
313
|
type: "static";
|
|
314
|
-
content: string;
|
|
315
314
|
id: string;
|
|
315
|
+
content: string;
|
|
316
316
|
enabled: boolean;
|
|
317
317
|
priority: number;
|
|
318
318
|
}, {
|
|
319
319
|
type: "static";
|
|
320
|
-
content: string;
|
|
321
320
|
id: string;
|
|
321
|
+
content: string;
|
|
322
322
|
priority: number;
|
|
323
323
|
enabled?: boolean | undefined;
|
|
324
324
|
}>, z.ZodObject<{
|
|
@@ -433,8 +433,8 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
|
|
|
433
433
|
}, "strict", z.ZodTypeAny, {
|
|
434
434
|
contributors: ({
|
|
435
435
|
type: "static";
|
|
436
|
-
content: string;
|
|
437
436
|
id: string;
|
|
437
|
+
content: string;
|
|
438
438
|
enabled: boolean;
|
|
439
439
|
priority: number;
|
|
440
440
|
} | {
|
|
@@ -471,8 +471,8 @@ export declare const SystemPromptManagerConfigSchema: z.ZodObject<{
|
|
|
471
471
|
}, {
|
|
472
472
|
contributors: ({
|
|
473
473
|
type: "static";
|
|
474
|
-
content: string;
|
|
475
474
|
id: string;
|
|
475
|
+
content: string;
|
|
476
476
|
priority: number;
|
|
477
477
|
enabled?: boolean | undefined;
|
|
478
478
|
} | {
|
|
@@ -12,28 +12,28 @@ export declare const PromptConfigSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
|
12
12
|
prompt: z.ZodOptional<z.ZodString>;
|
|
13
13
|
prompts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
14
14
|
}, "strict", z.ZodTypeAny, {
|
|
15
|
-
prompt?: string | undefined;
|
|
16
15
|
description?: string | undefined;
|
|
16
|
+
prompt?: string | undefined;
|
|
17
17
|
excludedTools?: string[] | undefined;
|
|
18
18
|
prompts?: Record<string, string> | undefined;
|
|
19
19
|
}, {
|
|
20
|
-
prompt?: string | undefined;
|
|
21
20
|
description?: string | undefined;
|
|
21
|
+
prompt?: string | undefined;
|
|
22
22
|
excludedTools?: string[] | undefined;
|
|
23
23
|
prompts?: Record<string, string> | undefined;
|
|
24
24
|
}>, {
|
|
25
|
-
prompt?: string | undefined;
|
|
26
25
|
description?: string | undefined;
|
|
26
|
+
prompt?: string | undefined;
|
|
27
27
|
excludedTools?: string[] | undefined;
|
|
28
28
|
prompts?: Record<string, string> | undefined;
|
|
29
29
|
}, {
|
|
30
|
-
prompt?: string | undefined;
|
|
31
30
|
description?: string | undefined;
|
|
31
|
+
prompt?: string | undefined;
|
|
32
32
|
excludedTools?: string[] | undefined;
|
|
33
33
|
prompts?: Record<string, string> | undefined;
|
|
34
34
|
}>, {
|
|
35
|
-
prompt?: string | undefined;
|
|
36
35
|
description?: string | undefined;
|
|
36
|
+
prompt?: string | undefined;
|
|
37
37
|
excludedTools?: string[] | undefined;
|
|
38
38
|
prompts?: Record<string, string> | undefined;
|
|
39
39
|
}, unknown>;
|
|
@@ -7,8 +7,23 @@ import { isImageFile } from '../../file-system/binary-utils.js';
|
|
|
7
7
|
const ReadFileInputSchema = z
|
|
8
8
|
.object({
|
|
9
9
|
filePath: z.string().describe('Path to the file to read (absolute or relative to working directory)'),
|
|
10
|
-
limit: z
|
|
11
|
-
|
|
10
|
+
limit: z
|
|
11
|
+
.number()
|
|
12
|
+
.int()
|
|
13
|
+
.positive()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Maximum number of lines to read for text files (default: 2000), or pages for PDFs in text mode (default: 100, max: 200)'),
|
|
16
|
+
offset: z
|
|
17
|
+
.number()
|
|
18
|
+
.int()
|
|
19
|
+
.min(1)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Starting line number (1-based) for text files, or starting page number for PDFs. ' +
|
|
22
|
+
'If the file is truncated, you MUST set offset to the next line/page number to continue reading.'),
|
|
23
|
+
pdfMode: z
|
|
24
|
+
.enum(['text', 'base64'])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("PDF read mode: 'text' (default) extracts text page by page with pagination support, 'base64' returns raw PDF as attachment for multimodal analysis"),
|
|
12
27
|
})
|
|
13
28
|
.strict();
|
|
14
29
|
/**
|
|
@@ -29,14 +44,18 @@ const ReadFileInputSchema = z
|
|
|
29
44
|
*/
|
|
30
45
|
export function createReadFileTool(fileSystemService) {
|
|
31
46
|
return {
|
|
32
|
-
description: 'Read the contents of a file. Supports relative/absolute paths
|
|
47
|
+
description: 'Read the contents of a file. Supports relative/absolute paths and pagination. ' +
|
|
48
|
+
'For PDFs, defaults to text extraction with page-by-page pagination (use pdfMode="base64" for raw attachment). ' +
|
|
49
|
+
'Images are returned as base64 attachments. ' +
|
|
50
|
+
'Must continue running read_file tool with the correct offset to finish reading the file.',
|
|
33
51
|
async execute(input, _context) {
|
|
34
|
-
const { filePath, limit, offset } = input;
|
|
52
|
+
const { filePath, limit, offset, pdfMode } = input;
|
|
35
53
|
try {
|
|
36
54
|
// Call file system service
|
|
37
55
|
const result = await fileSystemService.readFile(filePath, {
|
|
38
56
|
limit,
|
|
39
57
|
offset,
|
|
58
|
+
pdfMode,
|
|
40
59
|
});
|
|
41
60
|
// Transform attachment format (singular → plural array)
|
|
42
61
|
let attachments;
|
|
@@ -55,6 +74,7 @@ export function createReadFileTool(fileSystemService) {
|
|
|
55
74
|
content: result.formattedContent,
|
|
56
75
|
lines: result.lines,
|
|
57
76
|
message: result.message,
|
|
77
|
+
pdfMetadata: result.pdfMetadata,
|
|
58
78
|
preview: result.preview,
|
|
59
79
|
size: result.size,
|
|
60
80
|
success: true,
|
|
@@ -3,6 +3,7 @@ import { CONNECTOR_TYPES } from '../../core/domain/entities/connector-type.js';
|
|
|
3
3
|
import { HookConnector } from './hook/hook-connector.js';
|
|
4
4
|
import { McpConnector } from './mcp/mcp-connector.js';
|
|
5
5
|
import { RulesConnector } from './rules/rules-connector.js';
|
|
6
|
+
import { SkillConnector } from './skill/skill-connector.js';
|
|
6
7
|
/**
|
|
7
8
|
* Factory and orchestration layer for connectors.
|
|
8
9
|
* Creates connector instances and manages connector operations.
|
|
@@ -16,6 +17,7 @@ export class ConnectorManager {
|
|
|
16
17
|
['hook', new HookConnector({ fileService, projectRoot })],
|
|
17
18
|
['mcp', new McpConnector({ fileService, projectRoot, templateService })],
|
|
18
19
|
['rules', new RulesConnector({ fileService, projectRoot, templateService })],
|
|
20
|
+
['skill', new SkillConnector({ fileService, projectRoot })],
|
|
19
21
|
]);
|
|
20
22
|
}
|
|
21
23
|
async getAllInstalledConnectors() {
|
|
@@ -21,7 +21,7 @@ type HookConnectorOptions = {
|
|
|
21
21
|
* - Safe uninstall: Only removes ByteRover hooks by command match
|
|
22
22
|
*/
|
|
23
23
|
export declare class HookConnector implements IConnector {
|
|
24
|
-
readonly
|
|
24
|
+
readonly connectorType: ConnectorType;
|
|
25
25
|
private readonly fileService;
|
|
26
26
|
private readonly projectRoot;
|
|
27
27
|
private readonly supportedAgents;
|
|
@@ -23,7 +23,7 @@ function parseJsonAsRecord(content) {
|
|
|
23
23
|
* - Safe uninstall: Only removes ByteRover hooks by command match
|
|
24
24
|
*/
|
|
25
25
|
export class HookConnector {
|
|
26
|
-
|
|
26
|
+
connectorType = 'hook';
|
|
27
27
|
fileService;
|
|
28
28
|
projectRoot;
|
|
29
29
|
supportedAgents;
|
|
@@ -31,7 +31,7 @@ export class HookConnector {
|
|
|
31
31
|
this.fileService = options.fileService;
|
|
32
32
|
this.projectRoot = options.projectRoot;
|
|
33
33
|
this.supportedAgents = Object.entries(AGENT_CONNECTOR_CONFIG)
|
|
34
|
-
.filter(([_, config]) => config.supported.includes(this.
|
|
34
|
+
.filter(([_, config]) => config.supported.includes(this.connectorType))
|
|
35
35
|
.map(([agent]) => agent);
|
|
36
36
|
}
|
|
37
37
|
getConfigPath(agent) {
|
|
@@ -104,7 +104,7 @@ export class HookConnector {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
isSupported(agent) {
|
|
107
|
-
return AGENT_CONNECTOR_CONFIG[agent].supported.includes(this.
|
|
107
|
+
return AGENT_CONNECTOR_CONFIG[agent].supported.includes(this.connectorType);
|
|
108
108
|
}
|
|
109
109
|
async status(agent) {
|
|
110
110
|
if (!this.isSupported(agent)) {
|
|
@@ -24,7 +24,7 @@ type McpConnectorOptions = {
|
|
|
24
24
|
* - Safe uninstall: Only removes ByteRover's MCP server entry and rule content
|
|
25
25
|
*/
|
|
26
26
|
export declare class McpConnector implements IConnector {
|
|
27
|
-
readonly
|
|
27
|
+
readonly connectorType: ConnectorType;
|
|
28
28
|
private readonly fileService;
|
|
29
29
|
private readonly projectRoot;
|
|
30
30
|
private readonly ruleFileManager;
|