byterover-cli 3.0.1 → 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 (196) hide show
  1. package/.env.production +4 -0
  2. package/README.md +17 -0
  3. package/dist/agent/core/domain/tools/constants.d.ts +1 -0
  4. package/dist/agent/core/domain/tools/constants.js +1 -0
  5. package/dist/agent/core/interfaces/cipher-services.d.ts +8 -0
  6. package/dist/agent/core/interfaces/i-cipher-agent.d.ts +1 -0
  7. package/dist/agent/infra/agent/agent-error-codes.d.ts +0 -1
  8. package/dist/agent/infra/agent/agent-error-codes.js +0 -1
  9. package/dist/agent/infra/agent/agent-error.d.ts +0 -1
  10. package/dist/agent/infra/agent/agent-error.js +0 -1
  11. package/dist/agent/infra/agent/agent-schemas.d.ts +8 -0
  12. package/dist/agent/infra/agent/agent-schemas.js +1 -0
  13. package/dist/agent/infra/agent/agent-state-manager.d.ts +1 -3
  14. package/dist/agent/infra/agent/agent-state-manager.js +1 -3
  15. package/dist/agent/infra/agent/base-agent.d.ts +1 -1
  16. package/dist/agent/infra/agent/base-agent.js +1 -1
  17. package/dist/agent/infra/agent/cipher-agent.d.ts +15 -1
  18. package/dist/agent/infra/agent/cipher-agent.js +188 -3
  19. package/dist/agent/infra/agent/index.d.ts +1 -1
  20. package/dist/agent/infra/agent/index.js +1 -1
  21. package/dist/agent/infra/agent/service-initializer.d.ts +3 -3
  22. package/dist/agent/infra/agent/service-initializer.js +14 -8
  23. package/dist/agent/infra/agent/types.d.ts +0 -1
  24. package/dist/agent/infra/file-system/file-system-service.js +6 -5
  25. package/dist/agent/infra/folder-pack/folder-pack-service.d.ts +1 -0
  26. package/dist/agent/infra/folder-pack/folder-pack-service.js +29 -15
  27. package/dist/agent/infra/llm/providers/openai.js +12 -0
  28. package/dist/agent/infra/llm/stream-to-text.d.ts +7 -0
  29. package/dist/agent/infra/llm/stream-to-text.js +14 -0
  30. package/dist/agent/infra/map/abstract-generator.d.ts +22 -0
  31. package/dist/agent/infra/map/abstract-generator.js +67 -0
  32. package/dist/agent/infra/map/abstract-queue.d.ts +67 -0
  33. package/dist/agent/infra/map/abstract-queue.js +218 -0
  34. package/dist/agent/infra/memory/memory-deduplicator.d.ts +44 -0
  35. package/dist/agent/infra/memory/memory-deduplicator.js +88 -0
  36. package/dist/agent/infra/memory/memory-manager.d.ts +1 -0
  37. package/dist/agent/infra/memory/memory-manager.js +6 -5
  38. package/dist/agent/infra/sandbox/curate-service.d.ts +4 -2
  39. package/dist/agent/infra/sandbox/curate-service.js +20 -7
  40. package/dist/agent/infra/sandbox/local-sandbox.d.ts +5 -0
  41. package/dist/agent/infra/sandbox/local-sandbox.js +57 -1
  42. package/dist/agent/infra/sandbox/sandbox-service.js +1 -0
  43. package/dist/agent/infra/sandbox/tools-sdk.d.ts +13 -1
  44. package/dist/agent/infra/sandbox/tools-sdk.js +9 -1
  45. package/dist/agent/infra/session/session-compressor.d.ts +43 -0
  46. package/dist/agent/infra/session/session-compressor.js +296 -0
  47. package/dist/agent/infra/session/session-manager.d.ts +7 -0
  48. package/dist/agent/infra/session/session-manager.js +9 -0
  49. package/dist/agent/infra/tools/implementations/curate-tool.d.ts +3 -2
  50. package/dist/agent/infra/tools/implementations/curate-tool.js +54 -27
  51. package/dist/agent/infra/tools/implementations/expand-knowledge-tool.d.ts +3 -3
  52. package/dist/agent/infra/tools/implementations/expand-knowledge-tool.js +34 -7
  53. package/dist/agent/infra/tools/implementations/ingest-resource-tool.d.ts +17 -0
  54. package/dist/agent/infra/tools/implementations/ingest-resource-tool.js +224 -0
  55. package/dist/agent/infra/tools/implementations/memory-symbol-tree.d.ts +8 -0
  56. package/dist/agent/infra/tools/implementations/search-knowledge-service.d.ts +1 -1
  57. package/dist/agent/infra/tools/implementations/search-knowledge-service.js +392 -106
  58. package/dist/agent/infra/tools/implementations/search-knowledge-tool.js +2 -2
  59. package/dist/agent/infra/tools/implementations/write-file-tool.d.ts +2 -1
  60. package/dist/agent/infra/tools/implementations/write-file-tool.js +16 -2
  61. package/dist/agent/infra/tools/tool-provider.js +1 -0
  62. package/dist/agent/infra/tools/tool-registry.d.ts +3 -0
  63. package/dist/agent/infra/tools/tool-registry.js +16 -5
  64. package/dist/agent/infra/tools/write-guard.d.ts +11 -0
  65. package/dist/agent/infra/tools/write-guard.js +48 -0
  66. package/dist/agent/resources/prompts/system-prompt.yml +9 -0
  67. package/dist/agent/resources/tools/expand_knowledge.txt +4 -0
  68. package/dist/agent/resources/tools/search_knowledge.txt +11 -1
  69. package/dist/oclif/commands/curate/index.js +4 -3
  70. package/dist/oclif/commands/curate/view.js +2 -2
  71. package/dist/oclif/commands/main.js +13 -0
  72. package/dist/oclif/commands/query.js +4 -3
  73. package/dist/oclif/commands/source/add.d.ts +12 -0
  74. package/dist/oclif/commands/source/add.js +42 -0
  75. package/dist/oclif/commands/source/index.d.ts +6 -0
  76. package/dist/oclif/commands/source/index.js +8 -0
  77. package/dist/oclif/commands/source/list.d.ts +6 -0
  78. package/dist/oclif/commands/source/list.js +32 -0
  79. package/dist/oclif/commands/source/remove.d.ts +9 -0
  80. package/dist/oclif/commands/source/remove.js +33 -0
  81. package/dist/oclif/commands/status.d.ts +5 -1
  82. package/dist/oclif/commands/status.js +41 -6
  83. package/dist/oclif/commands/worktree/add.d.ts +12 -0
  84. package/dist/oclif/commands/worktree/add.js +44 -0
  85. package/dist/oclif/commands/worktree/index.d.ts +6 -0
  86. package/dist/oclif/commands/worktree/index.js +8 -0
  87. package/dist/oclif/commands/worktree/list.d.ts +6 -0
  88. package/dist/oclif/commands/worktree/list.js +28 -0
  89. package/dist/oclif/commands/worktree/remove.d.ts +9 -0
  90. package/dist/oclif/commands/worktree/remove.js +35 -0
  91. package/dist/oclif/hooks/init/validate-brv-config.js +4 -0
  92. package/dist/oclif/lib/daemon-client.d.ts +4 -2
  93. package/dist/oclif/lib/daemon-client.js +19 -3
  94. package/dist/server/constants.d.ts +8 -0
  95. package/dist/server/constants.js +10 -0
  96. package/dist/server/core/domain/client/client-info.d.ts +7 -0
  97. package/dist/server/core/domain/client/client-info.js +11 -0
  98. package/dist/server/core/domain/knowledge/memory-scoring.d.ts +3 -3
  99. package/dist/server/core/domain/knowledge/memory-scoring.js +5 -5
  100. package/dist/server/core/domain/knowledge/summary-types.d.ts +4 -0
  101. package/dist/server/core/domain/project/worktrees-schema.d.ts +29 -0
  102. package/dist/server/core/domain/project/worktrees-schema.js +17 -0
  103. package/dist/server/core/domain/source/source-operations.d.ts +31 -0
  104. package/dist/server/core/domain/source/source-operations.js +201 -0
  105. package/dist/server/core/domain/source/source-schema.d.ts +94 -0
  106. package/dist/server/core/domain/source/source-schema.js +121 -0
  107. package/dist/server/core/domain/transport/schemas.d.ts +18 -10
  108. package/dist/server/core/domain/transport/schemas.js +4 -0
  109. package/dist/server/core/domain/transport/task-info.d.ts +2 -0
  110. package/dist/server/core/interfaces/client/i-client-manager.d.ts +13 -0
  111. package/dist/server/core/interfaces/executor/i-curate-executor.d.ts +4 -0
  112. package/dist/server/core/interfaces/executor/i-folder-pack-executor.d.ts +7 -3
  113. package/dist/server/core/interfaces/executor/i-query-executor.d.ts +2 -0
  114. package/dist/server/infra/client/client-manager.d.ts +1 -0
  115. package/dist/server/infra/client/client-manager.js +16 -0
  116. package/dist/server/infra/context-tree/derived-artifact.js +5 -1
  117. package/dist/server/infra/context-tree/file-context-tree-manifest-service.d.ts +2 -1
  118. package/dist/server/infra/context-tree/file-context-tree-manifest-service.js +43 -7
  119. package/dist/server/infra/context-tree/file-context-tree-summary-service.js +20 -2
  120. package/dist/server/infra/daemon/agent-process.js +15 -5
  121. package/dist/server/infra/executor/curate-executor.js +6 -3
  122. package/dist/server/infra/executor/direct-search-responder.js +5 -1
  123. package/dist/server/infra/executor/folder-pack-executor.js +88 -7
  124. package/dist/server/infra/executor/query-executor.d.ts +23 -0
  125. package/dist/server/infra/executor/query-executor.js +125 -23
  126. package/dist/server/infra/mcp/mcp-mode-detector.d.ts +7 -5
  127. package/dist/server/infra/mcp/mcp-mode-detector.js +11 -18
  128. package/dist/server/infra/mcp/mcp-server.d.ts +1 -0
  129. package/dist/server/infra/mcp/mcp-server.js +11 -6
  130. package/dist/server/infra/mcp/tools/brv-curate-tool.d.ts +2 -1
  131. package/dist/server/infra/mcp/tools/brv-curate-tool.js +9 -16
  132. package/dist/server/infra/mcp/tools/brv-query-tool.d.ts +2 -1
  133. package/dist/server/infra/mcp/tools/brv-query-tool.js +9 -16
  134. package/dist/server/infra/mcp/tools/mcp-project-context.d.ts +11 -0
  135. package/dist/server/infra/mcp/tools/mcp-project-context.js +54 -0
  136. package/dist/server/infra/process/connection-coordinator.js +11 -0
  137. package/dist/server/infra/process/feature-handlers.js +4 -1
  138. package/dist/server/infra/process/task-router.d.ts +1 -0
  139. package/dist/server/infra/process/task-router.js +60 -5
  140. package/dist/server/infra/project/resolve-project.d.ts +106 -0
  141. package/dist/server/infra/project/resolve-project.js +473 -0
  142. package/dist/server/infra/transport/handlers/index.d.ts +4 -0
  143. package/dist/server/infra/transport/handlers/index.js +2 -0
  144. package/dist/server/infra/transport/handlers/source-handler.d.ts +12 -0
  145. package/dist/server/infra/transport/handlers/source-handler.js +37 -0
  146. package/dist/server/infra/transport/handlers/status-handler.js +65 -13
  147. package/dist/server/infra/transport/handlers/worktree-handler.d.ts +12 -0
  148. package/dist/server/infra/transport/handlers/worktree-handler.js +67 -0
  149. package/dist/server/infra/transport/transport-connector.d.ts +10 -4
  150. package/dist/server/infra/transport/transport-connector.js +2 -2
  151. package/dist/server/utils/curate-result-parser.d.ts +4 -4
  152. package/dist/server/utils/path-utils.d.ts +5 -0
  153. package/dist/server/utils/path-utils.js +11 -1
  154. package/dist/shared/transport/events/client-events.d.ts +3 -0
  155. package/dist/shared/transport/events/client-events.js +3 -0
  156. package/dist/shared/transport/events/index.d.ts +13 -0
  157. package/dist/shared/transport/events/index.js +9 -0
  158. package/dist/shared/transport/events/source-events.d.ts +30 -0
  159. package/dist/shared/transport/events/source-events.js +5 -0
  160. package/dist/shared/transport/events/status-events.d.ts +5 -0
  161. package/dist/shared/transport/events/task-events.d.ts +4 -1
  162. package/dist/shared/transport/events/worktree-events.d.ts +31 -0
  163. package/dist/shared/transport/events/worktree-events.js +5 -0
  164. package/dist/shared/transport/types/dto.d.ts +26 -0
  165. package/dist/tui/features/commands/definitions/index.js +6 -0
  166. package/dist/tui/features/commands/definitions/source-add.d.ts +2 -0
  167. package/dist/tui/features/commands/definitions/source-add.js +48 -0
  168. package/dist/tui/features/commands/definitions/source-list.d.ts +2 -0
  169. package/dist/tui/features/commands/definitions/source-list.js +47 -0
  170. package/dist/tui/features/commands/definitions/source-remove.d.ts +2 -0
  171. package/dist/tui/features/commands/definitions/source-remove.js +38 -0
  172. package/dist/tui/features/commands/definitions/source.d.ts +2 -0
  173. package/dist/tui/features/commands/definitions/source.js +8 -0
  174. package/dist/tui/features/commands/definitions/worktree-add.d.ts +2 -0
  175. package/dist/tui/features/commands/definitions/worktree-add.js +35 -0
  176. package/dist/tui/features/commands/definitions/worktree-list.d.ts +2 -0
  177. package/dist/tui/features/commands/definitions/worktree-list.js +36 -0
  178. package/dist/tui/features/commands/definitions/worktree-remove.d.ts +2 -0
  179. package/dist/tui/features/commands/definitions/worktree-remove.js +33 -0
  180. package/dist/tui/features/commands/definitions/worktree.d.ts +2 -0
  181. package/dist/tui/features/commands/definitions/worktree.js +8 -0
  182. package/dist/tui/features/curate/api/create-curate-task.js +3 -1
  183. package/dist/tui/features/query/api/create-query-task.js +3 -1
  184. package/dist/tui/features/source/api/source-api.d.ts +4 -0
  185. package/dist/tui/features/source/api/source-api.js +22 -0
  186. package/dist/tui/features/status/api/get-status.js +2 -1
  187. package/dist/tui/features/status/utils/format-status.js +23 -1
  188. package/dist/tui/features/transport/components/transport-initializer.js +36 -1
  189. package/dist/tui/features/worktree/api/worktree-api.d.ts +4 -0
  190. package/dist/tui/features/worktree/api/worktree-api.js +22 -0
  191. package/dist/tui/repl-startup.d.ts +2 -0
  192. package/dist/tui/repl-startup.js +5 -3
  193. package/dist/tui/stores/transport-store.d.ts +6 -0
  194. package/dist/tui/stores/transport-store.js +6 -0
  195. package/oclif.manifest.json +261 -1
  196. package/package.json +10 -4
@@ -24,9 +24,13 @@ export { ResetHandler } from './reset-handler.js';
24
24
  export type { ResetHandlerDeps } from './reset-handler.js';
25
25
  export { ReviewHandler } from './review-handler.js';
26
26
  export type { ReviewHandlerDeps } from './review-handler.js';
27
+ export { SourceHandler } from './source-handler.js';
28
+ export type { SourceHandlerDeps } from './source-handler.js';
27
29
  export { SpaceHandler } from './space-handler.js';
28
30
  export type { SpaceHandlerDeps } from './space-handler.js';
29
31
  export { StatusHandler } from './status-handler.js';
30
32
  export type { StatusHandlerDeps } from './status-handler.js';
31
33
  export { VcHandler } from './vc-handler.js';
32
34
  export type { IVcHandlerDeps } from './vc-handler.js';
35
+ export { WorktreeHandler } from './worktree-handler.js';
36
+ export type { WorktreeHandlerDeps } from './worktree-handler.js';
@@ -11,6 +11,8 @@ export { PullHandler } from './pull-handler.js';
11
11
  export { PushHandler } from './push-handler.js';
12
12
  export { ResetHandler } from './reset-handler.js';
13
13
  export { ReviewHandler } from './review-handler.js';
14
+ export { SourceHandler } from './source-handler.js';
14
15
  export { SpaceHandler } from './space-handler.js';
15
16
  export { StatusHandler } from './status-handler.js';
16
17
  export { VcHandler } from './vc-handler.js';
18
+ export { WorktreeHandler } from './worktree-handler.js';
@@ -0,0 +1,12 @@
1
+ import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
2
+ import { type ProjectPathResolver } from './handler-types.js';
3
+ export interface SourceHandlerDeps {
4
+ resolveProjectPath: ProjectPathResolver;
5
+ transport: ITransportServer;
6
+ }
7
+ export declare class SourceHandler {
8
+ private readonly resolveProjectPath;
9
+ private readonly transport;
10
+ constructor(deps: SourceHandlerDeps);
11
+ setup(): void;
12
+ }
@@ -0,0 +1,37 @@
1
+ import { SourceEvents, } from '../../../../shared/transport/events/source-events.js';
2
+ import { addSource, listSourceStatuses, removeSource } from '../../../core/domain/source/source-operations.js';
3
+ import { resolveRequiredProjectPath } from './handler-types.js';
4
+ export class SourceHandler {
5
+ resolveProjectPath;
6
+ transport;
7
+ constructor(deps) {
8
+ this.resolveProjectPath = deps.resolveProjectPath;
9
+ this.transport = deps.transport;
10
+ }
11
+ setup() {
12
+ this.transport.onRequest(SourceEvents.ADD, async (data, clientId) => {
13
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
14
+ const result = addSource(projectPath, data.targetPath, data.alias);
15
+ return {
16
+ message: result.message,
17
+ success: result.success,
18
+ };
19
+ });
20
+ this.transport.onRequest(SourceEvents.REMOVE, async (data, clientId) => {
21
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
22
+ const result = removeSource(projectPath, data.aliasOrPath);
23
+ return {
24
+ message: result.message,
25
+ success: result.success,
26
+ };
27
+ });
28
+ this.transport.onRequest(SourceEvents.LIST, async (_data, clientId) => {
29
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
30
+ const result = listSourceStatuses(projectPath);
31
+ return {
32
+ error: result.error,
33
+ statuses: result.statuses,
34
+ };
35
+ });
36
+ }
37
+ }
@@ -1,6 +1,9 @@
1
+ import { readFile } from 'node:fs/promises';
1
2
  import { join } from 'node:path';
2
3
  import { StatusEvents } from '../../../../shared/transport/events/status-events.js';
3
4
  import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../constants.js';
5
+ import { listSourceStatuses } from '../../../core/domain/source/source-operations.js';
6
+ import { BrokenWorktreePointerError, MalformedWorktreePointerError, resolveProject } from '../../project/resolve-project.js';
4
7
  import { resolveRequiredProjectPath } from './handler-types.js';
5
8
  /**
6
9
  * Handles status:get event.
@@ -24,18 +27,45 @@ export class StatusHandler {
24
27
  this.transport = deps.transport;
25
28
  }
26
29
  setup() {
27
- this.transport.onRequest(StatusEvents.GET, async (_data, clientId) => {
30
+ this.transport.onRequest(StatusEvents.GET, async (data, clientId) => {
28
31
  const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
29
- const status = await this.collectStatus(projectPath);
32
+ const request = data;
33
+ const cwd = request?.cwd;
34
+ const projectRootFlag = request?.projectRootFlag;
35
+ const status = await this.collectStatus(projectPath, cwd, projectRootFlag);
30
36
  return { status };
31
37
  });
32
38
  }
33
- async collectStatus(projectPath) {
39
+ async collectStatus(projectPath, clientCwd, projectRootFlag) {
34
40
  const result = {
35
41
  authStatus: 'unknown',
36
42
  contextTreeStatus: 'unknown',
37
43
  currentDirectory: projectPath,
44
+ projectRoot: projectPath,
38
45
  };
46
+ // Resolve workspace awareness from client cwd (if provided)
47
+ // Use resolved projectRoot for all downstream checks to avoid inconsistency
48
+ let effectiveProjectPath = projectPath;
49
+ if (clientCwd || projectRootFlag) {
50
+ try {
51
+ const resolution = resolveProject({ cwd: clientCwd, projectRootFlag });
52
+ if (resolution) {
53
+ result.projectRoot = resolution.projectRoot;
54
+ result.worktreeRoot = resolution.worktreeRoot;
55
+ result.resolutionSource = resolution.source;
56
+ effectiveProjectPath = resolution.projectRoot;
57
+ }
58
+ }
59
+ catch (error) {
60
+ // Surface broken/malformed link errors as actionable status info
61
+ if (error instanceof BrokenWorktreePointerError || error instanceof MalformedWorktreePointerError) {
62
+ result.resolverError = error.message;
63
+ }
64
+ // Fall through with projectPath defaults for config/context checks
65
+ }
66
+ }
67
+ // Preserve actual client working directory for backward compat
68
+ result.currentDirectory = clientCwd ?? projectPath;
39
69
  // Auth status
40
70
  try {
41
71
  const token = await this.tokenStore.load();
@@ -53,11 +83,11 @@ export class StatusHandler {
53
83
  catch {
54
84
  result.authStatus = 'unknown';
55
85
  }
56
- // Project status
86
+ // Project status — use effectiveProjectPath for consistency with resolved root
57
87
  try {
58
- const isInitialized = await this.projectConfigStore.exists(projectPath);
88
+ const isInitialized = await this.projectConfigStore.exists(effectiveProjectPath);
59
89
  if (isInitialized) {
60
- const config = await this.projectConfigStore.read(projectPath);
90
+ const config = await this.projectConfigStore.read(effectiveProjectPath);
61
91
  if (config) {
62
92
  result.teamName = config.teamName;
63
93
  result.spaceName = config.spaceName;
@@ -65,22 +95,31 @@ export class StatusHandler {
65
95
  }
66
96
  }
67
97
  catch { }
68
- // Context tree status
98
+ // Abstract generation queue status (written by agent process via abstract-queue.ts)
69
99
  try {
70
- const contextTreeExists = await this.contextTreeService.exists(projectPath);
100
+ const queueStatusPath = join(projectPath, BRV_DIR, '_queue_status.json');
101
+ const raw = await readFile(queueStatusPath, 'utf8');
102
+ result.abstractQueue = JSON.parse(raw);
103
+ }
104
+ catch {
105
+ // File doesn't exist yet — no queue running
106
+ }
107
+ // Context tree status — use effectiveProjectPath for consistency with resolved root
108
+ try {
109
+ const contextTreeExists = await this.contextTreeService.exists(effectiveProjectPath);
71
110
  if (contextTreeExists) {
72
- const hasGitVc = await this.contextTreeService.hasGitRepo(projectPath);
111
+ const hasGitVc = await this.contextTreeService.hasGitRepo(effectiveProjectPath);
73
112
  if (hasGitVc) {
74
113
  result.contextTreeStatus = 'git_vc';
75
114
  }
76
115
  else {
77
- result.contextTreeDir = join(projectPath, BRV_DIR, CONTEXT_TREE_DIR);
116
+ result.contextTreeDir = join(effectiveProjectPath, BRV_DIR, CONTEXT_TREE_DIR);
78
117
  result.contextTreeRelativeDir = join(BRV_DIR, CONTEXT_TREE_DIR);
79
- const hasSnapshot = await this.contextTreeSnapshotService.hasSnapshot(projectPath);
118
+ const hasSnapshot = await this.contextTreeSnapshotService.hasSnapshot(effectiveProjectPath);
80
119
  if (!hasSnapshot) {
81
- await this.contextTreeSnapshotService.initEmptySnapshot(projectPath);
120
+ await this.contextTreeSnapshotService.initEmptySnapshot(effectiveProjectPath);
82
121
  }
83
- const changes = await this.contextTreeSnapshotService.getChanges(projectPath);
122
+ const changes = await this.contextTreeSnapshotService.getChanges(effectiveProjectPath);
84
123
  const hasChanges = changes.added.length > 0 || changes.modified.length > 0 || changes.deleted.length > 0;
85
124
  if (hasChanges) {
86
125
  result.contextTreeStatus = 'has_changes';
@@ -129,6 +168,19 @@ export class StatusHandler {
129
168
  catch {
130
169
  // Best-effort — if the log is unavailable, skip review info
131
170
  }
171
+ // Knowledge sources status
172
+ try {
173
+ const sourcesResult = listSourceStatuses(effectiveProjectPath);
174
+ if (sourcesResult.error) {
175
+ result.sourcesError = sourcesResult.error;
176
+ }
177
+ else if (sourcesResult.statuses.length > 0) {
178
+ result.sources = sourcesResult.statuses;
179
+ }
180
+ }
181
+ catch {
182
+ // Best-effort — swallow errors
183
+ }
132
184
  return result;
133
185
  }
134
186
  }
@@ -0,0 +1,12 @@
1
+ import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
2
+ import { type ProjectPathResolver } from './handler-types.js';
3
+ export interface WorktreeHandlerDeps {
4
+ resolveProjectPath: ProjectPathResolver;
5
+ transport: ITransportServer;
6
+ }
7
+ export declare class WorktreeHandler {
8
+ private readonly resolveProjectPath;
9
+ private readonly transport;
10
+ constructor(deps: WorktreeHandlerDeps);
11
+ setup(): void;
12
+ }
@@ -0,0 +1,67 @@
1
+ import { WorktreeEvents, } from '../../../../shared/transport/events/worktree-events.js';
2
+ import { addWorktree, findParentProject, listWorktrees, removeWorktree, resolveProject } from '../../project/resolve-project.js';
3
+ import { resolveRequiredProjectPath } from './handler-types.js';
4
+ export class WorktreeHandler {
5
+ resolveProjectPath;
6
+ transport;
7
+ constructor(deps) {
8
+ this.resolveProjectPath = deps.resolveProjectPath;
9
+ this.transport = deps.transport;
10
+ }
11
+ setup() {
12
+ this.transport.onRequest(WorktreeEvents.ADD, async (data, clientId) => {
13
+ // Resolve the parent project from client registration
14
+ let projectPath;
15
+ try {
16
+ projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
17
+ }
18
+ catch {
19
+ // Client not associated — fall through to auto-detect
20
+ }
21
+ // Auto-detect: if no project resolved, or client's project IS the worktree path
22
+ // (user ran `brv worktree add` from a child dir with no args), walk up to find parent
23
+ if (!projectPath || projectPath === data.worktreePath) {
24
+ const parent = findParentProject(data.worktreePath);
25
+ if (parent) {
26
+ projectPath = parent;
27
+ }
28
+ else if (!projectPath) {
29
+ return { message: 'No parent project found for the target directory.', success: false };
30
+ }
31
+ }
32
+ const result = addWorktree(projectPath, data.worktreePath, { force: data.force });
33
+ return {
34
+ backedUp: result.backedUp,
35
+ message: result.message,
36
+ success: result.success,
37
+ };
38
+ });
39
+ this.transport.onRequest(WorktreeEvents.REMOVE, async (data) => {
40
+ const targetPath = data.worktreePath;
41
+ const result = removeWorktree(targetPath);
42
+ return {
43
+ message: result.message,
44
+ success: result.success,
45
+ };
46
+ });
47
+ this.transport.onRequest(WorktreeEvents.LIST, async (_data, clientId) => {
48
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
49
+ const resolution = resolveProject({ cwd: projectPath });
50
+ if (!resolution) {
51
+ return {
52
+ projectRoot: projectPath,
53
+ source: 'direct',
54
+ worktreeRoot: projectPath,
55
+ worktrees: [],
56
+ };
57
+ }
58
+ const worktrees = listWorktrees(resolution.projectRoot);
59
+ return {
60
+ projectRoot: resolution.projectRoot,
61
+ source: resolution.source,
62
+ worktreeRoot: resolution.worktreeRoot,
63
+ worktrees,
64
+ };
65
+ });
66
+ }
67
+ }
@@ -1,13 +1,19 @@
1
1
  import { type ConnectionResult } from '@campfirein/brv-transport-client';
2
- /** Function type for transport connection (for DI/testing in use cases). */
3
- export type TransportConnector = (fromDir?: string) => Promise<ConnectionResult>;
2
+ /**
3
+ * Function type for transport connection (for DI/testing in use cases).
4
+ *
5
+ * Callers must pass an explicit resolved projectPath. This avoids silently
6
+ * falling back to the transport library's own walk-up discovery, which is
7
+ * not workspace-link-aware.
8
+ */
9
+ export type TransportConnector = (fromDir: string | undefined, projectPath: string) => Promise<ConnectionResult>;
4
10
  /**
5
11
  * Creates a transport connector that auto-starts the daemon if needed.
6
12
  *
7
13
  * Thin wrapper around connectToDaemon() for DI compatibility with use cases
8
14
  * (QueryUseCase, CurateUseCase, StatusUseCase).
9
15
  *
10
- * projectPath is auto-filled by the transport library from the discovered
11
- * project root (walks up from fromDir to find .brv/).
16
+ * When an explicit projectPath is provided it takes priority over the
17
+ * transport library's walk-up discovery, making the connector workspace-link-aware.
12
18
  */
13
19
  export declare function createDaemonAwareConnector(projectPath?: string): TransportConnector;
@@ -6,8 +6,8 @@ import { resolveLocalServerMainPath } from '../../utils/server-main-resolver.js'
6
6
  * Thin wrapper around connectToDaemon() for DI compatibility with use cases
7
7
  * (QueryUseCase, CurateUseCase, StatusUseCase).
8
8
  *
9
- * projectPath is auto-filled by the transport library from the discovered
10
- * project root (walks up from fromDir to find .brv/).
9
+ * When an explicit projectPath is provided it takes priority over the
10
+ * transport library's walk-up discovery, making the connector workspace-link-aware.
11
11
  */
12
12
  export function createDaemonAwareConnector(projectPath) {
13
13
  return (fromDir) => connectToDaemon({
@@ -98,22 +98,22 @@ export declare const CurateResultSchema: z.ZodObject<{
98
98
  failed?: number | undefined;
99
99
  deleted?: number | undefined;
100
100
  updated?: number | undefined;
101
- added?: number | undefined;
102
101
  merged?: number | undefined;
102
+ added?: number | undefined;
103
103
  }, {
104
104
  failed?: number | undefined;
105
105
  deleted?: number | undefined;
106
106
  updated?: number | undefined;
107
- added?: number | undefined;
108
107
  merged?: number | undefined;
108
+ added?: number | undefined;
109
109
  }>>;
110
110
  }, "strip", z.ZodTypeAny, {
111
111
  summary?: {
112
112
  failed?: number | undefined;
113
113
  deleted?: number | undefined;
114
114
  updated?: number | undefined;
115
- added?: number | undefined;
116
115
  merged?: number | undefined;
116
+ added?: number | undefined;
117
117
  } | undefined;
118
118
  applied?: {
119
119
  path: string;
@@ -135,8 +135,8 @@ export declare const CurateResultSchema: z.ZodObject<{
135
135
  failed?: number | undefined;
136
136
  deleted?: number | undefined;
137
137
  updated?: number | undefined;
138
- added?: number | undefined;
139
138
  merged?: number | undefined;
139
+ added?: number | undefined;
140
140
  } | undefined;
141
141
  applied?: {
142
142
  path: string;
@@ -36,3 +36,8 @@ export declare const sanitizeProjectPath: (resolvedPath: string) => string;
36
36
  * @returns Absolute path to the project's data directory
37
37
  */
38
38
  export declare const getProjectDataDir: (cwd: string) => string;
39
+ /**
40
+ * Checks that candidate is an ancestor of (or equal to) descendant.
41
+ * Both paths are normalized via resolve() before comparison.
42
+ */
43
+ export declare function isDescendantOf(descendant: string, ancestor: string): boolean;
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { realpathSync } from 'node:fs';
3
- import { join } from 'node:path';
3
+ import { join, resolve, sep } from 'node:path';
4
4
  import { GLOBAL_PROJECTS_DIR } from '../constants.js';
5
5
  import { getGlobalDataDir } from './global-data-path.js';
6
6
  /**
@@ -90,3 +90,13 @@ export const getProjectDataDir = (cwd) => {
90
90
  const sanitized = sanitizeProjectPath(resolved);
91
91
  return join(getGlobalDataDir(), GLOBAL_PROJECTS_DIR, sanitized);
92
92
  };
93
+ /**
94
+ * Checks that candidate is an ancestor of (or equal to) descendant.
95
+ * Both paths are normalized via resolve() before comparison.
96
+ */
97
+ export function isDescendantOf(descendant, ancestor) {
98
+ const normalizedDescendant = resolve(descendant);
99
+ const normalizedAncestor = resolve(ancestor);
100
+ const ancestorPrefix = normalizedAncestor.endsWith(sep) ? normalizedAncestor : normalizedAncestor + sep;
101
+ return normalizedDescendant === normalizedAncestor || normalizedDescendant.startsWith(ancestorPrefix);
102
+ }
@@ -0,0 +1,3 @@
1
+ export declare const ClientEvents: {
2
+ readonly ASSOCIATE_PROJECT: "client:associateProject";
3
+ };
@@ -0,0 +1,3 @@
1
+ export const ClientEvents = {
2
+ ASSOCIATE_PROJECT: 'client:associateProject',
3
+ };
@@ -1,6 +1,7 @@
1
1
  export * from '../types/dto.js';
2
2
  export * from './agent-events.js';
3
3
  export * from './auth-events.js';
4
+ export * from './client-events.js';
4
5
  export * from './config-events.js';
5
6
  export * from './connector-events.js';
6
7
  export * from './hub-events.js';
@@ -15,9 +16,11 @@ export * from './push-events.js';
15
16
  export * from './reset-events.js';
16
17
  export * from './review-events.js';
17
18
  export * from './session-events.js';
19
+ export * from './source-events.js';
18
20
  export * from './space-events.js';
19
21
  export * from './status-events.js';
20
22
  export * from './task-events.js';
23
+ export * from './worktree-events.js';
21
24
  /**
22
25
  * Array of all event group objects for iteration.
23
26
  * Use this to subscribe to all events without key collisions.
@@ -41,6 +44,8 @@ export declare const AllEventGroups: readonly [{
41
44
  readonly START_LOGIN: "auth:startLogin";
42
45
  readonly STATE_CHANGED: "auth:stateChanged";
43
46
  readonly UPDATED: "auth:updated";
47
+ }, {
48
+ readonly ASSOCIATE_PROJECT: "client:associateProject";
44
49
  }, {
45
50
  readonly GET_ENVIRONMENT: "config:getEnvironment";
46
51
  readonly GET_PROJECT: "config:getProject";
@@ -117,6 +122,10 @@ export declare const AllEventGroups: readonly [{
117
122
  readonly SWITCHED: "session:switched";
118
123
  }, {
119
124
  readonly GET: "locations:get";
125
+ }, {
126
+ readonly ADD: "source:add";
127
+ readonly LIST: "source:list";
128
+ readonly REMOVE: "source:remove";
120
129
  }, {
121
130
  readonly LIST: "space:list";
122
131
  readonly SWITCH: "space:switch";
@@ -130,6 +139,10 @@ export declare const AllEventGroups: readonly [{
130
139
  readonly CREATED: "task:created";
131
140
  readonly ERROR: "task:error";
132
141
  readonly STARTED: "task:started";
142
+ }, {
143
+ readonly ADD: "worktree:add";
144
+ readonly LIST: "worktree:list";
145
+ readonly REMOVE: "worktree:remove";
133
146
  }];
134
147
  /**
135
148
  * Get all unique event values from all event groups.
@@ -3,6 +3,7 @@ export * from '../types/dto.js';
3
3
  // Event constants and types
4
4
  export * from './agent-events.js';
5
5
  export * from './auth-events.js';
6
+ export * from './client-events.js';
6
7
  export * from './config-events.js';
7
8
  export * from './connector-events.js';
8
9
  export * from './hub-events.js';
@@ -17,12 +18,15 @@ export * from './push-events.js';
17
18
  export * from './reset-events.js';
18
19
  export * from './review-events.js';
19
20
  export * from './session-events.js';
21
+ export * from './source-events.js';
20
22
  export * from './space-events.js';
21
23
  export * from './status-events.js';
22
24
  export * from './task-events.js';
25
+ export * from './worktree-events.js';
23
26
  // Utility exports
24
27
  import { AgentEvents } from './agent-events.js';
25
28
  import { AuthEvents } from './auth-events.js';
29
+ import { ClientEvents } from './client-events.js';
26
30
  import { ConfigEvents } from './config-events.js';
27
31
  import { ConnectorEvents } from './connector-events.js';
28
32
  import { HubEvents } from './hub-events.js';
@@ -37,9 +41,11 @@ import { PushEvents } from './push-events.js';
37
41
  import { ResetEvents } from './reset-events.js';
38
42
  import { ReviewEvents } from './review-events.js';
39
43
  import { SessionEvents } from './session-events.js';
44
+ import { SourceEvents } from './source-events.js';
40
45
  import { SpaceEvents } from './space-events.js';
41
46
  import { StatusEvents } from './status-events.js';
42
47
  import { TaskEvents } from './task-events.js';
48
+ import { WorktreeEvents } from './worktree-events.js';
43
49
  /**
44
50
  * Array of all event group objects for iteration.
45
51
  * Use this to subscribe to all events without key collisions.
@@ -47,6 +53,7 @@ import { TaskEvents } from './task-events.js';
47
53
  export const AllEventGroups = [
48
54
  AgentEvents,
49
55
  AuthEvents,
56
+ ClientEvents,
50
57
  ConfigEvents,
51
58
  ConnectorEvents,
52
59
  HubEvents,
@@ -61,9 +68,11 @@ export const AllEventGroups = [
61
68
  ReviewEvents,
62
69
  SessionEvents,
63
70
  LocationsEvents,
71
+ SourceEvents,
64
72
  SpaceEvents,
65
73
  StatusEvents,
66
74
  TaskEvents,
75
+ WorktreeEvents,
67
76
  ];
68
77
  /**
69
78
  * Get all unique event values from all event groups.
@@ -0,0 +1,30 @@
1
+ export declare const SourceEvents: {
2
+ readonly ADD: "source:add";
3
+ readonly LIST: "source:list";
4
+ readonly REMOVE: "source:remove";
5
+ };
6
+ export interface SourceAddRequest {
7
+ alias?: string;
8
+ targetPath: string;
9
+ }
10
+ export interface SourceAddResponse {
11
+ message: string;
12
+ success: boolean;
13
+ }
14
+ export interface SourceRemoveRequest {
15
+ aliasOrPath: string;
16
+ }
17
+ export interface SourceRemoveResponse {
18
+ message: string;
19
+ success: boolean;
20
+ }
21
+ export type SourceListRequest = void;
22
+ export interface SourceListResponse {
23
+ error?: string;
24
+ statuses: Array<{
25
+ alias: string;
26
+ contextTreeSize?: number;
27
+ projectRoot: string;
28
+ valid: boolean;
29
+ }>;
30
+ }
@@ -0,0 +1,5 @@
1
+ export const SourceEvents = {
2
+ ADD: 'source:add',
3
+ LIST: 'source:list',
4
+ REMOVE: 'source:remove',
5
+ };
@@ -2,6 +2,11 @@ import type { StatusDTO } from '../types/dto.js';
2
2
  export declare const StatusEvents: {
3
3
  readonly GET: "status:get";
4
4
  };
5
+ export interface StatusGetRequest {
6
+ cwd?: string;
7
+ projectRootFlag?: string;
8
+ verbose?: boolean;
9
+ }
5
10
  export interface StatusGetResponse {
6
11
  status: StatusDTO;
7
12
  }
@@ -11,8 +11,11 @@ export interface TaskCreateRequest {
11
11
  clientCwd?: string;
12
12
  content: string;
13
13
  files?: string[];
14
+ folderPath?: string;
15
+ projectPath?: string;
14
16
  taskId: string;
15
- type: 'curate' | 'query';
17
+ type: 'curate' | 'curate-folder' | 'query';
18
+ worktreeRoot?: string;
16
19
  }
17
20
  export interface TaskAckResponse {
18
21
  taskId: string;
@@ -0,0 +1,31 @@
1
+ export declare const WorktreeEvents: {
2
+ readonly ADD: "worktree:add";
3
+ readonly LIST: "worktree:list";
4
+ readonly REMOVE: "worktree:remove";
5
+ };
6
+ export interface WorktreeAddRequest {
7
+ force?: boolean;
8
+ worktreePath: string;
9
+ }
10
+ export interface WorktreeAddResponse {
11
+ backedUp?: boolean;
12
+ message: string;
13
+ success: boolean;
14
+ }
15
+ export interface WorktreeRemoveRequest {
16
+ worktreePath: string;
17
+ }
18
+ export interface WorktreeRemoveResponse {
19
+ message: string;
20
+ success: boolean;
21
+ }
22
+ export type WorktreeListRequest = void;
23
+ export interface WorktreeListResponse {
24
+ projectRoot: string;
25
+ source: 'direct' | 'flag' | 'linked';
26
+ worktreeRoot: string;
27
+ worktrees: Array<{
28
+ name: string;
29
+ worktreePath: string;
30
+ }>;
31
+ }
@@ -0,0 +1,5 @@
1
+ export const WorktreeEvents = {
2
+ ADD: 'worktree:add',
3
+ LIST: 'worktree:list',
4
+ REMOVE: 'worktree:remove',
5
+ };