snow-ai 0.4.11 → 0.4.13

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/dist/cli.js CHANGED
@@ -2,19 +2,37 @@
2
2
  // Show loading indicator immediately before any imports
3
3
  process.stdout.write('\x1b[?25l'); // Hide cursor
4
4
  process.stdout.write('⠋ Loading...\r');
5
+ // Import only critical dependencies synchronously
5
6
  import React from 'react';
6
7
  import { render, Text, Box } from 'ink';
7
8
  import Spinner from 'ink-spinner';
8
9
  import meow from 'meow';
9
- import { exec, execSync } from 'child_process';
10
- import { promisify } from 'util';
11
- import App from './app.js';
12
- import { vscodeConnection } from './utils/vscodeConnection.js';
13
- import { resourceMonitor } from './utils/resourceMonitor.js';
14
- import { initializeProfiles } from './utils/configManager.js';
15
- import { processManager } from './utils/processManager.js';
16
- import { enableDevMode, getDevUserId } from './utils/devMode.js';
17
- const execAsync = promisify(exec);
10
+ import { execSync } from 'child_process';
11
+ // Load heavy dependencies asynchronously
12
+ async function loadDependencies() {
13
+ const [appModule, vscodeModule, resourceModule, configModule, processModule, devModeModule, childProcessModule, utilModule,] = await Promise.all([
14
+ import('./app.js'),
15
+ import('./utils/vscodeConnection.js'),
16
+ import('./utils/resourceMonitor.js'),
17
+ import('./utils/configManager.js'),
18
+ import('./utils/processManager.js'),
19
+ import('./utils/devMode.js'),
20
+ import('child_process'),
21
+ import('util'),
22
+ ]);
23
+ return {
24
+ App: appModule.default,
25
+ vscodeConnection: vscodeModule.vscodeConnection,
26
+ resourceMonitor: resourceModule.resourceMonitor,
27
+ initializeProfiles: configModule.initializeProfiles,
28
+ processManager: processModule.processManager,
29
+ enableDevMode: devModeModule.enableDevMode,
30
+ getDevUserId: devModeModule.getDevUserId,
31
+ exec: childProcessModule.exec,
32
+ promisify: utilModule.promisify,
33
+ };
34
+ }
35
+ let execAsync;
18
36
  // Check for updates asynchronously
19
37
  async function checkForUpdates(currentVersion) {
20
38
  try {
@@ -78,39 +96,46 @@ if (cli.flags.update) {
78
96
  process.exit(1);
79
97
  }
80
98
  }
81
- // Handle dev mode flag
82
- if (cli.flags.dev) {
83
- enableDevMode();
84
- const userId = getDevUserId();
85
- console.log('🔧 Developer mode enabled');
86
- console.log(`📝 Using persistent userId: ${userId}`);
87
- console.log(`📂 Stored in: ~/.snow/dev-user-id\n`);
88
- }
89
- // Start resource monitoring in development/debug mode
90
- if (process.env['NODE_ENV'] === 'development' || process.env['DEBUG']) {
91
- resourceMonitor.startMonitoring(30000); // Monitor every 30 seconds
92
- // Check for leaks every 5 minutes
93
- setInterval(() => {
94
- const { hasLeak, reasons } = resourceMonitor.checkForLeaks();
95
- if (hasLeak) {
96
- console.error('⚠️ Potential memory leak detected:');
97
- reasons.forEach(reason => console.error(` - ${reason}`));
98
- }
99
- }, 5 * 60 * 1000);
100
- }
99
+ // Dev mode and resource monitoring will be initialized in Startup component
101
100
  // Startup component that shows loading spinner during update check
102
- const Startup = ({ version, skipWelcome, headlessPrompt, }) => {
101
+ const Startup = ({ version, skipWelcome, headlessPrompt, isDevMode, }) => {
103
102
  const [appReady, setAppReady] = React.useState(false);
103
+ const [AppComponent, setAppComponent] = React.useState(null);
104
104
  React.useEffect(() => {
105
105
  let mounted = true;
106
106
  const init = async () => {
107
- // Initialize profiles system first
107
+ // Load all dependencies in parallel
108
+ const deps = await loadDependencies();
109
+ // Setup execAsync for checkForUpdates
110
+ execAsync = deps.promisify(deps.exec);
111
+ // Initialize profiles system
108
112
  try {
109
- initializeProfiles();
113
+ deps.initializeProfiles();
110
114
  }
111
115
  catch (error) {
112
116
  console.error('Failed to initialize profiles:', error);
113
117
  }
118
+ // Handle dev mode
119
+ if (isDevMode) {
120
+ deps.enableDevMode();
121
+ const userId = deps.getDevUserId();
122
+ console.log('🔧 Developer mode enabled');
123
+ console.log(`📝 Using persistent userId: ${userId}`);
124
+ console.log(`📂 Stored in: ~/.snow/dev-user-id\n`);
125
+ }
126
+ // Start resource monitoring in development/debug mode
127
+ if (process.env['NODE_ENV'] === 'development' || process.env['DEBUG']) {
128
+ deps.resourceMonitor.startMonitoring(30000);
129
+ setInterval(() => {
130
+ const { hasLeak, reasons } = deps.resourceMonitor.checkForLeaks();
131
+ if (hasLeak) {
132
+ console.error('⚠️ Potential memory leak detected:');
133
+ reasons.forEach((reason) => console.error(` - ${reason}`));
134
+ }
135
+ }, 5 * 60 * 1000);
136
+ }
137
+ // Store for cleanup
138
+ global.__deps = deps;
114
139
  // Check for updates with timeout
115
140
  const updateCheckPromise = version
116
141
  ? checkForUpdates(version)
@@ -121,6 +146,7 @@ const Startup = ({ version, skipWelcome, headlessPrompt, }) => {
121
146
  new Promise(resolve => setTimeout(resolve, 3000)),
122
147
  ]);
123
148
  if (mounted) {
149
+ setAppComponent(() => deps.App);
124
150
  setAppReady(true);
125
151
  }
126
152
  };
@@ -128,15 +154,15 @@ const Startup = ({ version, skipWelcome, headlessPrompt, }) => {
128
154
  return () => {
129
155
  mounted = false;
130
156
  };
131
- }, [version]);
132
- if (!appReady) {
157
+ }, [version, isDevMode]);
158
+ if (!appReady || !AppComponent) {
133
159
  return (React.createElement(Box, { flexDirection: "column" },
134
160
  React.createElement(Box, null,
135
161
  React.createElement(Text, { color: "cyan" },
136
162
  React.createElement(Spinner, { type: "dots" })),
137
163
  React.createElement(Text, null, " Loading..."))));
138
164
  }
139
- return (React.createElement(App, { version: version, skipWelcome: skipWelcome, headlessPrompt: headlessPrompt }));
165
+ return (React.createElement(AppComponent, { version: version, skipWelcome: skipWelcome, headlessPrompt: headlessPrompt }));
140
166
  };
141
167
  // Disable bracketed paste mode on startup
142
168
  process.stdout.write('\x1b[?2004l');
@@ -146,12 +172,16 @@ process.stdout.write('\x1b[?25h'); // Show cursor
146
172
  // Re-enable on exit to avoid polluting parent shell
147
173
  const cleanup = () => {
148
174
  process.stdout.write('\x1b[?2004l');
149
- // Kill all child processes first
150
- processManager.killAll();
151
- // Stop resource monitoring
152
- resourceMonitor.stopMonitoring();
153
- // Disconnect VSCode connection before exit
154
- vscodeConnection.stop();
175
+ // Cleanup loaded dependencies if available
176
+ const deps = global.__deps;
177
+ if (deps) {
178
+ // Kill all child processes first
179
+ deps.processManager.killAll();
180
+ // Stop resource monitoring
181
+ deps.resourceMonitor.stopMonitoring();
182
+ // Disconnect VSCode connection before exit
183
+ deps.vscodeConnection.stop();
184
+ }
155
185
  };
156
186
  process.on('exit', cleanup);
157
187
  process.on('SIGINT', () => {
@@ -162,7 +192,7 @@ process.on('SIGTERM', () => {
162
192
  cleanup();
163
193
  process.exit(0);
164
194
  });
165
- render(React.createElement(Startup, { version: cli.pkg.version, skipWelcome: cli.flags.c, headlessPrompt: cli.flags.ask }), {
195
+ render(React.createElement(Startup, { version: cli.pkg.version, skipWelcome: cli.flags.c, headlessPrompt: cli.flags.ask, isDevMode: cli.flags.dev }), {
166
196
  exitOnCtrlC: false,
167
197
  patchConsole: true,
168
198
  });
@@ -139,9 +139,6 @@ export const en = {
139
139
  disabled: '[ ] Disabled',
140
140
  toggleHint: '(Press Enter to toggle)',
141
141
  enterValue: 'Enter value:',
142
- low: 'Low',
143
- medium: 'Medium',
144
- high: 'High',
145
142
  createNewProfile: 'Create New Profile',
146
143
  enterProfileName: 'Enter a name for the new configuration profile',
147
144
  profileNamePlaceholder: 'e.g., work, personal, test',
@@ -139,9 +139,6 @@ export const es = {
139
139
  disabled: '[ ] Deshabilitado',
140
140
  toggleHint: '(Presiona Enter para alternar)',
141
141
  enterValue: 'Ingresa valor:',
142
- low: 'Bajo',
143
- medium: 'Medio',
144
- high: 'Alto',
145
142
  createNewProfile: 'Crear Nuevo Perfil',
146
143
  enterProfileName: 'Ingresa el nombre del nuevo perfil',
147
144
  profileNamePlaceholder: 'Ej: work, personal, test',
@@ -139,9 +139,6 @@ export const ja = {
139
139
  disabled: '[ ] 無効',
140
140
  toggleHint: '(Enterキーで切り替え)',
141
141
  enterValue: '値を入力:',
142
- low: '低',
143
- medium: '中',
144
- high: '高',
145
142
  createNewProfile: '新しいプロファイルを作成',
146
143
  enterProfileName: '新しいプロファイルの名前を入力',
147
144
  profileNamePlaceholder: '例: work, personal, test',
@@ -139,9 +139,6 @@ export const ko = {
139
139
  disabled: '[ ] 비활성화됨',
140
140
  toggleHint: '(Enter 키로 토글)',
141
141
  enterValue: '값 입력:',
142
- low: '낮음',
143
- medium: '중간',
144
- high: '높음',
145
142
  createNewProfile: '새 프로필 생성',
146
143
  enterProfileName: '새 프로필 이름 입력',
147
144
  profileNamePlaceholder: '예: work, personal, test',
@@ -139,9 +139,6 @@ export const zhTW = {
139
139
  disabled: '[ ] 已停用',
140
140
  toggleHint: '(按 Enter 切換)',
141
141
  enterValue: '輸入值:',
142
- low: '低',
143
- medium: '中',
144
- high: '高',
145
142
  createNewProfile: '建立新配置',
146
143
  enterProfileName: '輸入新配置的名稱',
147
144
  profileNamePlaceholder: '例如: work, personal, test',
@@ -139,9 +139,6 @@ export const zh = {
139
139
  disabled: '[ ] 已禁用',
140
140
  toggleHint: '(按 Enter 切换)',
141
141
  enterValue: '输入值:',
142
- low: '低',
143
- medium: '中',
144
- high: '高',
145
142
  createNewProfile: '创建新配置',
146
143
  enterProfileName: '输入新配置的名称',
147
144
  profileNamePlaceholder: '例如: work, personal, test',
@@ -140,9 +140,6 @@ export type TranslationKeys = {
140
140
  disabled: string;
141
141
  toggleHint: string;
142
142
  enterValue: string;
143
- low: string;
144
- medium: string;
145
- high: string;
146
143
  createNewProfile: string;
147
144
  enterProfileName: string;
148
145
  profileNamePlaceholder: string;
@@ -1,19 +1,4 @@
1
1
  import React from 'react';
2
- import '../../utils/commands/clear.js';
3
- import '../../utils/commands/resume.js';
4
- import '../../utils/commands/mcp.js';
5
- import '../../utils/commands/yolo.js';
6
- import '../../utils/commands/init.js';
7
- import '../../utils/commands/ide.js';
8
- import '../../utils/commands/compact.js';
9
- import '../../utils/commands/home.js';
10
- import '../../utils/commands/review.js';
11
- import '../../utils/commands/role.js';
12
- import '../../utils/commands/usage.js';
13
- import '../../utils/commands/export.js';
14
- import '../../utils/commands/agent.js';
15
- import '../../utils/commands/todoPicker.js';
16
- import '../../utils/commands/help.js';
17
2
  type Props = {
18
3
  skipWelcome?: boolean;
19
4
  };
@@ -39,22 +39,6 @@ import { CodebaseIndexAgent } from '../../agents/codebaseIndexAgent.js';
39
39
  import { loadCodebaseConfig } from '../../utils/codebaseConfig.js';
40
40
  import { codebaseSearchEvents } from '../../utils/codebaseSearchEvents.js';
41
41
  import { logger } from '../../utils/logger.js';
42
- // Import commands to register them
43
- import '../../utils/commands/clear.js';
44
- import '../../utils/commands/resume.js';
45
- import '../../utils/commands/mcp.js';
46
- import '../../utils/commands/yolo.js';
47
- import '../../utils/commands/init.js';
48
- import '../../utils/commands/ide.js';
49
- import '../../utils/commands/compact.js';
50
- import '../../utils/commands/home.js';
51
- import '../../utils/commands/review.js';
52
- import '../../utils/commands/role.js';
53
- import '../../utils/commands/usage.js';
54
- import '../../utils/commands/export.js';
55
- import '../../utils/commands/agent.js';
56
- import '../../utils/commands/todoPicker.js';
57
- import '../../utils/commands/help.js';
58
42
  export default function ChatScreen({ skipWelcome }) {
59
43
  const { t } = useI18n();
60
44
  const { theme } = useTheme();
@@ -110,6 +94,29 @@ export default function ChatScreen({ skipWelcome }) {
110
94
  useEffect(() => {
111
95
  pendingMessagesRef.current = pendingMessages;
112
96
  }, [pendingMessages]);
97
+ // Load commands dynamically to avoid blocking initial render
98
+ useEffect(() => {
99
+ // Use Promise.all to load all commands in parallel
100
+ Promise.all([
101
+ import('../../utils/commands/clear.js'),
102
+ import('../../utils/commands/resume.js'),
103
+ import('../../utils/commands/mcp.js'),
104
+ import('../../utils/commands/yolo.js'),
105
+ import('../../utils/commands/init.js'),
106
+ import('../../utils/commands/ide.js'),
107
+ import('../../utils/commands/compact.js'),
108
+ import('../../utils/commands/home.js'),
109
+ import('../../utils/commands/review.js'),
110
+ import('../../utils/commands/role.js'),
111
+ import('../../utils/commands/usage.js'),
112
+ import('../../utils/commands/export.js'),
113
+ import('../../utils/commands/agent.js'),
114
+ import('../../utils/commands/todoPicker.js'),
115
+ import('../../utils/commands/help.js'),
116
+ ]).catch(error => {
117
+ console.error('Failed to load commands:', error);
118
+ });
119
+ }, []);
113
120
  // Auto-start codebase indexing on mount if enabled
114
121
  useEffect(() => {
115
122
  const startCodebaseIndexing = async () => {
@@ -147,6 +154,7 @@ export default function ChatScreen({ skipWelcome }) {
147
154
  }
148
155
  });
149
156
  setWatcherEnabled(true);
157
+ setCodebaseIndexing(false); // Ensure loading UI is hidden
150
158
  return;
151
159
  }
152
160
  // If watcher was enabled before but indexing not completed, restore it
@@ -174,6 +182,7 @@ export default function ChatScreen({ skipWelcome }) {
174
182
  }
175
183
  });
176
184
  setWatcherEnabled(true);
185
+ setCodebaseIndexing(false); // Ensure loading UI is hidden when restoring watcher
177
186
  }
178
187
  // Start or resume indexing in background
179
188
  setCodebaseIndexing(true);
@@ -555,9 +555,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
555
555
  React.createElement(Text, { color: theme.colors.menuSecondary }, responsesReasoningEffort.toUpperCase()))),
556
556
  isCurrentlyEditing && (React.createElement(Box, { marginLeft: 3 },
557
557
  React.createElement(Select, { options: [
558
- { label: t.configScreen.low, value: 'low' },
559
- { label: t.configScreen.medium, value: 'medium' },
560
- { label: t.configScreen.high, value: 'high' },
558
+ { label: 'LOW', value: 'low' },
559
+ { label: 'MEDIUM', value: 'medium' },
560
+ { label: 'HIGH', value: 'high' },
561
+ { label: 'XHIGH', value: 'xhigh' },
561
562
  ], defaultValue: responsesReasoningEffort, onChange: value => {
562
563
  setResponsesReasoningEffort(value);
563
564
  setIsEditing(false);
@@ -1046,9 +1047,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
1046
1047
  searchTerm),
1047
1048
  React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange }))),
1048
1049
  currentField === 'responsesReasoningEffort' && (React.createElement(Select, { options: [
1049
- { label: t.configScreen.low, value: 'low' },
1050
- { label: t.configScreen.medium, value: 'medium' },
1051
- { label: t.configScreen.high, value: 'high' },
1050
+ { label: 'LOW', value: 'low' },
1051
+ { label: 'MEDIUM', value: 'medium' },
1052
+ { label: 'HIGH', value: 'high' },
1053
+ { label: 'XHIGH', value: 'xhigh' },
1052
1054
  ], defaultValue: responsesReasoningEffort, onChange: value => {
1053
1055
  setResponsesReasoningEffort(value);
1054
1056
  setIsEditing(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {