byterover-cli 1.3.0 → 1.5.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 (171) hide show
  1. package/README.md +71 -6
  2. package/dist/core/domain/cipher/errors/file-system-error.d.ts +11 -0
  3. package/dist/core/domain/cipher/errors/file-system-error.js +17 -0
  4. package/dist/core/domain/cipher/file-system/types.d.ts +40 -6
  5. package/dist/core/domain/cipher/process/types.d.ts +1 -1
  6. package/dist/core/domain/entities/agent.d.ts +1 -1
  7. package/dist/core/domain/entities/agent.js +5 -0
  8. package/dist/core/domain/entities/provider-config.d.ts +92 -0
  9. package/dist/core/domain/entities/provider-config.js +181 -0
  10. package/dist/core/domain/entities/provider-registry.d.ts +55 -0
  11. package/dist/core/domain/entities/provider-registry.js +74 -0
  12. package/dist/core/interfaces/cipher/cipher-services.d.ts +0 -3
  13. package/dist/core/interfaces/cipher/i-content-generator.d.ts +30 -0
  14. package/dist/core/interfaces/cipher/i-content-generator.js +12 -1
  15. package/dist/core/interfaces/cipher/index.d.ts +0 -2
  16. package/dist/core/interfaces/cipher/message-factory.d.ts +4 -1
  17. package/dist/core/interfaces/cipher/message-factory.js +5 -0
  18. package/dist/core/interfaces/cipher/message-types.d.ts +19 -1
  19. package/dist/core/interfaces/i-provider-config-store.d.ts +88 -0
  20. package/dist/core/interfaces/i-provider-keychain-store.d.ts +33 -0
  21. package/dist/infra/cipher/file-system/binary-utils.d.ts +15 -2
  22. package/dist/infra/cipher/file-system/binary-utils.js +26 -3
  23. package/dist/infra/cipher/file-system/file-system-service.d.ts +9 -0
  24. package/dist/infra/cipher/file-system/file-system-service.js +96 -13
  25. package/dist/infra/cipher/file-system/pdf-extractor.d.ts +100 -0
  26. package/dist/infra/cipher/file-system/pdf-extractor.js +226 -0
  27. package/dist/infra/cipher/http/internal-llm-http-service.d.ts +40 -0
  28. package/dist/infra/cipher/http/internal-llm-http-service.js +152 -2
  29. package/dist/infra/cipher/llm/formatters/gemini-formatter.js +8 -1
  30. package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +2 -3
  31. package/dist/infra/cipher/llm/generators/byterover-content-generator.js +20 -11
  32. package/dist/infra/cipher/llm/generators/openrouter-content-generator.d.ts +1 -0
  33. package/dist/infra/cipher/llm/generators/openrouter-content-generator.js +26 -0
  34. package/dist/infra/cipher/llm/internal-llm-service.d.ts +13 -0
  35. package/dist/infra/cipher/llm/internal-llm-service.js +75 -4
  36. package/dist/infra/cipher/llm/model-capabilities.d.ts +74 -0
  37. package/dist/infra/cipher/llm/model-capabilities.js +157 -0
  38. package/dist/infra/cipher/llm/openrouter-llm-service.d.ts +35 -1
  39. package/dist/infra/cipher/llm/openrouter-llm-service.js +216 -28
  40. package/dist/infra/cipher/llm/stream-processor.d.ts +22 -2
  41. package/dist/infra/cipher/llm/stream-processor.js +78 -4
  42. package/dist/infra/cipher/llm/thought-parser.d.ts +1 -1
  43. package/dist/infra/cipher/llm/thought-parser.js +5 -5
  44. package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.d.ts +49 -0
  45. package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.js +272 -0
  46. package/dist/infra/cipher/llm/transformers/reasoning-extractor.d.ts +71 -0
  47. package/dist/infra/cipher/llm/transformers/reasoning-extractor.js +253 -0
  48. package/dist/infra/cipher/process/process-service.js +1 -1
  49. package/dist/infra/cipher/session/chat-session.d.ts +2 -0
  50. package/dist/infra/cipher/session/chat-session.js +13 -2
  51. package/dist/infra/cipher/storage/message-storage-service.js +4 -0
  52. package/dist/infra/cipher/tools/implementations/bash-exec-tool.js +3 -3
  53. package/dist/infra/cipher/tools/implementations/read-file-tool.js +24 -4
  54. package/dist/infra/cipher/tools/implementations/task-tool.js +1 -1
  55. package/dist/infra/connectors/rules/rules-connector-config.d.ts +4 -0
  56. package/dist/infra/connectors/rules/rules-connector-config.js +4 -0
  57. package/dist/infra/http/openrouter-api-client.d.ts +148 -0
  58. package/dist/infra/http/openrouter-api-client.js +161 -0
  59. package/dist/infra/mcp/tools/brv-curate-tool.d.ts +10 -4
  60. package/dist/infra/mcp/tools/brv-curate-tool.js +9 -4
  61. package/dist/infra/mcp/tools/task-result-waiter.js +9 -1
  62. package/dist/infra/process/agent-worker.js +178 -70
  63. package/dist/infra/process/transport-handlers.d.ts +25 -4
  64. package/dist/infra/process/transport-handlers.js +57 -10
  65. package/dist/infra/repl/commands/connectors-command.js +2 -2
  66. package/dist/infra/repl/commands/index.js +5 -0
  67. package/dist/infra/repl/commands/model-command.d.ts +13 -0
  68. package/dist/infra/repl/commands/model-command.js +212 -0
  69. package/dist/infra/repl/commands/provider-command.d.ts +13 -0
  70. package/dist/infra/repl/commands/provider-command.js +181 -0
  71. package/dist/infra/repl/commands/space/switch-command.js +0 -2
  72. package/dist/infra/repl/transport-client-helper.js +6 -2
  73. package/dist/infra/storage/file-provider-config-store.d.ts +83 -0
  74. package/dist/infra/storage/file-provider-config-store.js +157 -0
  75. package/dist/infra/storage/provider-keychain-store.d.ts +37 -0
  76. package/dist/infra/storage/provider-keychain-store.js +75 -0
  77. package/dist/infra/transport/socket-io-transport-client.d.ts +20 -0
  78. package/dist/infra/transport/socket-io-transport-client.js +88 -1
  79. package/dist/infra/usecase/curate-use-case.js +10 -4
  80. package/dist/infra/usecase/space-switch-use-case.d.ts +0 -10
  81. package/dist/infra/usecase/space-switch-use-case.js +7 -37
  82. package/dist/oclif/hooks/init/welcome.js +4 -17
  83. package/dist/resources/prompts/curate.yml +1 -0
  84. package/dist/resources/tools/bash_exec.txt +1 -1
  85. package/dist/resources/tools/read_file.txt +5 -2
  86. package/dist/tui/components/api-key-dialog.d.ts +39 -0
  87. package/dist/tui/components/api-key-dialog.js +94 -0
  88. package/dist/tui/components/execution/execution-changes.d.ts +3 -1
  89. package/dist/tui/components/execution/execution-changes.js +4 -4
  90. package/dist/tui/components/execution/execution-content.d.ts +1 -1
  91. package/dist/tui/components/execution/execution-content.js +4 -12
  92. package/dist/tui/components/execution/execution-input.js +1 -1
  93. package/dist/tui/components/execution/execution-progress.d.ts +10 -13
  94. package/dist/tui/components/execution/execution-progress.js +70 -17
  95. package/dist/tui/components/execution/execution-reasoning.d.ts +16 -0
  96. package/dist/tui/components/execution/execution-reasoning.js +34 -0
  97. package/dist/tui/components/execution/execution-tool.d.ts +23 -0
  98. package/dist/tui/components/execution/execution-tool.js +125 -0
  99. package/dist/tui/components/execution/expanded-log-view.js +3 -3
  100. package/dist/tui/components/execution/log-item.d.ts +2 -0
  101. package/dist/tui/components/execution/log-item.js +6 -4
  102. package/dist/tui/components/index.d.ts +2 -0
  103. package/dist/tui/components/index.js +2 -0
  104. package/dist/tui/components/inline-prompts/inline-select.js +3 -2
  105. package/dist/tui/components/model-dialog.d.ts +63 -0
  106. package/dist/tui/components/model-dialog.js +89 -0
  107. package/dist/tui/components/onboarding/onboarding-flow.js +8 -2
  108. package/dist/tui/components/provider-dialog.d.ts +27 -0
  109. package/dist/tui/components/provider-dialog.js +31 -0
  110. package/dist/tui/components/reasoning-text.d.ts +26 -0
  111. package/dist/tui/components/reasoning-text.js +49 -0
  112. package/dist/tui/components/selectable-list.d.ts +54 -0
  113. package/dist/tui/components/selectable-list.js +180 -0
  114. package/dist/tui/components/streaming-text.d.ts +30 -0
  115. package/dist/tui/components/streaming-text.js +52 -0
  116. package/dist/tui/contexts/tasks-context.d.ts +15 -0
  117. package/dist/tui/contexts/tasks-context.js +224 -40
  118. package/dist/tui/contexts/theme-context.d.ts +1 -0
  119. package/dist/tui/contexts/theme-context.js +3 -2
  120. package/dist/tui/hooks/use-activity-logs.js +7 -1
  121. package/dist/tui/types/messages.d.ts +32 -5
  122. package/dist/tui/utils/index.d.ts +1 -1
  123. package/dist/tui/utils/index.js +1 -1
  124. package/dist/tui/utils/log.d.ts +0 -9
  125. package/dist/tui/utils/log.js +2 -53
  126. package/dist/tui/views/command-view.js +4 -1
  127. package/dist/utils/file-validator.js +8 -4
  128. package/oclif.manifest.json +1 -54
  129. package/package.json +4 -2
  130. package/dist/core/interfaces/cipher/i-coding-agent-log-parser.d.ts +0 -20
  131. package/dist/core/interfaces/cipher/i-coding-agent-log-watcher.d.ts +0 -31
  132. package/dist/core/interfaces/i-file-watcher-service.d.ts +0 -41
  133. package/dist/core/interfaces/i-file-watcher-service.js +0 -1
  134. package/dist/core/interfaces/parser/i-clean-parser-service.d.ts +0 -18
  135. package/dist/core/interfaces/parser/i-clean-parser-service.js +0 -1
  136. package/dist/core/interfaces/parser/i-raw-parser-service.d.ts +0 -17
  137. package/dist/core/interfaces/parser/i-raw-parser-service.js +0 -1
  138. package/dist/core/interfaces/parser/i-session-normalizer.d.ts +0 -56
  139. package/dist/core/interfaces/parser/i-session-normalizer.js +0 -1
  140. package/dist/infra/cipher/parsers/coding-agent-log-parser.d.ts +0 -24
  141. package/dist/infra/cipher/parsers/coding-agent-log-parser.js +0 -51
  142. package/dist/infra/cipher/watcher/coding-agent-log-watcher.d.ts +0 -14
  143. package/dist/infra/cipher/watcher/coding-agent-log-watcher.js +0 -55
  144. package/dist/infra/parsers/clean/clean-claude-service.d.ts +0 -111
  145. package/dist/infra/parsers/clean/clean-claude-service.js +0 -271
  146. package/dist/infra/parsers/clean/clean-codex-service.d.ts +0 -231
  147. package/dist/infra/parsers/clean/clean-codex-service.js +0 -534
  148. package/dist/infra/parsers/clean/clean-copilot-service.d.ts +0 -255
  149. package/dist/infra/parsers/clean/clean-copilot-service.js +0 -729
  150. package/dist/infra/parsers/clean/clean-cursor-service.d.ts +0 -161
  151. package/dist/infra/parsers/clean/clean-cursor-service.js +0 -432
  152. package/dist/infra/parsers/clean/clean-parser-service-factory.d.ts +0 -54
  153. package/dist/infra/parsers/clean/clean-parser-service-factory.js +0 -80
  154. package/dist/infra/parsers/clean/shared.d.ts +0 -84
  155. package/dist/infra/parsers/clean/shared.js +0 -273
  156. package/dist/infra/parsers/raw/raw-claude-service.d.ts +0 -195
  157. package/dist/infra/parsers/raw/raw-claude-service.js +0 -548
  158. package/dist/infra/parsers/raw/raw-codex-service.d.ts +0 -313
  159. package/dist/infra/parsers/raw/raw-codex-service.js +0 -782
  160. package/dist/infra/parsers/raw/raw-copilot-service.d.ts +0 -196
  161. package/dist/infra/parsers/raw/raw-copilot-service.js +0 -558
  162. package/dist/infra/parsers/raw/raw-cursor-service.d.ts +0 -316
  163. package/dist/infra/parsers/raw/raw-cursor-service.js +0 -818
  164. package/dist/infra/parsers/raw/raw-parser-service-factory.d.ts +0 -54
  165. package/dist/infra/parsers/raw/raw-parser-service-factory.js +0 -81
  166. package/dist/infra/watcher/file-watcher-service.d.ts +0 -10
  167. package/dist/infra/watcher/file-watcher-service.js +0 -81
  168. package/dist/oclif/commands/watch.d.ts +0 -25
  169. package/dist/oclif/commands/watch.js +0 -175
  170. /package/dist/core/interfaces/{cipher/i-coding-agent-log-parser.js → i-provider-config-store.js} +0 -0
  171. /package/dist/core/interfaces/{cipher/i-coding-agent-log-watcher.js → i-provider-keychain-store.js} +0 -0
@@ -1,54 +0,0 @@
1
- /**
2
- * Raw Parser Service Factory
3
- * Routes IDE-based parsing requests to the appropriate parser service
4
- * Supports: Claude Code, Cursor, GitHub Copilot, Codex
5
- */
6
- import { Agent } from '../../../core/domain/entities/agent.js';
7
- import { IRawParserService } from '../../../core/interfaces/parser/i-raw-parser-service.js';
8
- /**
9
- * Raw Parser Service Factory
10
- * Creates and returns appropriate parser service for the given IDE
11
- */
12
- export declare class RawParserServiceFactory {
13
- private static readonly SUPPORTED_IDES;
14
- /**
15
- * Create a raw parser service for the specified IDE
16
- *
17
- * Factory method that instantiates the appropriate raw parser service
18
- * based on the provided IDE type. Routes to specialized service classes.
19
- *
20
- * @param ide - The IDE type: 'Claude Code', 'Cursor', 'Github Copilot', or 'Codex'
21
- * @returns The appropriate raw parser service instance
22
- * @throws Error if IDE is not supported
23
- */
24
- static createRawParserService(ide: Agent): IRawParserService;
25
- /**
26
- * Get list of supported IDEs
27
- *
28
- * Returns array of all IDE types that have corresponding raw parser services.
29
- *
30
- * @returns Array of supported IDE type strings
31
- */
32
- static getSupportedIDEs(): Agent[];
33
- /**
34
- * Check if IDE is supported
35
- *
36
- * Validates whether the provided IDE string corresponds to a supported IDE.
37
- *
38
- * @param ide - IDE name to validate
39
- * @returns True if IDE is in supported list, false otherwise
40
- */
41
- static isSupported(ide: Agent): boolean;
42
- /**
43
- * Parse conversations for the specified IDE
44
- *
45
- * Creates appropriate service and delegates to its parse method to extract
46
- * and transform raw conversation data from IDE storage.
47
- *
48
- * @param ide - The IDE type (Claude Code, Cursor, Github Copilot, Codex)
49
- * @param customDir - Path to custom directory containing IDE session data
50
- * @param outputDir - Optional output directory (defaults to process.cwd()/.brv/logs/{ide}/raw)
51
- * @returns Promise resolving to true if parsing succeeded, false otherwise
52
- */
53
- static parseConversations(ide: Agent, customDir: string, outputDir?: string): Promise<boolean>;
54
- }
@@ -1,81 +0,0 @@
1
- /**
2
- * Raw Parser Service Factory
3
- * Routes IDE-based parsing requests to the appropriate parser service
4
- * Supports: Claude Code, Cursor, GitHub Copilot, Codex
5
- */
6
- import { ClaudeRawService } from './raw-claude-service.js';
7
- import { CodexRawService } from './raw-codex-service.js';
8
- import { CopilotRawService } from './raw-copilot-service.js';
9
- import { CursorRawService } from './raw-cursor-service.js';
10
- /**
11
- * Raw Parser Service Factory
12
- * Creates and returns appropriate parser service for the given IDE
13
- */
14
- export class RawParserServiceFactory {
15
- static SUPPORTED_IDES = ['Claude Code', 'Cursor', 'Github Copilot', 'Codex'];
16
- /**
17
- * Create a raw parser service for the specified IDE
18
- *
19
- * Factory method that instantiates the appropriate raw parser service
20
- * based on the provided IDE type. Routes to specialized service classes.
21
- *
22
- * @param ide - The IDE type: 'Claude Code', 'Cursor', 'Github Copilot', or 'Codex'
23
- * @returns The appropriate raw parser service instance
24
- * @throws Error if IDE is not supported
25
- */
26
- static createRawParserService(ide) {
27
- switch (ide) {
28
- case 'Claude Code': {
29
- return new ClaudeRawService(ide);
30
- }
31
- case 'Codex': {
32
- return new CodexRawService(ide);
33
- }
34
- case 'Cursor': {
35
- return new CursorRawService(ide);
36
- }
37
- case 'Github Copilot': {
38
- return new CopilotRawService(ide);
39
- }
40
- default: {
41
- throw new Error(`Unsupported IDE: ${ide}. Supported IDEs are: claude, cursor, copilot, codex`);
42
- }
43
- }
44
- }
45
- /**
46
- * Get list of supported IDEs
47
- *
48
- * Returns array of all IDE types that have corresponding raw parser services.
49
- *
50
- * @returns Array of supported IDE type strings
51
- */
52
- static getSupportedIDEs() {
53
- return [...this.SUPPORTED_IDES];
54
- }
55
- /**
56
- * Check if IDE is supported
57
- *
58
- * Validates whether the provided IDE string corresponds to a supported IDE.
59
- *
60
- * @param ide - IDE name to validate
61
- * @returns True if IDE is in supported list, false otherwise
62
- */
63
- static isSupported(ide) {
64
- return this.getSupportedIDEs().includes(ide);
65
- }
66
- /**
67
- * Parse conversations for the specified IDE
68
- *
69
- * Creates appropriate service and delegates to its parse method to extract
70
- * and transform raw conversation data from IDE storage.
71
- *
72
- * @param ide - The IDE type (Claude Code, Cursor, Github Copilot, Codex)
73
- * @param customDir - Path to custom directory containing IDE session data
74
- * @param outputDir - Optional output directory (defaults to process.cwd()/.brv/logs/{ide}/raw)
75
- * @returns Promise resolving to true if parsing succeeded, false otherwise
76
- */
77
- static async parseConversations(ide, customDir, outputDir) {
78
- const service = this.createRawParserService(ide);
79
- return service.parse(customDir, outputDir);
80
- }
81
- }
@@ -1,10 +0,0 @@
1
- import type { FileEvent, IFileWatcherService } from '../../core/interfaces/i-file-watcher-service.js';
2
- export declare class FileWatcherService implements IFileWatcherService {
3
- private eventHandler;
4
- private watcher;
5
- constructor();
6
- setFileEventHandler(handler: (event: FileEvent) => Promise<void>): void;
7
- start(paths: string[]): Promise<void>;
8
- stop(): Promise<void>;
9
- private invokeHandler;
10
- }
@@ -1,81 +0,0 @@
1
- import { watch } from 'chokidar';
2
- export class FileWatcherService {
3
- eventHandler;
4
- watcher;
5
- constructor() {
6
- this.eventHandler = undefined;
7
- this.watcher = undefined;
8
- }
9
- setFileEventHandler(handler) {
10
- this.eventHandler = handler;
11
- }
12
- async start(paths) {
13
- this.watcher = watch(paths, {
14
- // `false` means the `add` events will be emitted for files already existing when the watcher starts.
15
- // May need to change to `true` in the future because:
16
- // - We only care about NEW LOGS that agents write AFTER we start watching.
17
- // - We don't want to process old/existing log files that were already there.
18
- // - Cleaner output - no flood of events when watcher starts.
19
- ignoreInitial: true,
20
- // Keep watching indefinitely (we want a long-running watcher)
21
- persistent: true,
22
- });
23
- // Register event LISTENERS for all file system events
24
- // Note: invokeHandler is async and handles errors internally
25
- this.watcher.on('add', async (path) => {
26
- await this.invokeHandler('add', path);
27
- });
28
- this.watcher.on('addDir', async (path) => {
29
- await this.invokeHandler('addDir', path);
30
- });
31
- this.watcher.on('change', async (path) => {
32
- await this.invokeHandler('change', path);
33
- });
34
- this.watcher.on('unlink', async (path) => {
35
- await this.invokeHandler('unlink', path);
36
- });
37
- this.watcher.on('unlinkDir', async (path) => {
38
- await this.invokeHandler('unlinkDir', path);
39
- });
40
- // Wait for watcher to be ready.
41
- // The 'ready' event fires when
42
- // chokidar has completed its initial scan of the directories (regardless of ignoreInitial setting)
43
- // With ignoreInitial: false, the timeline is:
44
- // 1. chokidar.watch() called
45
- // 2. Scans directories
46
- // 3. Emits 'add' events for 100 existing files
47
- // 4. 'ready' event fires ← "Done with initial scan"
48
- // 5. Now watching for new changes
49
- //
50
- // With ignoreInitial: true, timeline is:
51
- // 1. chokidar.watch() called
52
- // 2. Scans directories (still happens!)
53
- // 3. Doesn't emit 'add' events for existing files
54
- // 4. 'ready' event fires ← "Done with initial scan"
55
- // 5. Now watching for new changes
56
- // 'ready' is still useful for ignoreInitial: true.
57
- await new Promise((resolve) => {
58
- this.watcher?.on('ready', () => {
59
- resolve();
60
- });
61
- });
62
- }
63
- async stop() {
64
- if (this.watcher !== undefined) {
65
- await this.watcher.close();
66
- this.watcher = undefined;
67
- }
68
- this.eventHandler = undefined;
69
- }
70
- async invokeHandler(type, path) {
71
- if (this.eventHandler !== undefined) {
72
- const event = { path, type };
73
- try {
74
- await this.eventHandler(event);
75
- }
76
- catch (error) {
77
- console.error(`[FileWatcherService] Error in event handler for ${type} ${path}:`, error);
78
- }
79
- }
80
- }
81
- }
@@ -1,25 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- import type { IFileWatcherService } from '../../core/interfaces/i-file-watcher-service.js';
3
- import type { IProjectConfigStore } from '../../core/interfaces/i-project-config-store.js';
4
- import type { ITerminal } from '../../core/interfaces/i-terminal.js';
5
- export default class Watch extends Command {
6
- static description: string;
7
- static examples: string[];
8
- static flags: {
9
- clean: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
- debounce: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
- paths: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- };
13
- static hidden: boolean;
14
- protected terminal: ITerminal;
15
- private lastParseTime;
16
- private parsingInProgress;
17
- private pendingParse;
18
- protected createServices(): {
19
- fileWatcherService: IFileWatcherService;
20
- projectConfigStore: IProjectConfigStore;
21
- };
22
- run(): Promise<void>;
23
- protected waitForShutdownSignal(): Promise<void>;
24
- private triggerParsing;
25
- }
@@ -1,175 +0,0 @@
1
- import { Command, Flags } from '@oclif/core';
2
- import { isDevelopment } from '../../config/environment.js';
3
- import { ProjectConfigStore } from '../../infra/config/file-config-store.js';
4
- import { CleanParserServiceFactory } from '../../infra/parsers/clean/clean-parser-service-factory.js';
5
- import { RawParserServiceFactory } from '../../infra/parsers/raw/raw-parser-service-factory.js';
6
- import { OclifTerminal } from '../../infra/terminal/oclif-terminal.js';
7
- import { FileWatcherService } from '../../infra/watcher/file-watcher-service.js';
8
- export default class Watch extends Command {
9
- static description = 'Watch file system directories for changes and trigger parsing pipeline [Development only]';
10
- static examples = [
11
- '<%= config.bin %> <%= command.id %> --paths ./agent-logs',
12
- '<%= config.bin %> <%= command.id %> --paths ./logs,./outputs,./workspace',
13
- '<%= config.bin %> <%= command.id %> -p ./src,./lib',
14
- '# Use chat log path from config (if IDE was configured during init):',
15
- '<%= config.bin %> <%= command.id %>',
16
- ];
17
- static flags = {
18
- clean: Flags.boolean({
19
- allowNo: true,
20
- default: true,
21
- description: 'Run clean parsing after raw parsing (default: true)',
22
- }),
23
- debounce: Flags.integer({
24
- default: 2000,
25
- description: 'Debounce time in milliseconds before triggering parsing (default: 2000)',
26
- }),
27
- paths: Flags.string({
28
- char: 'p',
29
- description: 'Comma-separated list of directories to watch (defaults to configured chat log path)',
30
- required: false,
31
- }),
32
- };
33
- static hidden = !isDevelopment();
34
- terminal = {};
35
- lastParseTime = 0;
36
- parsingInProgress = false;
37
- pendingParse = false;
38
- createServices() {
39
- this.terminal = new OclifTerminal(this);
40
- return {
41
- fileWatcherService: new FileWatcherService(),
42
- projectConfigStore: new ProjectConfigStore(),
43
- };
44
- }
45
- async run() {
46
- const { flags } = await this.parse(Watch);
47
- const { fileWatcherService, projectConfigStore } = this.createServices();
48
- if (!isDevelopment()) {
49
- this.terminal.error('This command is only available in development environment');
50
- return;
51
- }
52
- let paths = [];
53
- let ideConfig = null;
54
- // Use explicit paths if provided
55
- if (flags.paths) {
56
- paths = flags.paths.split(',').map((p) => p.trim());
57
- }
58
- else {
59
- // Try to load chat log path from config
60
- try {
61
- const configExists = await projectConfigStore.exists();
62
- if (configExists) {
63
- const config = await projectConfigStore.read();
64
- if (config?.chatLogPath && config?.ide) {
65
- paths = [config.chatLogPath];
66
- ideConfig = config.ide;
67
- this.terminal.log(`ℹ Using chat log path from config (${config.ide})`);
68
- }
69
- }
70
- }
71
- catch {
72
- // Silently ignore config loading errors
73
- }
74
- if (paths.length === 0) {
75
- this.terminal.error('No paths specified. Either:\n' +
76
- ' 1. Use --paths flag: brv watch --paths ./logs,./outputs\n' +
77
- ' 2. Run "brv init" to configure IDE and detect workspaces');
78
- }
79
- }
80
- try {
81
- // Set up file event handler with parsing pipeline
82
- fileWatcherService.setFileEventHandler(async (event) => {
83
- this.terminal.log(`[${event.type}] ${event.path}`);
84
- // Only trigger parsing if IDE is configured
85
- if (ideConfig && (event.type === 'add' || event.type === 'change' || event.type === 'unlink')) {
86
- this.pendingParse = true;
87
- // Debounce parsing to avoid too frequent triggers
88
- if (!this.parsingInProgress && Date.now() - this.lastParseTime > flags.debounce) {
89
- this.triggerParsing(ideConfig, paths[0]).catch((error) => {
90
- this.terminal.warn(`⚠️ Parsing error: ${error instanceof Error ? error.message : String(error)}`);
91
- });
92
- }
93
- }
94
- });
95
- await fileWatcherService.start(paths);
96
- this.terminal.log(`\n🔍 Watching paths: ${paths.join(', ')}`);
97
- if (ideConfig) {
98
- this.terminal.log(`📊 Parsing pipeline enabled for: ${ideConfig}`);
99
- }
100
- this.terminal.log('Press Ctrl+C to stop...\n');
101
- await this.waitForShutdownSignal();
102
- }
103
- catch (error) {
104
- this.terminal.error(error instanceof Error ? error.message : 'Unknown Error');
105
- }
106
- finally {
107
- await fileWatcherService.stop();
108
- }
109
- }
110
- async waitForShutdownSignal() {
111
- return new Promise((resolve) => {
112
- const handleSignal = () => {
113
- this.terminal.log('\nShutting down watcher...');
114
- process.off('SIGINT', handleSignal);
115
- process.off('SIGTERM', handleSignal);
116
- resolve();
117
- };
118
- process.on('SIGINT', handleSignal);
119
- process.on('SIGTERM', handleSignal);
120
- });
121
- }
122
- async triggerParsing(ide, chatLogPath) {
123
- if (this.parsingInProgress) {
124
- return;
125
- }
126
- this.parsingInProgress = true;
127
- this.pendingParse = false;
128
- this.lastParseTime = Date.now();
129
- try {
130
- // Normalize IDE name for factory
131
- // Validate IDE is supported
132
- if (!RawParserServiceFactory.isSupported(ide)) {
133
- this.terminal.warn(`⚠️ Unsupported IDE: ${ide}`);
134
- return;
135
- }
136
- this.terminal.log('\n📥 Parsing triggered...');
137
- // Raw parsing phase
138
- let isRawSuccess = false;
139
- try {
140
- // Cast is safe because we already validated with isSupported()
141
- isRawSuccess = await RawParserServiceFactory.parseConversations(ide, chatLogPath);
142
- }
143
- catch (error) {
144
- this.terminal.warn(`⚠️ Raw parsing error: ${error instanceof Error ? error.message : String(error)}`);
145
- return;
146
- }
147
- // Clean parsing phase (if enabled)
148
- const { flags } = await this.parse(Watch);
149
- if (isRawSuccess && flags.clean) {
150
- try {
151
- const rawOutputDir = `${process.cwd()}/.brv/logs/${ide}/raw`;
152
- const cleanSessions = await CleanParserServiceFactory.parseConversations(ide, rawOutputDir);
153
- if (cleanSessions.length > 0) {
154
- this.terminal.log('✅ Clean parsing complete\n');
155
- }
156
- else {
157
- this.terminal.warn('⚠️ Clean parsing failed');
158
- }
159
- }
160
- catch (error) {
161
- this.terminal.warn(`⚠️ Clean parsing error: ${error instanceof Error ? error.message : String(error)}`);
162
- }
163
- }
164
- }
165
- finally {
166
- this.parsingInProgress = false;
167
- // If more files were added while parsing, queue another parse
168
- if (this.pendingParse) {
169
- this.triggerParsing(ide, chatLogPath).catch((error) => {
170
- this.terminal.warn(`⚠️ Parsing error: ${error instanceof Error ? error.message : String(error)}`);
171
- });
172
- }
173
- }
174
- }
175
- }