byterover-cli 3.1.0 → 3.2.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 (137) hide show
  1. package/.env.production +4 -0
  2. package/README.md +17 -0
  3. package/dist/agent/infra/agent/agent-schemas.d.ts +8 -0
  4. package/dist/agent/infra/agent/agent-schemas.js +1 -0
  5. package/dist/agent/infra/sandbox/curate-service.js +14 -0
  6. package/dist/agent/infra/sandbox/sandbox-service.js +1 -0
  7. package/dist/agent/infra/sandbox/tools-sdk.d.ts +10 -0
  8. package/dist/agent/infra/sandbox/tools-sdk.js +9 -1
  9. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +214 -101
  10. package/dist/agent/infra/tools/implementations/write-file-tool.d.ts +2 -1
  11. package/dist/agent/infra/tools/implementations/write-file-tool.js +16 -2
  12. package/dist/agent/infra/tools/tool-registry.js +1 -1
  13. package/dist/agent/infra/tools/write-guard.d.ts +11 -0
  14. package/dist/agent/infra/tools/write-guard.js +48 -0
  15. package/dist/agent/resources/prompts/system-prompt.yml +9 -0
  16. package/dist/agent/resources/tools/expand_knowledge.txt +4 -0
  17. package/dist/agent/resources/tools/search_knowledge.txt +11 -1
  18. package/dist/oclif/commands/curate/index.js +4 -3
  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.js +4 -3
  22. package/dist/oclif/commands/source/add.d.ts +12 -0
  23. package/dist/oclif/commands/source/add.js +42 -0
  24. package/dist/oclif/commands/source/index.d.ts +6 -0
  25. package/dist/oclif/commands/source/index.js +8 -0
  26. package/dist/oclif/commands/source/list.d.ts +6 -0
  27. package/dist/oclif/commands/source/list.js +32 -0
  28. package/dist/oclif/commands/source/remove.d.ts +9 -0
  29. package/dist/oclif/commands/source/remove.js +33 -0
  30. package/dist/oclif/commands/status.d.ts +5 -1
  31. package/dist/oclif/commands/status.js +41 -6
  32. package/dist/oclif/commands/worktree/add.d.ts +12 -0
  33. package/dist/oclif/commands/worktree/add.js +44 -0
  34. package/dist/oclif/commands/worktree/index.d.ts +6 -0
  35. package/dist/oclif/commands/worktree/index.js +8 -0
  36. package/dist/oclif/commands/worktree/list.d.ts +6 -0
  37. package/dist/oclif/commands/worktree/list.js +28 -0
  38. package/dist/oclif/commands/worktree/remove.d.ts +9 -0
  39. package/dist/oclif/commands/worktree/remove.js +35 -0
  40. package/dist/oclif/hooks/init/validate-brv-config.js +4 -0
  41. package/dist/oclif/lib/daemon-client.d.ts +4 -2
  42. package/dist/oclif/lib/daemon-client.js +19 -3
  43. package/dist/server/constants.d.ts +6 -0
  44. package/dist/server/constants.js +8 -0
  45. package/dist/server/core/domain/client/client-info.d.ts +7 -0
  46. package/dist/server/core/domain/client/client-info.js +11 -0
  47. package/dist/server/core/domain/project/worktrees-schema.d.ts +29 -0
  48. package/dist/server/core/domain/project/worktrees-schema.js +17 -0
  49. package/dist/server/core/domain/source/source-operations.d.ts +31 -0
  50. package/dist/server/core/domain/source/source-operations.js +201 -0
  51. package/dist/server/core/domain/source/source-schema.d.ts +94 -0
  52. package/dist/server/core/domain/source/source-schema.js +121 -0
  53. package/dist/server/core/domain/transport/schemas.d.ts +8 -0
  54. package/dist/server/core/domain/transport/schemas.js +4 -0
  55. package/dist/server/core/domain/transport/task-info.d.ts +2 -0
  56. package/dist/server/core/interfaces/client/i-client-manager.d.ts +13 -0
  57. package/dist/server/core/interfaces/executor/i-curate-executor.d.ts +4 -0
  58. package/dist/server/core/interfaces/executor/i-folder-pack-executor.d.ts +7 -3
  59. package/dist/server/core/interfaces/executor/i-query-executor.d.ts +2 -0
  60. package/dist/server/infra/client/client-manager.d.ts +1 -0
  61. package/dist/server/infra/client/client-manager.js +16 -0
  62. package/dist/server/infra/daemon/agent-process.js +15 -5
  63. package/dist/server/infra/executor/curate-executor.js +4 -2
  64. package/dist/server/infra/executor/direct-search-responder.js +5 -1
  65. package/dist/server/infra/executor/folder-pack-executor.js +23 -12
  66. package/dist/server/infra/executor/query-executor.d.ts +23 -0
  67. package/dist/server/infra/executor/query-executor.js +115 -21
  68. package/dist/server/infra/mcp/mcp-mode-detector.d.ts +7 -5
  69. package/dist/server/infra/mcp/mcp-mode-detector.js +11 -18
  70. package/dist/server/infra/mcp/mcp-server.d.ts +1 -0
  71. package/dist/server/infra/mcp/mcp-server.js +11 -6
  72. package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -1
  73. package/dist/server/infra/mcp/tools/brv-curate-tool.js +9 -16
  74. package/dist/server/infra/mcp/tools/brv-query-tool.d.ts +2 -1
  75. package/dist/server/infra/mcp/tools/brv-query-tool.js +9 -16
  76. package/dist/server/infra/mcp/tools/mcp-project-context.d.ts +11 -0
  77. package/dist/server/infra/mcp/tools/mcp-project-context.js +54 -0
  78. package/dist/server/infra/process/connection-coordinator.js +11 -0
  79. package/dist/server/infra/process/feature-handlers.js +4 -1
  80. package/dist/server/infra/process/task-router.d.ts +1 -0
  81. package/dist/server/infra/process/task-router.js +60 -5
  82. package/dist/server/infra/project/resolve-project.d.ts +106 -0
  83. package/dist/server/infra/project/resolve-project.js +473 -0
  84. package/dist/server/infra/transport/handlers/index.d.ts +4 -0
  85. package/dist/server/infra/transport/handlers/index.js +2 -0
  86. package/dist/server/infra/transport/handlers/source-handler.d.ts +12 -0
  87. package/dist/server/infra/transport/handlers/source-handler.js +37 -0
  88. package/dist/server/infra/transport/handlers/status-handler.js +55 -13
  89. package/dist/server/infra/transport/handlers/worktree-handler.d.ts +12 -0
  90. package/dist/server/infra/transport/handlers/worktree-handler.js +67 -0
  91. package/dist/server/infra/transport/transport-connector.d.ts +10 -4
  92. package/dist/server/infra/transport/transport-connector.js +2 -2
  93. package/dist/server/utils/path-utils.d.ts +5 -0
  94. package/dist/server/utils/path-utils.js +11 -1
  95. package/dist/shared/transport/events/client-events.d.ts +3 -0
  96. package/dist/shared/transport/events/client-events.js +3 -0
  97. package/dist/shared/transport/events/index.d.ts +13 -0
  98. package/dist/shared/transport/events/index.js +9 -0
  99. package/dist/shared/transport/events/source-events.d.ts +30 -0
  100. package/dist/shared/transport/events/source-events.js +5 -0
  101. package/dist/shared/transport/events/status-events.d.ts +5 -0
  102. package/dist/shared/transport/events/task-events.d.ts +4 -1
  103. package/dist/shared/transport/events/worktree-events.d.ts +31 -0
  104. package/dist/shared/transport/events/worktree-events.js +5 -0
  105. package/dist/shared/transport/types/dto.d.ts +19 -0
  106. package/dist/tui/features/commands/definitions/index.js +6 -0
  107. package/dist/tui/features/commands/definitions/source-add.d.ts +2 -0
  108. package/dist/tui/features/commands/definitions/source-add.js +48 -0
  109. package/dist/tui/features/commands/definitions/source-list.d.ts +2 -0
  110. package/dist/tui/features/commands/definitions/source-list.js +47 -0
  111. package/dist/tui/features/commands/definitions/source-remove.d.ts +2 -0
  112. package/dist/tui/features/commands/definitions/source-remove.js +38 -0
  113. package/dist/tui/features/commands/definitions/source.d.ts +2 -0
  114. package/dist/tui/features/commands/definitions/source.js +8 -0
  115. package/dist/tui/features/commands/definitions/worktree-add.d.ts +2 -0
  116. package/dist/tui/features/commands/definitions/worktree-add.js +35 -0
  117. package/dist/tui/features/commands/definitions/worktree-list.d.ts +2 -0
  118. package/dist/tui/features/commands/definitions/worktree-list.js +36 -0
  119. package/dist/tui/features/commands/definitions/worktree-remove.d.ts +2 -0
  120. package/dist/tui/features/commands/definitions/worktree-remove.js +33 -0
  121. package/dist/tui/features/commands/definitions/worktree.d.ts +2 -0
  122. package/dist/tui/features/commands/definitions/worktree.js +8 -0
  123. package/dist/tui/features/curate/api/create-curate-task.js +3 -1
  124. package/dist/tui/features/query/api/create-query-task.js +3 -1
  125. package/dist/tui/features/source/api/source-api.d.ts +4 -0
  126. package/dist/tui/features/source/api/source-api.js +22 -0
  127. package/dist/tui/features/status/api/get-status.js +2 -1
  128. package/dist/tui/features/status/utils/format-status.js +23 -1
  129. package/dist/tui/features/transport/components/transport-initializer.js +36 -1
  130. package/dist/tui/features/worktree/api/worktree-api.d.ts +4 -0
  131. package/dist/tui/features/worktree/api/worktree-api.js +22 -0
  132. package/dist/tui/repl-startup.d.ts +2 -0
  133. package/dist/tui/repl-startup.js +5 -3
  134. package/dist/tui/stores/transport-store.d.ts +6 -0
  135. package/dist/tui/stores/transport-store.js +6 -0
  136. package/oclif.manifest.json +418 -158
  137. package/package.json +1 -1
@@ -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)');
@@ -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
  }
@@ -13,8 +13,10 @@ const initialState = {
13
13
  connectionState: 'disconnected',
14
14
  error: null,
15
15
  isConnected: false,
16
+ projectPath: null,
16
17
  reconnectCount: 0,
17
18
  version: '',
19
+ worktreeRoot: null,
18
20
  };
19
21
  export const useTransportStore = create()((set) => ({
20
22
  ...initialState,
@@ -36,5 +38,9 @@ export const useTransportStore = create()((set) => ({
36
38
  error,
37
39
  isConnected: false,
38
40
  }),
41
+ setProjectInfo: (projectPath, worktreeRoot) => set({
42
+ projectPath: projectPath ?? null,
43
+ worktreeRoot: worktreeRoot ?? null,
44
+ }),
39
45
  setVersion: (version) => set({ version }),
40
46
  }));