@kirosnn/mosaic 0.0.7

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 (154) hide show
  1. package/.mosaic/mosaic.local.jsonc +0 -0
  2. package/MOSAIC.md +188 -0
  3. package/README.md +127 -0
  4. package/docs/mosaic.png +0 -0
  5. package/package.json +42 -0
  6. package/src/agent/Agent.ts +131 -0
  7. package/src/agent/context.ts +96 -0
  8. package/src/agent/index.ts +2 -0
  9. package/src/agent/prompts/systemPrompt.ts +138 -0
  10. package/src/agent/prompts/toolsPrompt.ts +139 -0
  11. package/src/agent/provider/anthropic.ts +122 -0
  12. package/src/agent/provider/google.ts +124 -0
  13. package/src/agent/provider/mistral.ts +117 -0
  14. package/src/agent/provider/ollama.ts +531 -0
  15. package/src/agent/provider/openai.ts +220 -0
  16. package/src/agent/provider/xai.ts +122 -0
  17. package/src/agent/tools/bash.ts +20 -0
  18. package/src/agent/tools/definitions.ts +27 -0
  19. package/src/agent/tools/edit.ts +23 -0
  20. package/src/agent/tools/executor.ts +751 -0
  21. package/src/agent/tools/explore.ts +18 -0
  22. package/src/agent/tools/exploreExecutor.ts +320 -0
  23. package/src/agent/tools/glob.ts +16 -0
  24. package/src/agent/tools/grep.ts +19 -0
  25. package/src/agent/tools/index.ts +4 -0
  26. package/src/agent/tools/list.ts +20 -0
  27. package/src/agent/tools/question.ts +20 -0
  28. package/src/agent/tools/read.ts +15 -0
  29. package/src/agent/tools/write.ts +21 -0
  30. package/src/agent/types.ts +155 -0
  31. package/src/components/App.tsx +174 -0
  32. package/src/components/CommandsModal.tsx +77 -0
  33. package/src/components/CustomInput.tsx +328 -0
  34. package/src/components/Main.tsx +1112 -0
  35. package/src/components/Notification.tsx +91 -0
  36. package/src/components/SelectList.tsx +47 -0
  37. package/src/components/Setup.tsx +528 -0
  38. package/src/components/ShortcutsModal.tsx +67 -0
  39. package/src/components/Welcome.tsx +39 -0
  40. package/src/components/main/ApprovalPanel.tsx +134 -0
  41. package/src/components/main/ChatPage.tsx +516 -0
  42. package/src/components/main/HomePage.tsx +111 -0
  43. package/src/components/main/QuestionPanel.tsx +85 -0
  44. package/src/components/main/ThinkingIndicator.tsx +101 -0
  45. package/src/components/main/types.ts +55 -0
  46. package/src/components/main/wrapText.ts +41 -0
  47. package/src/index.tsx +212 -0
  48. package/src/utils/approvalBridge.ts +129 -0
  49. package/src/utils/commands/echo.ts +22 -0
  50. package/src/utils/commands/help.ts +25 -0
  51. package/src/utils/commands/index.ts +68 -0
  52. package/src/utils/commands/init.ts +68 -0
  53. package/src/utils/commands/redo.ts +74 -0
  54. package/src/utils/commands/registry.ts +29 -0
  55. package/src/utils/commands/sessions.ts +129 -0
  56. package/src/utils/commands/types.ts +20 -0
  57. package/src/utils/commands/undo.ts +75 -0
  58. package/src/utils/commands/web.ts +77 -0
  59. package/src/utils/config.ts +357 -0
  60. package/src/utils/diff.ts +201 -0
  61. package/src/utils/diffRendering.tsx +62 -0
  62. package/src/utils/exploreBridge.ts +87 -0
  63. package/src/utils/fileChangeTracker.ts +98 -0
  64. package/src/utils/fileChangesBridge.ts +18 -0
  65. package/src/utils/history.ts +106 -0
  66. package/src/utils/markdown.tsx +232 -0
  67. package/src/utils/models.ts +304 -0
  68. package/src/utils/questionBridge.ts +122 -0
  69. package/src/utils/terminalUtils.ts +25 -0
  70. package/src/utils/toolFormatting.ts +384 -0
  71. package/src/utils/undoRedo.ts +429 -0
  72. package/src/utils/undoRedoBridge.ts +45 -0
  73. package/src/utils/undoRedoDb.ts +338 -0
  74. package/src/utils/uninstall.ts +45 -0
  75. package/src/utils/version.ts +3 -0
  76. package/src/web/app.tsx +606 -0
  77. package/src/web/assets/css/ChatPage.css +212 -0
  78. package/src/web/assets/css/FileExplorer.css +202 -0
  79. package/src/web/assets/css/HomePage.css +119 -0
  80. package/src/web/assets/css/Markdown.css +178 -0
  81. package/src/web/assets/css/MessageItem.css +160 -0
  82. package/src/web/assets/css/Sidebar.css +208 -0
  83. package/src/web/assets/css/SidebarModal.css +137 -0
  84. package/src/web/assets/css/ThinkingIndicator.css +47 -0
  85. package/src/web/assets/css/ToolMessage.css +148 -0
  86. package/src/web/assets/css/global.css +226 -0
  87. package/src/web/assets/fonts/Geist-Black.woff2 +0 -0
  88. package/src/web/assets/fonts/Geist-BlackItalic.woff2 +0 -0
  89. package/src/web/assets/fonts/Geist-Bold.woff2 +0 -0
  90. package/src/web/assets/fonts/Geist-BoldItalic.woff2 +0 -0
  91. package/src/web/assets/fonts/Geist-ExtraBold.woff2 +0 -0
  92. package/src/web/assets/fonts/Geist-ExtraBoldItalic.woff2 +0 -0
  93. package/src/web/assets/fonts/Geist-ExtraLight.woff2 +0 -0
  94. package/src/web/assets/fonts/Geist-ExtraLightItalic.woff2 +0 -0
  95. package/src/web/assets/fonts/Geist-Italic[wght].woff2 +0 -0
  96. package/src/web/assets/fonts/Geist-Light.woff2 +0 -0
  97. package/src/web/assets/fonts/Geist-LightItalic.woff2 +0 -0
  98. package/src/web/assets/fonts/Geist-Medium.woff2 +0 -0
  99. package/src/web/assets/fonts/Geist-MediumItalic.woff2 +0 -0
  100. package/src/web/assets/fonts/Geist-Regular.woff2 +0 -0
  101. package/src/web/assets/fonts/Geist-RegularItalic.woff2 +0 -0
  102. package/src/web/assets/fonts/Geist-SemiBold.woff2 +0 -0
  103. package/src/web/assets/fonts/Geist-SemiBoldItalic.woff2 +0 -0
  104. package/src/web/assets/fonts/Geist-Thin.woff2 +0 -0
  105. package/src/web/assets/fonts/Geist-ThinItalic.woff2 +0 -0
  106. package/src/web/assets/fonts/GeistMono-Black.woff2 +0 -0
  107. package/src/web/assets/fonts/GeistMono-BlackItalic.woff2 +0 -0
  108. package/src/web/assets/fonts/GeistMono-Bold.woff2 +0 -0
  109. package/src/web/assets/fonts/GeistMono-BoldItalic.woff2 +0 -0
  110. package/src/web/assets/fonts/GeistMono-ExtraBold.woff2 +0 -0
  111. package/src/web/assets/fonts/GeistMono-ExtraBoldItalic.woff2 +0 -0
  112. package/src/web/assets/fonts/GeistMono-ExtraLight.woff2 +0 -0
  113. package/src/web/assets/fonts/GeistMono-ExtraLightItalic.woff2 +0 -0
  114. package/src/web/assets/fonts/GeistMono-Italic.woff2 +0 -0
  115. package/src/web/assets/fonts/GeistMono-Italic[wght].woff2 +0 -0
  116. package/src/web/assets/fonts/GeistMono-Light.woff2 +0 -0
  117. package/src/web/assets/fonts/GeistMono-LightItalic.woff2 +0 -0
  118. package/src/web/assets/fonts/GeistMono-Medium.woff2 +0 -0
  119. package/src/web/assets/fonts/GeistMono-MediumItalic.woff2 +0 -0
  120. package/src/web/assets/fonts/GeistMono-Regular.woff2 +0 -0
  121. package/src/web/assets/fonts/GeistMono-SemiBold.woff2 +0 -0
  122. package/src/web/assets/fonts/GeistMono-SemiBoldItalic.woff2 +0 -0
  123. package/src/web/assets/fonts/GeistMono-Thin.woff2 +0 -0
  124. package/src/web/assets/fonts/GeistMono-ThinItalic.woff2 +0 -0
  125. package/src/web/assets/fonts/GeistMono[wght].woff2 +0 -0
  126. package/src/web/assets/fonts/Geist[wght].woff2 +0 -0
  127. package/src/web/assets/fonts/blauer-nue-regular.woff2 +0 -0
  128. package/src/web/assets/fonts/neue-montreal-regular.woff2 +0 -0
  129. package/src/web/assets/images/favicon-v2.svg +6 -0
  130. package/src/web/assets/images/favicon.png +0 -0
  131. package/src/web/assets/images/foruse.svg +5 -0
  132. package/src/web/assets/images/logo_black.svg +5 -0
  133. package/src/web/assets/images/logo_white.svg +5 -0
  134. package/src/web/assets/images/logoblack.png +0 -0
  135. package/src/web/assets/images/logowhite.png +0 -0
  136. package/src/web/build.ts +23 -0
  137. package/src/web/components/ApprovalPanel.tsx +191 -0
  138. package/src/web/components/ChatPage.tsx +273 -0
  139. package/src/web/components/FileExplorer.tsx +162 -0
  140. package/src/web/components/HomePage.tsx +121 -0
  141. package/src/web/components/MessageItem.tsx +178 -0
  142. package/src/web/components/Modal.tsx +30 -0
  143. package/src/web/components/QuestionPanel.tsx +149 -0
  144. package/src/web/components/Setup.tsx +211 -0
  145. package/src/web/components/Sidebar.tsx +292 -0
  146. package/src/web/components/ThinkingIndicator.tsx +85 -0
  147. package/src/web/logo_black.svg +5 -0
  148. package/src/web/logo_white.svg +5 -0
  149. package/src/web/router.ts +46 -0
  150. package/src/web/server.tsx +662 -0
  151. package/src/web/storage.ts +92 -0
  152. package/src/web/types.ts +17 -0
  153. package/src/web/utils.ts +61 -0
  154. package/tsconfig.json +33 -0
@@ -0,0 +1,67 @@
1
+ import { TextAttributes } from "@opentui/core";
2
+
3
+ interface ShortcutItem {
4
+ keys: string;
5
+ description: string;
6
+ }
7
+
8
+ interface ShortcutsModalProps {
9
+ activeTab: 0 | 1;
10
+ }
11
+
12
+ export function ShortcutsModal({ activeTab }: ShortcutsModalProps) {
13
+ const shortcutsGeneral: ShortcutItem[] = [
14
+ { keys: "Ctrl+P / Alt+P", description: "Open/close this shortcuts panel" },
15
+ { keys: "Alt+V (or Ctrl+V)", description: "Paste from clipboard into the focused input" },
16
+ { keys: "Enter", description: "Confirm / submit" },
17
+ { keys: "↑/↓ (or j/k)", description: "Navigate lists" },
18
+ { keys: "PageUp/PageDown", description: "Scroll chat faster" },
19
+ ];
20
+
21
+ const shortcutsSetup: ShortcutItem[] = [
22
+ { keys: "Esc", description: "Go back to the previous step" },
23
+ { keys: "Enter", description: "Confirm / next step" },
24
+ { keys: "Y / N", description: "Answer Yes/No questions" },
25
+ { keys: "↑/↓ (or j/k)", description: "Navigate provider/model lists" },
26
+ { keys: "Alt+V (or Ctrl+V)", description: "Paste API key into the focused input" },
27
+ ];
28
+
29
+ const shortcuts = activeTab === 0 ? shortcutsGeneral : shortcutsSetup;
30
+
31
+ return (
32
+ <box position="absolute" top={0} left={0} right={0} bottom={0} backgroundColor={"#0c0c0c"}>
33
+ <box width="100%" height="100%" justifyContent="center" alignItems="center">
34
+ <box flexDirection="column" width="80%" height="80%" backgroundColor="#1a1a1a" padding={2}>
35
+ <box marginBottom={1} flexDirection="row" justifyContent="space-between" width="100%">
36
+ <text attributes={TextAttributes.BOLD}>Keyboard shortcuts</text>
37
+ <text attributes={TextAttributes.DIM}>Esc to close the page</text>
38
+ </box>
39
+
40
+ <box marginBottom={1} flexDirection="row" width="100%">
41
+ <box paddingLeft={1} paddingRight={1} backgroundColor={activeTab === 0 ? '#2a2a2a' : 'transparent'}>
42
+ <text fg={activeTab === 0 ? "#ffca38" : undefined} attributes={activeTab === 0 ? TextAttributes.BOLD : TextAttributes.DIM}>F1 General</text>
43
+ </box>
44
+ <box marginLeft={1} paddingLeft={1} paddingRight={1} backgroundColor={activeTab === 1 ? '#2a2a2a' : 'transparent'}>
45
+ <text fg={activeTab === 1 ? "#ffca38" : undefined} attributes={activeTab === 1 ? TextAttributes.BOLD : TextAttributes.DIM}>F2 Setup</text>
46
+ </box>
47
+ </box>
48
+
49
+ <box flexDirection="column" width="100%" flexGrow={1}>
50
+ <box flexDirection="column" width="100%" overflow="scroll">
51
+ {shortcuts.map((s, idx) => (
52
+ <box key={idx} flexDirection="row" width="100%" marginBottom={1}>
53
+ <box width={22}>
54
+ <text fg="#ffca38" attributes={TextAttributes.BOLD}>{s.keys}</text>
55
+ </box>
56
+ <box flexGrow={1} minWidth={0}>
57
+ <text attributes={TextAttributes.DIM}>{s.description}</text>
58
+ </box>
59
+ </box>
60
+ ))}
61
+ </box>
62
+ </box>
63
+ </box>
64
+ </box>
65
+ </box>
66
+ );
67
+ }
@@ -0,0 +1,39 @@
1
+ import { TextAttributes } from "@opentui/core";
2
+ import { useKeyboard } from "@opentui/react";
3
+ import { VERSION } from "../utils/version";
4
+
5
+ interface WelcomeProps {
6
+ onComplete: () => void;
7
+ isFirstRun: boolean;
8
+ shortcutsOpen?: boolean;
9
+ commandsOpen?: boolean;
10
+ }
11
+
12
+ export function Welcome({ onComplete, isFirstRun, shortcutsOpen = false, commandsOpen = false }: WelcomeProps) {
13
+ useKeyboard((key) => {
14
+ if (shortcutsOpen) return;
15
+ if (key.name === 'return') {
16
+ onComplete();
17
+ }
18
+ });
19
+
20
+ return (
21
+ <box width="100%" height="100%" justifyContent="center" alignItems="center">
22
+ <box flexDirection="row">
23
+ <box flexDirection="column" alignItems="center" marginBottom={2}>
24
+ <text fg="#ffca38" attributes={TextAttributes.BOLD}>███╗ ███╗</text>
25
+ <text fg="#ffca38" attributes={TextAttributes.BOLD}>████╗ ████║</text>
26
+ <text fg="#ffca38" attributes={TextAttributes.BOLD}>███╔████╔███║</text>
27
+ </box>
28
+ <box flexDirection="column" alignItems="flex-start" marginLeft={2}>
29
+ <text attributes={TextAttributes.DIM}>Mosaic welcomes you !</text>
30
+ <text attributes={TextAttributes.DIM}>Mosaic CLI v{VERSION}</text>
31
+ <text attributes={TextAttributes.DIM}>Now are you ready to {isFirstRun ? 'configure' : 'use'} it ?</text>
32
+ </box>
33
+ </box>
34
+ <box marginTop={1}>
35
+ <text attributes={TextAttributes.DIM}>Press Enter to continue...</text>
36
+ </box>
37
+ </box>
38
+ );
39
+ }
@@ -0,0 +1,134 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { TextAttributes } from '@opentui/core';
3
+ import { useKeyboard } from '@opentui/react';
4
+ import { CustomInput } from '../CustomInput';
5
+ import type { ApprovalRequest } from '../../utils/approvalBridge';
6
+ import { renderDiffLine } from '../../utils/diffRendering';
7
+
8
+ interface ApprovalPanelProps {
9
+ request: ApprovalRequest;
10
+ disabled?: boolean;
11
+ onRespond: (approved: boolean, customResponse?: string) => void;
12
+ }
13
+
14
+ export function ApprovalPanel({ request, disabled = false, onRespond }: ApprovalPanelProps) {
15
+ const [selectedIndex, setSelectedIndex] = useState(0);
16
+ const [scrollOffset, setScrollOffset] = useState(0);
17
+ const allOptions = ['Yes', 'No'];
18
+
19
+ useEffect(() => {
20
+ setSelectedIndex(0);
21
+ setScrollOffset(0);
22
+ }, [request.id]);
23
+
24
+ const previewLines = request.preview.content.split('\n');
25
+ const maxVisiblePreviewLines = 15;
26
+ const canScroll = previewLines.length > maxVisiblePreviewLines;
27
+
28
+ useKeyboard((key) => {
29
+ if (disabled) return;
30
+
31
+ if ((key.name === 'up' || key.name === 'k') && key.shift && canScroll) {
32
+ setScrollOffset(prev => Math.max(0, prev - 1));
33
+ return;
34
+ }
35
+
36
+ if ((key.name === 'down' || key.name === 'j') && key.shift && canScroll) {
37
+ setScrollOffset(prev => Math.min(previewLines.length - maxVisiblePreviewLines, prev + 1));
38
+ return;
39
+ }
40
+
41
+ if (key.name === 'up' || key.name === 'k') {
42
+ setSelectedIndex(prev => (prev === 0 ? allOptions.length - 1 : prev - 1));
43
+ return;
44
+ }
45
+
46
+ if (key.name === 'down' || key.name === 'j') {
47
+ setSelectedIndex(prev => (prev === allOptions.length - 1 ? 0 : prev + 1));
48
+ return;
49
+ }
50
+
51
+ if (key.name === 'return') {
52
+ if (selectedIndex === 0) {
53
+ onRespond(true);
54
+ } else {
55
+ onRespond(false);
56
+ }
57
+ return;
58
+ }
59
+ });
60
+
61
+ const handleCustomSubmit = (text: string) => {
62
+ if (!text || !text.trim()) {
63
+ return;
64
+ }
65
+ onRespond(false, text);
66
+ };
67
+
68
+
69
+ const titleMatch = request.preview.title.match(/^(.+?)\s*\((.+)\)$/);
70
+ const toolName = titleMatch ? titleMatch[1] : request.preview.title;
71
+ const toolInfo = titleMatch ? titleMatch[2] : null;
72
+
73
+ return (
74
+ <box flexDirection="column" width="100%" backgroundColor="#1a1a1a" paddingLeft={1} paddingRight={1} paddingTop={1} paddingBottom={1}>
75
+ <box flexDirection="row" marginBottom={1}>
76
+ <text fg={"#ffffff"}>{toolName}</text>
77
+ {toolInfo && (
78
+ <>
79
+ <text fg={"#ffffff"}> </text>
80
+ <text fg={"#ffffff"} attributes={TextAttributes.DIM}>({toolInfo})</text>
81
+ </>
82
+ )}
83
+ </box>
84
+
85
+ <box
86
+ flexDirection="column"
87
+ marginBottom={1}
88
+ paddingLeft={1}
89
+ paddingRight={1}
90
+ paddingBottom={1}
91
+ >
92
+ {previewLines.slice(scrollOffset, scrollOffset + maxVisiblePreviewLines).map((line, displayIndex) => {
93
+ const index = scrollOffset + displayIndex;
94
+ return renderDiffLine(line, `preview-line-${index}`);
95
+ })}
96
+ {canScroll && (
97
+ <text fg="#808080" attributes={TextAttributes.DIM}>
98
+ {scrollOffset > 0 ? '↑ ' : ' '}
99
+ Line {scrollOffset + 1}-{Math.min(scrollOffset + maxVisiblePreviewLines, previewLines.length)} of {previewLines.length}
100
+ {scrollOffset + maxVisiblePreviewLines < previewLines.length ? ' ↓' : ''}
101
+ {' (Shift+↑/↓ to scroll)'}
102
+ </text>
103
+ )}
104
+ </box>
105
+
106
+ <box flexDirection="column">
107
+ {allOptions.map((option, index) => {
108
+ const selected = index === selectedIndex;
109
+
110
+ return (
111
+ <box
112
+ key={`${request.id}-${index}`}
113
+ flexDirection="row"
114
+ backgroundColor='transparent'
115
+ paddingLeft={1}
116
+ paddingRight={1}
117
+ >
118
+ <text
119
+ fg='white'
120
+ attributes={selected ? TextAttributes.NONE : TextAttributes.DIM}
121
+ >
122
+ {selected ? '> ' : ' '}{option}
123
+ </text>
124
+ </box>
125
+ );
126
+ })}
127
+ </box>
128
+
129
+ <box flexDirection="row" paddingLeft={1} >
130
+ <CustomInput onSubmit={handleCustomSubmit} placeholder="> Tell Mosaic what to do instead and press Enter" focused={!disabled} disableHistory={true} />
131
+ </box>
132
+ </box>
133
+ );
134
+ }