byterover-cli 1.0.4 → 1.1.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 +24 -11
- package/dist/commands/curate.js +1 -1
- package/dist/commands/hook-prompt-submit.d.ts +27 -0
- package/dist/commands/hook-prompt-submit.js +39 -0
- package/dist/commands/main.d.ts +13 -0
- package/dist/commands/main.js +53 -2
- package/dist/commands/query.js +1 -1
- package/dist/commands/status.js +8 -3
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/core/domain/cipher/llm/registry.js +53 -2
- package/dist/core/domain/cipher/llm/types.d.ts +2 -0
- package/dist/core/domain/cipher/process/types.d.ts +7 -0
- package/dist/core/domain/cipher/session/session-metadata.d.ts +178 -0
- package/dist/core/domain/cipher/session/session-metadata.js +147 -0
- package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
- package/dist/core/domain/cipher/tools/constants.js +1 -0
- package/dist/core/domain/entities/agent.d.ts +16 -0
- package/dist/core/domain/entities/agent.js +24 -0
- package/dist/core/domain/entities/connector-type.d.ts +9 -0
- package/dist/core/domain/entities/connector-type.js +8 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +2 -0
- package/dist/core/domain/errors/task-error.d.ts +4 -0
- package/dist/core/domain/errors/task-error.js +7 -0
- package/dist/core/domain/knowledge/markdown-writer.d.ts +15 -18
- package/dist/core/domain/knowledge/markdown-writer.js +232 -34
- package/dist/core/domain/knowledge/relation-parser.d.ts +25 -39
- package/dist/core/domain/knowledge/relation-parser.js +39 -61
- package/dist/core/domain/transport/schemas.d.ts +77 -2
- package/dist/core/domain/transport/schemas.js +51 -2
- package/dist/core/interfaces/cipher/i-session-persistence.d.ts +133 -0
- package/dist/core/interfaces/cipher/i-session-persistence.js +7 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +6 -0
- package/dist/core/interfaces/connectors/connector-types.d.ts +57 -0
- package/dist/core/interfaces/connectors/i-connector-manager.d.ts +72 -0
- package/dist/core/interfaces/connectors/i-connector.d.ts +54 -0
- package/dist/core/interfaces/connectors/i-connector.js +1 -0
- package/dist/core/interfaces/executor/i-curate-executor.d.ts +2 -2
- package/dist/core/interfaces/i-context-file-reader.d.ts +3 -0
- package/dist/core/interfaces/i-file-service.d.ts +7 -0
- package/dist/core/interfaces/usecase/i-connectors-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-connectors-use-case.js +1 -0
- package/dist/core/interfaces/usecase/{i-clear-use-case.d.ts → i-reset-use-case.d.ts} +1 -1
- package/dist/core/interfaces/usecase/i-reset-use-case.js +1 -0
- package/dist/hooks/init/update-notifier.d.ts +1 -0
- package/dist/hooks/init/update-notifier.js +10 -1
- package/dist/infra/cipher/agent/agent-schemas.d.ts +6 -6
- package/dist/infra/cipher/agent/service-initializer.js +4 -4
- package/dist/infra/cipher/file-system/binary-utils.d.ts +7 -12
- package/dist/infra/cipher/file-system/binary-utils.js +46 -31
- package/dist/infra/cipher/file-system/context-tree-file-system-factory.js +3 -2
- package/dist/infra/cipher/file-system/file-system-service.js +1 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +3 -5
- package/dist/infra/cipher/interactive-loop.js +3 -1
- package/dist/infra/cipher/llm/context/context-manager.d.ts +2 -2
- package/dist/infra/cipher/llm/context/context-manager.js +63 -18
- package/dist/infra/cipher/llm/formatters/gemini-formatter.d.ts +13 -0
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +146 -15
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +6 -2
- package/dist/infra/cipher/llm/internal-llm-service.js +2 -2
- package/dist/infra/cipher/llm/thought-parser.d.ts +21 -0
- package/dist/infra/cipher/llm/thought-parser.js +27 -0
- package/dist/infra/cipher/llm/tool-output-processor.d.ts +10 -0
- package/dist/infra/cipher/llm/tool-output-processor.js +80 -7
- package/dist/infra/cipher/process/process-service.js +11 -3
- package/dist/infra/cipher/session/chat-session.d.ts +7 -2
- package/dist/infra/cipher/session/chat-session.js +90 -52
- package/dist/infra/cipher/session/session-metadata-store.d.ts +52 -0
- package/dist/infra/cipher/session/session-metadata-store.js +406 -0
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.d.ts +6 -7
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +57 -18
- package/dist/infra/cipher/tools/implementations/curate-tool.js +132 -36
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +38 -17
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.d.ts +7 -0
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.js +303 -0
- package/dist/infra/cipher/tools/implementations/task-tool.js +1 -0
- package/dist/infra/cipher/tools/index.d.ts +1 -0
- package/dist/infra/cipher/tools/index.js +1 -0
- package/dist/infra/cipher/tools/tool-manager.js +1 -0
- package/dist/infra/cipher/tools/tool-registry.js +7 -0
- package/dist/infra/connectors/connector-manager.d.ts +32 -0
- package/dist/infra/connectors/connector-manager.js +156 -0
- package/dist/infra/connectors/hook/hook-connector-config.d.ts +52 -0
- package/dist/infra/connectors/hook/hook-connector-config.js +41 -0
- package/dist/infra/connectors/hook/hook-connector.d.ts +46 -0
- package/dist/infra/connectors/hook/hook-connector.js +231 -0
- package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.d.ts +2 -2
- package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.js +1 -1
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +95 -0
- package/dist/infra/{rule/agent-rule-config.js → connectors/rules/rules-connector-config.js} +10 -10
- package/dist/infra/connectors/rules/rules-connector.d.ts +41 -0
- package/dist/infra/connectors/rules/rules-connector.js +204 -0
- package/dist/infra/{rule/rule-template-service.d.ts → connectors/shared/template-service.d.ts} +3 -3
- package/dist/infra/{rule/rule-template-service.js → connectors/shared/template-service.js} +1 -1
- package/dist/infra/context-tree/file-context-file-reader.js +4 -0
- package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +5 -2
- package/dist/infra/context-tree/file-context-tree-writer-service.js +20 -5
- package/dist/infra/core/executors/curate-executor.d.ts +2 -2
- package/dist/infra/core/executors/curate-executor.js +7 -7
- package/dist/infra/core/executors/query-executor.d.ts +12 -0
- package/dist/infra/core/executors/query-executor.js +62 -1
- package/dist/infra/core/task-processor.d.ts +2 -2
- package/dist/infra/file/fs-file-service.d.ts +7 -0
- package/dist/infra/file/fs-file-service.js +15 -1
- package/dist/infra/process/agent-worker.d.ts +2 -2
- package/dist/infra/process/agent-worker.js +626 -142
- package/dist/infra/process/constants.d.ts +1 -1
- package/dist/infra/process/constants.js +1 -1
- package/dist/infra/process/ipc-types.d.ts +17 -4
- package/dist/infra/process/ipc-types.js +3 -3
- package/dist/infra/process/parent-heartbeat.d.ts +47 -0
- package/dist/infra/process/parent-heartbeat.js +118 -0
- package/dist/infra/process/process-manager.d.ts +89 -1
- package/dist/infra/process/process-manager.js +293 -9
- package/dist/infra/process/task-queue-manager.d.ts +13 -0
- package/dist/infra/process/task-queue-manager.js +19 -0
- package/dist/infra/process/transport-handlers.d.ts +3 -0
- package/dist/infra/process/transport-handlers.js +82 -5
- package/dist/infra/process/transport-worker.js +9 -69
- package/dist/infra/repl/commands/connectors-command.d.ts +8 -0
- package/dist/infra/repl/commands/{gen-rules-command.js → connectors-command.js} +21 -10
- package/dist/infra/repl/commands/index.js +8 -4
- package/dist/infra/repl/commands/init-command.js +11 -7
- package/dist/infra/repl/commands/new-command.d.ts +14 -0
- package/dist/infra/repl/commands/new-command.js +61 -0
- package/dist/infra/repl/commands/query-command.js +22 -2
- package/dist/infra/repl/commands/{clear-command.d.ts → reset-command.d.ts} +2 -2
- package/dist/infra/repl/commands/{clear-command.js → reset-command.js} +11 -11
- package/dist/infra/transport/socket-io-transport-client.d.ts +68 -0
- package/dist/infra/transport/socket-io-transport-client.js +283 -7
- package/dist/infra/usecase/connectors-use-case.d.ts +59 -0
- package/dist/infra/usecase/connectors-use-case.js +203 -0
- package/dist/infra/usecase/init-use-case.d.ts +8 -43
- package/dist/infra/usecase/init-use-case.js +29 -253
- package/dist/infra/usecase/logout-use-case.js +2 -2
- package/dist/infra/usecase/pull-use-case.js +5 -5
- package/dist/infra/usecase/push-use-case.js +5 -5
- package/dist/infra/usecase/{clear-use-case.d.ts → reset-use-case.d.ts} +5 -5
- package/dist/infra/usecase/{clear-use-case.js → reset-use-case.js} +7 -8
- package/dist/infra/usecase/space-list-use-case.js +3 -3
- package/dist/infra/usecase/space-switch-use-case.js +3 -3
- package/dist/resources/prompts/curate.yml +75 -13
- package/dist/resources/prompts/explore.yml +34 -0
- package/dist/resources/prompts/query-orchestrator.yml +112 -0
- package/dist/resources/prompts/system-prompt.yml +12 -2
- package/dist/resources/tools/curate.txt +60 -15
- package/dist/resources/tools/search_knowledge.txt +32 -0
- package/dist/templates/sections/brv-instructions.md +98 -0
- package/dist/tui/components/inline-prompts/inline-confirm.js +2 -2
- package/dist/tui/components/onboarding/onboarding-flow.js +14 -10
- package/dist/tui/components/onboarding/welcome-box.js +1 -1
- package/dist/tui/contexts/onboarding-context.d.ts +4 -0
- package/dist/tui/contexts/onboarding-context.js +14 -2
- package/dist/tui/views/command-view.js +19 -0
- package/dist/utils/file-validator.d.ts +1 -1
- package/dist/utils/file-validator.js +34 -35
- package/dist/utils/type-guards.d.ts +5 -0
- package/dist/utils/type-guards.js +7 -0
- package/oclif.manifest.json +32 -6
- package/package.json +4 -1
- package/dist/config/context-tree-domains.d.ts +0 -29
- package/dist/config/context-tree-domains.js +0 -29
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +0 -3
- package/dist/infra/repl/commands/gen-rules-command.d.ts +0 -7
- package/dist/infra/rule/agent-rule-config.d.ts +0 -19
- package/dist/infra/usecase/generate-rules-use-case.d.ts +0 -61
- package/dist/infra/usecase/generate-rules-use-case.js +0 -285
- /package/dist/core/interfaces/{usecase/i-clear-use-case.js → connectors/connector-types.js} +0 -0
- /package/dist/core/interfaces/{usecase/i-generate-rules-use-case.js → connectors/i-connector-manager.js} +0 -0
- /package/dist/infra/{rule → connectors/shared}/constants.d.ts +0 -0
- /package/dist/infra/{rule → connectors/shared}/constants.js +0 -0
|
@@ -22,6 +22,8 @@ export interface OnboardingContextValue {
|
|
|
22
22
|
hasCurated: boolean;
|
|
23
23
|
/** Whether query has been completed at least once */
|
|
24
24
|
hasQueried: boolean;
|
|
25
|
+
/** Whether user has acknowledged init completion */
|
|
26
|
+
initAcknowledged: boolean;
|
|
25
27
|
/** Whether the project is initialized (brvConfig exists) */
|
|
26
28
|
isInitialized: boolean;
|
|
27
29
|
/** Whether we're still loading the dismissed state */
|
|
@@ -30,6 +32,8 @@ export interface OnboardingContextValue {
|
|
|
30
32
|
queryAcknowledged: boolean;
|
|
31
33
|
/** Set curate acknowledged state */
|
|
32
34
|
setCurateAcknowledged: (value: boolean) => void;
|
|
35
|
+
/** Set init acknowledged state */
|
|
36
|
+
setInitAcknowledged: (value: boolean) => void;
|
|
33
37
|
/** Set query acknowledged state */
|
|
34
38
|
setQueryAcknowledged: (value: boolean) => void;
|
|
35
39
|
/** Whether onboarding should be shown */
|
|
@@ -65,6 +65,7 @@ export function OnboardingProvider({ children }) {
|
|
|
65
65
|
}
|
|
66
66
|
}, [isInitialConfigLoaded, isInitialized]);
|
|
67
67
|
// Track acknowledgment for completed steps (user pressed Enter after seeing output)
|
|
68
|
+
const [initAcknowledged, setInitAcknowledgedState] = useState(false);
|
|
68
69
|
const [curateAcknowledged, setCurateAcknowledgedState] = useState(false);
|
|
69
70
|
const [queryAcknowledged, setQueryAcknowledgedState] = useState(false);
|
|
70
71
|
// Track if init was completed during this onboarding session (to avoid duplicate tracking)
|
|
@@ -76,6 +77,10 @@ export function OnboardingProvider({ children }) {
|
|
|
76
77
|
trackingService.track('onboarding:init_completed');
|
|
77
78
|
}
|
|
78
79
|
}, [isInitialized, trackingService]);
|
|
80
|
+
// Wrapper for setInitAcknowledged that also tracks
|
|
81
|
+
const setInitAcknowledged = useCallback((value) => {
|
|
82
|
+
setInitAcknowledgedState(value);
|
|
83
|
+
}, []);
|
|
79
84
|
// Wrapper for setCurateAcknowledged that also tracks
|
|
80
85
|
const setCurateAcknowledged = useCallback((value) => {
|
|
81
86
|
setCurateAcknowledgedState(value);
|
|
@@ -110,10 +115,13 @@ export function OnboardingProvider({ children }) {
|
|
|
110
115
|
return { hasCurated: curateCompleted, hasQueried: queryCompleted };
|
|
111
116
|
}, [tasks]);
|
|
112
117
|
// Derive current step (considering acknowledgment)
|
|
113
|
-
// Stay on
|
|
118
|
+
// Stay on each step until user acknowledges the completion
|
|
114
119
|
const currentStep = useMemo(() => {
|
|
115
120
|
if (!isInitialized)
|
|
116
121
|
return 'init';
|
|
122
|
+
// isInitialized is true but not yet acknowledged -> stay on init
|
|
123
|
+
if (!initAcknowledged)
|
|
124
|
+
return 'init';
|
|
117
125
|
if (!hasCurated)
|
|
118
126
|
return 'curate';
|
|
119
127
|
// hasCurated is true but not yet acknowledged -> stay on curate
|
|
@@ -125,7 +133,7 @@ export function OnboardingProvider({ children }) {
|
|
|
125
133
|
if (!queryAcknowledged)
|
|
126
134
|
return 'query';
|
|
127
135
|
return 'complete';
|
|
128
|
-
}, [isInitialized, hasCurated, hasQueried, curateAcknowledged, queryAcknowledged]);
|
|
136
|
+
}, [isInitialized, initAcknowledged, hasCurated, hasQueried, curateAcknowledged, queryAcknowledged]);
|
|
129
137
|
// Show onboarding if:
|
|
130
138
|
// 1. Project was not initialized after initial config check, AND
|
|
131
139
|
// 2. User has never dismissed onboarding before (persisted)
|
|
@@ -146,10 +154,12 @@ export function OnboardingProvider({ children }) {
|
|
|
146
154
|
currentStep,
|
|
147
155
|
hasCurated,
|
|
148
156
|
hasQueried,
|
|
157
|
+
initAcknowledged,
|
|
149
158
|
isInitialized,
|
|
150
159
|
isLoadingDismissed,
|
|
151
160
|
queryAcknowledged,
|
|
152
161
|
setCurateAcknowledged,
|
|
162
|
+
setInitAcknowledged,
|
|
153
163
|
setQueryAcknowledged,
|
|
154
164
|
shouldShowOnboarding,
|
|
155
165
|
totalSteps: 3, // init, curate, query (complete is not counted)
|
|
@@ -160,9 +170,11 @@ export function OnboardingProvider({ children }) {
|
|
|
160
170
|
hasCurated,
|
|
161
171
|
hasQueried,
|
|
162
172
|
hasDismissed,
|
|
173
|
+
initAcknowledged,
|
|
163
174
|
isInitialized,
|
|
164
175
|
isLoadingDismissed,
|
|
165
176
|
queryAcknowledged,
|
|
177
|
+
setInitAcknowledged,
|
|
166
178
|
shouldShowOnboarding,
|
|
167
179
|
]);
|
|
168
180
|
return _jsx(OnboardingContext.Provider, { value: contextValue, children: children });
|
|
@@ -231,6 +231,7 @@ export const CommandView = ({ availableHeight }) => {
|
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
233
|
}, [activePrompt?.type, appendShortcuts, removeShortcuts]);
|
|
234
|
+
/* eslint-disable complexity -- Command execution requires handling multiple command types and states */
|
|
234
235
|
const executeCommand = useCallback(async (value) => {
|
|
235
236
|
const trimmed = value.trim();
|
|
236
237
|
if (!trimmed)
|
|
@@ -301,6 +302,23 @@ export const CommandView = ({ availableHeight }) => {
|
|
|
301
302
|
setActivePrompt(null);
|
|
302
303
|
const needReloadAuth = trimmed.startsWith('/login') || trimmed.startsWith('/logout');
|
|
303
304
|
const needReloadBrvConfig = trimmed.startsWith('/space switch') || trimmed.startsWith('/init');
|
|
305
|
+
const needNewSession = trimmed.startsWith('/new');
|
|
306
|
+
// Handle /new command - create new session and clear messages
|
|
307
|
+
if (needNewSession && client) {
|
|
308
|
+
try {
|
|
309
|
+
const response = await client.request('agent:newSession', { reason: 'User requested new session' });
|
|
310
|
+
/* eslint-disable max-depth -- UI state handling requires this nesting level */
|
|
311
|
+
if (response.success) {
|
|
312
|
+
// Clear the messages to start fresh
|
|
313
|
+
setMessages([]);
|
|
314
|
+
clearTasks();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// Error handling - the command already showed feedback
|
|
319
|
+
}
|
|
320
|
+
/* eslint-enable max-depth */
|
|
321
|
+
}
|
|
304
322
|
// Refresh state after commands that change auth or project state
|
|
305
323
|
if (needReloadAuth || needReloadBrvConfig) {
|
|
306
324
|
clearTasks();
|
|
@@ -323,6 +341,7 @@ export const CommandView = ({ availableHeight }) => {
|
|
|
323
341
|
}
|
|
324
342
|
}
|
|
325
343
|
}, [clearTasks, client, exit, handleSlashCommand, reloadAuth, reloadBrvConfig]);
|
|
344
|
+
/* eslint-enable complexity */
|
|
326
345
|
const handleSubmit = useCallback(async (value) => {
|
|
327
346
|
if (mode === 'console' && !isStreaming) {
|
|
328
347
|
await executeCommand(value);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Checks:
|
|
4
4
|
* 1. File exists
|
|
5
5
|
* 2. File is within project directory (where .brv exists)
|
|
6
|
-
* 3. File is
|
|
6
|
+
* 3. File is supported by read_file tool (text, images, or PDFs)
|
|
7
7
|
*
|
|
8
8
|
* @param filePath - The file path to validate (can be relative, absolute, or tilde)
|
|
9
9
|
* @param projectRoot - The project root directory (current working directory)
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { isBinaryFile, isMediaFile } from '../infra/cipher/file-system/binary-utils.js';
|
|
4
5
|
/**
|
|
5
6
|
* Normalize file path - handles relative, absolute, tilde, symlinks
|
|
6
7
|
* Returns absolute canonical path
|
|
7
8
|
* @param filePath - The file path to normalize
|
|
8
9
|
* @returns Normalized absolute path
|
|
9
10
|
*/
|
|
10
|
-
function normalizeFilePath(filePath) {
|
|
11
|
+
function normalizeFilePath(filePath, baseDir) {
|
|
11
12
|
// Expand tilde to home directory
|
|
12
13
|
const expanded = filePath.startsWith('~') ? filePath.replace(/^~/, os.homedir()) : filePath;
|
|
13
|
-
// Resolve to absolute path
|
|
14
|
-
const absolute = path.resolve(expanded);
|
|
14
|
+
// Resolve to absolute path using baseDir for relative paths
|
|
15
|
+
const absolute = path.isAbsolute(expanded) ? expanded : path.resolve(baseDir ?? process.cwd(), expanded);
|
|
15
16
|
// Resolve symlinks (only if file exists)
|
|
16
17
|
try {
|
|
17
18
|
return fs.realpathSync(absolute);
|
|
@@ -21,44 +22,23 @@ function normalizeFilePath(filePath) {
|
|
|
21
22
|
return absolute;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Check if file is text (no null bytes in first 8KB)
|
|
26
|
-
* Returns false for binary files (images, PDFs, compiled binaries, etc.)
|
|
27
|
-
* @param filePath - The file path to check
|
|
28
|
-
* @returns true if file is text, false if binary
|
|
29
|
-
*/
|
|
30
|
-
function isTextFile(filePath) {
|
|
31
|
-
try {
|
|
32
|
-
const buffer = Buffer.alloc(8192);
|
|
33
|
-
const fd = fs.openSync(filePath, 'r');
|
|
34
|
-
const bytesRead = fs.readSync(fd, buffer, 0, 8192, 0);
|
|
35
|
-
fs.closeSync(fd);
|
|
36
|
-
// Check for null bytes (indicates binary file)
|
|
37
|
-
for (let i = 0; i < bytesRead; i++) {
|
|
38
|
-
if (buffer[i] === 0)
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
// If we can't read the file, consider it non-text
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
25
|
/**
|
|
49
26
|
* Validate file for --files flag in brv curate command
|
|
50
27
|
* Checks:
|
|
51
28
|
* 1. File exists
|
|
52
29
|
* 2. File is within project directory (where .brv exists)
|
|
53
|
-
* 3. File is
|
|
30
|
+
* 3. File is supported by read_file tool (text, images, or PDFs)
|
|
54
31
|
*
|
|
55
32
|
* @param filePath - The file path to validate (can be relative, absolute, or tilde)
|
|
56
33
|
* @param projectRoot - The project root directory (current working directory)
|
|
57
34
|
* @returns Validation result with normalized path or error message
|
|
58
35
|
*/
|
|
59
36
|
export function validateFileForCurate(filePath, projectRoot) {
|
|
60
|
-
// Normalize
|
|
61
|
-
|
|
37
|
+
// Normalize projectRoot first to ensure consistent behavior
|
|
38
|
+
// This handles cases where projectRoot might be relative
|
|
39
|
+
const normalizedProjectRoot = normalizeFilePath(projectRoot);
|
|
40
|
+
// Normalize file path using normalizedProjectRoot as base for relative paths
|
|
41
|
+
const normalized = normalizeFilePath(filePath, normalizedProjectRoot);
|
|
62
42
|
// Check existence
|
|
63
43
|
if (!fs.existsSync(normalized)) {
|
|
64
44
|
return { error: `File does not exist: ${filePath}`, valid: false };
|
|
@@ -68,14 +48,33 @@ export function validateFileForCurate(filePath, projectRoot) {
|
|
|
68
48
|
if (!stats.isFile()) {
|
|
69
49
|
return { error: `Path is not a file: ${filePath}`, valid: false };
|
|
70
50
|
}
|
|
71
|
-
// Check within project (
|
|
72
|
-
const normalizedProjectRoot = normalizeFilePath(projectRoot);
|
|
51
|
+
// Check within project (both paths are already normalized)
|
|
73
52
|
if (!normalized.startsWith(normalizedProjectRoot + path.sep) && normalized !== normalizedProjectRoot) {
|
|
74
53
|
return { error: `File is outside project directory: ${filePath}`, valid: false };
|
|
75
54
|
}
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
55
|
+
// Read sample buffer for file type detection
|
|
56
|
+
let buffer;
|
|
57
|
+
let bytesRead;
|
|
58
|
+
try {
|
|
59
|
+
buffer = Buffer.alloc(4096);
|
|
60
|
+
const fd = fs.openSync(normalized, 'r');
|
|
61
|
+
bytesRead = fs.readSync(fd, buffer, 0, 4096, 0);
|
|
62
|
+
fs.closeSync(fd);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { error: `Cannot read file: ${filePath}`, valid: false };
|
|
66
|
+
}
|
|
67
|
+
const sampleBuffer = buffer.subarray(0, bytesRead);
|
|
68
|
+
// Check file type using binary-utils (same logic as read_file tool)
|
|
69
|
+
// Allow media files (images/PDFs) - read_file can handle these
|
|
70
|
+
// For PDFs, also validate magic bytes to reject fake PDFs (e.g., binary.pdf)
|
|
71
|
+
if (isMediaFile(normalized, sampleBuffer)) {
|
|
72
|
+
return { normalizedPath: normalized, valid: true };
|
|
73
|
+
}
|
|
74
|
+
// Check if it's a binary file (using same logic as read_file tool)
|
|
75
|
+
if (isBinaryFile(normalized, sampleBuffer)) {
|
|
76
|
+
return { error: `File type not supported: ${filePath}`, valid: false };
|
|
79
77
|
}
|
|
78
|
+
// It's a text file - supported
|
|
80
79
|
return { normalizedPath: normalized, valid: true };
|
|
81
80
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"required": false
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"description": "Curate context to the context tree (connects to running brv instance)\n\nRequires a running brv instance. Start one with: brv
|
|
12
|
+
"description": "Curate context to the context tree (connects to running brv instance)\n\nRequires a running brv instance. Start one with: brv\n\nGood examples:\n- \"Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts\"\n- \"API rate limit is 100 req/min per user. Implemented using Redis with sliding window in rateLimiter.ts\"\nBad examples:\n- \"Authentication\" or \"JWT tokens\" (too vague, lacks context)\n- \"Rate limiting\" (no implementation details or file references)",
|
|
13
13
|
"examples": [
|
|
14
14
|
"# Curate context - queues task for background processing",
|
|
15
15
|
"<%= config.bin %> <%= command.id %> \"Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies via authMiddleware.ts\"",
|
|
@@ -45,6 +45,27 @@
|
|
|
45
45
|
"curate.js"
|
|
46
46
|
]
|
|
47
47
|
},
|
|
48
|
+
"hook-prompt-submit": {
|
|
49
|
+
"aliases": [],
|
|
50
|
+
"args": {},
|
|
51
|
+
"description": "Internal: Pre-prompt hook for coding agents",
|
|
52
|
+
"flags": {},
|
|
53
|
+
"hasDynamicHelp": false,
|
|
54
|
+
"hidden": true,
|
|
55
|
+
"hiddenAliases": [],
|
|
56
|
+
"id": "hook-prompt-submit",
|
|
57
|
+
"pluginAlias": "byterover-cli",
|
|
58
|
+
"pluginName": "byterover-cli",
|
|
59
|
+
"pluginType": "core",
|
|
60
|
+
"strict": true,
|
|
61
|
+
"enableJsonFlag": false,
|
|
62
|
+
"isESM": true,
|
|
63
|
+
"relativePath": [
|
|
64
|
+
"dist",
|
|
65
|
+
"commands",
|
|
66
|
+
"hook-prompt-submit.js"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
48
69
|
"main": {
|
|
49
70
|
"aliases": [],
|
|
50
71
|
"args": {},
|
|
@@ -75,7 +96,7 @@
|
|
|
75
96
|
"required": true
|
|
76
97
|
}
|
|
77
98
|
},
|
|
78
|
-
"description": "Query and retrieve information from the context tree (connects to running brv instance)\n\nRequires a running brv instance. Start one with: brv
|
|
99
|
+
"description": "Query and retrieve information from the context tree (connects to running brv instance)\n\nRequires a running brv instance. Start one with: brv\n\nGood:\n- \"How is user authentication implemented?\"\n- \"What are the API rate limits and where are they enforced?\"\nBad:\n- \"auth\" or \"authentication\" (too vague, not a question)\n- \"show me code\" (not specific about what information is needed)",
|
|
79
100
|
"examples": [
|
|
80
101
|
"# Ask questions about patterns, decisions, or implementation details",
|
|
81
102
|
"<%= config.bin %> <%= command.id %> What are the coding standards?",
|
|
@@ -109,9 +130,14 @@
|
|
|
109
130
|
"description": "Show CLI status and project information. Display local context tree managed by ByteRover CLI",
|
|
110
131
|
"examples": [
|
|
111
132
|
"<%= config.bin %> <%= command.id %>",
|
|
112
|
-
"# Check status after login
|
|
113
|
-
"
|
|
114
|
-
"
|
|
133
|
+
"# Check status after login (in REPL):",
|
|
134
|
+
"/login",
|
|
135
|
+
"/status",
|
|
136
|
+
"",
|
|
137
|
+
"# Verify project initialization (in REPL):",
|
|
138
|
+
"/init",
|
|
139
|
+
"/status",
|
|
140
|
+
"",
|
|
115
141
|
"<%= config.bin %> <%= command.id %> /path/to/project",
|
|
116
142
|
"<%= config.bin %> <%= command.id %> --format json"
|
|
117
143
|
],
|
|
@@ -198,5 +224,5 @@
|
|
|
198
224
|
]
|
|
199
225
|
}
|
|
200
226
|
},
|
|
201
|
-
"version": "1.0
|
|
227
|
+
"version": "1.1.0"
|
|
202
228
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byterover-cli",
|
|
3
3
|
"description": "ByteRover's CLI",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"author": "ByteRover",
|
|
6
6
|
"bin": {
|
|
7
7
|
"brv": "./bin/run.js"
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"inquirer-file-selector": "^1.0.1",
|
|
33
33
|
"js-yaml": "^4.1.1",
|
|
34
34
|
"keytar": "^7.9.0",
|
|
35
|
+
"minisearch": "^7.2.0",
|
|
35
36
|
"mixpanel": "^0.18.1",
|
|
36
37
|
"nanoid": "^5.1.6",
|
|
37
38
|
"open": "^10.2.0",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"react": "^19.2.1",
|
|
40
41
|
"socket.io": "^4.8.1",
|
|
41
42
|
"socket.io-client": "^4.8.1",
|
|
43
|
+
"stopword": "^3.1.5",
|
|
42
44
|
"update-notifier": "^7.3.1",
|
|
43
45
|
"zod": "^3.25.76",
|
|
44
46
|
"zod-to-json-schema": "^3.24.6"
|
|
@@ -55,6 +57,7 @@
|
|
|
55
57
|
"@types/node": "^20",
|
|
56
58
|
"@types/react": "^19.2.7",
|
|
57
59
|
"@types/sinon": "^17.0.4",
|
|
60
|
+
"@types/stopword": "^2.0.3",
|
|
58
61
|
"chai": "^4",
|
|
59
62
|
"eslint": "^9",
|
|
60
63
|
"eslint-config-oclif": "^6",
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Domain configurations for the context tree structure.
|
|
3
|
-
* Each domain represents a specific area of knowledge in the project.
|
|
4
|
-
*
|
|
5
|
-
* @deprecated Domains are now created dynamically based on content semantics.
|
|
6
|
-
* This file is kept for backward compatibility only.
|
|
7
|
-
*/
|
|
8
|
-
export interface DomainConfig {
|
|
9
|
-
description: string;
|
|
10
|
-
name: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Example domain names for reference only.
|
|
14
|
-
* Domains are now created dynamically by the agent based on content.
|
|
15
|
-
*
|
|
16
|
-
* @deprecated Domains are created dynamically. These are kept as examples only.
|
|
17
|
-
*/
|
|
18
|
-
export declare const EXAMPLE_DOMAIN_NAMES: readonly ["authentication", "api_design", "data_models", "error_handling", "ui_components", "testing_patterns", "configuration", "logging", "security", "performance"];
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated Domains are now created dynamically based on content semantics.
|
|
21
|
-
* The agent will create domain names that are semantically meaningful for the curated content.
|
|
22
|
-
* This constant is kept for backward compatibility but is no longer used.
|
|
23
|
-
*/
|
|
24
|
-
export declare const DEFAULT_CONTEXT_TREE_DOMAINS: DomainConfig[];
|
|
25
|
-
/**
|
|
26
|
-
* Alias for backward compatibility.
|
|
27
|
-
* @deprecated Domains are created dynamically. This constant is no longer used.
|
|
28
|
-
*/
|
|
29
|
-
export declare const CONTEXT_TREE_DOMAINS: DomainConfig[];
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Example domain names for reference only.
|
|
3
|
-
* Domains are now created dynamically by the agent based on content.
|
|
4
|
-
*
|
|
5
|
-
* @deprecated Domains are created dynamically. These are kept as examples only.
|
|
6
|
-
*/
|
|
7
|
-
export const EXAMPLE_DOMAIN_NAMES = [
|
|
8
|
-
'authentication',
|
|
9
|
-
'api_design',
|
|
10
|
-
'data_models',
|
|
11
|
-
'error_handling',
|
|
12
|
-
'ui_components',
|
|
13
|
-
'testing_patterns',
|
|
14
|
-
'configuration',
|
|
15
|
-
'logging',
|
|
16
|
-
'security',
|
|
17
|
-
'performance',
|
|
18
|
-
];
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated Domains are now created dynamically based on content semantics.
|
|
21
|
-
* The agent will create domain names that are semantically meaningful for the curated content.
|
|
22
|
-
* This constant is kept for backward compatibility but is no longer used.
|
|
23
|
-
*/
|
|
24
|
-
export const DEFAULT_CONTEXT_TREE_DOMAINS = [];
|
|
25
|
-
/**
|
|
26
|
-
* Alias for backward compatibility.
|
|
27
|
-
* @deprecated Domains are created dynamically. This constant is no longer used.
|
|
28
|
-
*/
|
|
29
|
-
export const CONTEXT_TREE_DOMAINS = DEFAULT_CONTEXT_TREE_DOMAINS;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type Agent } from '../../core/domain/entities/agent.js';
|
|
2
|
-
import { type WriteMode } from '../../core/interfaces/i-file-service.js';
|
|
3
|
-
/**
|
|
4
|
-
* Configuration for agent-specific rule files.
|
|
5
|
-
*/
|
|
6
|
-
export type AgentRuleConfig = {
|
|
7
|
-
/**
|
|
8
|
-
* The file path where the agent's rules should be written.
|
|
9
|
-
*/
|
|
10
|
-
filePath: string;
|
|
11
|
-
/**
|
|
12
|
-
* The write mode to use when writing the rule file.
|
|
13
|
-
*/
|
|
14
|
-
writeMode: WriteMode;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Mapping of agents to their rule file configurations.
|
|
18
|
-
*/
|
|
19
|
-
export declare const AGENT_RULE_CONFIGS: Record<Agent, AgentRuleConfig>;
|
|
@@ -1,61 +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 type { IRuleTemplateService } from '../../core/interfaces/i-rule-template-service.js';
|
|
4
|
-
import type { ITerminal } from '../../core/interfaces/i-terminal.js';
|
|
5
|
-
import type { ITrackingService } from '../../core/interfaces/i-tracking-service.js';
|
|
6
|
-
import type { IGenerateRulesUseCase } from '../../core/interfaces/usecase/i-generate-rules-use-case.js';
|
|
7
|
-
import { LegacyRuleDetector } from '../rule/legacy-rule-detector.js';
|
|
8
|
-
type CleanupStrategy = 'automatic' | 'manual';
|
|
9
|
-
export declare class GenerateRulesUseCase implements IGenerateRulesUseCase {
|
|
10
|
-
private readonly fileService;
|
|
11
|
-
private readonly legacyRuleDetector;
|
|
12
|
-
private readonly templateService;
|
|
13
|
-
private readonly terminal;
|
|
14
|
-
private readonly trackingService;
|
|
15
|
-
constructor(fileService: IFileService, legacyRuleDetector: LegacyRuleDetector, templateService: IRuleTemplateService, terminal: ITerminal, trackingService: ITrackingService);
|
|
16
|
-
/**
|
|
17
|
-
* Prompts the user to select an agent.
|
|
18
|
-
* @returns The selected agent
|
|
19
|
-
*/
|
|
20
|
-
protected promptForAgentSelection(): Promise<Agent>;
|
|
21
|
-
/**
|
|
22
|
-
* Prompts the user to choose cleanup strategy for legacy rules.
|
|
23
|
-
* @returns The chosen cleanup strategy
|
|
24
|
-
*/
|
|
25
|
-
protected promptForCleanupStrategy(): Promise<CleanupStrategy>;
|
|
26
|
-
/**
|
|
27
|
-
* Prompts the user to create a new rule file.
|
|
28
|
-
* This method is protected to allow test overrides.
|
|
29
|
-
* @param agent The agent for which the rule file doesn't exist
|
|
30
|
-
* @param filePath The path where the file would be created
|
|
31
|
-
* @returns True if the user wants to create the file, false otherwise
|
|
32
|
-
*/
|
|
33
|
-
protected promptForFileCreation(agent: Agent, filePath: string): Promise<boolean>;
|
|
34
|
-
/**
|
|
35
|
-
* Prompts the user to confirm overwriting an existing rule file.
|
|
36
|
-
* This method is protected to allow test overrides.
|
|
37
|
-
* @param agent The agent for which the rule file exists
|
|
38
|
-
* @returns True if the user confirms overwrite, false otherwise
|
|
39
|
-
*/
|
|
40
|
-
protected promptForOverwriteConfirmation(agent: Agent): Promise<boolean>;
|
|
41
|
-
run(): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Appends ByteRover rules to a file that has no ByteRover content.
|
|
44
|
-
*/
|
|
45
|
-
private appendRulesToFile;
|
|
46
|
-
/**
|
|
47
|
-
* Creates a new rule file with ByteRover rules.
|
|
48
|
-
*/
|
|
49
|
-
private createNewRuleFile;
|
|
50
|
-
/**
|
|
51
|
-
* Handles legacy rules cleanup with user choice of automatic or manual.
|
|
52
|
-
*/
|
|
53
|
-
private handleLegacyRulesCleanup;
|
|
54
|
-
private performAutomaticCleanup;
|
|
55
|
-
private performManualCleanup;
|
|
56
|
-
/**
|
|
57
|
-
* Replaces existing ByteRover rules (with boundary markers) with new rules.
|
|
58
|
-
*/
|
|
59
|
-
private replaceExistingRules;
|
|
60
|
-
}
|
|
61
|
-
export {};
|