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
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Source extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Source extends Command {
3
+ static description = 'Manage knowledge sources (read-only references to other projects)';
4
+ static examples = ['<%= config.bin %> <%= command.id %> --help'];
5
+ async run() {
6
+ await this.config.runCommand('help', ['source']);
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SourceList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,32 @@
1
+ import { Command } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { SourceEvents } from '../../../shared/transport/events/source-events.js';
4
+ import { formatConnectionError, withDaemonRetry } from '../../lib/daemon-client.js';
5
+ export default class SourceList extends Command {
6
+ static description = 'List all knowledge sources and their status';
7
+ static examples = ['<%= config.bin %> <%= command.id %>'];
8
+ async run() {
9
+ try {
10
+ const result = await withDaemonRetry(async (client) => client.requestWithAck(SourceEvents.LIST), { projectPath: process.cwd() });
11
+ if (result.error) {
12
+ this.error(result.error, { exit: 1 });
13
+ }
14
+ if (result.statuses.length === 0) {
15
+ this.log('No knowledge sources configured.');
16
+ return;
17
+ }
18
+ this.log('Knowledge Sources:');
19
+ for (const source of result.statuses) {
20
+ if (source.valid) {
21
+ this.log(` ${source.alias} → ${source.projectRoot} ${chalk.green('(valid)')}`);
22
+ }
23
+ else {
24
+ this.log(` ${source.alias} → ${source.projectRoot} ${chalk.red(`[BROKEN - run brv source remove ${source.alias}]`)}`);
25
+ }
26
+ }
27
+ }
28
+ catch (error) {
29
+ this.error(formatConnectionError(error), { exit: 1 });
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SourceRemove extends Command {
3
+ static args: {
4
+ aliasOrPath: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,33 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { SourceEvents } from '../../../shared/transport/events/source-events.js';
3
+ import { formatConnectionError, withDaemonRetry } from '../../lib/daemon-client.js';
4
+ export default class SourceRemove extends Command {
5
+ static args = {
6
+ aliasOrPath: Args.string({
7
+ description: 'Alias or path of the knowledge source to remove',
8
+ required: true,
9
+ }),
10
+ };
11
+ static description = 'Remove a knowledge source';
12
+ static examples = [
13
+ '<%= config.bin %> <%= command.id %> shared-lib',
14
+ '<%= config.bin %> <%= command.id %> /path/to/shared-lib',
15
+ ];
16
+ async run() {
17
+ const { args } = await this.parse(SourceRemove);
18
+ try {
19
+ const result = await withDaemonRetry(async (client) => client.requestWithAck(SourceEvents.REMOVE, {
20
+ aliasOrPath: args.aliasOrPath,
21
+ }), { projectPath: process.cwd() });
22
+ if (result.success) {
23
+ this.log(result.message);
24
+ }
25
+ else {
26
+ this.error(result.message, { exit: 1 });
27
+ }
28
+ }
29
+ catch (error) {
30
+ this.error(formatConnectionError(error), { exit: 1 });
31
+ }
32
+ }
33
+ }
@@ -6,8 +6,12 @@ export default class Status extends Command {
6
6
  static examples: string[];
7
7
  static flags: {
8
8
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-root': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
11
  };
10
- protected fetchStatus(options?: DaemonClientOptions): Promise<StatusDTO>;
12
+ protected fetchStatus(options?: DaemonClientOptions & {
13
+ projectRootFlag?: string;
14
+ }): Promise<StatusDTO>;
11
15
  run(): Promise<void>;
12
16
  private formatTextOutput;
13
17
  private logVcHint;
@@ -1,6 +1,6 @@
1
1
  import { Command, Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import { StatusEvents } from '../../shared/transport/events/status-events.js';
3
+ import { StatusEvents, } from '../../shared/transport/events/status-events.js';
4
4
  import { formatConnectionError, withDaemonRetry } from '../lib/daemon-client.js';
5
5
  import { writeJsonResponse } from '../lib/json-response.js';
6
6
  export default class Status extends Command {
@@ -13,18 +13,29 @@ export default class Status extends Command {
13
13
  description: 'Output format',
14
14
  options: ['text', 'json'],
15
15
  }),
16
+ 'project-root': Flags.string({
17
+ description: 'Explicit project root path (overrides auto-detection)',
18
+ required: false,
19
+ }),
20
+ verbose: Flags.boolean({
21
+ char: 'v',
22
+ default: false,
23
+ description: 'Show resolution source and diagnostic info',
24
+ }),
16
25
  };
17
26
  async fetchStatus(options) {
27
+ const request = { cwd: process.cwd(), projectRootFlag: options?.projectRootFlag };
18
28
  return withDaemonRetry(async (client) => {
19
- const response = await client.requestWithAck(StatusEvents.GET);
29
+ const response = await client.requestWithAck(StatusEvents.GET, request);
20
30
  return response.status;
21
31
  }, options);
22
32
  }
23
33
  async run() {
24
34
  const { flags } = await this.parse(Status);
35
+ const projectRootFlag = flags['project-root'];
25
36
  const isJson = flags.format === 'json';
26
37
  try {
27
- const status = await this.fetchStatus({ projectPath: process.cwd() });
38
+ const status = await this.fetchStatus({ projectPath: process.cwd(), projectRootFlag });
28
39
  if (isJson) {
29
40
  writeJsonResponse({
30
41
  command: 'status',
@@ -33,7 +44,7 @@ export default class Status extends Command {
33
44
  });
34
45
  }
35
46
  else {
36
- this.formatTextOutput(status);
47
+ this.formatTextOutput(status, flags.verbose);
37
48
  this.logVcHint();
38
49
  }
39
50
  }
@@ -51,7 +62,7 @@ export default class Status extends Command {
51
62
  }
52
63
  }
53
64
  }
54
- formatTextOutput(status) {
65
+ formatTextOutput(status, verbose = false) {
55
66
  this.log(`CLI Version: ${this.config.version}`);
56
67
  // Auth status (cloud sync only — not required for local usage)
57
68
  switch (status.authStatus) {
@@ -71,7 +82,31 @@ export default class Status extends Command {
71
82
  this.log('Account: Unable to check');
72
83
  }
73
84
  }
74
- this.log(`Current Directory: ${status.currentDirectory}`);
85
+ this.log(`Project: ${status.projectRoot ?? status.currentDirectory}`);
86
+ if (status.worktreeRoot && status.worktreeRoot !== status.projectRoot) {
87
+ this.log(`Worktree: ${status.worktreeRoot} (linked)`);
88
+ }
89
+ if (status.resolverError) {
90
+ this.log(chalk.yellow(`⚠ ${status.resolverError}`));
91
+ }
92
+ if (verbose && status.resolutionSource) {
93
+ this.log(`Resolution: ${status.resolutionSource}`);
94
+ }
95
+ // Knowledge sources
96
+ if (status.sourcesError) {
97
+ this.log(chalk.yellow(`⚠ ${status.sourcesError}`));
98
+ }
99
+ else if (status.sources && status.sources.length > 0) {
100
+ this.log('Knowledge Sources:');
101
+ for (const source of status.sources) {
102
+ if (source.valid) {
103
+ this.log(` ${source.alias} → ${source.projectRoot} ${chalk.green('(valid)')}`);
104
+ }
105
+ else {
106
+ this.log(` ${source.alias} → ${source.projectRoot} ${chalk.red(`[BROKEN - run brv source remove ${source.alias}]`)}`);
107
+ }
108
+ }
109
+ }
75
110
  // Space
76
111
  if (status.teamName && status.spaceName) {
77
112
  this.log(`Space: ${status.teamName}/${status.spaceName}`);
@@ -104,6 +139,10 @@ export default class Status extends Command {
104
139
  this.log('Context Tree: No changes');
105
140
  break;
106
141
  }
142
+ case 'no_vc': {
143
+ this.log('Context Tree: Managed by Byterover version control (use brv vc commands)');
144
+ break;
145
+ }
107
146
  case 'not_initialized': {
108
147
  this.log('Context Tree: Not initialized');
109
148
  break;
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class WorktreeAdd extends Command {
3
+ static args: {
4
+ path: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,44 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { resolve } from 'node:path';
3
+ import { WorktreeEvents } from '../../../shared/transport/events/worktree-events.js';
4
+ import { formatConnectionError, withDaemonRetry } from '../../lib/daemon-client.js';
5
+ export default class WorktreeAdd extends Command {
6
+ static args = {
7
+ path: Args.string({
8
+ description: 'Path to the directory to register as a worktree (relative or absolute)',
9
+ required: false,
10
+ }),
11
+ };
12
+ static description = 'Register a directory as a worktree of this project';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %> packages/api',
15
+ '<%= config.bin %> <%= command.id %> ../other-checkout',
16
+ '<%= config.bin %> <%= command.id %> (auto-detect parent from subdirectory)',
17
+ ];
18
+ static flags = {
19
+ force: Flags.boolean({
20
+ default: false,
21
+ description: 'Replace existing .brv/ directory in target with a worktree pointer',
22
+ }),
23
+ };
24
+ async run() {
25
+ const { args, flags } = await this.parse(WorktreeAdd);
26
+ const cwd = resolve(process.cwd());
27
+ const worktreePath = args.path ? resolve(args.path) : cwd;
28
+ try {
29
+ const result = await withDaemonRetry(async (client) => client.requestWithAck(WorktreeEvents.ADD, {
30
+ force: flags.force,
31
+ worktreePath,
32
+ }), { projectPath: cwd });
33
+ if (result.success) {
34
+ this.log(result.message);
35
+ }
36
+ else {
37
+ this.error(result.message, { exit: 1 });
38
+ }
39
+ }
40
+ catch (error) {
41
+ this.error(formatConnectionError(error), { exit: 1 });
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Worktree extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Worktree extends Command {
3
+ static description = 'Manage worktree links for subdirectories and sibling checkouts';
4
+ static examples = ['<%= config.bin %> <%= command.id %> --help'];
5
+ async run() {
6
+ await this.config.runCommand('help', ['worktree']);
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class WorktreeList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,28 @@
1
+ import { Command } from '@oclif/core';
2
+ import { WorktreeEvents } from '../../../shared/transport/events/worktree-events.js';
3
+ import { formatConnectionError, withDaemonRetry } from '../../lib/daemon-client.js';
4
+ export default class WorktreeList extends Command {
5
+ static description = 'Show the current worktree link and list all registered worktrees';
6
+ static examples = ['<%= config.bin %> <%= command.id %>'];
7
+ async run() {
8
+ try {
9
+ const result = await withDaemonRetry(async (client) => client.requestWithAck(WorktreeEvents.LIST), { projectPath: process.cwd() });
10
+ if (result.source === 'linked') {
11
+ this.log(`Worktree: ${result.worktreeRoot}`);
12
+ this.log(`Linked to: ${result.projectRoot}`);
13
+ }
14
+ else {
15
+ this.log(`Project: ${result.projectRoot}`);
16
+ }
17
+ if (result.worktrees.length > 0) {
18
+ this.log('\nRegistered worktrees:');
19
+ for (const wt of result.worktrees) {
20
+ this.log(` ${wt.name} → ${wt.worktreePath}`);
21
+ }
22
+ }
23
+ }
24
+ catch (error) {
25
+ this.error(formatConnectionError(error), { exit: 1 });
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class WorktreeRemove extends Command {
3
+ static args: {
4
+ path: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,35 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { resolve } from 'node:path';
3
+ import { WorktreeEvents } from '../../../shared/transport/events/worktree-events.js';
4
+ import { formatConnectionError, withDaemonRetry } from '../../lib/daemon-client.js';
5
+ export default class WorktreeRemove extends Command {
6
+ static args = {
7
+ path: Args.string({
8
+ description: 'Path to the worktree to remove (defaults to cwd)',
9
+ required: false,
10
+ }),
11
+ };
12
+ static description = 'Remove a worktree registration and its .brv pointer';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %> (remove cwd as worktree)',
15
+ '<%= config.bin %> <%= command.id %> packages/api (remove from parent)',
16
+ ];
17
+ async run() {
18
+ const { args } = await this.parse(WorktreeRemove);
19
+ const targetPath = args.path ? resolve(args.path) : resolve(process.cwd());
20
+ try {
21
+ const result = await withDaemonRetry(async (client) => client.requestWithAck(WorktreeEvents.REMOVE, {
22
+ worktreePath: targetPath,
23
+ }), { projectPath: process.cwd() });
24
+ if (result.success) {
25
+ this.log(result.message);
26
+ }
27
+ else {
28
+ this.error(result.message, { exit: 1 });
29
+ }
30
+ }
31
+ catch (error) {
32
+ this.error(formatConnectionError(error), { exit: 1 });
33
+ }
34
+ }
35
+ }
@@ -2,6 +2,7 @@ import { BRV_CONFIG_VERSION } from '../../../server/constants.js';
2
2
  import { ensureProjectInitialized } from '../../../server/infra/config/auto-init.js';
3
3
  import { ProjectConfigStore } from '../../../server/infra/config/file-config-store.js';
4
4
  import { FileContextTreeService } from '../../../server/infra/context-tree/file-context-tree-service.js';
5
+ import { isWorktreePointer } from '../../../server/infra/project/resolve-project.js';
5
6
  import { syncConfigToXdg } from '../../../server/utils/config-xdg-sync.js';
6
7
  /**
7
8
  * Commands that should skip auto-init and config version validation.
@@ -22,6 +23,9 @@ const HELP_FLAGS = new Set(['--h', '--help', '-h', '-help']);
22
23
  export const validateBrvConfigVersion = async (commandId, configStore, argv = [], autoInitDeps) => {
23
24
  if (argv.some((arg) => HELP_FLAGS.has(arg)))
24
25
  return;
26
+ // Skip auto-init if .brv is a pointer file (worktree) — the project lives elsewhere
27
+ if (isWorktreePointer(process.cwd()))
28
+ return;
25
29
  const exists = await configStore.exists();
26
30
  if (!exists && COMMANDS_NEED_AUTO_INIT.has(commandId)) {
27
31
  const deps = autoInitDeps ?? {
@@ -5,6 +5,8 @@ export interface DaemonClientOptions {
5
5
  maxRetries?: number;
6
6
  /** Explicit project path — bypasses walk-up discovery. Use for `init` where .brv/ doesn't exist yet. */
7
7
  projectPath?: string;
8
+ /** Explicit --project-root flag value to override auto-detection */
9
+ projectRootFlag?: string;
8
10
  /** Delay between retries in ms. Default: 2000. Set to 0 in tests. */
9
11
  retryDelayMs?: number;
10
12
  /** Optional transport connector for DI/testing */
@@ -13,14 +15,14 @@ export interface DaemonClientOptions {
13
15
  /**
14
16
  * Connects to the daemon, auto-starting it if needed.
15
17
  */
16
- export declare function connectToDaemonClient(options?: Pick<DaemonClientOptions, 'transportConnector'>): Promise<ConnectionResult>;
18
+ export declare function connectToDaemonClient(options?: Pick<DaemonClientOptions, 'projectRootFlag' | 'transportConnector'>): Promise<ConnectionResult>;
17
19
  /**
18
20
  * Executes an operation against the daemon with retry logic.
19
21
  *
20
22
  * Retries on infrastructure failures (daemon spawn timeout, connection dropped,
21
23
  * agent disconnected). Does NOT retry on business errors (auth, validation, etc.).
22
24
  */
23
- export declare function withDaemonRetry<T>(fn: (client: ITransportClient, projectRoot?: string) => Promise<T>, options?: DaemonClientOptions & {
25
+ export declare function withDaemonRetry<T>(fn: (client: ITransportClient, projectRoot?: string, worktreeRoot?: string) => Promise<T>, options?: DaemonClientOptions & {
24
26
  /** Called before each retry with attempt number (1-indexed) */
25
27
  onRetry?: (attempt: number, maxRetries: number) => void;
26
28
  }): Promise<T>;
@@ -1,5 +1,6 @@
1
1
  import { ConnectionError, ConnectionFailedError, DaemonSpawnError, InstanceCrashedError, NoInstanceRunningError, TransportRequestError, TransportRequestTimeoutError, } from '@campfirein/brv-transport-client';
2
2
  import { TaskErrorCode } from '../../server/core/domain/errors/task-error.js';
3
+ import { resolveProject } from '../../server/infra/project/resolve-project.js';
3
4
  import { createDaemonAwareConnector } from '../../server/infra/transport/transport-connector.js';
4
5
  import { getSandboxEnvironmentName, isSandboxEnvironment, isSandboxNetworkError, } from '../../server/utils/sandbox-detector.js';
5
6
  import { VcErrorCode } from '../../shared/transport/events/vc-events.js';
@@ -17,7 +18,6 @@ const USER_FRIENDLY_MESSAGES = {
17
18
  [TaskErrorCode.OAUTH_TOKEN_EXPIRED]: 'OAuth token has expired. Run "brv providers connect <provider> --oauth" to reconnect.',
18
19
  [TaskErrorCode.PROJECT_NOT_INIT]: 'Project not initialized. Run "brv restart" to reinitialize.',
19
20
  [TaskErrorCode.PROVIDER_NOT_CONFIGURED]: 'No provider connected. Run "brv providers connect byterover" to use the free built-in provider, or connect another provider.',
20
- [TaskErrorCode.SPACE_NOT_CONFIGURED]: 'No space configured. Run "brv space list" to see available spaces, then "brv space switch --team <team> --name <space>" to select one.',
21
21
  [TaskErrorCode.SPACE_NOT_FOUND]: 'Space not found. Check your configuration.',
22
22
  [TaskErrorCode.VC_GIT_INITIALIZED]: 'ByteRover version control is active. Use brv vc commands instead of legacy sync commands.',
23
23
  [VcErrorCode.AUTH_FAILED]: 'Authentication failed. Run brv login.',
@@ -34,12 +34,22 @@ const USER_FRIENDLY_MESSAGES = {
34
34
  // UNCOMMITTED_CHANGES intentionally omitted: fall through to server's detailed message with file paths
35
35
  // USER_NOT_CONFIGURED intentionally omitted: fall through to server's specific hint with actual values
36
36
  };
37
+ function resolveRequiredProjectPath(projectRootFlag) {
38
+ const resolution = resolveProject({ projectRootFlag });
39
+ if (!resolution) {
40
+ // No .brv found at cwd — fall back to cwd. The daemon will auto-init .brv/ on first connection.
41
+ const cwd = process.cwd();
42
+ return { projectRoot: cwd, worktreeRoot: cwd };
43
+ }
44
+ return resolution;
45
+ }
37
46
  /**
38
47
  * Connects to the daemon, auto-starting it if needed.
39
48
  */
40
49
  export async function connectToDaemonClient(options) {
41
50
  const connector = options?.transportConnector ?? createDaemonAwareConnector();
42
- return connector();
51
+ const resolution = resolveRequiredProjectPath(options?.projectRootFlag);
52
+ return connector(undefined, resolution.projectRoot);
43
53
  }
44
54
  /**
45
55
  * Executes an operation against the daemon with retry logic.
@@ -51,14 +61,19 @@ export async function withDaemonRetry(fn, options) {
51
61
  const maxRetries = options?.maxRetries ?? MAX_RETRIES;
52
62
  const retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
53
63
  const connector = options?.transportConnector ?? createDaemonAwareConnector(options?.projectPath);
64
+ // Pre-resolve project (workspace-link-aware) so the connector registers
65
+ // with the correct projectPath and callers get the resolved worktreeRoot.
66
+ const resolution = resolveRequiredProjectPath(options?.projectRootFlag);
67
+ const resolvedProjectPath = resolution.projectRoot;
68
+ const resolvedWorkspaceRoot = resolution.worktreeRoot;
54
69
  let lastError;
55
70
  /* eslint-disable no-await-in-loop -- intentional sequential retry loop */
56
71
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
57
72
  let client;
58
73
  try {
59
- const { client: connectedClient, projectRoot } = await connector();
74
+ const { client: connectedClient, projectRoot } = await connector(undefined, resolvedProjectPath);
60
75
  client = connectedClient;
61
- const value = await fn(client, projectRoot);
76
+ const value = await fn(client, projectRoot ?? resolvedProjectPath, resolvedWorkspaceRoot);
62
77
  await client.disconnect().catch(() => { });
63
78
  return value;
64
79
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Text formatting for the `brv search` CLI command.
3
+ * Pure function — no oclif, transport, or daemon dependencies.
4
+ */
5
+ import type { SearchKnowledgeResult } from '../../agent/infra/sandbox/tools-sdk.js';
6
+ /**
7
+ * Format search results for terminal display.
8
+ * Returns an array of lines (without trailing newlines).
9
+ */
10
+ export declare function formatSearchTextOutput(searchResult: SearchKnowledgeResult): string[];
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Text formatting for the `brv search` CLI command.
3
+ * Pure function — no oclif, transport, or daemon dependencies.
4
+ */
5
+ /**
6
+ * Format search results for terminal display.
7
+ * Returns an array of lines (without trailing newlines).
8
+ */
9
+ export function formatSearchTextOutput(searchResult) {
10
+ const lines = [];
11
+ if (searchResult.totalFound === 0) {
12
+ lines.push('', 'No results found.', '');
13
+ return lines;
14
+ }
15
+ const displayed = searchResult.results.length;
16
+ const total = searchResult.totalFound;
17
+ const countLabel = displayed < total ? `Showing ${displayed} of ${total} results` : `Found ${total} result${total === 1 ? '' : 's'}`;
18
+ lines.push('', `${countLabel}:`, '');
19
+ for (const [i, result] of searchResult.results.entries()) {
20
+ const scoreStr = (result.score * 100).toFixed(0);
21
+ const excerpt = result.excerpt && result.excerpt.length > 120 ? `${result.excerpt.slice(0, 117)}...` : result.excerpt;
22
+ lines.push(` ${i + 1}. ${result.title} [${scoreStr}%]`, ` Path: ${result.path}`, ...(excerpt ? [` ${excerpt}`] : []), ...(result.backlinkCount ? [` Backlinks: ${result.backlinkCount}`] : []), '');
23
+ }
24
+ return lines;
25
+ }
@@ -58,6 +58,12 @@ export interface WaitForTaskOptions {
58
58
  /** Timeout in ms (default: 5 minutes) */
59
59
  timeoutMs?: number;
60
60
  }
61
+ /** Default timeout for task completion (seconds) — shared across curate/query commands. */
62
+ export declare const DEFAULT_TIMEOUT_SECONDS = 300;
63
+ /** Minimum --timeout value accepted from the CLI (seconds). */
64
+ export declare const MIN_TIMEOUT_SECONDS = 10;
65
+ /** Maximum --timeout value accepted from the CLI (seconds). */
66
+ export declare const MAX_TIMEOUT_SECONDS = 3600;
61
67
  /**
62
68
  * Format tool call for CLI display (simplified version of TUI formatToolDisplay).
63
69
  */
@@ -4,8 +4,14 @@ import { ReviewEvents } from '../../shared/transport/events/review-events.js';
4
4
  import { writeJsonResponse } from './json-response.js';
5
5
  /** Grace period before treating 'reconnecting' as daemon death (ms) */
6
6
  const DISCONNECT_GRACE_MS = 10_000;
7
+ /** Default timeout for task completion (seconds) — shared across curate/query commands. */
8
+ export const DEFAULT_TIMEOUT_SECONDS = 300;
9
+ /** Minimum --timeout value accepted from the CLI (seconds). */
10
+ export const MIN_TIMEOUT_SECONDS = 10;
11
+ /** Maximum --timeout value accepted from the CLI (seconds). */
12
+ export const MAX_TIMEOUT_SECONDS = 3600;
7
13
  /** Default timeout for task completion (ms) */
8
- const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
14
+ const DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_SECONDS * 1000;
9
15
  /**
10
16
  * Format tool call for CLI display (simplified version of TUI formatToolDisplay).
11
17
  */
@@ -70,13 +76,14 @@ export function waitForTaskCompletion(options, log) {
70
76
  if (!completed) {
71
77
  completed = true;
72
78
  cleanup();
79
+ const timeoutMessage = `Task timed out after ${timeoutMs / 1000}s`;
73
80
  if (isText) {
74
- reject(new Error('Task timed out after 5 minutes'));
81
+ reject(new Error(timeoutMessage));
75
82
  }
76
83
  else {
77
84
  writeJsonResponse({
78
85
  command,
79
- data: { event: 'error', message: 'Task timed out after 5 minutes', status: 'error' },
86
+ data: { event: 'error', message: timeoutMessage, status: 'error' },
80
87
  success: false,
81
88
  });
82
89
  resolve();
@@ -1,6 +1,12 @@
1
1
  export declare const BRV_DIR = ".brv";
2
2
  export declare const PROJECT_CONFIG_FILE = "config.json";
3
3
  export declare const BRV_CONFIG_VERSION = "0.0.1";
4
+ export declare const WORKTREES_DIR = "worktrees";
5
+ export declare const WORKTREE_LINK_METADATA = "link.json";
6
+ export declare const SOURCES_FILE = "sources.json";
7
+ export declare const SHARED_SOURCE_LOCAL_SCORE_BOOST = 0.1;
8
+ export declare const MCP_ASSOCIATE_PROJECT_TIMEOUT_MS = 3000;
9
+ export declare const MCP_ASSOCIATE_PROJECT_MAX_ATTEMPTS = 2;
4
10
  export declare const GLOBAL_CONFIG_DIR = "brv";
5
11
  export declare const GLOBAL_CONFIG_FILE = "config.json";
6
12
  export declare const GLOBAL_CONFIG_VERSION = "0.0.1";
@@ -61,4 +67,4 @@ export declare const MANIFEST_FILE = "_manifest.json";
61
67
  export declare const ARCHIVE_IMPORTANCE_THRESHOLD = 35;
62
68
  export declare const DEFAULT_GHOST_CUE_MAX_TOKENS = 220;
63
69
  /** .gitignore content for the context tree — ignore derived artifacts only */
64
- export declare const CONTEXT_TREE_GITIGNORE = "# Derived artifacts \u2014 do not track\n.gitignore\n.snapshot.json\n_manifest.json\n_index.md\n";
70
+ export declare const CONTEXT_TREE_GITIGNORE = "# Derived artifacts \u2014 do not track\n.gitignore\n.snapshot.json\n_manifest.json\n_index.md\n*.abstract.md\n*.overview.md\n";
@@ -1,6 +1,14 @@
1
1
  export const BRV_DIR = '.brv';
2
2
  export const PROJECT_CONFIG_FILE = 'config.json';
3
3
  export const BRV_CONFIG_VERSION = '0.0.1';
4
+ // Worktree linking (git-style: .brv is a file pointing to parent project)
5
+ export const WORKTREES_DIR = 'worktrees';
6
+ export const WORKTREE_LINK_METADATA = 'link.json';
7
+ // Knowledge sources (read-only references to other projects)
8
+ export const SOURCES_FILE = 'sources.json';
9
+ export const SHARED_SOURCE_LOCAL_SCORE_BOOST = 0.1;
10
+ export const MCP_ASSOCIATE_PROJECT_TIMEOUT_MS = 3000;
11
+ export const MCP_ASSOCIATE_PROJECT_MAX_ATTEMPTS = 2;
4
12
  // Global config constants (user-level, stored in XDG config directory)
5
13
  export const GLOBAL_CONFIG_DIR = 'brv';
6
14
  export const GLOBAL_CONFIG_FILE = 'config.json';
@@ -86,4 +94,6 @@ export const CONTEXT_TREE_GITIGNORE = `# Derived artifacts — do not track
86
94
  .snapshot.json
87
95
  _manifest.json
88
96
  _index.md
97
+ *.abstract.md
98
+ *.overview.md
89
99
  `;
@@ -78,5 +78,12 @@ export declare class ClientInfo {
78
78
  * Called after MCP initialize handshake provides clientInfo.
79
79
  */
80
80
  setAgentName(agentName: string): void;
81
+ /**
82
+ * Update this client's project path, even if already associated.
83
+ * Used for reassociation after worktree add/remove operations.
84
+ *
85
+ * @returns The previous project path (undefined if not previously associated)
86
+ */
87
+ updateProjectPath(projectPath: string): string | undefined;
81
88
  }
82
89
  export {};
@@ -84,4 +84,15 @@ export class ClientInfo {
84
84
  setAgentName(agentName) {
85
85
  this._agentName = agentName;
86
86
  }
87
+ /**
88
+ * Update this client's project path, even if already associated.
89
+ * Used for reassociation after worktree add/remove operations.
90
+ *
91
+ * @returns The previous project path (undefined if not previously associated)
92
+ */
93
+ updateProjectPath(projectPath) {
94
+ const oldPath = this._projectPath;
95
+ this._projectPath = projectPath;
96
+ return oldPath;
97
+ }
87
98
  }