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.
Files changed (38) hide show
  1. package/.env.production +0 -4
  2. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +12 -2
  3. package/dist/oclif/commands/curate/index.d.ts +1 -0
  4. package/dist/oclif/commands/curate/index.js +15 -1
  5. package/dist/oclif/commands/query.d.ts +1 -0
  6. package/dist/oclif/commands/query.js +17 -3
  7. package/dist/oclif/commands/search.d.ts +20 -0
  8. package/dist/oclif/commands/search.js +186 -0
  9. package/dist/oclif/commands/status.js +4 -0
  10. package/dist/oclif/lib/daemon-client.js +0 -1
  11. package/dist/oclif/lib/search-format.d.ts +10 -0
  12. package/dist/oclif/lib/search-format.js +25 -0
  13. package/dist/oclif/lib/task-client.d.ts +6 -0
  14. package/dist/oclif/lib/task-client.js +10 -3
  15. package/dist/server/constants.d.ts +1 -1
  16. package/dist/server/constants.js +2 -0
  17. package/dist/server/core/domain/errors/task-error.d.ts +2 -2
  18. package/dist/server/core/domain/errors/task-error.js +5 -4
  19. package/dist/server/core/domain/transport/schemas.d.ts +10 -10
  20. package/dist/server/core/domain/transport/schemas.js +3 -3
  21. package/dist/server/core/interfaces/executor/i-search-executor.d.ts +34 -0
  22. package/dist/server/core/interfaces/executor/i-search-executor.js +1 -0
  23. package/dist/server/core/interfaces/executor/index.d.ts +1 -0
  24. package/dist/server/core/interfaces/executor/index.js +1 -0
  25. package/dist/server/infra/daemon/agent-process.js +20 -7
  26. package/dist/server/infra/executor/search-executor.d.ts +17 -0
  27. package/dist/server/infra/executor/search-executor.js +30 -0
  28. package/dist/server/infra/transport/handlers/pull-handler.js +3 -3
  29. package/dist/server/infra/transport/handlers/push-handler.js +3 -3
  30. package/dist/server/infra/transport/handlers/status-handler.js +25 -18
  31. package/dist/server/templates/skill/SKILL.md +25 -5
  32. package/dist/shared/transport/search-content.d.ts +28 -0
  33. package/dist/shared/transport/search-content.js +38 -0
  34. package/dist/shared/transport/types/dto.d.ts +1 -1
  35. package/dist/tui/features/status/utils/format-status.js +5 -0
  36. package/dist/tui/utils/error-messages.js +2 -2
  37. package/oclif.manifest.json +425 -341
  38. 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
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './i-curate-executor.js';
2
2
  export * from './i-folder-pack-executor.js';
3
3
  export * from './i-query-executor.js';
4
+ export * from './i-search-executor.js';
@@ -1,3 +1,4 @@
1
1
  export * from './i-curate-executor.js';
2
2
  export * from './i-folder-pack-executor.js';
3
3
  export * from './i-query-executor.js';
4
+ export * from './i-search-executor.js';
@@ -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
- const freshProviderConfig = await transport.requestWithAck(TransportStateEventNames.GET_PROVIDER_CONFIG);
320
- const validationError = validateProviderForTask(freshProviderConfig);
321
- if (validationError) {
322
- transport.request(TransportTaskEventNames.ERROR, { clientId, error: validationError, taskId });
323
- return;
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, SpaceNotConfiguredError, } from '../../../core/domain/errors/task-error.js';
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 SpaceNotConfiguredError();
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 SpaceNotConfiguredError();
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, SpaceNotConfiguredError, } from '../../../core/domain/errors/task-error.js';
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 SpaceNotConfiguredError();
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 SpaceNotConfiguredError();
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
- const config = await this.projectConfigStore.read(effectiveProjectPath);
91
- if (config) {
92
- result.teamName = config.teamName;
93
- result.spaceName = config.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 hasSnapshot = await this.contextTreeSnapshotService.hasSnapshot(effectiveProjectPath);
119
- if (!hasSnapshot) {
120
- await this.contextTreeSnapshotService.initEmptySnapshot(effectiveProjectPath);
121
- }
122
- const changes = await this.contextTreeSnapshotService.getChanges(effectiveProjectPath);
123
- const hasChanges = changes.added.length > 0 || changes.modified.length > 0 || changes.deleted.length > 0;
124
- if (hasChanges) {
125
- result.contextTreeStatus = 'has_changes';
126
- result.contextTreeChanges = {
127
- added: changes.added,
128
- deleted: changes.deleted,
129
- modified: changes.modified,
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 = 'no_changes';
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. Curate Context
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
- ### 3. Review Pending Changes
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
- ### 4. LLM Provider Setup
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
- ### 5. Project Locations
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
- ### 6. Version Control
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