snow-ai 0.1.12 → 0.2.2

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 (97) hide show
  1. package/dist/api/chat.d.ts +65 -2
  2. package/dist/api/chat.js +299 -16
  3. package/dist/api/responses.d.ts +52 -0
  4. package/dist/api/responses.js +541 -0
  5. package/dist/api/systemPrompt.d.ts +4 -0
  6. package/dist/api/systemPrompt.js +43 -0
  7. package/dist/app.js +15 -4
  8. package/dist/cli.js +38 -2
  9. package/dist/hooks/useConversation.d.ts +32 -0
  10. package/dist/hooks/useConversation.js +403 -0
  11. package/dist/hooks/useGlobalNavigation.d.ts +6 -0
  12. package/dist/hooks/useGlobalNavigation.js +15 -0
  13. package/dist/hooks/useSessionManagement.d.ts +10 -0
  14. package/dist/hooks/useSessionManagement.js +43 -0
  15. package/dist/hooks/useSessionSave.d.ts +8 -0
  16. package/dist/hooks/useSessionSave.js +52 -0
  17. package/dist/hooks/useToolConfirmation.d.ts +18 -0
  18. package/dist/hooks/useToolConfirmation.js +49 -0
  19. package/dist/mcp/bash.d.ts +57 -0
  20. package/dist/mcp/bash.js +138 -0
  21. package/dist/mcp/filesystem.d.ts +307 -0
  22. package/dist/mcp/filesystem.js +520 -0
  23. package/dist/mcp/todo.d.ts +55 -0
  24. package/dist/mcp/todo.js +329 -0
  25. package/dist/test/logger-test.d.ts +1 -0
  26. package/dist/test/logger-test.js +7 -0
  27. package/dist/types/index.d.ts +1 -1
  28. package/dist/ui/components/ChatInput.d.ts +15 -2
  29. package/dist/ui/components/ChatInput.js +445 -59
  30. package/dist/ui/components/CommandPanel.d.ts +2 -2
  31. package/dist/ui/components/CommandPanel.js +11 -7
  32. package/dist/ui/components/DiffViewer.d.ts +9 -0
  33. package/dist/ui/components/DiffViewer.js +93 -0
  34. package/dist/ui/components/FileList.d.ts +14 -0
  35. package/dist/ui/components/FileList.js +131 -0
  36. package/dist/ui/components/MCPInfoPanel.d.ts +2 -0
  37. package/dist/ui/components/MCPInfoPanel.js +74 -0
  38. package/dist/ui/components/MCPInfoScreen.d.ts +7 -0
  39. package/dist/ui/components/MCPInfoScreen.js +27 -0
  40. package/dist/ui/components/MarkdownRenderer.d.ts +7 -0
  41. package/dist/ui/components/MarkdownRenderer.js +110 -0
  42. package/dist/ui/components/Menu.d.ts +5 -2
  43. package/dist/ui/components/Menu.js +60 -9
  44. package/dist/ui/components/MessageList.d.ts +30 -2
  45. package/dist/ui/components/MessageList.js +64 -12
  46. package/dist/ui/components/PendingMessages.js +1 -1
  47. package/dist/ui/components/ScrollableSelectInput.d.ts +29 -0
  48. package/dist/ui/components/ScrollableSelectInput.js +157 -0
  49. package/dist/ui/components/SessionListScreen.d.ts +7 -0
  50. package/dist/ui/components/SessionListScreen.js +196 -0
  51. package/dist/ui/components/SessionListScreenWrapper.d.ts +7 -0
  52. package/dist/ui/components/SessionListScreenWrapper.js +14 -0
  53. package/dist/ui/components/TodoTree.d.ts +15 -0
  54. package/dist/ui/components/TodoTree.js +60 -0
  55. package/dist/ui/components/ToolConfirmation.d.ts +8 -0
  56. package/dist/ui/components/ToolConfirmation.js +38 -0
  57. package/dist/ui/components/ToolResultPreview.d.ts +12 -0
  58. package/dist/ui/components/ToolResultPreview.js +115 -0
  59. package/dist/ui/pages/ChatScreen.d.ts +4 -0
  60. package/dist/ui/pages/ChatScreen.js +385 -196
  61. package/dist/ui/pages/MCPConfigScreen.d.ts +6 -0
  62. package/dist/ui/pages/MCPConfigScreen.js +55 -0
  63. package/dist/ui/pages/ModelConfigScreen.js +73 -12
  64. package/dist/ui/pages/WelcomeScreen.js +17 -11
  65. package/dist/utils/apiConfig.d.ts +12 -0
  66. package/dist/utils/apiConfig.js +95 -9
  67. package/dist/utils/commandExecutor.d.ts +2 -1
  68. package/dist/utils/commands/init.d.ts +2 -0
  69. package/dist/utils/commands/init.js +93 -0
  70. package/dist/utils/commands/mcp.d.ts +2 -0
  71. package/dist/utils/commands/mcp.js +12 -0
  72. package/dist/utils/commands/resume.d.ts +2 -0
  73. package/dist/utils/commands/resume.js +12 -0
  74. package/dist/utils/commands/yolo.d.ts +2 -0
  75. package/dist/utils/commands/yolo.js +12 -0
  76. package/dist/utils/fileUtils.d.ts +44 -0
  77. package/dist/utils/fileUtils.js +222 -0
  78. package/dist/utils/index.d.ts +4 -0
  79. package/dist/utils/index.js +6 -0
  80. package/dist/utils/logger.d.ts +31 -0
  81. package/dist/utils/logger.js +97 -0
  82. package/dist/utils/mcpToolsManager.d.ts +47 -0
  83. package/dist/utils/mcpToolsManager.js +476 -0
  84. package/dist/utils/messageFormatter.d.ts +12 -0
  85. package/dist/utils/messageFormatter.js +32 -0
  86. package/dist/utils/sessionConverter.d.ts +6 -0
  87. package/dist/utils/sessionConverter.js +61 -0
  88. package/dist/utils/sessionManager.d.ts +39 -0
  89. package/dist/utils/sessionManager.js +141 -0
  90. package/dist/utils/textBuffer.d.ts +36 -7
  91. package/dist/utils/textBuffer.js +265 -179
  92. package/dist/utils/todoPreprocessor.d.ts +5 -0
  93. package/dist/utils/todoPreprocessor.js +19 -0
  94. package/dist/utils/toolExecutor.d.ts +21 -0
  95. package/dist/utils/toolExecutor.js +28 -0
  96. package/package.json +12 -3
  97. package/readme.md +2 -2
@@ -0,0 +1,222 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ /**
5
+ * Get line count for a file
6
+ */
7
+ export function getFileLineCount(filePath) {
8
+ return new Promise((resolve) => {
9
+ try {
10
+ if (!fs.existsSync(filePath)) {
11
+ resolve(0);
12
+ return;
13
+ }
14
+ const content = fs.readFileSync(filePath, 'utf-8');
15
+ const lines = content.split('\n').length;
16
+ resolve(lines);
17
+ }
18
+ catch (error) {
19
+ resolve(0);
20
+ }
21
+ });
22
+ }
23
+ /**
24
+ * Check if file is an image based on extension
25
+ */
26
+ function isImageFile(filePath) {
27
+ const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'];
28
+ const ext = path.extname(filePath).toLowerCase();
29
+ return imageExtensions.includes(ext);
30
+ }
31
+ /**
32
+ * Get MIME type from file extension
33
+ */
34
+ function getMimeType(filePath) {
35
+ const ext = path.extname(filePath).toLowerCase();
36
+ const mimeTypes = {
37
+ '.png': 'image/png',
38
+ '.jpg': 'image/jpeg',
39
+ '.jpeg': 'image/jpeg',
40
+ '.gif': 'image/gif',
41
+ '.webp': 'image/webp',
42
+ '.bmp': 'image/bmp',
43
+ '.svg': 'image/svg+xml'
44
+ };
45
+ return mimeTypes[ext] || 'application/octet-stream';
46
+ }
47
+ /**
48
+ * Get file information including line count
49
+ */
50
+ export async function getFileInfo(filePath) {
51
+ try {
52
+ // Try multiple path resolutions in order of preference
53
+ const pathsToTry = [
54
+ filePath, // Original path as provided
55
+ path.resolve(process.cwd(), filePath), // Relative to current working directory
56
+ path.resolve(filePath), // Absolute resolution
57
+ ];
58
+ // Remove duplicates while preserving order
59
+ const uniquePaths = [...new Set(pathsToTry)];
60
+ let actualPath = filePath;
61
+ let exists = false;
62
+ // Try each path until we find one that exists
63
+ for (const tryPath of uniquePaths) {
64
+ if (fs.existsSync(tryPath)) {
65
+ actualPath = tryPath;
66
+ exists = true;
67
+ break;
68
+ }
69
+ }
70
+ // Check if it's an image file
71
+ const isImage = isImageFile(actualPath);
72
+ let imageData;
73
+ let mimeType;
74
+ let lineCount = 0;
75
+ if (exists) {
76
+ if (isImage) {
77
+ // Read image as base64
78
+ const buffer = fs.readFileSync(actualPath);
79
+ const base64 = buffer.toString('base64');
80
+ mimeType = getMimeType(actualPath);
81
+ imageData = `data:${mimeType};base64,${base64}`;
82
+ }
83
+ else {
84
+ lineCount = await getFileLineCount(actualPath);
85
+ }
86
+ }
87
+ return {
88
+ path: filePath, // Keep original path for display
89
+ lineCount,
90
+ exists,
91
+ isImage,
92
+ imageData,
93
+ mimeType
94
+ };
95
+ }
96
+ catch (error) {
97
+ return {
98
+ path: filePath,
99
+ lineCount: 0,
100
+ exists: false
101
+ };
102
+ }
103
+ }
104
+ /**
105
+ * Format file tree display for messages
106
+ */
107
+ export function formatFileTree(files) {
108
+ if (files.length === 0)
109
+ return '';
110
+ return files.map(file => `└─ Read \`${file.path}\`${file.exists ? ` (total line ${file.lineCount})` : ' (file not found)'}`).join('\n');
111
+ }
112
+ /**
113
+ * Parse @file references from message content and check if they exist
114
+ * Also supports direct file paths (pasted from VSCode drag & drop)
115
+ */
116
+ export async function parseAndValidateFileReferences(content) {
117
+ const foundFiles = [];
118
+ // Pattern 1: @file references (e.g., @path/to/file.ts)
119
+ const atFileRegex = /@([A-Za-z0-9\-._/\\:]+\.[a-zA-Z]+)(?=\s|$)/g;
120
+ let match;
121
+ while ((match = atFileRegex.exec(content)) !== null) {
122
+ if (match[1]) {
123
+ foundFiles.push(match[1]);
124
+ }
125
+ }
126
+ // Pattern 2: Direct absolute/relative paths (e.g., c:\Users\...\file.ts or ./src/file.ts)
127
+ // Match paths that look like file paths with extensions, but NOT @-prefixed ones
128
+ const directPathRegex = /(?<!@)(?:^|\s)((?:[a-zA-Z]:[\\\/]|\.{1,2}[\\\/]|[\\\/])(?:[A-Za-z0-9\-._/\\:()[\] ]+)\.[a-zA-Z]+)(?=\s|$)/g;
129
+ while ((match = directPathRegex.exec(content)) !== null) {
130
+ if (match[1]) {
131
+ const trimmedPath = match[1].trim();
132
+ // Only add if it looks like a real file path
133
+ if (trimmedPath && !foundFiles.includes(trimmedPath)) {
134
+ foundFiles.push(trimmedPath);
135
+ }
136
+ }
137
+ }
138
+ // Remove duplicates
139
+ const uniqueFiles = [...new Set(foundFiles)];
140
+ // Check which files actually exist
141
+ const fileInfos = await Promise.all(uniqueFiles.map(async (filePath) => {
142
+ const info = await getFileInfo(filePath);
143
+ return info;
144
+ }));
145
+ // Filter only existing files
146
+ const validFiles = fileInfos.filter(file => file.exists);
147
+ // Clean content - keep paths as user typed them
148
+ const cleanContent = content;
149
+ return {
150
+ cleanContent,
151
+ validFiles
152
+ };
153
+ }
154
+ /**
155
+ * Create message with file read instructions for AI
156
+ */
157
+ export function createMessageWithFileInstructions(content, files, systemInfo) {
158
+ const parts = [content];
159
+ // Add system info if provided
160
+ if (systemInfo) {
161
+ const systemInfoLines = [
162
+ `└─ Platform: ${systemInfo.platform}`,
163
+ `└─ Shell: ${systemInfo.shell}`,
164
+ `└─ Working Directory: ${systemInfo.workingDirectory}`
165
+ ];
166
+ parts.push(systemInfoLines.join('\n'));
167
+ }
168
+ // Add file instructions if provided
169
+ if (files.length > 0) {
170
+ const fileInstructions = files
171
+ .map(f => `└─ Read \`${f.path}\` (total line ${f.lineCount})`)
172
+ .join('\n');
173
+ parts.push(fileInstructions);
174
+ }
175
+ return parts.join('\n');
176
+ }
177
+ /**
178
+ * Get system information (OS, shell, working directory)
179
+ */
180
+ export function getSystemInfo() {
181
+ // Get OS platform
182
+ const platform = (() => {
183
+ const platformType = os.platform();
184
+ switch (platformType) {
185
+ case 'win32':
186
+ return 'Windows';
187
+ case 'darwin':
188
+ return 'macOS';
189
+ case 'linux':
190
+ return 'Linux';
191
+ default:
192
+ return platformType;
193
+ }
194
+ })();
195
+ // Get shell type
196
+ const shell = (() => {
197
+ const shellPath = process.env['SHELL'] || process.env['ComSpec'] || '';
198
+ const shellName = path.basename(shellPath).toLowerCase();
199
+ if (shellName.includes('cmd'))
200
+ return 'cmd.exe';
201
+ if (shellName.includes('powershell'))
202
+ return 'PowerShell';
203
+ if (shellName.includes('pwsh'))
204
+ return 'PowerShell';
205
+ if (shellName.includes('zsh'))
206
+ return 'zsh';
207
+ if (shellName.includes('bash'))
208
+ return 'bash';
209
+ if (shellName.includes('fish'))
210
+ return 'fish';
211
+ if (shellName.includes('sh'))
212
+ return 'sh';
213
+ return shellName || 'shell';
214
+ })();
215
+ // Get working directory
216
+ const workingDirectory = process.cwd();
217
+ return {
218
+ platform,
219
+ shell,
220
+ workingDirectory
221
+ };
222
+ }
@@ -1,4 +1,8 @@
1
1
  import { Command } from '../types/index.js';
2
+ import './commands/clear.js';
3
+ import './commands/resume.js';
4
+ export { Logger, LogLevel, logger } from './logger.js';
5
+ export { default as defaultLogger } from './logger.js';
2
6
  export declare function formatCommand(command: Command): string;
3
7
  export declare function parseInput(input: string): {
4
8
  command: string;
@@ -1,3 +1,9 @@
1
+ // Import commands to register them
2
+ import './commands/clear.js';
3
+ import './commands/resume.js';
4
+ // Export logger
5
+ export { Logger, LogLevel, logger } from './logger.js';
6
+ export { default as defaultLogger } from './logger.js';
1
7
  export function formatCommand(command) {
2
8
  return `${command.name.padEnd(12)} ${command.description}`;
3
9
  }
@@ -0,0 +1,31 @@
1
+ export declare enum LogLevel {
2
+ ERROR = 0,
3
+ WARN = 1,
4
+ INFO = 2,
5
+ DEBUG = 3
6
+ }
7
+ export interface LoggerConfig {
8
+ logDir?: string;
9
+ maxFileSize?: number;
10
+ dateFormat?: string;
11
+ }
12
+ export declare class Logger {
13
+ private readonly logDir;
14
+ private readonly maxFileSize;
15
+ constructor(config?: LoggerConfig);
16
+ private ensureLogDirectory;
17
+ private formatDate;
18
+ private formatTimestamp;
19
+ private getLogFilePath;
20
+ private shouldRotateLog;
21
+ private rotateLog;
22
+ private writeLog;
23
+ error(message: string, meta?: any): void;
24
+ warn(message: string, meta?: any): void;
25
+ info(message: string, meta?: any): void;
26
+ debug(message: string, meta?: any): void;
27
+ log(level: LogLevel, message: string, meta?: any): void;
28
+ }
29
+ declare const defaultLogger: Logger;
30
+ export default defaultLogger;
31
+ export { defaultLogger as logger };
@@ -0,0 +1,97 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ export var LogLevel;
5
+ (function (LogLevel) {
6
+ LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
7
+ LogLevel[LogLevel["WARN"] = 1] = "WARN";
8
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
9
+ LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
10
+ })(LogLevel || (LogLevel = {}));
11
+ export class Logger {
12
+ constructor(config = {}) {
13
+ Object.defineProperty(this, "logDir", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: void 0
18
+ });
19
+ Object.defineProperty(this, "maxFileSize", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: void 0
24
+ });
25
+ this.logDir = config.logDir || path.join(homedir(), '.snow', 'log');
26
+ this.maxFileSize = config.maxFileSize || 10 * 1024 * 1024; // 10MB
27
+ this.ensureLogDirectory();
28
+ }
29
+ ensureLogDirectory() {
30
+ if (!fs.existsSync(this.logDir)) {
31
+ fs.mkdirSync(this.logDir, { recursive: true });
32
+ }
33
+ }
34
+ formatDate(date) {
35
+ const year = date.getFullYear();
36
+ const month = String(date.getMonth() + 1).padStart(2, '0');
37
+ const day = String(date.getDate()).padStart(2, '0');
38
+ return `${year}-${month}-${day}`;
39
+ }
40
+ formatTimestamp(date) {
41
+ return date.toISOString();
42
+ }
43
+ getLogFilePath(level) {
44
+ const dateString = this.formatDate(new Date());
45
+ const levelName = LogLevel[level].toLowerCase();
46
+ return path.join(this.logDir, `${dateString}-${levelName}.log`);
47
+ }
48
+ shouldRotateLog(filePath) {
49
+ if (!fs.existsSync(filePath)) {
50
+ return false;
51
+ }
52
+ const stats = fs.statSync(filePath);
53
+ return stats.size >= this.maxFileSize;
54
+ }
55
+ rotateLog(filePath) {
56
+ const timestamp = Date.now();
57
+ const ext = path.extname(filePath);
58
+ const basename = path.basename(filePath, ext);
59
+ const dirname = path.dirname(filePath);
60
+ const rotatedPath = path.join(dirname, `${basename}-${timestamp}${ext}`);
61
+ fs.renameSync(filePath, rotatedPath);
62
+ }
63
+ writeLog(level, message, meta) {
64
+ const timestamp = this.formatTimestamp(new Date());
65
+ const levelName = LogLevel[level].toUpperCase().padEnd(5);
66
+ const logEntry = {
67
+ timestamp,
68
+ level: levelName.trim(),
69
+ message,
70
+ ...(meta && { meta }),
71
+ };
72
+ const logLine = JSON.stringify(logEntry) + '\n';
73
+ const filePath = this.getLogFilePath(level);
74
+ if (this.shouldRotateLog(filePath)) {
75
+ this.rotateLog(filePath);
76
+ }
77
+ fs.appendFileSync(filePath, logLine, 'utf8');
78
+ }
79
+ error(message, meta) {
80
+ this.writeLog(LogLevel.ERROR, message, meta);
81
+ }
82
+ warn(message, meta) {
83
+ this.writeLog(LogLevel.WARN, message, meta);
84
+ }
85
+ info(message, meta) {
86
+ this.writeLog(LogLevel.INFO, message, meta);
87
+ }
88
+ debug(message, meta) {
89
+ this.writeLog(LogLevel.DEBUG, message, meta);
90
+ }
91
+ log(level, message, meta) {
92
+ this.writeLog(level, message, meta);
93
+ }
94
+ }
95
+ const defaultLogger = new Logger();
96
+ export default defaultLogger;
97
+ export { defaultLogger as logger };
@@ -0,0 +1,47 @@
1
+ import { TodoService } from '../mcp/todo.js';
2
+ export interface MCPTool {
3
+ type: "function";
4
+ function: {
5
+ name: string;
6
+ description: string;
7
+ parameters: any;
8
+ };
9
+ }
10
+ export interface MCPServiceTools {
11
+ serviceName: string;
12
+ tools: Array<{
13
+ name: string;
14
+ description: string;
15
+ inputSchema: any;
16
+ }>;
17
+ isBuiltIn: boolean;
18
+ connected: boolean;
19
+ error?: string;
20
+ }
21
+ /**
22
+ * Get the TODO service instance
23
+ */
24
+ export declare function getTodoService(): TodoService;
25
+ /**
26
+ * Manually refresh the tools cache (for configuration changes)
27
+ */
28
+ export declare function refreshMCPToolsCache(): Promise<void>;
29
+ /**
30
+ * Clear the tools cache (useful for testing or forcing refresh)
31
+ */
32
+ export declare function clearMCPToolsCache(): void;
33
+ /**
34
+ * Collect all available MCP tools from built-in and user-configured services
35
+ * Uses caching to avoid reconnecting on every message
36
+ */
37
+ export declare function collectAllMCPTools(): Promise<MCPTool[]>;
38
+ /**
39
+ * Get detailed information about all MCP services and their tools
40
+ * Uses cached data when available
41
+ */
42
+ export declare function getMCPServicesInfo(): Promise<MCPServiceTools[]>;
43
+ /**
44
+ * Execute an MCP tool by parsing the prefixed tool name
45
+ * Only connects to the service when actually needed
46
+ */
47
+ export declare function executeMCPTool(toolName: string, args: any): Promise<any>;