byterover-cli 3.2.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/.env.production +0 -4
- package/dist/agent/infra/tools/implementations/search-knowledge-service.js +12 -2
- package/dist/oclif/commands/curate/index.d.ts +1 -0
- package/dist/oclif/commands/curate/index.js +15 -1
- package/dist/oclif/commands/query.d.ts +1 -0
- package/dist/oclif/commands/query.js +17 -3
- package/dist/oclif/commands/search.d.ts +20 -0
- package/dist/oclif/commands/search.js +186 -0
- package/dist/oclif/commands/status.js +4 -0
- package/dist/oclif/lib/daemon-client.js +0 -1
- 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 +1 -1
- package/dist/server/constants.js +2 -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/transport/schemas.d.ts +10 -10
- package/dist/server/core/domain/transport/schemas.js +3 -3
- 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/daemon/agent-process.js +20 -7
- 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/transport/handlers/pull-handler.js +3 -3
- package/dist/server/infra/transport/handlers/push-handler.js +3 -3
- package/dist/server/infra/transport/handlers/status-handler.js +25 -18
- package/dist/server/templates/skill/SKILL.md +25 -5
- 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 +1 -1
- package/dist/tui/features/status/utils/format-status.js +5 -0
- package/dist/tui/utils/error-messages.js +2 -2
- package/oclif.manifest.json +425 -341
- package/package.json +1 -1
|
@@ -314,7 +314,7 @@ export const TaskExecuteSchema = z.object({
|
|
|
314
314
|
/** Unique task identifier */
|
|
315
315
|
taskId: z.string(),
|
|
316
316
|
/** Task type */
|
|
317
|
-
type: z.enum(['curate', 'curate-folder', 'query']),
|
|
317
|
+
type: z.enum(['curate', 'curate-folder', 'query', 'search']),
|
|
318
318
|
/** Workspace root for scoped query/curate */
|
|
319
319
|
worktreeRoot: z.string().optional(),
|
|
320
320
|
});
|
|
@@ -374,7 +374,7 @@ export const TaskCreatedSchema = z.object({
|
|
|
374
374
|
/** Unique task identifier */
|
|
375
375
|
taskId: z.string(),
|
|
376
376
|
/** Task type */
|
|
377
|
-
type: z.enum(['curate', 'curate-folder', 'query']),
|
|
377
|
+
type: z.enum(['curate', 'curate-folder', 'query', 'search']),
|
|
378
378
|
});
|
|
379
379
|
/**
|
|
380
380
|
* task:started - Agent begins processing the task
|
|
@@ -475,7 +475,7 @@ export const LlmToolResultEventSchema = z.object({
|
|
|
475
475
|
// ============================================================================
|
|
476
476
|
// Request/Response Schemas (for client → server commands)
|
|
477
477
|
// ============================================================================
|
|
478
|
-
export const TaskTypeSchema = z.enum(['curate', 'curate-folder', 'query']);
|
|
478
|
+
export const TaskTypeSchema = z.enum(['curate', 'curate-folder', 'query', 'search']);
|
|
479
479
|
/**
|
|
480
480
|
* Request to create a new task
|
|
481
481
|
*/
|
|
@@ -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 {};
|
|
@@ -28,6 +28,7 @@ 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';
|
|
@@ -36,6 +37,7 @@ import { TransportAgentEventNames, TransportDaemonEventNames, TransportStateEven
|
|
|
36
37
|
import { CurateExecutor } from '../executor/curate-executor.js';
|
|
37
38
|
import { FolderPackExecutor } from '../executor/folder-pack-executor.js';
|
|
38
39
|
import { QueryExecutor } from '../executor/query-executor.js';
|
|
40
|
+
import { SearchExecutor } from '../executor/search-executor.js';
|
|
39
41
|
import { AgentInstanceDiscovery } from '../transport/agent-instance-discovery.js';
|
|
40
42
|
import { createAgentLogger } from './agent-logger.js';
|
|
41
43
|
import { resolveSessionId } from './session-resolver.js';
|
|
@@ -301,10 +303,11 @@ async function start() {
|
|
|
301
303
|
fileSystem: fileSystemService,
|
|
302
304
|
searchService,
|
|
303
305
|
});
|
|
306
|
+
const searchExecutor = new SearchExecutor(searchService);
|
|
304
307
|
transport.on(TransportTaskEventNames.EXECUTE, (task) => {
|
|
305
308
|
agentLog(`task:execute received taskId=${task.taskId} type=${task.type} activeTaskCount=${activeTaskCount + 1}`);
|
|
306
309
|
// eslint-disable-next-line no-void
|
|
307
|
-
void executeTask(task, curateExecutor, folderPackExecutor, queryExecutor);
|
|
310
|
+
void executeTask(task, curateExecutor, folderPackExecutor, queryExecutor, searchExecutor);
|
|
308
311
|
});
|
|
309
312
|
// 8. Register with transport server (for TransportHandlers tracking)
|
|
310
313
|
await transport.requestWithAck('agent:register', { projectPath });
|
|
@@ -312,15 +315,19 @@ async function start() {
|
|
|
312
315
|
process.send?.({ clientId, type: 'ready' });
|
|
313
316
|
agentLog('Ready — listening for tasks');
|
|
314
317
|
}
|
|
315
|
-
async function executeTask(task, curateExecutor, folderPackExecutor, queryExecutor) {
|
|
318
|
+
async function executeTask(task, curateExecutor, folderPackExecutor, queryExecutor, searchExecutor) {
|
|
316
319
|
const { clientCwd, clientId, content, files, folderPath, taskId, type, worktreeRoot } = task;
|
|
317
320
|
if (!transport || !agent)
|
|
318
321
|
return;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (
|
|
322
|
-
transport.
|
|
323
|
-
|
|
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
|
+
}
|
|
324
331
|
}
|
|
325
332
|
activeTaskCount++;
|
|
326
333
|
try {
|
|
@@ -398,6 +405,12 @@ async function executeTask(task, curateExecutor, folderPackExecutor, queryExecut
|
|
|
398
405
|
result = await queryExecutor.executeWithAgent(agent, { query: content, taskId, worktreeRoot });
|
|
399
406
|
break;
|
|
400
407
|
}
|
|
408
|
+
case 'search': {
|
|
409
|
+
const searchOptions = decodeSearchContent(content);
|
|
410
|
+
const searchResult = await searchExecutor.execute(searchOptions);
|
|
411
|
+
result = JSON.stringify(searchResult);
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
401
414
|
}
|
|
402
415
|
// Emit task:completed
|
|
403
416
|
agentLog(`task:completed taskId=${taskId}`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SearchExecutor - Executes context tree searches via SearchKnowledgeService.
|
|
3
|
+
*
|
|
4
|
+
* Unlike QueryExecutor (Tier 0-4 with LLM synthesis), SearchExecutor is
|
|
5
|
+
* pure retrieval: BM25 index lookup → scored results. No LLM, no agent
|
|
6
|
+
* session, no sandbox, no token cost.
|
|
7
|
+
*
|
|
8
|
+
* This is the engine behind `brv search`. The CLI command and transport
|
|
9
|
+
* layer handle I/O; this module handles the search logic.
|
|
10
|
+
*/
|
|
11
|
+
import type { ISearchKnowledgeService, SearchKnowledgeResult } from '../../../agent/infra/sandbox/tools-sdk.js';
|
|
12
|
+
import type { ISearchExecutor, SearchExecuteOptions } from '../../core/interfaces/executor/i-search-executor.js';
|
|
13
|
+
export declare class SearchExecutor implements ISearchExecutor {
|
|
14
|
+
private readonly searchService;
|
|
15
|
+
constructor(searchService: ISearchKnowledgeService);
|
|
16
|
+
execute(options: SearchExecuteOptions): Promise<SearchKnowledgeResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SearchExecutor - Executes context tree searches via SearchKnowledgeService.
|
|
3
|
+
*
|
|
4
|
+
* Unlike QueryExecutor (Tier 0-4 with LLM synthesis), SearchExecutor is
|
|
5
|
+
* pure retrieval: BM25 index lookup → scored results. No LLM, no agent
|
|
6
|
+
* session, no sandbox, no token cost.
|
|
7
|
+
*
|
|
8
|
+
* This is the engine behind `brv search`. The CLI command and transport
|
|
9
|
+
* layer handle I/O; this module handles the search logic.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_LIMIT = 10;
|
|
12
|
+
const MAX_LIMIT = 50;
|
|
13
|
+
export class SearchExecutor {
|
|
14
|
+
searchService;
|
|
15
|
+
constructor(searchService) {
|
|
16
|
+
this.searchService = searchService;
|
|
17
|
+
}
|
|
18
|
+
async execute(options) {
|
|
19
|
+
const query = options.query.trim();
|
|
20
|
+
if (!query) {
|
|
21
|
+
return { message: 'Empty query', results: [], totalFound: 0 };
|
|
22
|
+
}
|
|
23
|
+
const scope = options.scope?.trim() || undefined;
|
|
24
|
+
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(options.limit ?? DEFAULT_LIMIT)));
|
|
25
|
+
return this.searchService.search(query, {
|
|
26
|
+
limit,
|
|
27
|
+
...(scope ? { scope } : {}),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PullEvents, } from '../../../../shared/transport/events/pull-events.js';
|
|
2
|
-
import { LocalChangesExistError, NotAuthenticatedError, ProjectNotInitError,
|
|
2
|
+
import { LegacySyncUnavailableError, LocalChangesExistError, NotAuthenticatedError, ProjectNotInitError, } from '../../../core/domain/errors/task-error.js';
|
|
3
3
|
import { guardAgainstGitVc, hasAnyChanges, resolveRequiredProjectPath, } from './handler-types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Handles pull:* events.
|
|
@@ -42,7 +42,7 @@ export class PullHandler {
|
|
|
42
42
|
throw new ProjectNotInitError();
|
|
43
43
|
}
|
|
44
44
|
if (!config.teamId || !config.spaceId) {
|
|
45
|
-
throw new
|
|
45
|
+
throw new LegacySyncUnavailableError();
|
|
46
46
|
}
|
|
47
47
|
// Check for local changes that would be overwritten
|
|
48
48
|
const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
|
|
@@ -79,7 +79,7 @@ export class PullHandler {
|
|
|
79
79
|
throw new ProjectNotInitError();
|
|
80
80
|
}
|
|
81
81
|
if (!config.teamId || !config.spaceId) {
|
|
82
|
-
throw new
|
|
82
|
+
throw new LegacySyncUnavailableError();
|
|
83
83
|
}
|
|
84
84
|
const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
|
|
85
85
|
const hasLocalChanges = hasAnyChanges(changes);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, relative } from 'node:path';
|
|
2
2
|
import { PushEvents, } from '../../../../shared/transport/events/push-events.js';
|
|
3
|
-
import { NotAuthenticatedError, ProjectNotInitError,
|
|
3
|
+
import { LegacySyncUnavailableError, NotAuthenticatedError, ProjectNotInitError, } from '../../../core/domain/errors/task-error.js';
|
|
4
4
|
import { mapToPushContexts } from '../../cogit/context-tree-to-push-context-mapper.js';
|
|
5
5
|
import { guardAgainstGitVc, resolveRequiredProjectPath, } from './handler-types.js';
|
|
6
6
|
/** Path prefix of the context tree relative to the project root. */
|
|
@@ -88,7 +88,7 @@ export class PushHandler {
|
|
|
88
88
|
throw new ProjectNotInitError();
|
|
89
89
|
}
|
|
90
90
|
if (!config.teamId || !config.spaceId) {
|
|
91
|
-
throw new
|
|
91
|
+
throw new LegacySyncUnavailableError();
|
|
92
92
|
}
|
|
93
93
|
this.broadcastToProject(projectPath, PushEvents.PROGRESS, { message: 'Reading context files...', step: 'reading' });
|
|
94
94
|
const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
|
|
@@ -163,7 +163,7 @@ export class PushHandler {
|
|
|
163
163
|
throw new ProjectNotInitError();
|
|
164
164
|
}
|
|
165
165
|
if (!config.teamId || !config.spaceId) {
|
|
166
|
-
throw new
|
|
166
|
+
throw new LegacySyncUnavailableError();
|
|
167
167
|
}
|
|
168
168
|
const hasSnapshot = await this.contextTreeSnapshotService.hasSnapshot(projectPath);
|
|
169
169
|
if (!hasSnapshot) {
|
|
@@ -84,13 +84,14 @@ export class StatusHandler {
|
|
|
84
84
|
result.authStatus = 'unknown';
|
|
85
85
|
}
|
|
86
86
|
// Project status — use effectiveProjectPath for consistency with resolved root
|
|
87
|
+
let projectConfig;
|
|
87
88
|
try {
|
|
88
89
|
const isInitialized = await this.projectConfigStore.exists(effectiveProjectPath);
|
|
89
90
|
if (isInitialized) {
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
result.teamName =
|
|
93
|
-
result.spaceName =
|
|
91
|
+
projectConfig = await this.projectConfigStore.read(effectiveProjectPath);
|
|
92
|
+
if (projectConfig) {
|
|
93
|
+
result.teamName = projectConfig.teamName;
|
|
94
|
+
result.spaceName = projectConfig.spaceName;
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -115,22 +116,28 @@ export class StatusHandler {
|
|
|
115
116
|
else {
|
|
116
117
|
result.contextTreeDir = join(effectiveProjectPath, BRV_DIR, CONTEXT_TREE_DIR);
|
|
117
118
|
result.contextTreeRelativeDir = join(BRV_DIR, CONTEXT_TREE_DIR);
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
await this.contextTreeSnapshotService.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
119
|
+
const hasLegacySyncConfig = Boolean(projectConfig?.teamId && projectConfig?.spaceId);
|
|
120
|
+
if (hasLegacySyncConfig) {
|
|
121
|
+
const hasSnapshot = await this.contextTreeSnapshotService.hasSnapshot(effectiveProjectPath);
|
|
122
|
+
if (!hasSnapshot) {
|
|
123
|
+
await this.contextTreeSnapshotService.initEmptySnapshot(effectiveProjectPath);
|
|
124
|
+
}
|
|
125
|
+
const changes = await this.contextTreeSnapshotService.getChanges(effectiveProjectPath);
|
|
126
|
+
const hasChanges = changes.added.length > 0 || changes.modified.length > 0 || changes.deleted.length > 0;
|
|
127
|
+
if (hasChanges) {
|
|
128
|
+
result.contextTreeStatus = 'has_changes';
|
|
129
|
+
result.contextTreeChanges = {
|
|
130
|
+
added: changes.added,
|
|
131
|
+
deleted: changes.deleted,
|
|
132
|
+
modified: changes.modified,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
result.contextTreeStatus = 'no_changes';
|
|
137
|
+
}
|
|
131
138
|
}
|
|
132
139
|
else {
|
|
133
|
-
result.contextTreeStatus = '
|
|
140
|
+
result.contextTreeStatus = 'no_vc';
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
}
|
|
@@ -34,7 +34,27 @@ Knowledge is stored in `.brv/context-tree/` as human-readable Markdown files.
|
|
|
34
34
|
brv query "How is authentication implemented?"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
### 2.
|
|
37
|
+
### 2. Search Context Tree
|
|
38
|
+
**Overview:** Retrieve a ranked list of matching files from `.brv/context-tree/` via pure BM25 lookup. Unlike `brv query`, this does NOT call an LLM — no synthesis, no token cost, no provider setup needed. Returns structured results with paths, scores, and excerpts.
|
|
39
|
+
|
|
40
|
+
**Use this skill when:**
|
|
41
|
+
- You need file paths to read rather than a synthesized answer
|
|
42
|
+
- You want fast, cheap retrieval with no LLM overhead
|
|
43
|
+
- You're in an automated pipeline that consumes structured results
|
|
44
|
+
|
|
45
|
+
**Do NOT use this skill when:**
|
|
46
|
+
- You need a natural-language answer synthesized from multiple files — use `brv query` instead
|
|
47
|
+
- The information is already present in your current context
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
brv search "authentication patterns"
|
|
51
|
+
brv search "JWT tokens" --limit 5 --scope "auth/"
|
|
52
|
+
brv search "auth" --format json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Flags:** `--limit N` (1-50, default 10), `--scope "domain/"` (path prefix filter), `--format json` (structured output for automation).
|
|
56
|
+
|
|
57
|
+
### 3. Curate Context
|
|
38
58
|
**Overview:** Analyze and save knowledge to the local knowledge base. Uses a configured LLM provider to categorize and structure the context you provide.
|
|
39
59
|
|
|
40
60
|
**Use this skill when:**
|
|
@@ -79,7 +99,7 @@ brv curate view --since 1h --status completed
|
|
|
79
99
|
brv curate view --help
|
|
80
100
|
```
|
|
81
101
|
|
|
82
|
-
###
|
|
102
|
+
### 4. Review Pending Changes
|
|
83
103
|
**Overview:** After a curate operation, some changes may require human review before being applied. Use `brv review` to list, approve, or reject pending operations.
|
|
84
104
|
|
|
85
105
|
**Use this when:**
|
|
@@ -142,7 +162,7 @@ brv review approve <taskId> --format json
|
|
|
142
162
|
brv review reject <taskId> --format json
|
|
143
163
|
```
|
|
144
164
|
|
|
145
|
-
###
|
|
165
|
+
### 5. LLM Provider Setup
|
|
146
166
|
`brv query` and `brv curate` require a configured LLM provider. Connect the default ByteRover provider (no API key needed):
|
|
147
167
|
|
|
148
168
|
```bash
|
|
@@ -156,7 +176,7 @@ brv providers list
|
|
|
156
176
|
brv providers connect openai --api-key sk-xxx --model gpt-4.1
|
|
157
177
|
```
|
|
158
178
|
|
|
159
|
-
###
|
|
179
|
+
### 6. Project Locations
|
|
160
180
|
**Overview:** List registered projects and their context tree paths. Returns project metadata including initialization status and active state. Use `-f json` for machine-readable output.
|
|
161
181
|
|
|
162
182
|
**Use this when:**
|
|
@@ -174,7 +194,7 @@ brv locations -f json
|
|
|
174
194
|
|
|
175
195
|
JSON fields: `projectPath`, `contextTreePath`, `isCurrent`, `isActive`, `isInitialized`.
|
|
176
196
|
|
|
177
|
-
###
|
|
197
|
+
### 7. Version Control
|
|
178
198
|
**Overview:** `brv vc` provides git-based version control for your context tree. It uses standard git semantics — branching, committing, merging, history, and conflict resolution — all working locally with no authentication required. Remote sync with a team is optional. The legacy `brv push`, `brv pull`, and `brv space` commands are deprecated — use `brv vc push`, `brv vc pull`, and `brv vc clone`/`brv vc remote add` instead.
|
|
179
199
|
|
|
180
200
|
**Use this when:**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode/decode helpers for search task content payloads.
|
|
3
|
+
*
|
|
4
|
+
* The transport layer's TaskCreateRequest has a single `content: string`
|
|
5
|
+
* field. For search tasks, we pack {query, limit?, scope?} as JSON so
|
|
6
|
+
* the agent process can reconstruct the structured options.
|
|
7
|
+
*
|
|
8
|
+
* Lives in shared/ because both the CLI (encoder) and the daemon
|
|
9
|
+
* agent-process (decoder) depend on it. Keeping it in oclif/ would
|
|
10
|
+
* create a circular dependency (server → oclif).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Encode search options as JSON content payload for the transport layer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function encodeSearchContent(options: {
|
|
16
|
+
limit?: number;
|
|
17
|
+
query: string;
|
|
18
|
+
scope?: string;
|
|
19
|
+
}): string;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a JSON-encoded search content payload back into options.
|
|
22
|
+
* Falls back to treating the entire string as a plain query if parsing fails.
|
|
23
|
+
*/
|
|
24
|
+
export declare function decodeSearchContent(content: string): {
|
|
25
|
+
limit?: number;
|
|
26
|
+
query: string;
|
|
27
|
+
scope?: string;
|
|
28
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode/decode helpers for search task content payloads.
|
|
3
|
+
*
|
|
4
|
+
* The transport layer's TaskCreateRequest has a single `content: string`
|
|
5
|
+
* field. For search tasks, we pack {query, limit?, scope?} as JSON so
|
|
6
|
+
* the agent process can reconstruct the structured options.
|
|
7
|
+
*
|
|
8
|
+
* Lives in shared/ because both the CLI (encoder) and the daemon
|
|
9
|
+
* agent-process (decoder) depend on it. Keeping it in oclif/ would
|
|
10
|
+
* create a circular dependency (server → oclif).
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Encode search options as JSON content payload for the transport layer.
|
|
14
|
+
*/
|
|
15
|
+
export function encodeSearchContent(options) {
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
limit: options.limit,
|
|
18
|
+
query: options.query,
|
|
19
|
+
scope: options.scope,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse a JSON-encoded search content payload back into options.
|
|
24
|
+
* Falls back to treating the entire string as a plain query if parsing fails.
|
|
25
|
+
*/
|
|
26
|
+
export function decodeSearchContent(content) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(content);
|
|
29
|
+
return {
|
|
30
|
+
limit: typeof parsed.limit === 'number' ? parsed.limit : undefined,
|
|
31
|
+
query: typeof parsed.query === 'string' ? parsed.query : content,
|
|
32
|
+
scope: typeof parsed.scope === 'string' ? parsed.scope : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { query: content };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -133,7 +133,7 @@ export interface StatusDTO {
|
|
|
133
133
|
contextTreeDir?: string;
|
|
134
134
|
/** Relative path to the context tree directory from project root (e.g., '.brv/context-tree') */
|
|
135
135
|
contextTreeRelativeDir?: string;
|
|
136
|
-
contextTreeStatus: 'git_vc' | 'has_changes' | 'no_changes' | 'not_initialized' | 'unknown';
|
|
136
|
+
contextTreeStatus: 'git_vc' | 'has_changes' | 'no_changes' | 'no_vc' | 'not_initialized' | 'unknown';
|
|
137
137
|
/** @deprecated Use projectRoot instead. Kept for backward compatibility. */
|
|
138
138
|
currentDirectory: string;
|
|
139
139
|
/** Number of files with pending HITL review (0 if none or unavailable). */
|
|
@@ -76,6 +76,10 @@ export function formatStatus(status, version) {
|
|
|
76
76
|
lines.push('Context Tree: No changes');
|
|
77
77
|
break;
|
|
78
78
|
}
|
|
79
|
+
case 'no_vc': {
|
|
80
|
+
lines.push('Context Tree: Managed by Byterover version control (use /vc commands)');
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
79
83
|
case 'not_initialized': {
|
|
80
84
|
lines.push('Context Tree: Not initialized');
|
|
81
85
|
break;
|
|
@@ -89,5 +93,6 @@ export function formatStatus(status, version) {
|
|
|
89
93
|
lines.push(chalk.yellow(`Pending Reviews: ${status.pendingReviewCount} ${fileLabel} need review`) +
|
|
90
94
|
(status.reviewUrl ? `\n Review: ${chalk.blue(status.reviewUrl)}` : ''));
|
|
91
95
|
}
|
|
96
|
+
lines.push('', 'Tip: Version control is now available for your context tree.', 'Learn more: https://docs.byterover.dev/git-semantic/overview');
|
|
92
97
|
return lines.join('\n');
|
|
93
98
|
}
|
|
@@ -9,20 +9,20 @@
|
|
|
9
9
|
const USER_FRIENDLY_MESSAGES = {
|
|
10
10
|
ERR_AGENT_NOT_INITIALIZED: "Agent failed to initialize. Run 'brv restart' to force a clean restart.",
|
|
11
11
|
ERR_CONTEXT_TREE_NOT_INIT: 'Context tree not initialized.',
|
|
12
|
+
ERR_LEGACY_SYNC_UNAVAILABLE: 'Legacy cloud sync (push/pull) is not available for this project. Use /vc init to start using version control. Learn more: https://docs.byterover.dev/git-semantic/overview',
|
|
12
13
|
ERR_LOCAL_CHANGES_EXIST: 'You have local changes. Run /push to save your changes before pulling.',
|
|
13
14
|
ERR_NOT_AUTHENTICATED: 'Not authenticated. This is required for cloud sync. Run /login to connect your account.',
|
|
14
15
|
ERR_OAUTH_REFRESH_FAILED: 'OAuth token refresh failed. Run /providers to reconnect your provider.',
|
|
15
16
|
ERR_OAUTH_TOKEN_EXPIRED: 'OAuth token has expired. Run /providers to reconnect your provider.',
|
|
16
17
|
ERR_PROJECT_NOT_INIT: "Project not initialized. Run 'brv restart' to reinitialize.",
|
|
17
18
|
ERR_PROVIDER_NOT_CONFIGURED: 'No provider connected. Run /providers connect byterover to use the free built-in provider, or connect another provider.',
|
|
18
|
-
ERR_SPACE_NOT_CONFIGURED: 'No space configured. Run /space switch to select a space first.',
|
|
19
19
|
ERR_SPACE_NOT_FOUND: 'Space not found. Check your configuration.',
|
|
20
20
|
ERR_VC_AUTH_FAILED: 'Authentication failed. Run /login.',
|
|
21
21
|
ERR_VC_BRANCH_ALREADY_EXISTS: 'Branch already exists.',
|
|
22
22
|
ERR_VC_CANNOT_DELETE_CURRENT_BRANCH: 'Cannot delete the currently checked-out branch.',
|
|
23
23
|
ERR_VC_CONFIG_KEY_NOT_SET: 'Config key is not set.',
|
|
24
24
|
ERR_VC_CONFLICT_MARKERS_PRESENT: 'Conflict markers detected. Run /vc conflicts to view them. Resolve conflicts and run /vc add before pushing.',
|
|
25
|
-
ERR_VC_GIT_INITIALIZED: 'ByteRover version control is active. Use /vc commands instead of legacy sync commands.',
|
|
25
|
+
ERR_VC_GIT_INITIALIZED: 'ByteRover version control is active. Use /vc commands instead of legacy sync commands. Learn more: https://docs.byterover.dev/git-semantic/overview',
|
|
26
26
|
ERR_VC_GIT_NOT_INITIALIZED: 'ByteRover version control not initialized. Run /vc init first.',
|
|
27
27
|
ERR_VC_INVALID_ACTION: 'Invalid action.',
|
|
28
28
|
// ERR_VC_INVALID_BRANCH_NAME intentionally omitted: fall through to server's message with actual branch name
|