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.
Files changed (158) hide show
  1. package/README.md +17 -0
  2. package/dist/agent/infra/agent/agent-schemas.d.ts +8 -0
  3. package/dist/agent/infra/agent/agent-schemas.js +1 -0
  4. package/dist/agent/infra/sandbox/curate-service.js +14 -0
  5. package/dist/agent/infra/sandbox/sandbox-service.js +1 -0
  6. package/dist/agent/infra/sandbox/tools-sdk.d.ts +10 -0
  7. package/dist/agent/infra/sandbox/tools-sdk.js +9 -1
  8. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +226 -103
  9. package/dist/agent/infra/tools/implementations/write-file-tool.d.ts +2 -1
  10. package/dist/agent/infra/tools/implementations/write-file-tool.js +16 -2
  11. package/dist/agent/infra/tools/tool-registry.js +1 -1
  12. package/dist/agent/infra/tools/write-guard.d.ts +11 -0
  13. package/dist/agent/infra/tools/write-guard.js +48 -0
  14. package/dist/agent/resources/prompts/system-prompt.yml +9 -0
  15. package/dist/agent/resources/tools/expand_knowledge.txt +4 -0
  16. package/dist/agent/resources/tools/search_knowledge.txt +11 -1
  17. package/dist/oclif/commands/curate/index.d.ts +1 -0
  18. package/dist/oclif/commands/curate/index.js +19 -4
  19. package/dist/oclif/commands/curate/view.js +2 -2
  20. package/dist/oclif/commands/main.js +13 -0
  21. package/dist/oclif/commands/query.d.ts +1 -0
  22. package/dist/oclif/commands/query.js +19 -4
  23. package/dist/oclif/commands/search.d.ts +20 -0
  24. package/dist/oclif/commands/search.js +186 -0
  25. package/dist/oclif/commands/source/add.d.ts +12 -0
  26. package/dist/oclif/commands/source/add.js +42 -0
  27. package/dist/oclif/commands/source/index.d.ts +6 -0
  28. package/dist/oclif/commands/source/index.js +8 -0
  29. package/dist/oclif/commands/source/list.d.ts +6 -0
  30. package/dist/oclif/commands/source/list.js +32 -0
  31. package/dist/oclif/commands/source/remove.d.ts +9 -0
  32. package/dist/oclif/commands/source/remove.js +33 -0
  33. package/dist/oclif/commands/status.d.ts +5 -1
  34. package/dist/oclif/commands/status.js +45 -6
  35. package/dist/oclif/commands/worktree/add.d.ts +12 -0
  36. package/dist/oclif/commands/worktree/add.js +44 -0
  37. package/dist/oclif/commands/worktree/index.d.ts +6 -0
  38. package/dist/oclif/commands/worktree/index.js +8 -0
  39. package/dist/oclif/commands/worktree/list.d.ts +6 -0
  40. package/dist/oclif/commands/worktree/list.js +28 -0
  41. package/dist/oclif/commands/worktree/remove.d.ts +9 -0
  42. package/dist/oclif/commands/worktree/remove.js +35 -0
  43. package/dist/oclif/hooks/init/validate-brv-config.js +4 -0
  44. package/dist/oclif/lib/daemon-client.d.ts +4 -2
  45. package/dist/oclif/lib/daemon-client.js +19 -4
  46. package/dist/oclif/lib/search-format.d.ts +10 -0
  47. package/dist/oclif/lib/search-format.js +25 -0
  48. package/dist/oclif/lib/task-client.d.ts +6 -0
  49. package/dist/oclif/lib/task-client.js +10 -3
  50. package/dist/server/constants.d.ts +7 -1
  51. package/dist/server/constants.js +10 -0
  52. package/dist/server/core/domain/client/client-info.d.ts +7 -0
  53. package/dist/server/core/domain/client/client-info.js +11 -0
  54. package/dist/server/core/domain/errors/task-error.d.ts +2 -2
  55. package/dist/server/core/domain/errors/task-error.js +5 -4
  56. package/dist/server/core/domain/project/worktrees-schema.d.ts +29 -0
  57. package/dist/server/core/domain/project/worktrees-schema.js +17 -0
  58. package/dist/server/core/domain/source/source-operations.d.ts +31 -0
  59. package/dist/server/core/domain/source/source-operations.js +201 -0
  60. package/dist/server/core/domain/source/source-schema.d.ts +94 -0
  61. package/dist/server/core/domain/source/source-schema.js +121 -0
  62. package/dist/server/core/domain/transport/schemas.d.ts +18 -10
  63. package/dist/server/core/domain/transport/schemas.js +7 -3
  64. package/dist/server/core/domain/transport/task-info.d.ts +2 -0
  65. package/dist/server/core/interfaces/client/i-client-manager.d.ts +13 -0
  66. package/dist/server/core/interfaces/executor/i-curate-executor.d.ts +4 -0
  67. package/dist/server/core/interfaces/executor/i-folder-pack-executor.d.ts +7 -3
  68. package/dist/server/core/interfaces/executor/i-query-executor.d.ts +2 -0
  69. package/dist/server/core/interfaces/executor/i-search-executor.d.ts +34 -0
  70. package/dist/server/core/interfaces/executor/i-search-executor.js +1 -0
  71. package/dist/server/core/interfaces/executor/index.d.ts +1 -0
  72. package/dist/server/core/interfaces/executor/index.js +1 -0
  73. package/dist/server/infra/client/client-manager.d.ts +1 -0
  74. package/dist/server/infra/client/client-manager.js +16 -0
  75. package/dist/server/infra/daemon/agent-process.js +35 -12
  76. package/dist/server/infra/executor/curate-executor.js +4 -2
  77. package/dist/server/infra/executor/direct-search-responder.js +5 -1
  78. package/dist/server/infra/executor/folder-pack-executor.js +23 -12
  79. package/dist/server/infra/executor/query-executor.d.ts +23 -0
  80. package/dist/server/infra/executor/query-executor.js +115 -21
  81. package/dist/server/infra/executor/search-executor.d.ts +17 -0
  82. package/dist/server/infra/executor/search-executor.js +30 -0
  83. package/dist/server/infra/mcp/mcp-mode-detector.d.ts +7 -5
  84. package/dist/server/infra/mcp/mcp-mode-detector.js +11 -18
  85. package/dist/server/infra/mcp/mcp-server.d.ts +1 -0
  86. package/dist/server/infra/mcp/mcp-server.js +11 -6
  87. package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -1
  88. package/dist/server/infra/mcp/tools/brv-curate-tool.js +9 -16
  89. package/dist/server/infra/mcp/tools/brv-query-tool.d.ts +2 -1
  90. package/dist/server/infra/mcp/tools/brv-query-tool.js +9 -16
  91. package/dist/server/infra/mcp/tools/mcp-project-context.d.ts +11 -0
  92. package/dist/server/infra/mcp/tools/mcp-project-context.js +54 -0
  93. package/dist/server/infra/process/connection-coordinator.js +11 -0
  94. package/dist/server/infra/process/feature-handlers.js +4 -1
  95. package/dist/server/infra/process/task-router.d.ts +1 -0
  96. package/dist/server/infra/process/task-router.js +60 -5
  97. package/dist/server/infra/project/resolve-project.d.ts +106 -0
  98. package/dist/server/infra/project/resolve-project.js +473 -0
  99. package/dist/server/infra/transport/handlers/index.d.ts +4 -0
  100. package/dist/server/infra/transport/handlers/index.js +2 -0
  101. package/dist/server/infra/transport/handlers/pull-handler.js +3 -3
  102. package/dist/server/infra/transport/handlers/push-handler.js +3 -3
  103. package/dist/server/infra/transport/handlers/source-handler.d.ts +12 -0
  104. package/dist/server/infra/transport/handlers/source-handler.js +37 -0
  105. package/dist/server/infra/transport/handlers/status-handler.js +76 -27
  106. package/dist/server/infra/transport/handlers/worktree-handler.d.ts +12 -0
  107. package/dist/server/infra/transport/handlers/worktree-handler.js +67 -0
  108. package/dist/server/infra/transport/transport-connector.d.ts +10 -4
  109. package/dist/server/infra/transport/transport-connector.js +2 -2
  110. package/dist/server/templates/skill/SKILL.md +25 -5
  111. package/dist/server/utils/path-utils.d.ts +5 -0
  112. package/dist/server/utils/path-utils.js +11 -1
  113. package/dist/shared/transport/events/client-events.d.ts +3 -0
  114. package/dist/shared/transport/events/client-events.js +3 -0
  115. package/dist/shared/transport/events/index.d.ts +13 -0
  116. package/dist/shared/transport/events/index.js +9 -0
  117. package/dist/shared/transport/events/source-events.d.ts +30 -0
  118. package/dist/shared/transport/events/source-events.js +5 -0
  119. package/dist/shared/transport/events/status-events.d.ts +5 -0
  120. package/dist/shared/transport/events/task-events.d.ts +4 -1
  121. package/dist/shared/transport/events/worktree-events.d.ts +31 -0
  122. package/dist/shared/transport/events/worktree-events.js +5 -0
  123. package/dist/shared/transport/search-content.d.ts +28 -0
  124. package/dist/shared/transport/search-content.js +38 -0
  125. package/dist/shared/transport/types/dto.d.ts +20 -1
  126. package/dist/tui/features/commands/definitions/index.js +6 -0
  127. package/dist/tui/features/commands/definitions/source-add.d.ts +2 -0
  128. package/dist/tui/features/commands/definitions/source-add.js +48 -0
  129. package/dist/tui/features/commands/definitions/source-list.d.ts +2 -0
  130. package/dist/tui/features/commands/definitions/source-list.js +47 -0
  131. package/dist/tui/features/commands/definitions/source-remove.d.ts +2 -0
  132. package/dist/tui/features/commands/definitions/source-remove.js +38 -0
  133. package/dist/tui/features/commands/definitions/source.d.ts +2 -0
  134. package/dist/tui/features/commands/definitions/source.js +8 -0
  135. package/dist/tui/features/commands/definitions/worktree-add.d.ts +2 -0
  136. package/dist/tui/features/commands/definitions/worktree-add.js +35 -0
  137. package/dist/tui/features/commands/definitions/worktree-list.d.ts +2 -0
  138. package/dist/tui/features/commands/definitions/worktree-list.js +36 -0
  139. package/dist/tui/features/commands/definitions/worktree-remove.d.ts +2 -0
  140. package/dist/tui/features/commands/definitions/worktree-remove.js +33 -0
  141. package/dist/tui/features/commands/definitions/worktree.d.ts +2 -0
  142. package/dist/tui/features/commands/definitions/worktree.js +8 -0
  143. package/dist/tui/features/curate/api/create-curate-task.js +3 -1
  144. package/dist/tui/features/query/api/create-query-task.js +3 -1
  145. package/dist/tui/features/source/api/source-api.d.ts +4 -0
  146. package/dist/tui/features/source/api/source-api.js +22 -0
  147. package/dist/tui/features/status/api/get-status.js +2 -1
  148. package/dist/tui/features/status/utils/format-status.js +28 -1
  149. package/dist/tui/features/transport/components/transport-initializer.js +36 -1
  150. package/dist/tui/features/worktree/api/worktree-api.d.ts +4 -0
  151. package/dist/tui/features/worktree/api/worktree-api.js +22 -0
  152. package/dist/tui/repl-startup.d.ts +2 -0
  153. package/dist/tui/repl-startup.js +5 -3
  154. package/dist/tui/stores/transport-store.d.ts +6 -0
  155. package/dist/tui/stores/transport-store.js +6 -0
  156. package/dist/tui/utils/error-messages.js +2 -2
  157. package/oclif.manifest.json +380 -36
  158. package/package.json +1 -1
@@ -102,6 +102,12 @@ export interface HubEntryDTO {
102
102
  type: 'agent-skill' | 'bundle';
103
103
  version: string;
104
104
  }
105
+ export interface SourceStatusDTO {
106
+ alias: string;
107
+ contextTreeSize?: number;
108
+ projectRoot: string;
109
+ valid: boolean;
110
+ }
105
111
  export interface ProjectLocationDTO {
106
112
  /** Absolute path to the context tree directory (e.g., '/Users/foo/project/.brv/context-tree') */
107
113
  contextTreePath: string;
@@ -127,13 +133,26 @@ export interface StatusDTO {
127
133
  contextTreeDir?: string;
128
134
  /** Relative path to the context tree directory from project root (e.g., '.brv/context-tree') */
129
135
  contextTreeRelativeDir?: string;
130
- contextTreeStatus: 'git_vc' | 'has_changes' | 'no_changes' | 'not_initialized' | 'unknown';
136
+ contextTreeStatus: 'git_vc' | 'has_changes' | 'no_changes' | 'no_vc' | 'not_initialized' | 'unknown';
137
+ /** @deprecated Use projectRoot instead. Kept for backward compatibility. */
131
138
  currentDirectory: string;
132
139
  /** Number of files with pending HITL review (0 if none or unavailable). */
133
140
  pendingReviewCount?: number;
141
+ /** Absolute path to the project root (directory containing .brv/) */
142
+ projectRoot?: string;
143
+ /** How the project root was discovered */
144
+ resolutionSource?: 'direct' | 'flag' | 'linked';
145
+ /** Actionable error message when resolver fails (broken/malformed worktree pointer) */
146
+ resolverError?: string;
134
147
  /** URL to the local review UI (only set when pendingReviewCount > 0). */
135
148
  reviewUrl?: string;
149
+ /** Knowledge sources from other projects' context trees (read-only) */
150
+ sources?: SourceStatusDTO[];
151
+ /** Error message when sources.json is malformed */
152
+ sourcesError?: string;
136
153
  spaceName?: string;
137
154
  teamName?: string;
138
155
  userEmail?: string;
156
+ /** Stable workspace root (link directory), or projectRoot if unlinked */
157
+ worktreeRoot?: string;
139
158
  }
@@ -12,9 +12,11 @@ import { pullCommand } from './pull.js';
12
12
  import { pushCommand } from './push.js';
13
13
  import { queryCommand } from './query.js';
14
14
  import { resetCommand } from './reset.js';
15
+ import { sourceCommand } from './source.js';
15
16
  import { spaceCommand } from './space.js';
16
17
  import { statusCommand } from './status.js';
17
18
  import { vcCommand } from './vc.js';
19
+ import { worktreeCommand } from './worktree.js';
18
20
  /**
19
21
  * Load all REPL slash commands.
20
22
  *
@@ -41,6 +43,10 @@ export const load = () => [
41
43
  spaceCommand,
42
44
  // Git semantic (vc commands)
43
45
  vcCommand,
46
+ // Workspace management
47
+ worktreeCommand,
48
+ // Knowledge sources
49
+ sourceCommand,
44
50
  // Context tree management
45
51
  resetCommand,
46
52
  // Session management
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const sourceAddSubCommand: SlashCommand;
@@ -0,0 +1,48 @@
1
+ import { resolve } from 'node:path';
2
+ import { addSourceViaTransport } from '../../source/api/source-api.js';
3
+ import { Flags, parseReplArgs, toCommandFlags } from '../utils/arg-parser.js';
4
+ const sourceAddFlags = {
5
+ alias: Flags.string({
6
+ description: 'Custom alias for the source (defaults to directory name)',
7
+ }),
8
+ };
9
+ export const sourceAddSubCommand = {
10
+ async action(_context, args) {
11
+ const parsed = await parseReplArgs(args ?? '', { flags: sourceAddFlags, strict: false });
12
+ const targetArg = parsed.argv[0];
13
+ if (!targetArg) {
14
+ return {
15
+ content: 'Usage: /source add <path-to-project> [--alias <name>]',
16
+ messageType: 'error',
17
+ type: 'message',
18
+ };
19
+ }
20
+ const targetPath = resolve(targetArg);
21
+ try {
22
+ const result = await addSourceViaTransport(targetPath, parsed.flags.alias);
23
+ return {
24
+ content: result.message,
25
+ messageType: 'error',
26
+ type: 'message',
27
+ };
28
+ }
29
+ catch (error) {
30
+ const message = error instanceof Error ? error.message : String(error);
31
+ return {
32
+ content: `Source add failed: ${message}`,
33
+ messageType: 'error',
34
+ type: 'message',
35
+ };
36
+ }
37
+ },
38
+ args: [
39
+ {
40
+ description: 'Path to the target project containing .brv/',
41
+ name: 'path',
42
+ required: true,
43
+ },
44
+ ],
45
+ description: "Add a read-only knowledge source from another project's context tree",
46
+ flags: toCommandFlags(sourceAddFlags),
47
+ name: 'add',
48
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const sourceListSubCommand: SlashCommand;
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ import { listSourcesViaTransport } from '../../source/api/source-api.js';
3
+ export const sourceListSubCommand = {
4
+ async action() {
5
+ try {
6
+ const result = await listSourcesViaTransport();
7
+ if (result.error) {
8
+ return {
9
+ content: result.error,
10
+ messageType: 'error',
11
+ type: 'message',
12
+ };
13
+ }
14
+ if (result.statuses.length === 0) {
15
+ return {
16
+ content: 'No knowledge sources configured.',
17
+ messageType: 'error',
18
+ type: 'message',
19
+ };
20
+ }
21
+ const lines = ['Knowledge Sources:'];
22
+ for (const source of result.statuses) {
23
+ if (source.valid) {
24
+ lines.push(` ${source.alias} → ${source.projectRoot} ${chalk.green('(valid)')}`);
25
+ }
26
+ else {
27
+ lines.push(` ${source.alias} → ${source.projectRoot} ${chalk.red(`[BROKEN - run /source remove ${source.alias}]`)}`);
28
+ }
29
+ }
30
+ return {
31
+ content: lines.join('\n'),
32
+ messageType: 'error',
33
+ type: 'message',
34
+ };
35
+ }
36
+ catch (error) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ return {
39
+ content: `Source list failed: ${message}`,
40
+ messageType: 'error',
41
+ type: 'message',
42
+ };
43
+ }
44
+ },
45
+ description: 'List all knowledge sources and their status',
46
+ name: 'list',
47
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const sourceRemoveSubCommand: SlashCommand;
@@ -0,0 +1,38 @@
1
+ import { removeSourceViaTransport } from '../../source/api/source-api.js';
2
+ export const sourceRemoveSubCommand = {
3
+ async action(_context, args) {
4
+ const argTrimmed = args?.trim();
5
+ if (!argTrimmed) {
6
+ return {
7
+ content: 'Usage: /source remove <alias-or-path>',
8
+ messageType: 'error',
9
+ type: 'message',
10
+ };
11
+ }
12
+ try {
13
+ const result = await removeSourceViaTransport(argTrimmed);
14
+ return {
15
+ content: result.message,
16
+ messageType: 'error',
17
+ type: 'message',
18
+ };
19
+ }
20
+ catch (error) {
21
+ const message = error instanceof Error ? error.message : String(error);
22
+ return {
23
+ content: `Source remove failed: ${message}`,
24
+ messageType: 'error',
25
+ type: 'message',
26
+ };
27
+ }
28
+ },
29
+ args: [
30
+ {
31
+ description: 'Alias or path of the knowledge source to remove',
32
+ name: 'aliasOrPath',
33
+ required: true,
34
+ },
35
+ ],
36
+ description: 'Remove a knowledge source',
37
+ name: 'remove',
38
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const sourceCommand: SlashCommand;
@@ -0,0 +1,8 @@
1
+ import { sourceAddSubCommand } from './source-add.js';
2
+ import { sourceListSubCommand } from './source-list.js';
3
+ import { sourceRemoveSubCommand } from './source-remove.js';
4
+ export const sourceCommand = {
5
+ description: "Manage knowledge sources from other projects' context trees",
6
+ name: 'source',
7
+ subCommands: [sourceAddSubCommand, sourceRemoveSubCommand, sourceListSubCommand],
8
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const worktreeAddSubCommand: SlashCommand;
@@ -0,0 +1,35 @@
1
+ import { resolve } from 'node:path';
2
+ import { addWorktreeViaTransport } from '../../worktree/api/worktree-api.js';
3
+ export const worktreeAddSubCommand = {
4
+ async action(_context, args) {
5
+ const cwd = resolve(process.cwd());
6
+ const argTrimmed = args?.trim();
7
+ // Resolve worktree path: if argument provided, resolve relative to cwd; otherwise use cwd itself
8
+ const worktreePath = argTrimmed ? resolve(argTrimmed) : cwd;
9
+ try {
10
+ const result = await addWorktreeViaTransport(worktreePath, true);
11
+ return {
12
+ content: result.success ? result.message + ' Run /status to verify.' : result.message,
13
+ messageType: 'error',
14
+ type: 'message',
15
+ };
16
+ }
17
+ catch (error) {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ return {
20
+ content: `Worktree add failed: ${message}`,
21
+ messageType: 'error',
22
+ type: 'message',
23
+ };
24
+ }
25
+ },
26
+ args: [
27
+ {
28
+ description: 'Path to the directory to register as a worktree (auto-detects parent if omitted)',
29
+ name: 'path',
30
+ required: false,
31
+ },
32
+ ],
33
+ description: 'Register a directory as a worktree of a project',
34
+ name: 'add',
35
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const worktreeListSubCommand: SlashCommand;
@@ -0,0 +1,36 @@
1
+ import { listWorktreesViaTransport } from '../../worktree/api/worktree-api.js';
2
+ export const worktreeListSubCommand = {
3
+ async action() {
4
+ try {
5
+ const result = await listWorktreesViaTransport();
6
+ const lines = [];
7
+ if (result.source === 'linked') {
8
+ lines.push(`Worktree: ${result.worktreeRoot}`, `Linked to: ${result.projectRoot}`);
9
+ }
10
+ else {
11
+ lines.push(`Project: ${result.projectRoot}`);
12
+ }
13
+ if (result.worktrees.length > 0) {
14
+ lines.push('', 'Registered worktrees:');
15
+ for (const wt of result.worktrees) {
16
+ lines.push(` ${wt.name} → ${wt.worktreePath}`);
17
+ }
18
+ }
19
+ return {
20
+ content: lines.join('\n'),
21
+ messageType: 'error',
22
+ type: 'message',
23
+ };
24
+ }
25
+ catch (error) {
26
+ const message = error instanceof Error ? error.message : String(error);
27
+ return {
28
+ content: `Worktree list failed: ${message}`,
29
+ messageType: 'error',
30
+ type: 'message',
31
+ };
32
+ }
33
+ },
34
+ description: 'Show the current worktree link and list all registered worktrees',
35
+ name: 'list',
36
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const worktreeRemoveSubCommand: SlashCommand;
@@ -0,0 +1,33 @@
1
+ import { resolve } from 'node:path';
2
+ import { removeWorktreeViaTransport } from '../../worktree/api/worktree-api.js';
3
+ export const worktreeRemoveSubCommand = {
4
+ async action(_context, args) {
5
+ const argTrimmed = args?.trim();
6
+ const targetPath = argTrimmed ? resolve(argTrimmed) : resolve(process.cwd());
7
+ try {
8
+ const result = await removeWorktreeViaTransport(targetPath);
9
+ return {
10
+ content: result.message,
11
+ messageType: 'error',
12
+ type: 'message',
13
+ };
14
+ }
15
+ catch (error) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ return {
18
+ content: `Worktree remove failed: ${message}`,
19
+ messageType: 'error',
20
+ type: 'message',
21
+ };
22
+ }
23
+ },
24
+ args: [
25
+ {
26
+ description: 'Path to the worktree to remove (defaults to current directory)',
27
+ name: 'path',
28
+ required: false,
29
+ },
30
+ ],
31
+ description: 'Remove a worktree registration',
32
+ name: 'remove',
33
+ };
@@ -0,0 +1,2 @@
1
+ import type { SlashCommand } from '../../../types/commands.js';
2
+ export declare const worktreeCommand: SlashCommand;
@@ -0,0 +1,8 @@
1
+ import { worktreeAddSubCommand } from './worktree-add.js';
2
+ import { worktreeListSubCommand } from './worktree-list.js';
3
+ import { worktreeRemoveSubCommand } from './worktree-remove.js';
4
+ export const worktreeCommand = {
5
+ description: 'Manage worktree links for nested directories',
6
+ name: 'worktree',
7
+ subCommands: [worktreeAddSubCommand, worktreeRemoveSubCommand, worktreeListSubCommand],
8
+ };
@@ -15,7 +15,7 @@ import { useTransportStore } from '../../../stores/transport-store.js';
15
15
  * triggers the FolderPackExecutor on the server for full directory analysis.
16
16
  */
17
17
  export const createCurateTask = async ({ content, files, folders }) => {
18
- const { apiClient } = useTransportStore.getState();
18
+ const { apiClient, projectPath, worktreeRoot } = useTransportStore.getState();
19
19
  if (!apiClient) {
20
20
  throw new Error('Not connected to server');
21
21
  }
@@ -33,8 +33,10 @@ export const createCurateTask = async ({ content, files, folders }) => {
33
33
  content: resolvedContent,
34
34
  ...(hasFolder && folders ? { folderPath: folders[0] } : {}),
35
35
  ...(!hasFolder && files && files.length > 0 ? { files } : {}),
36
+ ...(projectPath ? { projectPath } : {}),
36
37
  taskId,
37
38
  type: taskType,
39
+ ...(worktreeRoot ? { worktreeRoot } : {}),
38
40
  });
39
41
  return { taskId };
40
42
  };
@@ -12,7 +12,7 @@ import { useTransportStore } from '../../../stores/transport-store.js';
12
12
  * Returns immediately after task is acknowledged - actual execution is async.
13
13
  */
14
14
  export const createQueryTask = async ({ query }) => {
15
- const { apiClient } = useTransportStore.getState();
15
+ const { apiClient, projectPath, worktreeRoot } = useTransportStore.getState();
16
16
  if (!apiClient) {
17
17
  throw new Error('Not connected to server');
18
18
  }
@@ -20,8 +20,10 @@ export const createQueryTask = async ({ query }) => {
20
20
  await apiClient.request(TaskEvents.CREATE, {
21
21
  clientCwd: process.cwd(),
22
22
  content: query,
23
+ ...(projectPath ? { projectPath } : {}),
23
24
  taskId,
24
25
  type: 'query',
26
+ ...(worktreeRoot ? { worktreeRoot } : {}),
25
27
  });
26
28
  return { taskId };
27
29
  };
@@ -0,0 +1,4 @@
1
+ import { type SourceAddResponse, type SourceListResponse, type SourceRemoveResponse } from '../../../../shared/transport/events/source-events.js';
2
+ export declare const addSourceViaTransport: (targetPath: string, alias?: string) => Promise<SourceAddResponse>;
3
+ export declare const removeSourceViaTransport: (aliasOrPath: string) => Promise<SourceRemoveResponse>;
4
+ export declare const listSourcesViaTransport: () => Promise<SourceListResponse>;
@@ -0,0 +1,22 @@
1
+ import { SourceEvents, } from '../../../../shared/transport/events/source-events.js';
2
+ import { useTransportStore } from '../../../stores/transport-store.js';
3
+ export const addSourceViaTransport = (targetPath, alias) => {
4
+ const { apiClient } = useTransportStore.getState();
5
+ if (!apiClient)
6
+ return Promise.reject(new Error('Not connected'));
7
+ const request = { alias, targetPath };
8
+ return apiClient.request(SourceEvents.ADD, request);
9
+ };
10
+ export const removeSourceViaTransport = (aliasOrPath) => {
11
+ const { apiClient } = useTransportStore.getState();
12
+ if (!apiClient)
13
+ return Promise.reject(new Error('Not connected'));
14
+ const request = { aliasOrPath };
15
+ return apiClient.request(SourceEvents.REMOVE, request);
16
+ };
17
+ export const listSourcesViaTransport = () => {
18
+ const { apiClient } = useTransportStore.getState();
19
+ if (!apiClient)
20
+ return Promise.reject(new Error('Not connected'));
21
+ return apiClient.request(SourceEvents.LIST);
22
+ };
@@ -5,7 +5,8 @@ export const getStatus = () => {
5
5
  const { apiClient } = useTransportStore.getState();
6
6
  if (!apiClient)
7
7
  return Promise.reject(new Error('Not connected'));
8
- return apiClient.request(StatusEvents.GET);
8
+ const request = { cwd: process.cwd() };
9
+ return apiClient.request(StatusEvents.GET, request);
9
10
  };
10
11
  export const getStatusQueryOptions = () => queryOptions({
11
12
  queryFn: getStatus,
@@ -22,13 +22,35 @@ export function formatStatus(status, version) {
22
22
  lines.push('Account: Unable to check');
23
23
  }
24
24
  }
25
- lines.push(`Current Directory: ${status.currentDirectory}`);
25
+ lines.push(`Project: ${status.projectRoot ?? status.currentDirectory}`);
26
+ if (status.worktreeRoot && status.worktreeRoot !== status.projectRoot) {
27
+ lines.push(`Worktree: ${status.worktreeRoot} (linked)`);
28
+ }
29
+ if (status.resolverError) {
30
+ lines.push(chalk.yellow(`⚠ ${status.resolverError}`));
31
+ }
26
32
  if (status.teamName && status.spaceName) {
27
33
  lines.push(`Space: ${status.teamName}/${status.spaceName}`);
28
34
  }
29
35
  else {
30
36
  lines.push('Space: Not connected');
31
37
  }
38
+ // Knowledge sources
39
+ if (status.sourcesError) {
40
+ lines.push(chalk.yellow(`⚠ ${status.sourcesError}`));
41
+ }
42
+ else if (status.sources && status.sources.length > 0) {
43
+ lines.push('Knowledge Sources:');
44
+ for (const source of status.sources) {
45
+ if (source.valid) {
46
+ const sizeInfo = source.contextTreeSize === undefined ? '' : ` [${source.contextTreeSize} files]`;
47
+ lines.push(` ${source.alias} → ${source.projectRoot} ${chalk.green('(valid)')}${sizeInfo}`);
48
+ }
49
+ else {
50
+ lines.push(` ${source.alias} → ${source.projectRoot} ${chalk.red(`[BROKEN - run brv source remove ${source.alias}]`)}`);
51
+ }
52
+ }
53
+ }
32
54
  switch (status.contextTreeStatus) {
33
55
  case 'git_vc': {
34
56
  lines.push('Context Tree: Byterover version control (use /vc commands)');
@@ -54,6 +76,10 @@ export function formatStatus(status, version) {
54
76
  lines.push('Context Tree: No changes');
55
77
  break;
56
78
  }
79
+ case 'no_vc': {
80
+ lines.push('Context Tree: Managed by Byterover version control (use /vc commands)');
81
+ break;
82
+ }
57
83
  case 'not_initialized': {
58
84
  lines.push('Context Tree: Not initialized');
59
85
  break;
@@ -67,5 +93,6 @@ export function formatStatus(status, version) {
67
93
  lines.push(chalk.yellow(`Pending Reviews: ${status.pendingReviewCount} ${fileLabel} need review`) +
68
94
  (status.reviewUrl ? `\n Review: ${chalk.blue(status.reviewUrl)}` : ''));
69
95
  }
96
+ lines.push('', 'Tip: Version control is now available for your context tree.', 'Learn more: https://docs.byterover.dev/git-semantic/overview');
70
97
  return lines.join('\n');
71
98
  }
@@ -8,6 +8,8 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
8
8
  */
9
9
  import { connectToDaemon, createDaemonReconnector, } from '@campfirein/brv-transport-client';
10
10
  import { useEffect } from 'react';
11
+ // eslint-disable-next-line no-restricted-imports -- fallback resolver when store has no projectPath
12
+ import { resolveProject } from '../../../../server/infra/project/resolve-project.js';
11
13
  import { getAllEventValues } from '../../../../shared/transport/events/index.js';
12
14
  import { initTransportLog, logTransportEvent } from '../../../lib/transport-logger.js';
13
15
  import { useTransportStore } from '../../../stores/transport-store.js';
@@ -16,6 +18,7 @@ export function TransportInitializer({ children }) {
16
18
  useEffect(() => {
17
19
  let mounted = true;
18
20
  let reconnectorHandle;
21
+ let unsubProjectSync;
19
22
  const eventUnsubscribes = [];
20
23
  function registerEventHandlers(client) {
21
24
  // Clear old handlers first
@@ -36,12 +39,43 @@ export function TransportInitializer({ children }) {
36
39
  try {
37
40
  initTransportLog();
38
41
  setConnectionState('connecting');
42
+ const getCurrentProjectPath = () => {
43
+ const storedProjectPath = useTransportStore.getState().projectPath;
44
+ if (storedProjectPath)
45
+ return storedProjectPath;
46
+ // Attempt live resolution as a last resort (e.g. store not yet populated).
47
+ // If resolution fails or returns null, throw — registering with raw cwd
48
+ // would put the client in the wrong room.
49
+ const resolution = resolveProject();
50
+ if (!resolution) {
51
+ throw new Error('No ByteRover project could be resolved. Ensure you are inside a brv project directory.');
52
+ }
53
+ return resolution.projectRoot;
54
+ };
39
55
  // connectToDaemon = ensureDaemonRunning (no-op, already running) + connect + register + join rooms
56
+ // Use resolved projectPath from store (set by oclif main via resolveProject()),
57
+ // falling back to process.cwd() for backwards compatibility.
58
+ //
59
+ // connectOptions is a mutable object — the reconnector captures it by reference
60
+ // (daemon-reconnector.js line 60), so mutating .projectPath here is visible
61
+ // on subsequent reconnect attempts.
40
62
  const connectOptions = {
41
63
  clientType: 'tui',
42
64
  joinRooms: ['broadcast-room'],
43
- projectPath: process.cwd(),
65
+ projectPath: getCurrentProjectPath(),
44
66
  };
67
+ // Keep connectOptions.projectPath in sync with the store so reconnects
68
+ // use the latest value (e.g. after reassociation from worktree add/remove).
69
+ // If projectPath is cleared and resolver fails, keep the last good value
70
+ // rather than registering with raw cwd.
71
+ unsubProjectSync = useTransportStore.subscribe((state) => {
72
+ try {
73
+ connectOptions.projectPath = state.projectPath ?? getCurrentProjectPath();
74
+ }
75
+ catch {
76
+ // Resolver failed — keep existing connectOptions.projectPath unchanged
77
+ }
78
+ });
45
79
  const { client: newClient } = await connectToDaemon(connectOptions);
46
80
  if (!mounted) {
47
81
  await newClient.disconnect();
@@ -89,6 +123,7 @@ export function TransportInitializer({ children }) {
89
123
  return () => {
90
124
  mounted = false;
91
125
  reconnectorHandle?.cancel();
126
+ unsubProjectSync?.();
92
127
  // Clean up all event handlers
93
128
  for (const unsub of eventUnsubscribes) {
94
129
  unsub();
@@ -0,0 +1,4 @@
1
+ import { type WorktreeAddResponse, type WorktreeListResponse, type WorktreeRemoveResponse } from '../../../../shared/transport/events/worktree-events.js';
2
+ export declare const addWorktreeViaTransport: (worktreePath: string, force?: boolean) => Promise<WorktreeAddResponse>;
3
+ export declare const removeWorktreeViaTransport: (worktreePath: string) => Promise<WorktreeRemoveResponse>;
4
+ export declare const listWorktreesViaTransport: () => Promise<WorktreeListResponse>;
@@ -0,0 +1,22 @@
1
+ import { WorktreeEvents, } from '../../../../shared/transport/events/worktree-events.js';
2
+ import { useTransportStore } from '../../../stores/transport-store.js';
3
+ export const addWorktreeViaTransport = (worktreePath, force) => {
4
+ const { apiClient } = useTransportStore.getState();
5
+ if (!apiClient)
6
+ return Promise.reject(new Error('Not connected'));
7
+ const request = { force, worktreePath };
8
+ return apiClient.request(WorktreeEvents.ADD, request);
9
+ };
10
+ export const removeWorktreeViaTransport = (worktreePath) => {
11
+ const { apiClient } = useTransportStore.getState();
12
+ if (!apiClient)
13
+ return Promise.reject(new Error('Not connected'));
14
+ const request = { worktreePath };
15
+ return apiClient.request(WorktreeEvents.REMOVE, request);
16
+ };
17
+ export const listWorktreesViaTransport = () => {
18
+ const { apiClient } = useTransportStore.getState();
19
+ if (!apiClient)
20
+ return Promise.reject(new Error('Not connected'));
21
+ return apiClient.request(WorktreeEvents.LIST);
22
+ };
@@ -5,7 +5,9 @@
5
5
  * - TransportInitializer connects to daemon via connectToDaemon()
6
6
  */
7
7
  export interface ReplOptions {
8
+ projectPath?: string;
8
9
  version: string;
10
+ worktreeRoot?: string;
9
11
  }
10
12
  /**
11
13
  * Start the ByteRover REPL
@@ -7,9 +7,11 @@ import { useTransportStore } from './stores/transport-store.js';
7
7
  * Start the ByteRover REPL
8
8
  */
9
9
  export async function startRepl(options) {
10
- const { version } = options;
11
- // Set version in store before rendering
12
- useTransportStore.getState().setVersion(version);
10
+ const { projectPath, version, worktreeRoot } = options;
11
+ // Set version and project info in store before rendering
12
+ const store = useTransportStore.getState();
13
+ store.setVersion(version);
14
+ store.setProjectInfo(projectPath, worktreeRoot);
13
15
  const { waitUntilExit } = render(_jsx(AppProviders, { children: _jsx(App, {}) }));
14
16
  await waitUntilExit();
15
17
  }
@@ -18,10 +18,14 @@ export interface TransportState {
18
18
  error: Error | null;
19
19
  /** Whether the client is connected */
20
20
  isConnected: boolean;
21
+ /** Resolved project path (where .brv/ lives) */
22
+ projectPath: null | string;
21
23
  /** Number of reconnection attempts */
22
24
  reconnectCount: number;
23
25
  /** App version */
24
26
  version: string;
27
+ /** Resolved workspace root (linked subdir or projectRoot if unlinked) */
28
+ worktreeRoot: null | string;
25
29
  }
26
30
  export interface TransportActions {
27
31
  /** Increment reconnect count */
@@ -34,6 +38,8 @@ export interface TransportActions {
34
38
  setConnectionState: (state: ConnectionState) => void;
35
39
  /** Set connection error */
36
40
  setError: (error: Error | null) => void;
41
+ /** Set resolved project info from oclif main */
42
+ setProjectInfo: (projectPath?: string, worktreeRoot?: string) => void;
37
43
  /** Set app version */
38
44
  setVersion: (version: string) => void;
39
45
  }