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.
- package/README.md +19 -13
- package/dist/commands/hook-prompt-submit.d.ts +27 -0
- package/dist/commands/hook-prompt-submit.js +39 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +61 -0
- package/dist/commands/status.js +8 -3
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/domain/cipher/agent-events/types.d.ts +44 -1
- package/dist/core/domain/cipher/tools/constants.d.ts +1 -0
- package/dist/core/domain/cipher/tools/constants.js +1 -0
- package/dist/core/domain/entities/agent.d.ts +16 -0
- package/dist/core/domain/entities/agent.js +78 -0
- package/dist/core/domain/entities/connector-type.d.ts +10 -0
- package/dist/core/domain/entities/connector-type.js +9 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +2 -0
- package/dist/core/domain/errors/task-error.d.ts +4 -0
- package/dist/core/domain/errors/task-error.js +7 -0
- package/dist/core/domain/transport/schemas.d.ts +40 -0
- package/dist/core/domain/transport/schemas.js +28 -0
- package/dist/core/interfaces/connectors/connector-types.d.ts +70 -0
- package/dist/core/interfaces/connectors/i-connector-manager.d.ts +72 -0
- package/dist/core/interfaces/connectors/i-connector-manager.js +1 -0
- package/dist/core/interfaces/connectors/i-connector.d.ts +54 -0
- package/dist/core/interfaces/connectors/i-connector.js +1 -0
- package/dist/core/interfaces/i-file-service.d.ts +7 -0
- package/dist/core/interfaces/i-mcp-config-writer.d.ts +40 -0
- package/dist/core/interfaces/i-mcp-config-writer.js +1 -0
- package/dist/core/interfaces/i-rule-template-service.d.ts +4 -2
- package/dist/core/interfaces/transport/i-transport-client.d.ts +7 -0
- package/dist/core/interfaces/usecase/i-connectors-use-case.d.ts +3 -0
- package/dist/core/interfaces/usecase/i-connectors-use-case.js +1 -0
- package/dist/hooks/init/update-notifier.d.ts +1 -0
- package/dist/hooks/init/update-notifier.js +10 -1
- package/dist/infra/cipher/agent/cipher-agent.d.ts +8 -0
- package/dist/infra/cipher/agent/cipher-agent.js +16 -0
- package/dist/infra/cipher/file-system/binary-utils.d.ts +7 -12
- package/dist/infra/cipher/file-system/binary-utils.js +46 -31
- package/dist/infra/cipher/llm/context/context-manager.d.ts +10 -2
- package/dist/infra/cipher/llm/context/context-manager.js +39 -2
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +48 -9
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +4 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +40 -12
- package/dist/infra/cipher/session/chat-session.d.ts +3 -0
- package/dist/infra/cipher/session/chat-session.js +7 -1
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.d.ts +6 -7
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +57 -18
- package/dist/infra/cipher/tools/implementations/curate-tool.d.ts +1 -8
- package/dist/infra/cipher/tools/implementations/curate-tool.js +380 -24
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +38 -17
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.d.ts +7 -0
- package/dist/infra/cipher/tools/implementations/search-knowledge-tool.js +303 -0
- package/dist/infra/cipher/tools/index.d.ts +1 -0
- package/dist/infra/cipher/tools/index.js +1 -0
- package/dist/infra/cipher/tools/tool-manager.js +1 -0
- package/dist/infra/cipher/tools/tool-registry.js +7 -0
- package/dist/infra/connectors/connector-manager.d.ts +32 -0
- package/dist/infra/connectors/connector-manager.js +158 -0
- package/dist/infra/connectors/hook/hook-connector-config.d.ts +52 -0
- package/dist/infra/connectors/hook/hook-connector-config.js +41 -0
- package/dist/infra/connectors/hook/hook-connector.d.ts +46 -0
- package/dist/infra/connectors/hook/hook-connector.js +231 -0
- package/dist/infra/connectors/mcp/index.d.ts +4 -0
- package/dist/infra/connectors/mcp/index.js +4 -0
- package/dist/infra/connectors/mcp/json-mcp-config-writer.d.ts +26 -0
- package/dist/infra/connectors/mcp/json-mcp-config-writer.js +71 -0
- package/dist/infra/connectors/mcp/mcp-connector-config.d.ts +229 -0
- package/dist/infra/connectors/mcp/mcp-connector-config.js +173 -0
- package/dist/infra/connectors/mcp/mcp-connector.d.ts +80 -0
- package/dist/infra/connectors/mcp/mcp-connector.js +324 -0
- package/dist/infra/connectors/mcp/toml-mcp-config-writer.d.ts +45 -0
- package/dist/infra/connectors/mcp/toml-mcp-config-writer.js +134 -0
- package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.d.ts +2 -2
- package/dist/infra/{rule → connectors/rules}/legacy-rule-detector.js +1 -1
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +95 -0
- package/dist/infra/{rule/agent-rule-config.js → connectors/rules/rules-connector-config.js} +10 -10
- package/dist/infra/connectors/rules/rules-connector.d.ts +34 -0
- package/dist/infra/connectors/rules/rules-connector.js +139 -0
- package/dist/infra/connectors/shared/rule-file-manager.d.ts +72 -0
- package/dist/infra/connectors/shared/rule-file-manager.js +119 -0
- package/dist/infra/connectors/shared/template-service.d.ts +27 -0
- package/dist/infra/connectors/shared/template-service.js +125 -0
- package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +5 -2
- package/dist/infra/context-tree/file-context-tree-writer-service.js +20 -5
- package/dist/infra/core/executors/curate-executor.d.ts +2 -2
- package/dist/infra/core/executors/curate-executor.js +7 -7
- package/dist/infra/core/executors/query-executor.d.ts +12 -0
- package/dist/infra/core/executors/query-executor.js +62 -1
- package/dist/infra/file/fs-file-service.d.ts +7 -0
- package/dist/infra/file/fs-file-service.js +15 -1
- package/dist/infra/mcp/index.d.ts +2 -0
- package/dist/infra/mcp/index.js +2 -0
- package/dist/infra/mcp/mcp-server.d.ts +58 -0
- package/dist/infra/mcp/mcp-server.js +178 -0
- package/dist/infra/mcp/tools/brv-curate-tool.d.ts +23 -0
- package/dist/infra/mcp/tools/brv-curate-tool.js +68 -0
- package/dist/infra/mcp/tools/brv-query-tool.d.ts +17 -0
- package/dist/infra/mcp/tools/brv-query-tool.js +68 -0
- package/dist/infra/mcp/tools/index.d.ts +3 -0
- package/dist/infra/mcp/tools/index.js +3 -0
- package/dist/infra/mcp/tools/task-result-waiter.d.ts +30 -0
- package/dist/infra/mcp/tools/task-result-waiter.js +56 -0
- package/dist/infra/process/agent-worker.d.ts +2 -2
- package/dist/infra/process/agent-worker.js +663 -142
- package/dist/infra/process/constants.d.ts +1 -1
- package/dist/infra/process/constants.js +1 -1
- package/dist/infra/process/ipc-types.d.ts +17 -4
- package/dist/infra/process/ipc-types.js +3 -3
- package/dist/infra/process/parent-heartbeat.d.ts +47 -0
- package/dist/infra/process/parent-heartbeat.js +118 -0
- package/dist/infra/process/process-manager.d.ts +79 -0
- package/dist/infra/process/process-manager.js +277 -3
- package/dist/infra/process/task-queue-manager.d.ts +13 -0
- package/dist/infra/process/task-queue-manager.js +19 -0
- package/dist/infra/process/transport-handlers.d.ts +3 -0
- package/dist/infra/process/transport-handlers.js +51 -5
- package/dist/infra/process/transport-worker.js +9 -69
- package/dist/infra/repl/commands/connectors-command.d.ts +8 -0
- package/dist/infra/repl/commands/{gen-rules-command.js → connectors-command.js} +21 -10
- package/dist/infra/repl/commands/curate-command.js +2 -2
- package/dist/infra/repl/commands/index.js +3 -2
- package/dist/infra/repl/commands/init-command.js +11 -7
- package/dist/infra/repl/commands/query-command.js +22 -2
- package/dist/infra/repl/commands/reset-command.js +1 -1
- package/dist/infra/transport/socket-io-transport-client.d.ts +75 -0
- package/dist/infra/transport/socket-io-transport-client.js +308 -7
- package/dist/infra/transport/socket-io-transport-server.js +4 -0
- package/dist/infra/usecase/connectors-use-case.d.ts +63 -0
- package/dist/infra/usecase/connectors-use-case.js +222 -0
- package/dist/infra/usecase/init-use-case.d.ts +8 -43
- package/dist/infra/usecase/init-use-case.js +27 -252
- package/dist/infra/usecase/logout-use-case.js +1 -1
- package/dist/infra/usecase/pull-use-case.js +5 -5
- package/dist/infra/usecase/push-use-case.js +4 -4
- package/dist/infra/usecase/reset-use-case.js +3 -4
- package/dist/infra/usecase/space-list-use-case.js +3 -3
- package/dist/infra/usecase/space-switch-use-case.js +3 -3
- package/dist/infra/usecase/status-use-case.d.ts +10 -0
- package/dist/infra/usecase/status-use-case.js +53 -0
- package/dist/resources/prompts/curate.yml +114 -4
- package/dist/resources/prompts/explore.yml +34 -0
- package/dist/resources/prompts/query-orchestrator.yml +112 -0
- package/dist/resources/prompts/system-prompt.yml +12 -2
- package/dist/resources/tools/search_knowledge.txt +32 -0
- package/dist/templates/mcp-base.md +1 -0
- package/dist/templates/sections/brv-instructions.md +98 -0
- package/dist/templates/sections/mcp-workflow.md +13 -0
- package/dist/tui/app.js +4 -1
- package/dist/tui/components/command-details.js +1 -1
- package/dist/tui/components/execution/execution-changes.d.ts +2 -0
- package/dist/tui/components/execution/execution-changes.js +5 -1
- package/dist/tui/components/execution/execution-content.d.ts +2 -0
- package/dist/tui/components/execution/execution-content.js +8 -18
- package/dist/tui/components/execution/execution-input.d.ts +2 -0
- package/dist/tui/components/execution/execution-input.js +6 -4
- package/dist/tui/components/execution/execution-progress.d.ts +2 -0
- package/dist/tui/components/execution/execution-progress.js +6 -2
- package/dist/tui/components/execution/expanded-log-view.d.ts +20 -0
- package/dist/tui/components/execution/expanded-log-view.js +75 -0
- package/dist/tui/components/execution/expanded-message-view.d.ts +24 -0
- package/dist/tui/components/execution/expanded-message-view.js +68 -0
- package/dist/tui/components/execution/index.d.ts +2 -0
- package/dist/tui/components/execution/index.js +2 -0
- package/dist/tui/components/execution/log-item.d.ts +4 -0
- package/dist/tui/components/execution/log-item.js +2 -2
- package/dist/tui/components/footer.js +1 -1
- package/dist/tui/components/index.d.ts +2 -1
- package/dist/tui/components/index.js +2 -1
- package/dist/tui/components/init.js +2 -9
- package/dist/tui/components/logo.js +4 -3
- package/dist/tui/components/markdown.d.ts +13 -0
- package/dist/tui/components/markdown.js +88 -0
- package/dist/tui/components/message-item.js +1 -1
- package/dist/tui/components/onboarding/onboarding-flow.js +14 -11
- package/dist/tui/components/onboarding/welcome-box.js +1 -1
- package/dist/tui/components/suggestions.js +3 -3
- package/dist/tui/contexts/mode-context.js +6 -2
- package/dist/tui/contexts/onboarding-context.d.ts +4 -0
- package/dist/tui/contexts/onboarding-context.js +14 -2
- package/dist/tui/hooks/index.d.ts +1 -0
- package/dist/tui/hooks/index.js +1 -0
- package/dist/tui/hooks/use-is-latest-version.d.ts +6 -0
- package/dist/tui/hooks/use-is-latest-version.js +22 -0
- package/dist/tui/views/command-view.d.ts +1 -1
- package/dist/tui/views/command-view.js +87 -98
- package/dist/tui/views/logs-view.d.ts +8 -0
- package/dist/tui/views/logs-view.js +55 -27
- package/dist/utils/file-validator.d.ts +1 -1
- package/dist/utils/file-validator.js +25 -28
- package/dist/utils/type-guards.d.ts +5 -0
- package/dist/utils/type-guards.js +7 -0
- package/oclif.manifest.json +55 -4
- package/package.json +12 -1
- package/dist/core/interfaces/usecase/i-generate-rules-use-case.d.ts +0 -3
- package/dist/infra/repl/commands/gen-rules-command.d.ts +0 -7
- package/dist/infra/rule/agent-rule-config.d.ts +0 -19
- package/dist/infra/rule/rule-template-service.d.ts +0 -18
- package/dist/infra/rule/rule-template-service.js +0 -88
- package/dist/infra/usecase/generate-rules-use-case.d.ts +0 -61
- package/dist/infra/usecase/generate-rules-use-case.js +0 -285
- /package/dist/core/interfaces/{usecase/i-generate-rules-use-case.js → connectors/connector-types.js} +0 -0
- /package/dist/infra/{rule → connectors/shared}/constants.d.ts +0 -0
- /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 {
|
|
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, {
|
|
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
|
-
|
|
250
|
-
|
|
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 = '
|
|
20
|
-
const QUERY_PROMPT = '
|
|
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 = (
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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:
|
|
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:
|
|
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));
|