byterover-cli 3.1.0 → 3.3.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 +17 -0
- package/dist/agent/infra/agent/agent-schemas.d.ts +8 -0
- package/dist/agent/infra/agent/agent-schemas.js +1 -0
- package/dist/agent/infra/sandbox/curate-service.js +14 -0
- package/dist/agent/infra/sandbox/sandbox-service.js +1 -0
- package/dist/agent/infra/sandbox/tools-sdk.d.ts +10 -0
- package/dist/agent/infra/sandbox/tools-sdk.js +9 -1
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +226 -103
- package/dist/agent/infra/tools/implementations/write-file-tool.d.ts +2 -1
- package/dist/agent/infra/tools/implementations/write-file-tool.js +16 -2
- package/dist/agent/infra/tools/tool-registry.js +1 -1
- package/dist/agent/infra/tools/write-guard.d.ts +11 -0
- package/dist/agent/infra/tools/write-guard.js +48 -0
- package/dist/agent/resources/prompts/system-prompt.yml +9 -0
- package/dist/agent/resources/tools/expand_knowledge.txt +4 -0
- package/dist/agent/resources/tools/search_knowledge.txt +11 -1
- package/dist/oclif/commands/curate/index.d.ts +1 -0
- package/dist/oclif/commands/curate/index.js +19 -4
- package/dist/oclif/commands/curate/view.js +2 -2
- package/dist/oclif/commands/main.js +13 -0
- package/dist/oclif/commands/query.d.ts +1 -0
- package/dist/oclif/commands/query.js +19 -4
- package/dist/oclif/commands/search.d.ts +20 -0
- package/dist/oclif/commands/search.js +186 -0
- package/dist/oclif/commands/source/add.d.ts +12 -0
- package/dist/oclif/commands/source/add.js +42 -0
- package/dist/oclif/commands/source/index.d.ts +6 -0
- package/dist/oclif/commands/source/index.js +8 -0
- package/dist/oclif/commands/source/list.d.ts +6 -0
- package/dist/oclif/commands/source/list.js +32 -0
- package/dist/oclif/commands/source/remove.d.ts +9 -0
- package/dist/oclif/commands/source/remove.js +33 -0
- package/dist/oclif/commands/status.d.ts +5 -1
- package/dist/oclif/commands/status.js +45 -6
- package/dist/oclif/commands/worktree/add.d.ts +12 -0
- package/dist/oclif/commands/worktree/add.js +44 -0
- package/dist/oclif/commands/worktree/index.d.ts +6 -0
- package/dist/oclif/commands/worktree/index.js +8 -0
- package/dist/oclif/commands/worktree/list.d.ts +6 -0
- package/dist/oclif/commands/worktree/list.js +28 -0
- package/dist/oclif/commands/worktree/remove.d.ts +9 -0
- package/dist/oclif/commands/worktree/remove.js +35 -0
- package/dist/oclif/hooks/init/validate-brv-config.js +4 -0
- package/dist/oclif/lib/daemon-client.d.ts +4 -2
- package/dist/oclif/lib/daemon-client.js +19 -4
- package/dist/oclif/lib/search-format.d.ts +10 -0
- package/dist/oclif/lib/search-format.js +25 -0
- package/dist/oclif/lib/task-client.d.ts +6 -0
- package/dist/oclif/lib/task-client.js +10 -3
- package/dist/server/constants.d.ts +7 -1
- package/dist/server/constants.js +10 -0
- package/dist/server/core/domain/client/client-info.d.ts +7 -0
- package/dist/server/core/domain/client/client-info.js +11 -0
- package/dist/server/core/domain/errors/task-error.d.ts +2 -2
- package/dist/server/core/domain/errors/task-error.js +5 -4
- package/dist/server/core/domain/project/worktrees-schema.d.ts +29 -0
- package/dist/server/core/domain/project/worktrees-schema.js +17 -0
- package/dist/server/core/domain/source/source-operations.d.ts +31 -0
- package/dist/server/core/domain/source/source-operations.js +201 -0
- package/dist/server/core/domain/source/source-schema.d.ts +94 -0
- package/dist/server/core/domain/source/source-schema.js +121 -0
- package/dist/server/core/domain/transport/schemas.d.ts +18 -10
- package/dist/server/core/domain/transport/schemas.js +7 -3
- package/dist/server/core/domain/transport/task-info.d.ts +2 -0
- package/dist/server/core/interfaces/client/i-client-manager.d.ts +13 -0
- package/dist/server/core/interfaces/executor/i-curate-executor.d.ts +4 -0
- package/dist/server/core/interfaces/executor/i-folder-pack-executor.d.ts +7 -3
- package/dist/server/core/interfaces/executor/i-query-executor.d.ts +2 -0
- package/dist/server/core/interfaces/executor/i-search-executor.d.ts +34 -0
- package/dist/server/core/interfaces/executor/i-search-executor.js +1 -0
- package/dist/server/core/interfaces/executor/index.d.ts +1 -0
- package/dist/server/core/interfaces/executor/index.js +1 -0
- package/dist/server/infra/client/client-manager.d.ts +1 -0
- package/dist/server/infra/client/client-manager.js +16 -0
- package/dist/server/infra/daemon/agent-process.js +35 -12
- package/dist/server/infra/executor/curate-executor.js +4 -2
- package/dist/server/infra/executor/direct-search-responder.js +5 -1
- package/dist/server/infra/executor/folder-pack-executor.js +23 -12
- package/dist/server/infra/executor/query-executor.d.ts +23 -0
- package/dist/server/infra/executor/query-executor.js +115 -21
- package/dist/server/infra/executor/search-executor.d.ts +17 -0
- package/dist/server/infra/executor/search-executor.js +30 -0
- package/dist/server/infra/mcp/mcp-mode-detector.d.ts +7 -5
- package/dist/server/infra/mcp/mcp-mode-detector.js +11 -18
- package/dist/server/infra/mcp/mcp-server.d.ts +1 -0
- package/dist/server/infra/mcp/mcp-server.js +11 -6
- package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -1
- package/dist/server/infra/mcp/tools/brv-curate-tool.js +9 -16
- package/dist/server/infra/mcp/tools/brv-query-tool.d.ts +2 -1
- package/dist/server/infra/mcp/tools/brv-query-tool.js +9 -16
- package/dist/server/infra/mcp/tools/mcp-project-context.d.ts +11 -0
- package/dist/server/infra/mcp/tools/mcp-project-context.js +54 -0
- package/dist/server/infra/process/connection-coordinator.js +11 -0
- package/dist/server/infra/process/feature-handlers.js +4 -1
- package/dist/server/infra/process/task-router.d.ts +1 -0
- package/dist/server/infra/process/task-router.js +60 -5
- package/dist/server/infra/project/resolve-project.d.ts +106 -0
- package/dist/server/infra/project/resolve-project.js +473 -0
- package/dist/server/infra/transport/handlers/index.d.ts +4 -0
- package/dist/server/infra/transport/handlers/index.js +2 -0
- package/dist/server/infra/transport/handlers/pull-handler.js +3 -3
- package/dist/server/infra/transport/handlers/push-handler.js +3 -3
- package/dist/server/infra/transport/handlers/source-handler.d.ts +12 -0
- package/dist/server/infra/transport/handlers/source-handler.js +37 -0
- package/dist/server/infra/transport/handlers/status-handler.js +76 -27
- package/dist/server/infra/transport/handlers/worktree-handler.d.ts +12 -0
- package/dist/server/infra/transport/handlers/worktree-handler.js +67 -0
- package/dist/server/infra/transport/transport-connector.d.ts +10 -4
- package/dist/server/infra/transport/transport-connector.js +2 -2
- package/dist/server/templates/skill/SKILL.md +25 -5
- package/dist/server/utils/path-utils.d.ts +5 -0
- package/dist/server/utils/path-utils.js +11 -1
- package/dist/shared/transport/events/client-events.d.ts +3 -0
- package/dist/shared/transport/events/client-events.js +3 -0
- package/dist/shared/transport/events/index.d.ts +13 -0
- package/dist/shared/transport/events/index.js +9 -0
- package/dist/shared/transport/events/source-events.d.ts +30 -0
- package/dist/shared/transport/events/source-events.js +5 -0
- package/dist/shared/transport/events/status-events.d.ts +5 -0
- package/dist/shared/transport/events/task-events.d.ts +4 -1
- package/dist/shared/transport/events/worktree-events.d.ts +31 -0
- package/dist/shared/transport/events/worktree-events.js +5 -0
- package/dist/shared/transport/search-content.d.ts +28 -0
- package/dist/shared/transport/search-content.js +38 -0
- package/dist/shared/transport/types/dto.d.ts +20 -1
- package/dist/tui/features/commands/definitions/index.js +6 -0
- package/dist/tui/features/commands/definitions/source-add.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-add.js +48 -0
- package/dist/tui/features/commands/definitions/source-list.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-list.js +47 -0
- package/dist/tui/features/commands/definitions/source-remove.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source-remove.js +38 -0
- package/dist/tui/features/commands/definitions/source.d.ts +2 -0
- package/dist/tui/features/commands/definitions/source.js +8 -0
- package/dist/tui/features/commands/definitions/worktree-add.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-add.js +35 -0
- package/dist/tui/features/commands/definitions/worktree-list.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-list.js +36 -0
- package/dist/tui/features/commands/definitions/worktree-remove.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree-remove.js +33 -0
- package/dist/tui/features/commands/definitions/worktree.d.ts +2 -0
- package/dist/tui/features/commands/definitions/worktree.js +8 -0
- package/dist/tui/features/curate/api/create-curate-task.js +3 -1
- package/dist/tui/features/query/api/create-query-task.js +3 -1
- package/dist/tui/features/source/api/source-api.d.ts +4 -0
- package/dist/tui/features/source/api/source-api.js +22 -0
- package/dist/tui/features/status/api/get-status.js +2 -1
- package/dist/tui/features/status/utils/format-status.js +28 -1
- package/dist/tui/features/transport/components/transport-initializer.js +36 -1
- package/dist/tui/features/worktree/api/worktree-api.d.ts +4 -0
- package/dist/tui/features/worktree/api/worktree-api.js +22 -0
- package/dist/tui/repl-startup.d.ts +2 -0
- package/dist/tui/repl-startup.js +5 -3
- package/dist/tui/stores/transport-store.d.ts +6 -0
- package/dist/tui/stores/transport-store.js +6 -0
- package/dist/tui/utils/error-messages.js +2 -2
- package/oclif.manifest.json +380 -36
- package/package.json +1 -1
|
@@ -114,4 +114,17 @@ export interface IClientManager {
|
|
|
114
114
|
* @param clientId - The client's Socket.IO ID
|
|
115
115
|
*/
|
|
116
116
|
unregister(clientId: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Update a client's project path, even if already associated.
|
|
119
|
+
* Used for reassociation after worktree add/remove operations.
|
|
120
|
+
* Moves the client from the old project index to the new one,
|
|
121
|
+
* and fires onProjectEmpty if the old project has no remaining external clients.
|
|
122
|
+
*
|
|
123
|
+
* No-op if client is unknown.
|
|
124
|
+
*
|
|
125
|
+
* @param clientId - The client's Socket.IO ID
|
|
126
|
+
* @param newProjectPath - The new project path to associate
|
|
127
|
+
* @returns The previous project path (undefined if client not found or not previously associated)
|
|
128
|
+
*/
|
|
129
|
+
updateProjectPath(clientId: string, newProjectPath: string): string | undefined;
|
|
117
130
|
}
|
|
@@ -10,8 +10,12 @@ export interface CurateExecuteOptions {
|
|
|
10
10
|
content: string;
|
|
11
11
|
/** Optional file paths for --files flag */
|
|
12
12
|
files?: string[];
|
|
13
|
+
/** Canonical project root where .brv/ lives (for post-processing: snapshot, summary, manifest) */
|
|
14
|
+
projectRoot?: string;
|
|
13
15
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
14
16
|
taskId: string;
|
|
17
|
+
/** Workspace root — linked subdir or same as projectRoot for direct projects */
|
|
18
|
+
worktreeRoot?: string;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* ICurateExecutor - Executes curate tasks with an injected CipherAgent.
|
|
@@ -4,14 +4,18 @@ import type { ICipherAgent } from '../../../../agent/core/interfaces/i-cipher-ag
|
|
|
4
4
|
* Agent uses its default session (Single-Session pattern).
|
|
5
5
|
*/
|
|
6
6
|
export interface FolderPackExecuteOptions {
|
|
7
|
-
/** Client's working directory for resolving relative paths */
|
|
7
|
+
/** Client's working directory for resolving relative paths (shell semantics) */
|
|
8
8
|
clientCwd?: string;
|
|
9
9
|
/** Optional context to guide the analysis */
|
|
10
10
|
content?: string;
|
|
11
|
-
/** Folder path to pack (relative to clientCwd or absolute) */
|
|
12
|
-
folderPath
|
|
11
|
+
/** Folder path to pack (relative to clientCwd or absolute). When absent, defaults to worktreeRoot. */
|
|
12
|
+
folderPath?: string;
|
|
13
|
+
/** Canonical project root where .brv/ lives (for temp file location) */
|
|
14
|
+
projectRoot?: string;
|
|
13
15
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
14
16
|
taskId: string;
|
|
17
|
+
/** Workspace root — linked subdir or same as projectRoot. Used as default folderPath when none supplied. */
|
|
18
|
+
worktreeRoot?: string;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* IFolderPackExecutor - Executes folder pack + curate tasks with an injected CipherAgent.
|
|
@@ -8,6 +8,8 @@ export interface QueryExecuteOptions {
|
|
|
8
8
|
query: string;
|
|
9
9
|
/** Task ID for event routing (required for concurrent task isolation) */
|
|
10
10
|
taskId: string;
|
|
11
|
+
/** Stable workspace root for scoping search and cache isolation */
|
|
12
|
+
worktreeRoot?: string;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* IQueryExecutor - Executes query tasks with an injected CipherAgent.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SearchKnowledgeResult } from '../../../../agent/infra/sandbox/tools-sdk.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for executing a context tree search.
|
|
4
|
+
* Search is stateless — no agent session, no LLM, no task isolation needed.
|
|
5
|
+
*/
|
|
6
|
+
export interface SearchExecuteOptions {
|
|
7
|
+
/** Maximum number of results to return (default: 10) */
|
|
8
|
+
limit?: number;
|
|
9
|
+
/** Search query */
|
|
10
|
+
query: string;
|
|
11
|
+
/** Path prefix to scope results (e.g. "auth/" for auth domain only) */
|
|
12
|
+
scope?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* ISearchExecutor - Executes search against the context tree's BM25 index.
|
|
16
|
+
*
|
|
17
|
+
* Unlike QueryExecutor (which requires a CipherAgent for LLM synthesis),
|
|
18
|
+
* SearchExecutor is stateless and returns raw search results directly.
|
|
19
|
+
* No agent session, no sandbox, no LLM call.
|
|
20
|
+
*
|
|
21
|
+
* Architecture:
|
|
22
|
+
* - SearchKnowledgeService provides BM25-indexed search
|
|
23
|
+
* - Executor wraps it with option validation
|
|
24
|
+
* - Results are SearchKnowledgeResult (paths, scores, excerpts)
|
|
25
|
+
*/
|
|
26
|
+
export interface ISearchExecutor {
|
|
27
|
+
/**
|
|
28
|
+
* Execute a context tree search.
|
|
29
|
+
*
|
|
30
|
+
* @param options - Search options (query, limit, scope)
|
|
31
|
+
* @returns Raw search results with paths, scores, and excerpts
|
|
32
|
+
*/
|
|
33
|
+
execute(options: SearchExecuteOptions): Promise<SearchKnowledgeResult>;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -41,6 +41,7 @@ export declare class ClientManager implements IClientManager {
|
|
|
41
41
|
register(clientId: string, type: ClientType, projectPath?: string): void;
|
|
42
42
|
setAgentName(clientId: string, agentName: string): void;
|
|
43
43
|
unregister(clientId: string): void;
|
|
44
|
+
updateProjectPath(clientId: string, newProjectPath: string): string | undefined;
|
|
44
45
|
private addToProjectIndex;
|
|
45
46
|
/**
|
|
46
47
|
* Check if a project has no remaining external clients.
|
|
@@ -111,6 +111,22 @@ export class ClientManager {
|
|
|
111
111
|
// Notify idle timeout policy
|
|
112
112
|
this.clientDisconnectedCallback?.();
|
|
113
113
|
}
|
|
114
|
+
updateProjectPath(clientId, newProjectPath) {
|
|
115
|
+
const client = this.clients.get(clientId);
|
|
116
|
+
if (!client)
|
|
117
|
+
return undefined;
|
|
118
|
+
const oldPath = client.updateProjectPath(newProjectPath);
|
|
119
|
+
// Move between project indexes
|
|
120
|
+
if (oldPath) {
|
|
121
|
+
this.removeFromProjectIndex(clientId, oldPath);
|
|
122
|
+
}
|
|
123
|
+
this.addToProjectIndex(clientId, newProjectPath);
|
|
124
|
+
// Check if old project is now empty
|
|
125
|
+
if (oldPath && oldPath !== newProjectPath && client.isExternalClient) {
|
|
126
|
+
this.checkProjectEmpty(oldPath);
|
|
127
|
+
}
|
|
128
|
+
return oldPath;
|
|
129
|
+
}
|
|
114
130
|
addToProjectIndex(clientId, projectPath) {
|
|
115
131
|
let members = this.projectClients.get(projectPath);
|
|
116
132
|
if (!members) {
|
|
@@ -28,13 +28,16 @@ import { FolderPackService } from '../../../agent/infra/folder-pack/folder-pack-
|
|
|
28
28
|
import { SessionMetadataStore } from '../../../agent/infra/session/session-metadata-store.js';
|
|
29
29
|
import { createSearchKnowledgeService } from '../../../agent/infra/tools/implementations/search-knowledge-service.js';
|
|
30
30
|
import { AuthEvents } from '../../../shared/transport/events/auth-events.js';
|
|
31
|
+
import { decodeSearchContent } from '../../../shared/transport/search-content.js';
|
|
31
32
|
import { getCurrentConfig } from '../../config/environment.js';
|
|
32
33
|
import { DEFAULT_LLM_MODEL, PROJECT } from '../../constants.js';
|
|
33
34
|
import { serializeTaskError, TaskError, TaskErrorCode } from '../../core/domain/errors/task-error.js';
|
|
35
|
+
import { loadSources } from '../../core/domain/source/source-schema.js';
|
|
34
36
|
import { TransportAgentEventNames, TransportDaemonEventNames, TransportStateEventNames, TransportTaskEventNames, } from '../../core/domain/transport/schemas.js';
|
|
35
37
|
import { CurateExecutor } from '../executor/curate-executor.js';
|
|
36
38
|
import { FolderPackExecutor } from '../executor/folder-pack-executor.js';
|
|
37
39
|
import { QueryExecutor } from '../executor/query-executor.js';
|
|
40
|
+
import { SearchExecutor } from '../executor/search-executor.js';
|
|
38
41
|
import { AgentInstanceDiscovery } from '../transport/agent-instance-discovery.js';
|
|
39
42
|
import { createAgentLogger } from './agent-logger.js';
|
|
40
43
|
import { resolveSessionId } from './session-resolver.js';
|
|
@@ -189,10 +192,14 @@ async function start() {
|
|
|
189
192
|
cachedProviderHeaders = providerResult.providerHeaders ? JSON.stringify(providerResult.providerHeaders) : undefined;
|
|
190
193
|
agentLog(`Provider: ${activeProvider}, Model: ${activeModel ?? 'default'}`);
|
|
191
194
|
// 5. Create CipherAgent with lazy providers + transport client
|
|
195
|
+
// Load knowledge sources early so shared context tree roots can be shared with both
|
|
196
|
+
// the agent's FileSystemService (via config) and the executor's FileSystemService
|
|
197
|
+
const sourcesData = loadSources(projectPath);
|
|
198
|
+
const sharedAllowedPaths = (sourcesData?.origins ?? []).map((o) => o.contextTreeRoot);
|
|
192
199
|
const envConfig = getCurrentConfig();
|
|
193
200
|
const agentConfig = {
|
|
194
201
|
apiBaseUrl: envConfig.llmApiBaseUrl,
|
|
195
|
-
fileSystem: { workingDirectory: projectPath },
|
|
202
|
+
fileSystem: { allowedPaths: ['.', ...sharedAllowedPaths], workingDirectory: projectPath },
|
|
196
203
|
llm: {
|
|
197
204
|
maxIterations: 10,
|
|
198
205
|
maxTokens: 4096,
|
|
@@ -279,7 +286,10 @@ async function start() {
|
|
|
279
286
|
}
|
|
280
287
|
});
|
|
281
288
|
// 6. Create FileSystemService + SearchKnowledgeService for smart query routing
|
|
282
|
-
const fileSystemService = new FileSystemService({
|
|
289
|
+
const fileSystemService = new FileSystemService({
|
|
290
|
+
allowedPaths: ['.', ...sharedAllowedPaths],
|
|
291
|
+
workingDirectory: projectPath,
|
|
292
|
+
});
|
|
283
293
|
await fileSystemService.initialize();
|
|
284
294
|
const searchService = createSearchKnowledgeService(fileSystemService, { baseDirectory: projectPath });
|
|
285
295
|
// 7. Create executors and listen for task:execute from pool
|
|
@@ -293,10 +303,11 @@ async function start() {
|
|
|
293
303
|
fileSystem: fileSystemService,
|
|
294
304
|
searchService,
|
|
295
305
|
});
|
|
306
|
+
const searchExecutor = new SearchExecutor(searchService);
|
|
296
307
|
transport.on(TransportTaskEventNames.EXECUTE, (task) => {
|
|
297
308
|
agentLog(`task:execute received taskId=${task.taskId} type=${task.type} activeTaskCount=${activeTaskCount + 1}`);
|
|
298
309
|
// eslint-disable-next-line no-void
|
|
299
|
-
void executeTask(task, curateExecutor, folderPackExecutor, queryExecutor);
|
|
310
|
+
void executeTask(task, curateExecutor, folderPackExecutor, queryExecutor, searchExecutor);
|
|
300
311
|
});
|
|
301
312
|
// 8. Register with transport server (for TransportHandlers tracking)
|
|
302
313
|
await transport.requestWithAck('agent:register', { projectPath });
|
|
@@ -304,15 +315,19 @@ async function start() {
|
|
|
304
315
|
process.send?.({ clientId, type: 'ready' });
|
|
305
316
|
agentLog('Ready — listening for tasks');
|
|
306
317
|
}
|
|
307
|
-
async function executeTask(task, curateExecutor, folderPackExecutor, queryExecutor) {
|
|
308
|
-
const { clientCwd, clientId, content, files, folderPath, taskId, type } = task;
|
|
318
|
+
async function executeTask(task, curateExecutor, folderPackExecutor, queryExecutor, searchExecutor) {
|
|
319
|
+
const { clientCwd, clientId, content, files, folderPath, taskId, type, worktreeRoot } = task;
|
|
309
320
|
if (!transport || !agent)
|
|
310
321
|
return;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
transport.
|
|
315
|
-
|
|
322
|
+
// Search tasks are pure BM25 retrieval — no LLM, no provider needed.
|
|
323
|
+
// Skip provider validation so search works even without a configured provider.
|
|
324
|
+
if (type !== 'search') {
|
|
325
|
+
const freshProviderConfig = await transport.requestWithAck(TransportStateEventNames.GET_PROVIDER_CONFIG);
|
|
326
|
+
const validationError = validateProviderForTask(freshProviderConfig);
|
|
327
|
+
if (validationError) {
|
|
328
|
+
transport.request(TransportTaskEventNames.ERROR, { clientId, error: validationError, taskId });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
316
331
|
}
|
|
317
332
|
activeTaskCount++;
|
|
318
333
|
try {
|
|
@@ -372,7 +387,7 @@ async function executeTask(task, curateExecutor, folderPackExecutor, queryExecut
|
|
|
372
387
|
let result;
|
|
373
388
|
switch (type) {
|
|
374
389
|
case 'curate': {
|
|
375
|
-
result = await curateExecutor.executeWithAgent(agent, { clientCwd, content, files, taskId });
|
|
390
|
+
result = await curateExecutor.executeWithAgent(agent, { clientCwd, content, files, projectRoot: projectPath, taskId, worktreeRoot });
|
|
376
391
|
break;
|
|
377
392
|
}
|
|
378
393
|
case 'curate-folder': {
|
|
@@ -380,12 +395,20 @@ async function executeTask(task, curateExecutor, folderPackExecutor, queryExecut
|
|
|
380
395
|
clientCwd,
|
|
381
396
|
content,
|
|
382
397
|
folderPath: folderPath,
|
|
398
|
+
projectRoot: projectPath,
|
|
383
399
|
taskId,
|
|
400
|
+
worktreeRoot,
|
|
384
401
|
});
|
|
385
402
|
break;
|
|
386
403
|
}
|
|
387
404
|
case 'query': {
|
|
388
|
-
result = await queryExecutor.executeWithAgent(agent, { query: content, taskId });
|
|
405
|
+
result = await queryExecutor.executeWithAgent(agent, { query: content, taskId, worktreeRoot });
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case 'search': {
|
|
409
|
+
const searchOptions = decodeSearchContent(content);
|
|
410
|
+
const searchResult = await searchExecutor.execute(searchOptions);
|
|
411
|
+
result = JSON.stringify(searchResult);
|
|
389
412
|
break;
|
|
390
413
|
}
|
|
391
414
|
}
|
|
@@ -36,7 +36,7 @@ export class CurateExecutor {
|
|
|
36
36
|
this.fileContentReader = fileContentReader ?? createFileContentReader();
|
|
37
37
|
}
|
|
38
38
|
async executeWithAgent(agent, options) {
|
|
39
|
-
const { clientCwd, content, files, taskId } = options;
|
|
39
|
+
const { clientCwd, content, files, projectRoot, taskId } = options;
|
|
40
40
|
// --- Phase 1: Preprocessing (no sessions created yet — safe to throw) ---
|
|
41
41
|
const fileReferenceInstructions = await this.processFileReferences(files ?? [], clientCwd);
|
|
42
42
|
const fullContext = fileReferenceInstructions ? `${content}\n${fileReferenceInstructions}` : content;
|
|
@@ -45,7 +45,9 @@ export class CurateExecutor {
|
|
|
45
45
|
const effectiveContext = compactionResult.context;
|
|
46
46
|
// --- Phase 3: Curation (session created AFTER preprocessing + compaction) ---
|
|
47
47
|
// Capture pre-curation state for snapshot diff (summary propagation)
|
|
48
|
-
|
|
48
|
+
// Post-processing (snapshot, summary, manifest) operates on projectRoot where .brv/ lives.
|
|
49
|
+
// worktreeRoot is a linked subdir — .brv/ does not exist there in linked setups.
|
|
50
|
+
const baseDir = projectRoot ?? clientCwd ?? process.cwd();
|
|
49
51
|
const snapshotService = new FileContextTreeSnapshotService({ baseDirectory: baseDir });
|
|
50
52
|
let preState;
|
|
51
53
|
try {
|
|
@@ -58,7 +58,11 @@ export function formatDirectResponse(query, results) {
|
|
|
58
58
|
return `### ${r.title}\n\n${truncatedContent}`;
|
|
59
59
|
})
|
|
60
60
|
.join('\n\n---\n\n');
|
|
61
|
-
const sources = topResults.map((r) =>
|
|
61
|
+
const sources = topResults.map((r) => {
|
|
62
|
+
// Paths starting with [ are already namespaced (linked results)
|
|
63
|
+
const displayPath = r.path.startsWith('[') ? r.path : `.brv/context-tree/${r.path}`;
|
|
64
|
+
return `- \`${displayPath}\``;
|
|
65
|
+
}).join('\n');
|
|
62
66
|
return `**Summary**: ${summary}
|
|
63
67
|
|
|
64
68
|
**Details**:
|
|
@@ -38,17 +38,28 @@ export class FolderPackExecutor {
|
|
|
38
38
|
this.folderPackService = folderPackService;
|
|
39
39
|
}
|
|
40
40
|
async executeWithAgent(agent, options) {
|
|
41
|
-
const { clientCwd, content, folderPath, taskId } = options;
|
|
41
|
+
const { clientCwd, content, folderPath, projectRoot, taskId, worktreeRoot } = options;
|
|
42
|
+
// Resolve folder path:
|
|
43
|
+
// - Absent folderPath → default to worktreeRoot (implicit workspace default)
|
|
44
|
+
// - Relative folderPath → resolve from clientCwd (shell semantics)
|
|
45
|
+
// - Absolute folderPath → use as-is
|
|
46
|
+
let absoluteFolderPath;
|
|
42
47
|
if (!folderPath) {
|
|
43
|
-
|
|
48
|
+
absoluteFolderPath = worktreeRoot ?? clientCwd ?? process.cwd();
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
else if (path.isAbsolute(folderPath)) {
|
|
51
|
+
absoluteFolderPath = folderPath;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const shellCwd = clientCwd ?? process.cwd();
|
|
55
|
+
absoluteFolderPath = path.resolve(shellCwd, folderPath);
|
|
56
|
+
}
|
|
57
|
+
// Temp file location: use projectRoot where .brv/ lives (accessible to sandbox)
|
|
58
|
+
const tempFileDir = projectRoot ?? clientCwd ?? process.cwd();
|
|
59
|
+
const snapshotService = new FileContextTreeSnapshotService({ baseDirectory: tempFileDir });
|
|
49
60
|
let preState;
|
|
50
61
|
try {
|
|
51
|
-
preState = await snapshotService.getCurrentState(
|
|
62
|
+
preState = await snapshotService.getCurrentState(tempFileDir);
|
|
52
63
|
}
|
|
53
64
|
catch {
|
|
54
65
|
// Fail-open: if snapshot fails, skip summary propagation
|
|
@@ -62,17 +73,17 @@ export class FolderPackExecutor {
|
|
|
62
73
|
// Use iterative extraction strategy (inspired by rlm)
|
|
63
74
|
// Stores packed folder in sandbox environment and lets agent iteratively query/extract
|
|
64
75
|
// This avoids token limits entirely - works for folders of any size
|
|
65
|
-
const response = await this.executeIterative(agent, packResult, content, absoluteFolderPath, taskId,
|
|
76
|
+
const response = await this.executeIterative(agent, packResult, content, absoluteFolderPath, taskId, tempFileDir);
|
|
66
77
|
if (preState) {
|
|
67
78
|
try {
|
|
68
|
-
const postState = await snapshotService.getCurrentState(
|
|
79
|
+
const postState = await snapshotService.getCurrentState(tempFileDir);
|
|
69
80
|
const changedPaths = diffStates(preState, postState);
|
|
70
81
|
if (changedPaths.length > 0) {
|
|
71
82
|
const summaryService = new FileContextTreeSummaryService();
|
|
72
|
-
const results = await summaryService.propagateStaleness(changedPaths, agent,
|
|
83
|
+
const results = await summaryService.propagateStaleness(changedPaths, agent, tempFileDir);
|
|
73
84
|
if (results.some((result) => result.actionTaken)) {
|
|
74
|
-
const manifestService = new FileContextTreeManifestService({ baseDirectory:
|
|
75
|
-
await manifestService.buildManifest(
|
|
85
|
+
const manifestService = new FileContextTreeManifestService({ baseDirectory: tempFileDir });
|
|
86
|
+
await manifestService.buildManifest(tempFileDir);
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
}
|
|
@@ -64,8 +64,31 @@ export declare class QueryExecutor implements IQueryExecutor {
|
|
|
64
64
|
* Compute a context tree fingerprint cheaply using file mtimes.
|
|
65
65
|
* Used for cache invalidation — if any file in the context tree changes,
|
|
66
66
|
* the fingerprint changes and cached results are invalidated.
|
|
67
|
+
*
|
|
68
|
+
* Includes worktreeRoot in the hash so different workspaces produce
|
|
69
|
+
* different fingerprints, preventing cross-workspace cache bleed.
|
|
67
70
|
*/
|
|
68
71
|
private computeContextTreeFingerprint;
|
|
72
|
+
/**
|
|
73
|
+
* Lightweight hash of currently valid shared source keys.
|
|
74
|
+
* Used by the fingerprint cache fast path to detect when a source target
|
|
75
|
+
* becomes broken (directory deleted) within the TTL window.
|
|
76
|
+
* Cost: one readFileSync + existsSync per source — sub-millisecond for typical setups.
|
|
77
|
+
*/
|
|
78
|
+
private computeSourceValidityHash;
|
|
79
|
+
/**
|
|
80
|
+
* Derive a workspace scope for search from the worktreeRoot.
|
|
81
|
+
* Returns the relative path from projectRoot to worktreeRoot,
|
|
82
|
+
* or undefined if they are the same (no scoping needed).
|
|
83
|
+
*
|
|
84
|
+
* KNOWN LIMITATION: Workspace scoping only works if the curated context
|
|
85
|
+
* tree has a subtree matching the workspace relative path (e.g., 'packages/api').
|
|
86
|
+
* Since the context tree is organized semantically by the LLM (topic-based),
|
|
87
|
+
* not by directory structure, scope filtering typically has 0 matches and
|
|
88
|
+
* falls through to unscoped search. A proper fix requires tagging curated
|
|
89
|
+
* files with source workspace metadata during curation.
|
|
90
|
+
*/
|
|
91
|
+
private deriveWorkspaceScope;
|
|
69
92
|
/**
|
|
70
93
|
* Extract key entities from a query for supplementary searches.
|
|
71
94
|
* Simple heuristic: split query, filter stopwords, keep significant terms.
|