byterover-cli 1.0.5 → 1.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 (204) hide show
  1. package/README.md +19 -13
  2. package/dist/commands/hook-prompt-submit.d.ts +27 -0
  3. package/dist/commands/hook-prompt-submit.js +39 -0
  4. package/dist/commands/mcp.d.ts +13 -0
  5. package/dist/commands/mcp.js +61 -0
  6. package/dist/commands/status.js +8 -3
  7. package/dist/constants.d.ts +1 -1
  8. package/dist/constants.js +1 -1
  9. package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
  10. package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
  11. package/dist/core/domain/cipher/tools/constants.js +1 -0
  12. package/dist/core/domain/entities/agent.d.ts +16 -0
  13. package/dist/core/domain/entities/agent.js +78 -0
  14. package/dist/core/domain/entities/connector-type.d.ts +10 -0
  15. package/dist/core/domain/entities/connector-type.js +9 -0
  16. package/dist/core/domain/entities/event.d.ts +1 -1
  17. package/dist/core/domain/entities/event.js +2 -0
  18. package/dist/core/domain/errors/task-error.d.ts +4 -0
  19. package/dist/core/domain/errors/task-error.js +7 -0
  20. package/dist/core/domain/transport/schemas.d.ts +40 -0
  21. package/dist/core/domain/transport/schemas.js +28 -0
  22. package/dist/core/interfaces/connectors/connector-types.d.ts +70 -0
  23. package/dist/core/interfaces/connectors/i-connector-manager.d.ts +72 -0
  24. package/dist/core/interfaces/connectors/i-connector-manager.js +1 -0
  25. package/dist/core/interfaces/connectors/i-connector.d.ts +54 -0
  26. package/dist/core/interfaces/connectors/i-connector.js +1 -0
  27. package/dist/core/interfaces/i-file-service.d.ts +7 -0
  28. package/dist/core/interfaces/i-mcp-config-writer.d.ts +40 -0
  29. package/dist/core/interfaces/i-mcp-config-writer.js +1 -0
  30. package/dist/core/interfaces/i-rule-template-service.d.ts +4 -2
  31. package/dist/core/interfaces/transport/i-transport-client.d.ts +7 -0
  32. package/dist/core/interfaces/usecase/i-connectors-use-case.d.ts +3 -0
  33. package/dist/core/interfaces/usecase/i-connectors-use-case.js +1 -0
  34. package/dist/hooks/init/update-notifier.d.ts +1 -0
  35. package/dist/hooks/init/update-notifier.js +10 -1
  36. package/dist/infra/cipher/agent/cipher-agent.d.ts +8 -0
  37. package/dist/infra/cipher/agent/cipher-agent.js +16 -0
  38. package/dist/infra/cipher/file-system/binary-utils.d.ts +7 -12
  39. package/dist/infra/cipher/file-system/binary-utils.js +46 -31
  40. package/dist/infra/cipher/llm/context/context-manager.d.ts +10 -2
  41. package/dist/infra/cipher/llm/context/context-manager.js +39 -2
  42. package/dist/infra/cipher/llm/formatters/gemini-formatter.js +48 -9
  43. package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
  44. package/dist/infra/cipher/llm/internal-llm-service.js +40 -12
  45. package/dist/infra/cipher/session/chat-session.d.ts +3 -0
  46. package/dist/infra/cipher/session/chat-session.js +7 -1
  47. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.d.ts +6 -7
  48. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +57 -18
  49. package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
  50. package/dist/infra/cipher/tools/implementations/curate-tool.js +380 -24
  51. package/dist/infra/cipher/tools/implementations/read-file-tool.js +38 -17
  52. package/dist/infra/cipher/tools/implementations/search-knowledge-tool.d.ts +7 -0
  53. package/dist/infra/cipher/tools/implementations/search-knowledge-tool.js +303 -0
  54. package/dist/infra/cipher/tools/index.d.ts +1 -0
  55. package/dist/infra/cipher/tools/index.js +1 -0
  56. package/dist/infra/cipher/tools/tool-manager.js +1 -0
  57. package/dist/infra/cipher/tools/tool-registry.js +7 -0
  58. package/dist/infra/connectors/connector-manager.d.ts +32 -0
  59. package/dist/infra/connectors/connector-manager.js +158 -0
  60. package/dist/infra/connectors/hook/hook-connector-config.d.ts +52 -0
  61. package/dist/infra/connectors/hook/hook-connector-config.js +41 -0
  62. package/dist/infra/connectors/hook/hook-connector.d.ts +46 -0
  63. package/dist/infra/connectors/hook/hook-connector.js +231 -0
  64. package/dist/infra/connectors/mcp/index.d.ts +4 -0
  65. package/dist/infra/connectors/mcp/index.js +4 -0
  66. package/dist/infra/connectors/mcp/json-mcp-config-writer.d.ts +26 -0
  67. package/dist/infra/connectors/mcp/json-mcp-config-writer.js +71 -0
  68. package/dist/infra/connectors/mcp/mcp-connector-config.d.ts +229 -0
  69. package/dist/infra/connectors/mcp/mcp-connector-config.js +173 -0
  70. package/dist/infra/connectors/mcp/mcp-connector.d.ts +80 -0
  71. package/dist/infra/connectors/mcp/mcp-connector.js +324 -0
  72. package/dist/infra/connectors/mcp/toml-mcp-config-writer.d.ts +45 -0
  73. package/dist/infra/connectors/mcp/toml-mcp-config-writer.js +134 -0
  74. package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.d.ts +2 -2
  75. package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.js +1 -1
  76. package/dist/infra/connectors/rules/rules-connector-config.d.ts +95 -0
  77. package/dist/infra/{rule/agent-rule-config.js → connectors/rules/rules-connector-config.js} +10 -10
  78. package/dist/infra/connectors/rules/rules-connector.d.ts +34 -0
  79. package/dist/infra/connectors/rules/rules-connector.js +139 -0
  80. package/dist/infra/connectors/shared/rule-file-manager.d.ts +72 -0
  81. package/dist/infra/connectors/shared/rule-file-manager.js +119 -0
  82. package/dist/infra/connectors/shared/template-service.d.ts +27 -0
  83. package/dist/infra/connectors/shared/template-service.js +125 -0
  84. package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +5 -2
  85. package/dist/infra/context-tree/file-context-tree-writer-service.js +20 -5
  86. package/dist/infra/core/executors/curate-executor.d.ts +2 -2
  87. package/dist/infra/core/executors/curate-executor.js +7 -7
  88. package/dist/infra/core/executors/query-executor.d.ts +12 -0
  89. package/dist/infra/core/executors/query-executor.js +62 -1
  90. package/dist/infra/file/fs-file-service.d.ts +7 -0
  91. package/dist/infra/file/fs-file-service.js +15 -1
  92. package/dist/infra/mcp/index.d.ts +2 -0
  93. package/dist/infra/mcp/index.js +2 -0
  94. package/dist/infra/mcp/mcp-server.d.ts +58 -0
  95. package/dist/infra/mcp/mcp-server.js +178 -0
  96. package/dist/infra/mcp/tools/brv-curate-tool.d.ts +23 -0
  97. package/dist/infra/mcp/tools/brv-curate-tool.js +68 -0
  98. package/dist/infra/mcp/tools/brv-query-tool.d.ts +17 -0
  99. package/dist/infra/mcp/tools/brv-query-tool.js +68 -0
  100. package/dist/infra/mcp/tools/index.d.ts +3 -0
  101. package/dist/infra/mcp/tools/index.js +3 -0
  102. package/dist/infra/mcp/tools/task-result-waiter.d.ts +30 -0
  103. package/dist/infra/mcp/tools/task-result-waiter.js +56 -0
  104. package/dist/infra/process/agent-worker.d.ts +2 -2
  105. package/dist/infra/process/agent-worker.js +663 -142
  106. package/dist/infra/process/constants.d.ts +1 -1
  107. package/dist/infra/process/constants.js +1 -1
  108. package/dist/infra/process/ipc-types.d.ts +17 -4
  109. package/dist/infra/process/ipc-types.js +3 -3
  110. package/dist/infra/process/parent-heartbeat.d.ts +47 -0
  111. package/dist/infra/process/parent-heartbeat.js +118 -0
  112. package/dist/infra/process/process-manager.d.ts +79 -0
  113. package/dist/infra/process/process-manager.js +277 -3
  114. package/dist/infra/process/task-queue-manager.d.ts +13 -0
  115. package/dist/infra/process/task-queue-manager.js +19 -0
  116. package/dist/infra/process/transport-handlers.d.ts +3 -0
  117. package/dist/infra/process/transport-handlers.js +51 -5
  118. package/dist/infra/process/transport-worker.js +9 -69
  119. package/dist/infra/repl/commands/connectors-command.d.ts +8 -0
  120. package/dist/infra/repl/commands/{gen-rules-command.js → connectors-command.js} +21 -10
  121. package/dist/infra/repl/commands/curate-command.js +2 -2
  122. package/dist/infra/repl/commands/index.js +3 -2
  123. package/dist/infra/repl/commands/init-command.js +11 -7
  124. package/dist/infra/repl/commands/query-command.js +22 -2
  125. package/dist/infra/repl/commands/reset-command.js +1 -1
  126. package/dist/infra/transport/socket-io-transport-client.d.ts +75 -0
  127. package/dist/infra/transport/socket-io-transport-client.js +308 -7
  128. package/dist/infra/transport/socket-io-transport-server.js +4 -0
  129. package/dist/infra/usecase/connectors-use-case.d.ts +63 -0
  130. package/dist/infra/usecase/connectors-use-case.js +222 -0
  131. package/dist/infra/usecase/init-use-case.d.ts +8 -43
  132. package/dist/infra/usecase/init-use-case.js +27 -252
  133. package/dist/infra/usecase/logout-use-case.js +1 -1
  134. package/dist/infra/usecase/pull-use-case.js +5 -5
  135. package/dist/infra/usecase/push-use-case.js +4 -4
  136. package/dist/infra/usecase/reset-use-case.js +3 -4
  137. package/dist/infra/usecase/space-list-use-case.js +3 -3
  138. package/dist/infra/usecase/space-switch-use-case.js +3 -3
  139. package/dist/infra/usecase/status-use-case.d.ts +10 -0
  140. package/dist/infra/usecase/status-use-case.js +53 -0
  141. package/dist/resources/prompts/curate.yml +114 -4
  142. package/dist/resources/prompts/explore.yml +34 -0
  143. package/dist/resources/prompts/query-orchestrator.yml +112 -0
  144. package/dist/resources/prompts/system-prompt.yml +12 -2
  145. package/dist/resources/tools/search_knowledge.txt +32 -0
  146. package/dist/templates/mcp-base.md +1 -0
  147. package/dist/templates/sections/brv-instructions.md +98 -0
  148. package/dist/templates/sections/mcp-workflow.md +13 -0
  149. package/dist/tui/app.js +4 -1
  150. package/dist/tui/components/command-details.js +1 -1
  151. package/dist/tui/components/execution/execution-changes.d.ts +2 -0
  152. package/dist/tui/components/execution/execution-changes.js +5 -1
  153. package/dist/tui/components/execution/execution-content.d.ts +2 -0
  154. package/dist/tui/components/execution/execution-content.js +8 -18
  155. package/dist/tui/components/execution/execution-input.d.ts +2 -0
  156. package/dist/tui/components/execution/execution-input.js +6 -4
  157. package/dist/tui/components/execution/execution-progress.d.ts +2 -0
  158. package/dist/tui/components/execution/execution-progress.js +6 -2
  159. package/dist/tui/components/execution/expanded-log-view.d.ts +20 -0
  160. package/dist/tui/components/execution/expanded-log-view.js +75 -0
  161. package/dist/tui/components/execution/expanded-message-view.d.ts +24 -0
  162. package/dist/tui/components/execution/expanded-message-view.js +68 -0
  163. package/dist/tui/components/execution/index.d.ts +2 -0
  164. package/dist/tui/components/execution/index.js +2 -0
  165. package/dist/tui/components/execution/log-item.d.ts +4 -0
  166. package/dist/tui/components/execution/log-item.js +2 -2
  167. package/dist/tui/components/footer.js +1 -1
  168. package/dist/tui/components/index.d.ts +2 -1
  169. package/dist/tui/components/index.js +2 -1
  170. package/dist/tui/components/init.js +2 -9
  171. package/dist/tui/components/logo.js +4 -3
  172. package/dist/tui/components/markdown.d.ts +13 -0
  173. package/dist/tui/components/markdown.js +88 -0
  174. package/dist/tui/components/message-item.js +1 -1
  175. package/dist/tui/components/onboarding/onboarding-flow.js +14 -11
  176. package/dist/tui/components/onboarding/welcome-box.js +1 -1
  177. package/dist/tui/components/suggestions.js +3 -3
  178. package/dist/tui/contexts/mode-context.js +6 -2
  179. package/dist/tui/contexts/onboarding-context.d.ts +4 -0
  180. package/dist/tui/contexts/onboarding-context.js +14 -2
  181. package/dist/tui/hooks/index.d.ts +1 -0
  182. package/dist/tui/hooks/index.js +1 -0
  183. package/dist/tui/hooks/use-is-latest-version.d.ts +6 -0
  184. package/dist/tui/hooks/use-is-latest-version.js +22 -0
  185. package/dist/tui/views/command-view.d.ts +1 -1
  186. package/dist/tui/views/command-view.js +87 -98
  187. package/dist/tui/views/logs-view.d.ts +8 -0
  188. package/dist/tui/views/logs-view.js +55 -27
  189. package/dist/utils/file-validator.d.ts +1 -1
  190. package/dist/utils/file-validator.js +25 -28
  191. package/dist/utils/type-guards.d.ts +5 -0
  192. package/dist/utils/type-guards.js +7 -0
  193. package/oclif.manifest.json +55 -4
  194. package/package.json +12 -1
  195. package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +0 -3
  196. package/dist/infra/repl/commands/gen-rules-command.d.ts +0 -7
  197. package/dist/infra/rule/agent-rule-config.d.ts +0 -19
  198. package/dist/infra/rule/rule-template-service.d.ts +0 -18
  199. package/dist/infra/rule/rule-template-service.js +0 -88
  200. package/dist/infra/usecase/generate-rules-use-case.d.ts +0 -61
  201. package/dist/infra/usecase/generate-rules-use-case.js +0 -285
  202. /package/dist/core/interfaces/{usecase/i-generate-rules-use-case.js → connectors/connector-types.js} +0 -0
  203. /package/dist/infra/{rule → connectors/shared}/constants.d.ts +0 -0
  204. /package/dist/infra/{rule → connectors/shared}/constants.js +0 -0
@@ -12,6 +12,8 @@ interface ProgressItem {
12
12
  toolCallName: string;
13
13
  }
14
14
  interface ExecutionProgressProps {
15
+ /** Whether content should be fully expanded (no truncation) */
16
+ isExpand?: boolean;
15
17
  /** Maximum number of lines (rows) this component can use, including hint line (default: 3) */
16
18
  maxLines?: number;
17
19
  /** Array of progress items */
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Execution Progress Component
4
4
  *
@@ -10,11 +10,15 @@ import Spinner from 'ink-spinner';
10
10
  import { useTheme } from '../../hooks/index.js';
11
11
  /** Default maximum number of lines (rows) for progress display */
12
12
  const DEFAULT_MAX_LINES = 3;
13
- export const ExecutionProgress = ({ maxLines = DEFAULT_MAX_LINES, progress }) => {
13
+ export const ExecutionProgress = ({ isExpand = false, maxLines = DEFAULT_MAX_LINES, progress, }) => {
14
14
  const { theme: { colors }, } = useTheme();
15
15
  if (!progress || progress.length === 0) {
16
16
  return null;
17
17
  }
18
+ // In expand mode, show all items without truncation
19
+ if (isExpand) {
20
+ return (_jsx(Box, { flexDirection: "column", children: progress.map((item) => (_jsxs(Box, { children: [item.status === 'completed' && _jsx(Text, { color: colors.primary, children: "\u2713 " }), item.status === 'running' && (_jsxs(Text, { color: colors.dimText, children: [_jsx(Spinner, { type: "dots" }), ' '] })), item.status === 'failed' && _jsx(Text, { color: colors.errorText, children: "\u2717 " }), _jsx(Text, { color: colors.dimText, children: item.toolCallName })] }, item.id))) }));
21
+ }
18
22
  const hasMore = progress.length > maxLines;
19
23
  // If there's overflow, reserve 1 line for hint, show (maxLines - 1) items
20
24
  const visibleCount = hasMore ? maxLines - 1 : maxLines;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Expanded Log View Component
3
+ *
4
+ * Full-screen overlay displaying a single log item with scrollable content.
5
+ * Activated by Ctrl+O on a selected log item, dismissed with Ctrl+O or Esc.
6
+ */
7
+ import React from 'react';
8
+ import type { ActivityLog } from '../../types.js';
9
+ interface ExpandedLogViewProps {
10
+ /** Available height for the expanded view (in terminal rows) */
11
+ availableHeight: number;
12
+ /** Whether input handling is active */
13
+ isActive: boolean;
14
+ /** The log to display in expanded view */
15
+ log: ActivityLog;
16
+ /** Callback when the view should close */
17
+ onClose: () => void;
18
+ }
19
+ export declare const ExpandedLogView: React.FC<ExpandedLogViewProps>;
20
+ export {};
@@ -0,0 +1,75 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Expanded Log View Component
4
+ *
5
+ * Full-screen overlay displaying a single log item with scrollable content.
6
+ * Activated by Ctrl+O on a selected log item, dismissed with Ctrl+O or Esc.
7
+ */
8
+ import { Box, Spacer, Text, useInput, useStdout } from 'ink';
9
+ import { ScrollView } from 'ink-scroll-view';
10
+ import { useEffect, useRef, useState } from 'react';
11
+ import { useTheme } from '../../hooks/index.js';
12
+ import { formatTime } from '../../utils/index.js';
13
+ import { ExecutionChanges } from './execution-changes.js';
14
+ import { ExecutionContent } from './execution-content.js';
15
+ import { ExecutionProgress } from './execution-progress.js';
16
+ import { ExecutionStatus } from './execution-status.js';
17
+ export const ExpandedLogView = ({ availableHeight, isActive, log, onClose, }) => {
18
+ const { theme: { colors }, } = useTheme();
19
+ const { stdout } = useStdout();
20
+ const scrollViewRef = useRef(null);
21
+ const [hasMoreBelow, setHasMoreBelow] = useState(false);
22
+ const updateScrollIndicator = () => {
23
+ if (!scrollViewRef.current)
24
+ return;
25
+ const currentOffset = scrollViewRef.current.getScrollOffset();
26
+ const maxOffset = scrollViewRef.current.getBottomOffset();
27
+ setHasMoreBelow(currentOffset < maxOffset);
28
+ };
29
+ useEffect(() => {
30
+ const timer = setTimeout(updateScrollIndicator, 50);
31
+ return () => clearTimeout(timer);
32
+ }, [log]);
33
+ // Terminal resize handling
34
+ useEffect(() => {
35
+ const handleResize = () => {
36
+ scrollViewRef.current?.remeasure();
37
+ updateScrollIndicator();
38
+ };
39
+ stdout?.on('resize', handleResize);
40
+ return () => {
41
+ stdout?.off('resize', handleResize);
42
+ };
43
+ }, [stdout]);
44
+ useInput((input, key) => {
45
+ if (!scrollViewRef.current)
46
+ return;
47
+ if ((key.ctrl && input === 'o') || key.escape) {
48
+ onClose();
49
+ }
50
+ if (key.upArrow || input === 'k') {
51
+ scrollViewRef.current.scrollBy(-1);
52
+ updateScrollIndicator();
53
+ }
54
+ if (key.downArrow || input === 'j') {
55
+ const currentOffset = scrollViewRef.current.getScrollOffset();
56
+ const maxOffset = scrollViewRef.current.getBottomOffset();
57
+ const newOffset = Math.min(currentOffset + 1, maxOffset);
58
+ scrollViewRef.current.scrollTo(newOffset);
59
+ updateScrollIndicator();
60
+ }
61
+ if (input === 'g') {
62
+ scrollViewRef.current.scrollTo(0);
63
+ updateScrollIndicator();
64
+ }
65
+ if (input === 'G') {
66
+ scrollViewRef.current.scrollTo(scrollViewRef.current.getBottomOffset());
67
+ updateScrollIndicator();
68
+ }
69
+ }, { isActive });
70
+ const displayTime = formatTime(log.timestamp);
71
+ return (_jsxs(Box, { flexDirection: "column", height: "100%", paddingX: 2, width: "100%", children: [_jsxs(Box, { children: [_jsxs(Text, { color: log.type === 'curate' ? colors.curateCommand : colors.queryCommand, children: ["[", log.type, "] "] }), _jsxs(Text, { color: colors.dimText, children: ["@", log.source ?? 'system'] }), _jsx(Text, { dimColor: true, children: " - [ctrl+o/esc] close | [\u2191\u2193/jk] scroll | [g/G] top/bottom" }), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", displayTime, "]"] })] }), _jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", height: availableHeight - 1, children: [_jsxs(ScrollView, { height: availableHeight - 2, ref: scrollViewRef, children: [_jsx(Box, { marginBottom: 1, paddingX: 1, children: _jsx(Text, { children: log.input }) }), _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [log.progress && (_jsx(ExecutionProgress, { isExpand: true, maxLines: Number.MAX_SAFE_INTEGER, progress: log.progress })), _jsx(ExecutionStatus, { status: log.status })] }), (log.status === 'failed' || log.status === 'completed') && (_jsx(Box, { paddingX: 1, children: _jsx(ExecutionContent, { bottomMargin: 0, content: log.content ?? '', isError: log.status === 'failed', isExpand: true, maxLines: Number.MAX_SAFE_INTEGER }) })), log.status === 'completed' && (_jsx(Box, { paddingX: 1, children: _jsx(ExecutionChanges, { created: log.changes.created, isExpand: true, maxChanges: {
72
+ created: Number.MAX_SAFE_INTEGER,
73
+ updated: Number.MAX_SAFE_INTEGER,
74
+ }, updated: log.changes.updated }) }))] }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: colors.dimText, children: hasMoreBelow ? '↓' : ' ' }) })] })] }));
75
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Expanded Message View Component
3
+ *
4
+ * Full-screen overlay displaying a single command message with scrollable output.
5
+ * Activated by Ctrl+O on a selected message, dismissed with Ctrl+O or Esc.
6
+ */
7
+ import React from 'react';
8
+ import type { CommandMessage } from '../../types.js';
9
+ interface ExpandedMessageViewProps {
10
+ /** Available height for the expanded view (in terminal rows) */
11
+ availableHeight: number;
12
+ /** Whether input handling is active */
13
+ isActive: boolean;
14
+ /** The message to display in expanded view */
15
+ message: CommandMessage;
16
+ /** Index of the message */
17
+ messageIndex: number;
18
+ /** Callback when the view should close */
19
+ onClose: () => void;
20
+ /** Render function for the message item */
21
+ renderMessageItem: (msg: CommandMessage, index: number, isExpanded?: boolean) => React.ReactNode;
22
+ }
23
+ export declare const ExpandedMessageView: React.FC<ExpandedMessageViewProps>;
24
+ export {};
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Expanded Message View Component
4
+ *
5
+ * Full-screen overlay displaying a single command message with scrollable output.
6
+ * Activated by Ctrl+O on a selected message, dismissed with Ctrl+O or Esc.
7
+ */
8
+ import { Box, Spacer, Text, useInput, useStdout } from 'ink';
9
+ import { ScrollView } from 'ink-scroll-view';
10
+ import { useEffect, useRef, useState } from 'react';
11
+ import { useTheme } from '../../hooks/index.js';
12
+ export const ExpandedMessageView = ({ availableHeight, isActive, message, messageIndex, onClose, renderMessageItem, }) => {
13
+ const { theme: { colors }, } = useTheme();
14
+ const { stdout } = useStdout();
15
+ const scrollViewRef = useRef(null);
16
+ const [hasMoreBelow, setHasMoreBelow] = useState(false);
17
+ const updateScrollIndicator = () => {
18
+ if (!scrollViewRef.current)
19
+ return;
20
+ const currentOffset = scrollViewRef.current.getScrollOffset();
21
+ const maxOffset = scrollViewRef.current.getBottomOffset();
22
+ setHasMoreBelow(currentOffset < maxOffset);
23
+ };
24
+ // Initial scroll position check
25
+ useEffect(() => {
26
+ // Delay to allow ScrollView to measure content
27
+ const timer = setTimeout(updateScrollIndicator, 50);
28
+ return () => clearTimeout(timer);
29
+ }, [message]);
30
+ // Terminal resize handling
31
+ useEffect(() => {
32
+ const handleResize = () => {
33
+ scrollViewRef.current?.remeasure();
34
+ updateScrollIndicator();
35
+ };
36
+ stdout?.on('resize', handleResize);
37
+ return () => {
38
+ stdout?.off('resize', handleResize);
39
+ };
40
+ }, [stdout]);
41
+ useInput((input, key) => {
42
+ if (!scrollViewRef.current)
43
+ return;
44
+ if ((key.ctrl && input === 'o') || key.escape) {
45
+ onClose();
46
+ }
47
+ if (key.upArrow || input === 'k') {
48
+ scrollViewRef.current.scrollBy(-1);
49
+ updateScrollIndicator();
50
+ }
51
+ if (key.downArrow || input === 'j') {
52
+ const currentOffset = scrollViewRef.current.getScrollOffset();
53
+ const maxOffset = scrollViewRef.current.getBottomOffset();
54
+ const newOffset = Math.min(currentOffset + 1, maxOffset);
55
+ scrollViewRef.current.scrollTo(newOffset);
56
+ updateScrollIndicator();
57
+ }
58
+ if (input === 'g') {
59
+ scrollViewRef.current.scrollTo(0);
60
+ updateScrollIndicator();
61
+ }
62
+ if (input === 'G') {
63
+ scrollViewRef.current.scrollTo(scrollViewRef.current.getBottomOffset());
64
+ updateScrollIndicator();
65
+ }
66
+ }, { isActive });
67
+ return (_jsxs(Box, { flexDirection: "column", height: "100%", paddingX: 2, width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "[ctrl+o/esc] close | [\u2191\u2193/jk] scroll | [g/G] top/bottom" }), _jsx(Spacer, {})] }), _jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", flexGrow: 1, height: availableHeight - 1, children: [_jsx(ScrollView, { height: availableHeight - 2, ref: scrollViewRef, children: renderMessageItem(message, messageIndex, true) }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: colors.dimText, children: hasMoreBelow ? "↓" : " " }) })] })] }));
68
+ };
@@ -8,4 +8,6 @@ export { ExecutionContent, truncateContent } from './execution-content.js';
8
8
  export { ExecutionInput } from './execution-input.js';
9
9
  export { ExecutionProgress } from './execution-progress.js';
10
10
  export { ExecutionStatus } from './execution-status.js';
11
+ export { ExpandedLogView } from './expanded-log-view.js';
12
+ export { ExpandedMessageView } from './expanded-message-view.js';
11
13
  export { LogItem } from './log-item.js';
@@ -8,4 +8,6 @@ export { ExecutionContent, truncateContent } from './execution-content.js';
8
8
  export { ExecutionInput } from './execution-input.js';
9
9
  export { ExecutionProgress } from './execution-progress.js';
10
10
  export { ExecutionStatus } from './execution-status.js';
11
+ export { ExpandedLogView } from './expanded-log-view.js';
12
+ export { ExpandedMessageView } from './expanded-message-view.js';
11
13
  export { LogItem } from './log-item.js';
@@ -9,6 +9,10 @@ import type { ActivityLog } from '../../types.js';
9
9
  interface LogItemProps {
10
10
  /** Dynamic heights based on terminal breakpoint */
11
11
  heights: MessageItemHeights;
12
+ /** Whether content should be fully expanded (no truncation) */
13
+ isExpand?: boolean;
14
+ /** Whether this log item is currently selected */
15
+ isSelected?: boolean;
12
16
  /** The activity log to display */
13
17
  log: ActivityLog;
14
18
  }
@@ -12,8 +12,8 @@ import { ExecutionContent } from './execution-content.js';
12
12
  import { ExecutionInput } from './execution-input.js';
13
13
  import { ExecutionProgress } from './execution-progress.js';
14
14
  import { ExecutionStatus } from './execution-status.js';
15
- export const LogItem = ({ heights, log }) => {
15
+ export const LogItem = ({ heights, isExpand, isSelected, log }) => {
16
16
  const { theme: { colors }, } = useTheme();
17
17
  const displayTime = formatTime(log.timestamp);
18
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { children: [_jsxs(Text, { color: log.type === 'curate' ? colors.curateCommand : colors.queryCommand, children: ["[", log.type, "] "] }), _jsxs(Text, { color: colors.dimText, children: ["@", log.source ?? 'system'] }), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", displayTime, "]"] })] }), _jsx(ExecutionInput, { input: log.input }), _jsxs(Box, { flexDirection: "column", children: [log.progress && _jsx(ExecutionProgress, { maxLines: heights.maxProgressItems, progress: log.progress }), _jsx(ExecutionStatus, { status: log.status })] }), (log.status === 'failed' || log.status === 'completed') && (_jsx(ExecutionContent, { bottomMargin: heights.contentBottomMargin, content: log.content ?? '', isError: log.status === 'failed', maxLines: heights.maxContentLines })), log.status === 'completed' && (_jsx(ExecutionChanges, { created: log.changes.created, maxChanges: heights.maxChanges, updated: log.changes.updated }))] }));
18
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { children: [_jsxs(Text, { color: log.type === 'curate' ? colors.curateCommand : colors.queryCommand, children: ["[", log.type, "] "] }), _jsxs(Text, { color: colors.dimText, children: ["@", log.source ?? 'system'] }), isSelected && (_jsxs(Text, { dimColor: true, italic: true, children: [" \u2190 [ctrl+o] to ", isExpand ? 'collapse' : 'expand'] })), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", displayTime, "]"] })] }), _jsx(ExecutionInput, { input: log.input, isExpand: isExpand }), _jsxs(Box, { flexDirection: "column", children: [log.progress && (_jsx(ExecutionProgress, { isExpand: isExpand, maxLines: heights.maxProgressItems, progress: log.progress })), _jsx(ExecutionStatus, { status: log.status })] }), (log.status === 'failed' || log.status === 'completed') && (_jsx(ExecutionContent, { bottomMargin: heights.contentBottomMargin, content: log.content ?? '', isError: log.status === 'failed', isExpand: isExpand, maxLines: heights.maxContentLines })), log.status === 'completed' && (_jsx(ExecutionChanges, { created: log.changes.created, isExpand: isExpand, maxChanges: heights.maxChanges, updated: log.changes.updated }))] }));
19
19
  };
@@ -13,5 +13,5 @@ export const Footer = () => {
13
13
  const { theme: { colors }, } = useTheme();
14
14
  // Filter out 'tab' shortcut during onboarding (tab switching is disabled)
15
15
  const visibleShortcuts = useMemo(() => (shouldShowOnboarding ? shortcuts.filter((s) => s.key !== 'tab') : shortcuts), [shortcuts, shouldShowOnboarding]);
16
- return (_jsx(Box, { gap: 1, paddingX: 1, width: "100%", children: visibleShortcuts.map((shortcut, index) => (_jsxs(Box, { children: [index > 0 && _jsx(Text, { color: colors.dimText, children: " \u2022 " }), _jsx(Text, { color: colors.text, children: shortcut.key }), _jsxs(Text, { color: colors.dimText, children: [" ", shortcut.description] })] }, shortcut.key))) }));
16
+ return (_jsx(Box, { paddingX: 1, width: "100%", children: visibleShortcuts.map((shortcut, index) => (_jsxs(Box, { children: [index > 0 && _jsx(Text, { color: colors.dimText, children: " \u2022 " }), _jsx(Text, { color: colors.text, children: shortcut.key }), _jsxs(Text, { color: colors.dimText, children: [" ", shortcut.description] })] }, shortcut.key))) }));
17
17
  };
@@ -3,13 +3,14 @@
3
3
  */
4
4
  export { CommandDetails } from './command-details.js';
5
5
  export { EnterPrompt } from './enter-prompt.js';
6
- export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, LogItem, truncateContent, } from './execution/index.js';
6
+ export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, ExpandedLogView, LogItem, truncateContent, } from './execution/index.js';
7
7
  export { Footer } from './footer.js';
8
8
  export { Header } from './header.js';
9
9
  export { Init } from './init.js';
10
10
  export type { InitProps } from './init.js';
11
11
  export { Logo } from './logo.js';
12
12
  export type { LogoVariant } from './logo.js';
13
+ export { Markdown } from './markdown.js';
13
14
  export { MessageItem } from './message-item.js';
14
15
  export { CopyablePrompt, OnboardingFlow, OnboardingStep, WelcomeBox } from './onboarding/index.js';
15
16
  export type { OnboardingStepType } from './onboarding/index.js';
@@ -3,11 +3,12 @@
3
3
  */
4
4
  export { CommandDetails } from './command-details.js';
5
5
  export { EnterPrompt } from './enter-prompt.js';
6
- export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, LogItem, truncateContent, } from './execution/index.js';
6
+ export { ExecutionChanges, ExecutionContent, ExecutionInput, ExecutionProgress, ExecutionStatus, ExpandedLogView, LogItem, truncateContent, } from './execution/index.js';
7
7
  export { Footer } from './footer.js';
8
8
  export { Header } from './header.js';
9
9
  export { Init } from './init.js';
10
10
  export { Logo } from './logo.js';
11
+ export { Markdown } from './markdown.js';
11
12
  export { MessageItem } from './message-item.js';
12
13
  export { CopyablePrompt, OnboardingFlow, OnboardingStep, WelcomeBox } from './onboarding/index.js';
13
14
  export { OutputLog } from './output-log.js';
@@ -237,17 +237,10 @@ export const Init = ({ active = true, autoStart = false, idleMessage = 'Your pro
237
237
  handleSelectResponse,
238
238
  maxSearchItems,
239
239
  ]);
240
- // Running state - show streaming output
241
- if (isRunningInit) {
242
- const { displayMessages } = getMessagesFromEnd(processedStreamingMessages, maxOutputLines);
243
- return (_jsx(Box, { flexDirection: "column", width: "100%", children: _jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", paddingX: 1, paddingY: 0, width: "100%", children: [displayMessages.map((streamMsg) => renderStreamingMessage(streamMsg)), renderActivePrompt()] }) }));
244
- }
245
240
  // Error state - show error with retry
246
241
  if (initError) {
247
242
  return (_jsxs(Box, { flexDirection: "column", rowGap: 1, children: [_jsxs(Text, { color: colors.errorText, children: ["Error: ", initError] }), _jsx(EnterPrompt, { action: "try again", active: active && !isRunningInit && !activePrompt, onEnter: runInit })] }));
248
243
  }
249
- if (autoStart) {
250
- return null;
251
- }
252
- return (_jsxs(Box, { flexDirection: "column", rowGap: 1, children: [showIdleMessage && _jsx(Text, { color: colors.text, children: idleMessage }), _jsx(EnterPrompt, { action: "initialize your project", active: active && !isRunningInit && !activePrompt, onEnter: runInit })] }));
244
+ const { displayMessages } = getMessagesFromEnd(processedStreamingMessages, maxOutputLines);
245
+ return (_jsxs(Box, { flexDirection: "column", rowGap: 1, children: [showIdleMessage && _jsx(Text, { color: colors.text, children: idleMessage }), displayMessages.length > 0 && (_jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", paddingX: 1, paddingY: 0, width: "100%", children: [displayMessages.map((streamMsg) => renderStreamingMessage(streamMsg)), renderActivePrompt()] })), !isRunningInit && displayMessages.length === 0 && (_jsx(EnterPrompt, { action: "initialize your project", active: active && !isRunningInit && !activePrompt, onEnter: runInit }))] }));
253
246
  };
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  */
9
9
  import { Box, Text, useStdout } from 'ink';
10
10
  import { useMemo } from 'react';
11
- import { useTheme } from '../hooks/index.js';
11
+ import { useIsLatestVersion, useTheme } from '../hooks/index.js';
12
12
  /**
13
13
  * Full ASCII logo "ByteRover" for large terminals
14
14
  */
@@ -83,6 +83,7 @@ function getLogoLines(variant, terminalWidth) {
83
83
  export const Logo = ({ compact, version }) => {
84
84
  const { stdout } = useStdout();
85
85
  const { theme: { colors }, } = useTheme();
86
+ const isLatestVersion = useIsLatestVersion(version ?? '');
86
87
  const terminalWidth = stdout?.columns ?? 80;
87
88
  const terminalHeight = stdout?.rows ?? 24;
88
89
  const variant = useMemo(() => (compact ? 'text' : selectLogoVariant(terminalWidth, terminalHeight)), [compact, terminalWidth, terminalHeight]);
@@ -90,9 +91,9 @@ export const Logo = ({ compact, version }) => {
90
91
  const headerLine = useMemo(() => (variant === 'full' && LOGO_FULL[0] ? getHeaderLine(LOGO_FULL[0], version ?? '', terminalWidth) : null), [variant, version, terminalWidth]);
91
92
  // Text-only logo for minimal terminals
92
93
  if (variant === 'text') {
93
- const textContent = MINI_LOGO + (version ? ` v${version}` : '');
94
+ const textContent = MINI_LOGO + (version ? ` v${version}` : '') + (isLatestVersion ? ' (latest)' : '');
94
95
  const padEnd = calculatePadEnd(textContent.length, terminalWidth);
95
- return (_jsxs(Box, { children: [_jsx(Text, { color: colors.logoDecor, children: PAD_START }), _jsxs(Text, { children: [_jsx(Text, { bold: true, color: colors.logoBold, children: MINI_LOGO }), version && _jsxs(Text, { color: colors.logoVersion, children: [" v", version] })] }), _jsx(Text, { color: colors.logoDecor, children: padEnd })] }));
96
+ return (_jsxs(Box, { children: [_jsx(Text, { color: colors.logoDecor, children: PAD_START }), _jsxs(Text, { children: [_jsx(Text, { bold: true, color: colors.logoBold, children: MINI_LOGO }), version && _jsxs(Text, { color: colors.logoVersion, children: [" v", version] }), isLatestVersion && _jsx(Text, { color: colors.logoVersion, children: " (latest)" })] }), _jsx(Text, { color: colors.logoDecor, children: padEnd })] }));
96
97
  }
97
98
  // ASCII logo with header line and version
98
99
  return (_jsxs(Box, { flexDirection: "column", children: [headerLine && (_jsxs(Box, { children: [_jsx(Text, { color: colors.logoDecor, children: headerLine.padStart }), _jsxs(Text, { children: [_jsx(Text, { children: headerLine.brv }), _jsx(Text, { children: headerLine.spaces }), _jsx(Text, { color: colors.logoVersion, children: headerLine.version })] }), _jsx(Text, { color: colors.logoDecor, children: headerLine.padEnd })] })), logoLines.map((line, index) => (_jsxs(Box, { children: [_jsx(Text, { color: colors.logoDecor, children: line.padStart }), _jsx(Text, { color: colors.logoBold, children: line.content }), _jsx(Text, { color: colors.logoDecor, children: line.padEnd })] }, index)))] }));
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Markdown Component
3
+ *
4
+ * Parses markdown strings using unified/remark-parse and renders them as Ink components.
5
+ * Supports headings, paragraphs, inline formatting (bold, italic, code), code blocks,
6
+ * lists (ordered/unordered), and links.
7
+ */
8
+ import React from 'react';
9
+ interface MarkdownProps {
10
+ children: string;
11
+ }
12
+ export declare const Markdown: ({ children }: MarkdownProps) => React.ReactElement;
13
+ export {};
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import remarkParse from 'remark-parse';
4
+ import { unified } from 'unified';
5
+ import { useTheme } from '../hooks/index.js';
6
+ const renderPhrasingContent = (nodes, theme) => nodes.map((node, index) => {
7
+ switch (node.type) {
8
+ case 'break': {
9
+ return _jsx(Text, { children: '\n' }, index);
10
+ }
11
+ case 'emphasis': {
12
+ return (_jsx(Text, { italic: true, children: renderPhrasingContent(node.children, theme) }, index));
13
+ }
14
+ case 'inlineCode': {
15
+ return (_jsx(Text, { backgroundColor: theme.colors.bg2, children: node.value }, index));
16
+ }
17
+ case 'link': {
18
+ return (_jsx(Text, { color: theme.colors.info, underline: true, children: renderPhrasingContent(node.children, theme) }, index));
19
+ }
20
+ case 'strong': {
21
+ return (_jsx(Text, { bold: true, children: renderPhrasingContent(node.children, theme) }, index));
22
+ }
23
+ case 'text': {
24
+ return _jsx(Text, { children: node.value }, index);
25
+ }
26
+ default: {
27
+ return null;
28
+ }
29
+ }
30
+ });
31
+ const renderListItem = (node, context, theme) => {
32
+ const bullet = context.ordered ? `${context.index + 1}. ` : '• ';
33
+ const hasNestedList = node.children.some((child) => child.type === 'list');
34
+ if (hasNestedList) {
35
+ return (_jsx(Box, { flexDirection: "column", children: node.children.map((child, childIndex) => {
36
+ if (child.type === 'paragraph') {
37
+ return (_jsxs(Box, { children: [_jsx(Text, { children: bullet }), _jsx(Text, { children: renderPhrasingContent(child.children, theme) })] }, childIndex));
38
+ }
39
+ if (child.type === 'list') {
40
+ return (_jsx(Box, { marginLeft: 2, children: renderList(child, theme) }, childIndex));
41
+ }
42
+ return renderNode(child, theme, childIndex);
43
+ }) }, context.index));
44
+ }
45
+ const content = node.children.map((child, childIndex) => {
46
+ if (child.type === 'paragraph') {
47
+ return _jsx(Text, { children: renderPhrasingContent(child.children, theme) }, childIndex);
48
+ }
49
+ return renderNode(child, theme, childIndex);
50
+ });
51
+ return (_jsxs(Box, { children: [_jsx(Text, { children: bullet }), content] }, context.index));
52
+ };
53
+ const renderList = (node, theme) => (_jsx(Box, { flexDirection: "column", children: node.children.map((item, index) => renderListItem(item, { index, ordered: node.ordered ?? false }, theme)) }));
54
+ const renderNode = (node, theme, key) => {
55
+ switch (node.type) {
56
+ case 'blockquote': {
57
+ const blockquote = node;
58
+ return (_jsx(Box, { marginBottom: 1, children: _jsx(Box, { borderBottom: false, borderLeft: true, borderLeftColor: theme.colors.dimText, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", children: blockquote.children.map((child, index) => renderNode(child, theme, index)) }) }, key));
59
+ }
60
+ case 'code': {
61
+ const code = node;
62
+ const langLabel = code.lang ? `[${code.lang}]` : '';
63
+ return (_jsxs(Box, { backgroundColor: theme.colors.bg2, flexDirection: "column", marginY: 1, paddingX: 1, children: [langLabel && _jsx(Text, { color: theme.colors.dimText, children: langLabel }), _jsx(Text, { children: code.value })] }, key));
64
+ }
65
+ case 'heading': {
66
+ const heading = node;
67
+ return (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, color: theme.colors.primary, children: ['#'.repeat(heading.depth), " ", renderPhrasingContent(heading.children, theme)] }) }, key));
68
+ }
69
+ case 'list': {
70
+ return (_jsx(Box, { marginBottom: 1, children: renderList(node, theme) }, key));
71
+ }
72
+ case 'paragraph': {
73
+ return (_jsx(Box, { children: _jsx(Text, { children: renderPhrasingContent(node.children, theme) }) }, key));
74
+ }
75
+ case 'thematicBreak': {
76
+ return (_jsx(Box, { marginY: 1, children: _jsx(Text, { color: theme.colors.dimText, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }) }, key));
77
+ }
78
+ default: {
79
+ return null;
80
+ }
81
+ }
82
+ };
83
+ const renderChildren = (children, theme) => children.map((child, index) => renderNode(child, theme, index));
84
+ export const Markdown = ({ children }) => {
85
+ const { theme } = useTheme();
86
+ const tree = unified().use(remarkParse).parse(children);
87
+ return _jsx(Box, { flexDirection: "column", children: renderChildren(tree.children, theme) });
88
+ };
@@ -8,5 +8,5 @@ import { Box, Text } from 'ink';
8
8
  import { useTheme } from '../hooks/index.js';
9
9
  export const MessageItem = ({ message }) => {
10
10
  const { theme: { colors } } = useTheme();
11
- return (_jsxs(Box, { alignItems: "flex-start", flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Box, { borderBottom: false, borderLeftColor: colors.primary, borderRight: false, borderStyle: "bold", borderTop: false }), _jsx(Text, { children: message.fromCommand })] }), _jsx(Box, { borderColor: colors.border, borderStyle: "single", width: "100%", children: _jsx(Text, { children: message.content }) })] }));
11
+ return (_jsxs(Box, { alignItems: "flex-start", flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Box, { borderBottom: false, borderLeftColor: colors.primary, borderRight: false, borderStyle: "bold", borderTop: false }), _jsx(Text, { children: message.fromCommand })] }), _jsx(Box, { borderColor: colors.border, borderStyle: "single", width: "100%", children: _jsx(Text, { children: message.content }) })] }));
12
12
  };
@@ -16,8 +16,8 @@ import { Init } from '../init.js';
16
16
  import { CopyablePrompt } from './copyable-prompt.js';
17
17
  import { OnboardingStep } from './onboarding-step.js';
18
18
  /** Example prompts for curate and query steps */
19
- const CURATE_PROMPT = 'run `brv curate "Auth uses JWT with 24h expiry. Tokens stored in httpOnly cookies"`';
20
- const QUERY_PROMPT = 'run `brv query "How is authentication implemented?"`';
19
+ const CURATE_PROMPT = 'Save our API authentication patterns, use brv curate';
20
+ const QUERY_PROMPT = 'How do we handle error responses?, use brv query';
21
21
  /** Minimum output lines to show before truncation */
22
22
  const MIN_OUTPUT_LINES = 3;
23
23
  /** Get step number for display */
@@ -37,11 +37,10 @@ function getStepNumber(step) {
37
37
  }
38
38
  }
39
39
  }
40
- // eslint-disable-next-line complexity -- React component renders multiple onboarding steps with conditional logic
41
40
  export const OnboardingFlow = ({ availableHeight, onInitComplete }) => {
42
- const { theme: { colors } } = useTheme();
41
+ const { theme: { colors }, } = useTheme();
43
42
  const { mode } = useMode();
44
- const { completeOnboarding, curateAcknowledged, currentStep, hasCurated, hasQueried, queryAcknowledged, setCurateAcknowledged, setQueryAcknowledged, totalSteps, } = useOnboarding();
43
+ const { completeOnboarding, curateAcknowledged, currentStep, hasCurated, hasQueried, initAcknowledged, isInitialized, queryAcknowledged, setCurateAcknowledged, setInitAcknowledged, setQueryAcknowledged, totalSteps, } = useOnboarding();
45
44
  const { logs } = useActivityLogs();
46
45
  const { messageItem } = useUIHeights();
47
46
  // Find running or queued curate/query logs
@@ -49,8 +48,8 @@ export const OnboardingFlow = ({ availableHeight, onInitComplete }) => {
49
48
  const queryLog = useMemo(() => logs.find((log) => log.type === 'query'), [logs]);
50
49
  // Onboarding UI overhead: step title (1) + description (1) + content margin top (1)
51
50
  const onboardingOverhead = 3;
52
- const enterPromptHeight = ((currentStep === 'curate' && hasCurated && !curateAcknowledged) ||
53
- (currentStep === 'query' && hasQueried && !queryAcknowledged))
51
+ const enterPromptHeight = (currentStep === 'curate' && hasCurated && !curateAcknowledged) ||
52
+ (currentStep === 'query' && hasQueried && !queryAcknowledged)
54
53
  ? 4
55
54
  : 0;
56
55
  const activeLog = currentStep === 'curate' ? curateLog : currentStep === 'query' ? queryLog : null;
@@ -70,6 +69,10 @@ export const OnboardingFlow = ({ availableHeight, onInitComplete }) => {
70
69
  completeOnboarding(true); // Pass true to indicate skipped
71
70
  }
72
71
  }, { isActive: isInWaitingState });
72
+ const renderInitContent = () => (_jsxs(Box, { flexDirection: "column", gap: 1, width: "100%", children: [_jsx(Init, { active: mode === 'activity' && currentStep === 'init', maxOutputLines: 10, showIdleMessage: false }), isInitialized && !initAcknowledged && (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { children: "Project initialized successfully!" }), _jsx(EnterPrompt, { action: "continue", active: isInitialized && !initAcknowledged && mode === 'activity' && currentStep === 'init', onEnter: () => {
73
+ setInitAcknowledged(true);
74
+ onInitComplete?.();
75
+ } })] }))] }));
73
76
  // Render curate step content
74
77
  const renderCurateContent = () => {
75
78
  // Show execution progress if curate is running
@@ -77,7 +80,7 @@ export const OnboardingFlow = ({ availableHeight, onInitComplete }) => {
77
80
  return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [_jsx(LogItem, { heights: { ...messageItem, maxContentLines: maxOutputLines }, log: curateLog }), hasCurated && !curateAcknowledged && (_jsx(EnterPrompt, { action: "continue", active: mode === 'activity' && currentStep === 'curate', onEnter: () => setCurateAcknowledged(true) }))] }));
78
81
  }
79
82
  // Show copyable prompt when waiting
80
- return (_jsxs(Box, { backgroundColor: colors.bg2, flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Text, { color: colors.text, wrap: "wrap", children: "Try saying this to your AI Agent:" }), _jsx(Box, { marginBottom: 1, paddingLeft: 4, children: _jsx(Text, { color: colors.primary, wrap: "wrap", children: CURATE_PROMPT }) }), _jsxs(Text, { children: [_jsx(CopyablePrompt, { buttonLabel: '[ctrl+y] to copy', isActive: mode === 'activity' && currentStep === 'curate', textToCopy: CURATE_PROMPT }), _jsx(Text, { color: colors.dimText, children: " | [Esc] to skip onboarding" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.dimText, children: "Waiting for curate..." }) })] }));
83
+ return (_jsxs(Box, { backgroundColor: colors.bg2, flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Text, { color: colors.text, wrap: "wrap", children: "Try saying this to your AI Agent:" }), _jsx(Box, { marginBottom: 1, paddingLeft: 4, children: _jsx(Text, { color: colors.primary, wrap: "wrap", children: CURATE_PROMPT }) }), _jsxs(Text, { children: [_jsx(CopyablePrompt, { buttonLabel: "[ctrl+y] to copy", isActive: mode === 'activity' && currentStep === 'curate', textToCopy: CURATE_PROMPT }), _jsx(Text, { color: colors.dimText, children: " | [Esc] to skip onboarding" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.dimText, children: "Waiting for curate..." }) })] }));
81
84
  };
82
85
  // Render query step content
83
86
  const renderQueryContent = () => {
@@ -86,9 +89,9 @@ export const OnboardingFlow = ({ availableHeight, onInitComplete }) => {
86
89
  return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [_jsx(LogItem, { heights: { ...messageItem, maxContentLines: maxOutputLines }, log: queryLog }), hasQueried && !queryAcknowledged && (_jsx(EnterPrompt, { action: "continue", active: mode === 'activity' && currentStep === 'query', onEnter: () => setQueryAcknowledged(true) }))] }));
87
90
  }
88
91
  // Show copyable prompt when waiting
89
- return (_jsxs(Box, { backgroundColor: colors.bg2, flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Text, { color: colors.text, wrap: "wrap", children: "You can now query your memory:" }), _jsx(Box, { marginBottom: 1, paddingLeft: 4, children: _jsx(Text, { color: colors.primary, wrap: "wrap", children: QUERY_PROMPT }) }), _jsxs(Text, { children: [_jsx(CopyablePrompt, { buttonLabel: '[ctrl+y] to copy', isActive: mode === 'activity' && currentStep === 'query', textToCopy: QUERY_PROMPT }), _jsx(Text, { color: colors.dimText, children: " | [Esc] to skip onboarding" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.dimText, children: "Waiting for query..." }) })] }));
92
+ return (_jsxs(Box, { backgroundColor: colors.bg2, flexDirection: "column", padding: 1, width: "100%", children: [_jsx(Text, { color: colors.text, wrap: "wrap", children: "You can now query your memory:" }), _jsx(Box, { marginBottom: 1, paddingLeft: 4, children: _jsx(Text, { color: colors.primary, wrap: "wrap", children: QUERY_PROMPT }) }), _jsxs(Text, { children: [_jsx(CopyablePrompt, { buttonLabel: "[ctrl+y] to copy", isActive: mode === 'activity' && currentStep === 'query', textToCopy: QUERY_PROMPT }), _jsx(Text, { color: colors.dimText, children: " | [Esc] to skip onboarding" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.dimText, children: "Waiting for query..." }) })] }));
90
93
  };
91
94
  // Render complete step content
92
- const renderCompleteContent = () => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.dimText, wrap: "wrap", children: "Activity logs will appear here as you use brv curate and brv query." }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.dimText, children: "Tips:" }), _jsx(Text, { color: colors.dimText, children: "- Press [Tab] to switch to commands view" }), _jsx(Text, { color: colors.dimText, children: "- Use /push to sync your context to the cloud" }), _jsx(Text, { color: colors.dimText, children: "- Use /gen-rules to generate agent rules" }), _jsx(Text, { color: colors.dimText, children: "- Type / for available commands" })] }), _jsx(EnterPrompt, { action: "finish onboarding", active: mode === 'activity' && currentStep === 'complete', onEnter: completeOnboarding })] }));
93
- return (_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", height: availableHeight, width: "100%", children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [currentStep === 'init' && (_jsx(OnboardingStep, { description: "Let's get your project set up with ByteRover.", stepNumber: getStepNumber('init'), title: "Welcome to ByteRover!", totalSteps: totalSteps, children: _jsx(Init, { active: mode === 'activity' && currentStep === 'init', maxOutputLines: MIN_OUTPUT_LINES, onInitComplete: onInitComplete, showIdleMessage: false }) })), currentStep === 'curate' && (_jsx(OnboardingStep, { description: "Great! Now let's add some context to your knowledge base.", stepNumber: getStepNumber('curate'), title: "Add Your First Context", totalSteps: totalSteps, children: renderCurateContent() })), currentStep === 'query' && (_jsx(OnboardingStep, { description: "Excellent! Your context is saved. Let's query it.", stepNumber: getStepNumber('query'), title: "Query Your Knowledge", totalSteps: totalSteps, children: renderQueryContent() })), currentStep === 'complete' && (_jsx(OnboardingStep, { description: "Your ByteRover workspace is ready!", showStepIndicator: false, stepNumber: totalSteps, title: "You're All Set!", totalSteps: totalSteps, children: renderCompleteContent() }))] }) }));
95
+ const renderCompleteContent = () => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.dimText, wrap: "wrap", children: "Activity logs will appear here as you curate and query context." }), _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.dimText, children: "Tips:" }), _jsx(Text, { color: colors.dimText, children: "- Press [Tab] to switch to commands view" }), _jsx(Text, { color: colors.dimText, children: "- Use /push to sync your context to the cloud" }), _jsx(Text, { color: colors.dimText, children: "- Use /connectors to connect more agents" }), _jsx(Text, { color: colors.dimText, children: "- Type / for available commands" })] }), _jsx(EnterPrompt, { action: "finish onboarding", active: mode === 'activity' && currentStep === 'complete', onEnter: completeOnboarding })] }));
96
+ return (_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", height: availableHeight, width: "100%", children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [currentStep === 'init' && (_jsx(OnboardingStep, { description: "Let's get your project set up with ByteRover.", stepNumber: getStepNumber('init'), title: "Welcome to ByteRover!", totalSteps: totalSteps, children: renderInitContent() })), currentStep === 'curate' && (_jsx(OnboardingStep, { description: "Great! Now let's add some context to your knowledge base.", stepNumber: getStepNumber('curate'), title: "Add Your First Context", totalSteps: totalSteps, children: renderCurateContent() })), currentStep === 'query' && (_jsx(OnboardingStep, { description: "Excellent! Your context is saved. Let's query it.", stepNumber: getStepNumber('query'), title: "Query Your Knowledge", totalSteps: totalSteps, children: renderQueryContent() })), currentStep === 'complete' && (_jsx(OnboardingStep, { description: "Your ByteRover workspace is ready!", showStepIndicator: false, stepNumber: totalSteps, title: "You're All Set!", totalSteps: totalSteps, children: renderCompleteContent() }))] }) }));
94
97
  };
@@ -19,5 +19,5 @@ export const WelcomeBox = ({ isCopyActive }) => {
19
19
  const randomIndex = Math.floor(Math.random() * welcomeExamplePrompts.length);
20
20
  return welcomeExamplePrompts[randomIndex];
21
21
  }, []);
22
- return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.dimText, children: "@agent" }), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", formatTime(new Date()), "]"] })] }), _jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", gap: 1, paddingX: 1, children: [_jsx(Text, { bold: true, color: colors.primary, children: "Welcome to ByteRover!" }), _jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [_jsxs(Text, { color: colors.text, children: ["Tell your AI Agent what to save or retrieve. Just start your prompt with \"", _jsx(Text, { color: colors.primary, children: "brv" }), "\"."] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, children: "Try saying this to your AI Agent:" }) }), _jsx(Box, { flexDirection: "column", paddingLeft: 4, children: _jsxs(Text, { color: colors.text, children: [randomPrompt, ' ', _jsx(CopyablePrompt, { buttonLabel: "[ctrl+y] to copy", isActive: isCopyActive, textToCopy: randomPrompt })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, dimColor: true, children: "Press [Tab] to switch to Console mode" }) })] })] })] }));
22
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, width: "100%", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.dimText, children: "@agent" }), _jsx(Spacer, {}), _jsxs(Text, { color: colors.dimText, children: ["[", formatTime(new Date()), "]"] })] }), _jsxs(Box, { borderColor: colors.border, borderStyle: "single", flexDirection: "column", gap: 1, paddingX: 1, children: [_jsx(Text, { bold: true, color: colors.primary, children: "Welcome to ByteRover!" }), _jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [_jsxs(Text, { color: colors.text, children: ["Tell your AI Agent what to save or retrieve. Just include \"", _jsx(Text, { color: colors.primary, children: "brv" }), "\" in your prompt."] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, children: "Try saying this to your AI Agent:" }) }), _jsx(Box, { flexDirection: "column", paddingLeft: 4, children: _jsxs(Text, { color: colors.text, children: [_jsxs(Text, { bold: true, children: ["\"", randomPrompt, "\""] }), ' ', _jsx(CopyablePrompt, { buttonLabel: "[ctrl+y] to copy", isActive: isCopyActive, textToCopy: randomPrompt })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, dimColor: true, children: "Press [Tab] to switch to Console mode" }) })] })] })] }));
23
23
  };
@@ -13,7 +13,7 @@ import { useMode } from '../contexts/mode-context.js';
13
13
  import { useTheme } from '../contexts/theme-context.js';
14
14
  import { useSlashCompletion } from '../hooks/index.js';
15
15
  import { CommandDetails } from './command-details.js';
16
- const MAX_VISIBLE_ITEMS = 5;
16
+ const MAX_VISIBLE_ITEMS = 7;
17
17
  export const Suggestions = ({ input, onInsert, onSelect }) => {
18
18
  const { theme: { colors }, } = useTheme();
19
19
  const { mode, setMode } = useMode();
@@ -140,7 +140,7 @@ export const Suggestions = ({ input, onInsert, onSelect }) => {
140
140
  // Don't show when user is typing arguments for a known command
141
141
  if (suggestions.length === 0) {
142
142
  if (isCommandAttempt && !hasMatchedCommand && input.trim().length > 1) {
143
- return (_jsx(Box, { borderColor: colors.border, borderStyle: "single", height: 7, paddingX: 1, children: _jsx(Text, { color: colors.dimText, children: "No commands found" }) }));
143
+ return (_jsx(Box, { borderColor: colors.border, borderStyle: "single", height: 9, paddingX: 1, children: _jsx(Text, { color: colors.dimText, children: "No commands found" }) }));
144
144
  }
145
145
  return null;
146
146
  }
@@ -152,7 +152,7 @@ export const Suggestions = ({ input, onInsert, onSelect }) => {
152
152
  // Calculate if there are more items above/below
153
153
  const hasMoreAbove = windowStart > 0;
154
154
  const hasMoreBelow = windowStart + visibleSuggestions.length < suggestions.length;
155
- return (_jsxs(Box, { borderColor: colors.border, borderStyle: "single", columnGap: 1, height: 7, paddingX: 1, children: [_jsxs(Box, { flexDirection: 'column', flexShrink: 0, children: [hasMoreAbove && (_jsxs(Text, { color: colors.dimText, dimColor: true, children: ["\u2191 ", windowStart, " more"] })), visibleSuggestions.map((suggestion, index) => {
155
+ return (_jsxs(Box, { borderColor: colors.border, borderStyle: "single", columnGap: 1, height: 9, overflowY: 'hidden', paddingX: 1, children: [_jsxs(Box, { flexDirection: 'column', flexShrink: 0, children: [hasMoreAbove && (_jsxs(Text, { color: colors.dimText, dimColor: true, children: ["\u2191 ", windowStart, " more"] })), visibleSuggestions.map((suggestion, index) => {
156
156
  const actualIndex = windowStart + index;
157
157
  const isActive = actualIndex === activeIndex;
158
158
  return (_jsx(Box, { children: _jsxs(Text, { backgroundColor: isActive ? colors.dimText : undefined, color: colors.text, children: [isActive ? '❯ ' : ' ', suggestion.label.padEnd(labelWidth)] }) }, suggestion.value));