snow-ai 0.3.17 → 0.3.19

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 (34) hide show
  1. package/dist/api/systemPrompt.js +5 -3
  2. package/dist/cli.js +3 -0
  3. package/dist/hooks/useAgentPicker.d.ts +10 -0
  4. package/dist/hooks/useAgentPicker.js +32 -0
  5. package/dist/hooks/useCommandPanel.js +8 -0
  6. package/dist/hooks/useFilePicker.d.ts +1 -0
  7. package/dist/hooks/useFilePicker.js +102 -23
  8. package/dist/hooks/useKeyboardInput.d.ts +22 -0
  9. package/dist/hooks/useKeyboardInput.js +109 -1
  10. package/dist/hooks/useStreamingState.js +16 -10
  11. package/dist/hooks/useTodoPicker.d.ts +16 -0
  12. package/dist/hooks/useTodoPicker.js +94 -0
  13. package/dist/mcp/aceCodeSearch.js +9 -1
  14. package/dist/mcp/bash.js +32 -4
  15. package/dist/ui/components/AgentPickerPanel.d.ts +8 -0
  16. package/dist/ui/components/AgentPickerPanel.js +74 -0
  17. package/dist/ui/components/ChatInput.js +32 -4
  18. package/dist/ui/components/FileList.d.ts +1 -0
  19. package/dist/ui/components/FileList.js +181 -32
  20. package/dist/ui/components/TodoPickerPanel.d.ts +14 -0
  21. package/dist/ui/components/TodoPickerPanel.js +117 -0
  22. package/dist/ui/pages/ChatScreen.d.ts +2 -0
  23. package/dist/ui/pages/ChatScreen.js +2 -0
  24. package/dist/utils/commandExecutor.d.ts +1 -1
  25. package/dist/utils/commands/agent.d.ts +2 -0
  26. package/dist/utils/commands/agent.js +12 -0
  27. package/dist/utils/commands/todoPicker.d.ts +2 -0
  28. package/dist/utils/commands/todoPicker.js +12 -0
  29. package/dist/utils/processManager.d.ts +27 -0
  30. package/dist/utils/processManager.js +75 -0
  31. package/dist/utils/subAgentExecutor.js +3 -12
  32. package/dist/utils/todoScanner.d.ts +8 -0
  33. package/dist/utils/todoScanner.js +148 -0
  34. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  export interface CommandResult {
2
2
  success: boolean;
3
3
  message?: string;
4
- action?: 'clear' | 'resume' | 'info' | 'showMcpInfo' | 'toggleYolo' | 'initProject' | 'compact' | 'showSessionPanel' | 'showMcpPanel' | 'showUsagePanel' | 'home' | 'review' | 'exportChat';
4
+ action?: 'clear' | 'resume' | 'info' | 'showMcpInfo' | 'toggleYolo' | 'initProject' | 'compact' | 'showSessionPanel' | 'showMcpPanel' | 'showUsagePanel' | 'home' | 'review' | 'exportChat' | 'showAgentPicker' | 'showTodoPicker';
5
5
  prompt?: string;
6
6
  alreadyConnected?: boolean;
7
7
  }
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { registerCommand } from '../commandExecutor.js';
2
+ // Agent picker command handler - shows agent selection panel
3
+ registerCommand('agent-', {
4
+ execute: () => {
5
+ return {
6
+ success: true,
7
+ action: 'showAgentPicker',
8
+ message: 'Showing sub-agent selection panel'
9
+ };
10
+ }
11
+ });
12
+ export default {};
@@ -0,0 +1,2 @@
1
+ declare const _default: {};
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { registerCommand } from '../commandExecutor.js';
2
+ // Todo picker command handler - shows todo selection panel
3
+ registerCommand('todo-', {
4
+ execute: () => {
5
+ return {
6
+ success: true,
7
+ action: 'showTodoPicker',
8
+ message: 'Showing TODO comment selection panel',
9
+ };
10
+ },
11
+ });
12
+ export default {};
@@ -0,0 +1,27 @@
1
+ import { ChildProcess } from 'child_process';
2
+ /**
3
+ * Process Manager
4
+ * Tracks and manages all child processes to ensure proper cleanup
5
+ */
6
+ declare class ProcessManager {
7
+ private processes;
8
+ private isShuttingDown;
9
+ /**
10
+ * Register a child process for tracking
11
+ */
12
+ register(process: ChildProcess): void;
13
+ /**
14
+ * Kill a specific process gracefully
15
+ */
16
+ private killProcess;
17
+ /**
18
+ * Kill all tracked processes
19
+ */
20
+ killAll(): void;
21
+ /**
22
+ * Get count of active processes
23
+ */
24
+ getActiveCount(): number;
25
+ }
26
+ export declare const processManager: ProcessManager;
27
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Process Manager
3
+ * Tracks and manages all child processes to ensure proper cleanup
4
+ */
5
+ class ProcessManager {
6
+ constructor() {
7
+ Object.defineProperty(this, "processes", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: new Set()
12
+ });
13
+ Object.defineProperty(this, "isShuttingDown", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: false
18
+ });
19
+ }
20
+ /**
21
+ * Register a child process for tracking
22
+ */
23
+ register(process) {
24
+ if (this.isShuttingDown) {
25
+ // If we're already shutting down, kill immediately
26
+ this.killProcess(process);
27
+ return;
28
+ }
29
+ this.processes.add(process);
30
+ // Auto-remove when process exits
31
+ const cleanup = () => {
32
+ this.processes.delete(process);
33
+ };
34
+ process.once('exit', cleanup);
35
+ process.once('error', cleanup);
36
+ }
37
+ /**
38
+ * Kill a specific process gracefully
39
+ */
40
+ killProcess(process) {
41
+ try {
42
+ if (process.pid && !process.killed) {
43
+ // Try graceful termination first
44
+ process.kill('SIGTERM');
45
+ // Force kill after 1 second if still alive
46
+ setTimeout(() => {
47
+ if (process.pid && !process.killed) {
48
+ process.kill('SIGKILL');
49
+ }
50
+ }, 1000);
51
+ }
52
+ }
53
+ catch (error) {
54
+ // Process might already be dead, ignore errors
55
+ }
56
+ }
57
+ /**
58
+ * Kill all tracked processes
59
+ */
60
+ killAll() {
61
+ this.isShuttingDown = true;
62
+ for (const process of this.processes) {
63
+ this.killProcess(process);
64
+ }
65
+ this.processes.clear();
66
+ }
67
+ /**
68
+ * Get count of active processes
69
+ */
70
+ getActiveCount() {
71
+ return this.processes.size;
72
+ }
73
+ }
74
+ // Export singleton instance
75
+ export const processManager = new ProcessManager();
@@ -63,10 +63,8 @@ export async function executeSubAgent(agentId, prompt, onMessage, abortSignal, r
63
63
  // Local session-approved tools for this sub-agent execution
64
64
  // This ensures tools approved during execution are immediately recognized
65
65
  const sessionApprovedTools = new Set();
66
- const maxIterations = 10; // Prevent infinite loops
67
- let iteration = 0;
68
- while (iteration < maxIterations) {
69
- iteration++;
66
+ // eslint-disable-next-line no-constant-condition
67
+ while (true) {
70
68
  // Check abort signal
71
69
  if (abortSignal?.aborted) {
72
70
  return {
@@ -260,14 +258,7 @@ export async function executeSubAgent(agentId, prompt, onMessage, abortSignal, r
260
258
  // Add tool results to conversation
261
259
  messages.push(...toolResults);
262
260
  // Continue to next iteration if there were tool calls
263
- // The loop will continue until no more tool calls or max iterations
264
- }
265
- if (iteration >= maxIterations) {
266
- return {
267
- success: false,
268
- result: finalResponse,
269
- error: 'Sub-agent exceeded maximum iterations',
270
- };
261
+ // The loop will continue until no more tool calls
271
262
  }
272
263
  return {
273
264
  success: true,
@@ -0,0 +1,8 @@
1
+ export interface TodoItem {
2
+ id: string;
3
+ file: string;
4
+ line: number;
5
+ content: string;
6
+ fullLine: string;
7
+ }
8
+ export declare function scanProjectTodos(projectRoot: string): TodoItem[];
@@ -0,0 +1,148 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ const IGNORE_PATTERNS = [
4
+ 'node_modules',
5
+ '.git',
6
+ 'dist',
7
+ 'build',
8
+ 'coverage',
9
+ '.next',
10
+ '.nuxt',
11
+ '.output',
12
+ 'out',
13
+ '.DS_Store',
14
+ '*.log',
15
+ '*.lock',
16
+ 'yarn.lock',
17
+ 'package-lock.json',
18
+ 'pnpm-lock.yaml',
19
+ ];
20
+ // Common task markers - support various formats
21
+ // Only include markers that clearly indicate actionable tasks
22
+ const TODO_PATTERNS = [
23
+ // Single-line comments with markers (// TODO, // FIXME, etc.)
24
+ /\/\/\s*(?:TODO|FIXME|HACK|XXX|BUG):?\s*(.+)/i,
25
+ // Block comments (/* TODO */)
26
+ /\/\*\s*(?:TODO|FIXME|HACK|XXX|BUG):?\s*(.+?)\s*\*\//i,
27
+ // Hash comments (# TODO) for Python, Ruby, Shell, etc.
28
+ /#\s*(?:TODO|FIXME|HACK|XXX|BUG):?\s*(.+)/i,
29
+ // HTML/XML comments (<!-- TODO -->)
30
+ /<!--\s*(?:TODO|FIXME|HACK|XXX|BUG):?\s*(.+?)\s*-->/i,
31
+ // JSDoc/PHPDoc style (@todo)
32
+ /\/\*\*?\s*@(?:todo|fixme):?\s*(.+?)(?:\s*\*\/|\n)/i,
33
+ // TODO with brackets/parentheses (common format for task assignment)
34
+ /\/\/\s*TODO\s*[\(\[\{]\s*(.+?)\s*[\)\]\}]/i,
35
+ /#\s*TODO\s*[\(\[\{]\s*(.+?)\s*[\)\]\}]/i,
36
+ // Multi-line block comment TODO (catches TODO on its own line)
37
+ /\/\*[\s\S]*?\bTODO:?\s*(.+?)[\s\S]*?\*\//i,
38
+ ];
39
+ function shouldIgnore(filePath) {
40
+ const relativePath = filePath;
41
+ return IGNORE_PATTERNS.some(pattern => {
42
+ if (pattern.includes('*')) {
43
+ const regex = new RegExp(pattern.replace(/\*/g, '.*'));
44
+ return regex.test(relativePath);
45
+ }
46
+ return relativePath.includes(pattern);
47
+ });
48
+ }
49
+ function scanFileForTodos(filePath, rootDir) {
50
+ try {
51
+ const content = fs.readFileSync(filePath, 'utf-8');
52
+ const lines = content.split('\n');
53
+ const todos = [];
54
+ lines.forEach((line, index) => {
55
+ for (const pattern of TODO_PATTERNS) {
56
+ const match = line.match(pattern);
57
+ if (match) {
58
+ const todoContent = match[1]?.trim() || '';
59
+ const relativePath = path.relative(rootDir, filePath);
60
+ todos.push({
61
+ id: `${relativePath}:${index + 1}`,
62
+ file: relativePath,
63
+ line: index + 1,
64
+ content: todoContent,
65
+ fullLine: line.trim(),
66
+ });
67
+ break;
68
+ }
69
+ }
70
+ });
71
+ return todos;
72
+ }
73
+ catch (error) {
74
+ // Ignore files that can't be read
75
+ return [];
76
+ }
77
+ }
78
+ function scanDirectory(dir, rootDir) {
79
+ let todos = [];
80
+ try {
81
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
82
+ for (const entry of entries) {
83
+ const fullPath = path.join(dir, entry.name);
84
+ const relativePath = path.relative(rootDir, fullPath);
85
+ if (shouldIgnore(relativePath)) {
86
+ continue;
87
+ }
88
+ if (entry.isDirectory()) {
89
+ todos = todos.concat(scanDirectory(fullPath, rootDir));
90
+ }
91
+ else if (entry.isFile()) {
92
+ // Only scan text files
93
+ const ext = path.extname(entry.name).toLowerCase();
94
+ const textExtensions = [
95
+ '.ts',
96
+ '.tsx',
97
+ '.js',
98
+ '.jsx',
99
+ '.py',
100
+ '.go',
101
+ '.rs',
102
+ '.java',
103
+ '.c',
104
+ '.cpp',
105
+ '.h',
106
+ '.hpp',
107
+ '.cs',
108
+ '.php',
109
+ '.rb',
110
+ '.swift',
111
+ '.kt',
112
+ '.scala',
113
+ '.sh',
114
+ '.bash',
115
+ '.zsh',
116
+ '.fish',
117
+ '.vim',
118
+ '.lua',
119
+ '.sql',
120
+ '.html',
121
+ '.css',
122
+ '.scss',
123
+ '.sass',
124
+ '.less',
125
+ '.vue',
126
+ '.svelte',
127
+ '.md',
128
+ '.txt',
129
+ '.json',
130
+ '.yaml',
131
+ '.yml',
132
+ '.toml',
133
+ '.xml',
134
+ ];
135
+ if (textExtensions.includes(ext) || ext === '') {
136
+ todos = todos.concat(scanFileForTodos(fullPath, rootDir));
137
+ }
138
+ }
139
+ }
140
+ }
141
+ catch (error) {
142
+ // Ignore directories that can't be read
143
+ }
144
+ return todos;
145
+ }
146
+ export function scanProjectTodos(projectRoot) {
147
+ return scanDirectory(projectRoot, projectRoot);
148
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.17",
3
+ "version": "0.3.19",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {