snow-ai 0.3.25 → 0.3.27

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.
@@ -75,6 +75,12 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
75
75
 
76
76
  ## 🚀 Execution Strategy - BALANCE ACTION & ANALYSIS
77
77
 
78
+ ## 🤖 Rigorous coding habits
79
+ - In any programming language or business logic, which is usually accompanied by many-to-many references to files, you also need to think about the impact of the modification and whether it will conflict with the user's original business.
80
+ - Using the optimal solution principle, you cannot choose risk scenarios such as hardcoding, logic simplification, etc., unless the user asks you to do so.
81
+ - Avoid duplication, users may have encapsulated some reusable functions, and you should try to find them instead of creating a new function right away.
82
+ - Compilable principle, you should not have low-level errors such as syntax errors, use tools to check for syntax errors, non-compilable code is meaningless.
83
+
78
84
  ### ⚡ Smart Action Mode
79
85
  **Principle: Understand enough to code correctly, but don't over-investigate**
80
86
 
@@ -95,38 +101,50 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
95
101
 
96
102
  **Golden Rule: Read what you need to write correct code, nothing more.**
97
103
 
98
- ### 📋 TODO Lists - Essential for Programming Tasks
99
-
100
- **✅ ALWAYS CREATE TODO WHEN encountering programming tasks:**
101
- - Any code implementation task (new features, bug fixes, refactoring)
102
- - Tasks involving multiple steps or files
103
- - When you need to track progress and ensure completion
104
- - To give users clear visibility into your work plan
105
-
106
- **TODO Guidelines:**
107
- 1. **Create Early**: Set up TODO list BEFORE starting implementation
108
- 2. **Be Specific**: Each item should be a concrete action
109
- 3. **Update Immediately**: Mark as completed immediately after finishing each task
110
- 4. **Focus on Completion**: Move from pending to completed, no intermediate states
111
-
112
- **TODO = Action List, NOT Investigation Plan**
113
- - ✅ "Create AuthService with login/logout methods"
114
- - ✅ "Add validation to UserForm component"
115
- - ✅ "Fix timeout bug in parser.ts"
116
- - "Update API routes to use new auth middleware"
117
- - ✅ "Run build and fix any errors"
118
- - "Read authentication files"
119
- - "Analyze current implementation"
120
- - "Investigate error handling patterns"
121
-
122
- **CRITICAL: Update TODO status IMMEDIATELY after completing each task!**
123
-
124
- **Workflow Example:**
125
- 1. User asks to add feature → Create TODO list immediately
126
- 2. Complete the first task Mark as completed
127
- 3. Move to next task → Complete and mark as completed
128
- 4. Repeat until all tasks completed
129
- 5. Focus on getting tasks done rather than tracking intermediate states
104
+ ### 📋 TODO Management - STRONGLY RECOMMENDED for Better Results!
105
+
106
+ **🎯 DEFAULT BEHAVIOR: Use TODO for ALL multi-step tasks (3+ steps)**
107
+
108
+ **✨ WHY TODO IS ESSENTIAL:**
109
+ - 📊 **Track progress** - Never lose your place in complex work
110
+ - **Ensure completeness** - Verify all steps are done
111
+ - 🎯 **Stay focused** - Clear roadmap prevents confusion
112
+ - 💪 **Build confidence** - Users see structured progress
113
+ - 🚀 **Better quality** - Systematic approach reduces errors
114
+
115
+ **⚡ WHEN TO USE TODO (Default for most tasks):**
116
+ - **ANY multi-file modification** (always use)
117
+ - ✅ **ANY feature implementation** (always use)
118
+ - **ANY refactoring task** (always use)
119
+ - ✅ **Bug fixes touching 2+ files** (recommended)
120
+ - ✅ **User requests with multiple requirements** (always use)
121
+ - ✅ **Unfamiliar codebase changes** (recommended)
122
+ - ⚠️ **SKIP ONLY for**: Single-file trivial edits (1-2 lines)
123
+
124
+ **🔧 USAGE RULES (Critical):**
125
+ 1. **⚠️ PARALLEL CALLS ONLY**: ALWAYS call TODO tools with action tools in the SAME function call block
126
+ 2. **Immediate updates**: Mark completed while performing work (not after)
127
+ 3. **Right sizing**: 3-7 main tasks, add subtasks if needed
128
+ 4. **Lifecycle Management**:
129
+ - New task = Create TODO at start
130
+ - Major requirement change = Delete old + create new
131
+ - Minor adjustment = Use todo-add or todo-update
132
+ - **CRITICAL**: Keep using TODO throughout the entire conversation!
133
+
134
+ **✅ CORRECT PATTERNS (Do this):**
135
+ - todo-create + filesystem-read Plan while gathering info
136
+ - ✅ todo-update(completed) + filesystem-edit → Update as you work
137
+ - ✅ todo-get + filesystem-read → Check status while reading
138
+ - ✅ todo-add + filesystem-edit → Add new task while working
139
+
140
+ **❌ FORBIDDEN PATTERNS (NEVER do this - WILL FAIL):**
141
+ - ❌ todo-create alone, wait for result, then work → VIOLATION! Call together!
142
+ - ❌ todo-update alone, wait, then continue → VIOLATION! Update while working!
143
+ - ❌ todo-get alone just to check → VIOLATION! Call with other tools!
144
+ - ❌ Skipping TODO for multi-file tasks → VIOLATION! Always use TODO!
145
+ - ❌ **Abandoning TODO mid-conversation** → VIOLATION! Keep using throughout dialogue!
146
+
147
+ **💡 BEST PRACTICE: Start every non-trivial task with todo-create + initial action in parallel!**
130
148
 
131
149
  ## 🛠️ Available Tools
132
150
 
@@ -19,6 +19,7 @@ type CommandHandlerOptions = {
19
19
  setShowMcpInfo: React.Dispatch<React.SetStateAction<boolean>>;
20
20
  setShowMcpPanel: React.Dispatch<React.SetStateAction<boolean>>;
21
21
  setShowUsagePanel: React.Dispatch<React.SetStateAction<boolean>>;
22
+ setShowHelpPanel: React.Dispatch<React.SetStateAction<boolean>>;
22
23
  setMcpPanelKey: React.Dispatch<React.SetStateAction<number>>;
23
24
  setYoloMode: React.Dispatch<React.SetStateAction<boolean>>;
24
25
  setContextUsage: React.Dispatch<React.SetStateAction<UsageInfo | null>>;
@@ -45,7 +45,8 @@ export async function executeContextCompression() {
45
45
  timestamp: Date.now(),
46
46
  });
47
47
  // 添加保留的最后一轮完整对话(保留完整的消息结构)
48
- if (compressionResult.preservedMessages && compressionResult.preservedMessages.length > 0) {
48
+ if (compressionResult.preservedMessages &&
49
+ compressionResult.preservedMessages.length > 0) {
49
50
  for (const msg of compressionResult.preservedMessages) {
50
51
  // 保留完整的消息结构,包括所有关键字段
51
52
  newSessionMessages.push({
@@ -56,7 +57,9 @@ export async function executeContextCompression() {
56
57
  ...(msg.tool_calls && { tool_calls: msg.tool_calls }),
57
58
  ...(msg.images && { images: msg.images }),
58
59
  ...(msg.reasoning && { reasoning: msg.reasoning }),
59
- ...(msg.subAgentInternal !== undefined && { subAgentInternal: msg.subAgentInternal }),
60
+ ...(msg.subAgentInternal !== undefined && {
61
+ subAgentInternal: msg.subAgentInternal,
62
+ }),
60
63
  });
61
64
  }
62
65
  }
@@ -220,6 +223,15 @@ export function useCommandHandler(options) {
220
223
  };
221
224
  options.setMessages(prev => [...prev, commandMessage]);
222
225
  }
226
+ else if (result.success && result.action === 'showHelpPanel') {
227
+ options.setShowHelpPanel(true);
228
+ const commandMessage = {
229
+ role: 'command',
230
+ content: '',
231
+ commandName: commandName,
232
+ };
233
+ options.setMessages(prev => [...prev, commandMessage]);
234
+ }
223
235
  else if (result.success && result.action === 'home') {
224
236
  // Reset terminal before navigating to welcome screen
225
237
  resetTerminal(stdout);
@@ -284,7 +296,10 @@ export function useCommandHandler(options) {
284
296
  return;
285
297
  }
286
298
  // Generate default filename with timestamp
287
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
299
+ const timestamp = new Date()
300
+ .toISOString()
301
+ .replace(/[:.]/g, '-')
302
+ .split('.')[0];
288
303
  const defaultFilename = `snow-chat-${timestamp}.txt`;
289
304
  // Show native save dialog
290
305
  const filePath = await showSaveDialog(defaultFilename, 'Export Chat Conversation');
@@ -1,6 +1,7 @@
1
1
  import { useState, useCallback } from 'react';
2
2
  // Command Definition
3
3
  const commands = [
4
+ { name: 'help', description: 'Show keyboard shortcuts and help information' },
4
5
  { name: 'clear', description: 'Clear chat context and conversation history' },
5
6
  { name: 'resume', description: 'Resume a conversation' },
6
7
  { name: 'mcp', description: 'Show Model Context Protocol services and tools' },
@@ -52,8 +53,40 @@ export function useCommandPanel(buffer, isProcessing = false) {
52
53
  if (!text.startsWith('/'))
53
54
  return [];
54
55
  const query = text.slice(1).toLowerCase();
55
- return commands.filter(command => command.name.toLowerCase().includes(query) ||
56
- command.description.toLowerCase().includes(query));
56
+ // Filter and sort commands by priority
57
+ // Priority order:
58
+ // 1. Command starts with query (highest)
59
+ // 2. Command contains query
60
+ // 3. Description starts with query
61
+ // 4. Description contains query (lowest)
62
+ const filtered = commands
63
+ .filter(command => command.name.toLowerCase().includes(query) ||
64
+ command.description.toLowerCase().includes(query))
65
+ .map(command => {
66
+ const nameLower = command.name.toLowerCase();
67
+ const descLower = command.description.toLowerCase();
68
+ let priority = 4; // Default: description contains query
69
+ if (nameLower.startsWith(query)) {
70
+ priority = 1; // Command starts with query
71
+ }
72
+ else if (nameLower.includes(query)) {
73
+ priority = 2; // Command contains query
74
+ }
75
+ else if (descLower.startsWith(query)) {
76
+ priority = 3; // Description starts with query
77
+ }
78
+ return { command, priority };
79
+ })
80
+ .sort((a, b) => {
81
+ // Sort by priority (lower number = higher priority)
82
+ if (a.priority !== b.priority) {
83
+ return a.priority - b.priority;
84
+ }
85
+ // If same priority, sort alphabetically by name
86
+ return a.command.name.localeCompare(b.command.name);
87
+ })
88
+ .map(item => item.command);
89
+ return filtered;
57
90
  }, [buffer]);
58
91
  // Update command panel state
59
92
  const updateCommandPanelState = useCallback((text) => {
package/dist/mcp/todo.js CHANGED
@@ -213,36 +213,43 @@ export class TodoService {
213
213
  return [
214
214
  {
215
215
  name: 'todo-create',
216
- description: `Create a TODO list for complex multi-step tasks (optional planning tool).
216
+ description: `✅ RECOMMENDED: Create TODO list for structured task execution. Use this for ALL multi-step tasks!
217
217
 
218
- ## CORE PRINCIPLE - FOCUS ON EXECUTION:
219
- TODO lists are OPTIONAL helpers for complex tasks. Your PRIMARY goal is COMPLETING THE WORK, not maintaining perfect TODO lists. Use this tool only when it genuinely helps organize complex work - don't let TODO management slow you down.
218
+ ⚠️ MANDATORY RULE - PARALLEL CALLS ONLY:
219
+ 🚫 NEVER call todo-create alone! MUST call with other tools in the SAME function call block.
220
+ ✅ ALWAYS: todo-create + filesystem-read (or other action tool) in parallel
221
+ ❌ FORBIDDEN: Call todo-create, wait for result, then call other tools
220
222
 
221
- ## WHEN TO USE (Optional):
222
- - Complex tasks with 5+ distinct steps that benefit from tracking
223
- - Long-running tasks where progress visibility helps the user
224
- - Tasks with multiple dependencies that need careful ordering
225
- - User explicitly requests a TODO list
223
+ ## 🎯 DEFAULT USAGE - Use TODO by default for:
224
+ ANY multi-file changes (always create TODO first)
225
+ ANY feature implementation (plan with TODO)
226
+ ANY refactoring work (track with TODO)
227
+ Bug fixes involving 2+ files (use TODO)
228
+ ✅ Tasks with 3+ distinct steps (create TODO)
229
+ ⚠️ SKIP ONLY: Single-file trivial edits (1-2 lines)
226
230
 
227
- ## WHEN TO SKIP:
228
- - Simple 1-3 step tasks (just do the work directly)
229
- - Straightforward file edits or single-function changes
230
- - Quick fixes or minor modifications
231
- - When TODO creation takes longer than just doing the task
231
+ ## 🚀 WHY CREATE TODO:
232
+ - Ensures all requirements are addressed
233
+ - Prevents missing critical steps
234
+ - Provides clear progress tracking
235
+ - Improves code quality through systematic approach
236
+ - Builds user confidence with visible structure
232
237
 
233
- ## LIFECYCLE MANAGEMENT:
234
- 1. **NEW REQUEST = NEW TODO LIST**: Completely new requirement? Delete old todos first, then create new list.
235
- 2. **INCREMENTAL REQUEST = USE TODO-ADD**: Adding to existing requirement? Use "todo-add" instead.
236
- 3. Use this tool ONLY when starting fresh (new session or new requirement after cleanup).
238
+ ## 📋 WHEN TO CALL:
239
+ 1. **NEW TASK**: Create TODO immediately when starting work (with parallel action)
240
+ 2. **NEW REQUIREMENT**: Delete old todos, create fresh list (with parallel action)
241
+ 3. **BEST PRACTICE**: Call todo-create + filesystem-read in parallel
237
242
 
238
- ## CREATION GUIDELINES:
239
- - Keep it simple and actionable
240
- - 3-7 main tasks is usually sufficient (don't over-plan)
241
- - Include verification steps only if critical
242
- - Order by dependencies
243
+ ## CREATION GUIDELINES:
244
+ - Break work into 3-7 clear, actionable tasks
245
+ - Order by logical dependencies
246
+ - Be specific (e.g., "Modify validateInput in form.ts" not "fix validation")
247
+ - Include verification step if critical
243
248
 
244
- ## WARNING:
245
- This REPLACES the entire TODO list. Never use it to "add more tasks" - use "todo-add" instead.`,
249
+ ## ⚠️ LIFECYCLE:
250
+ This REPLACES the entire TODO list. For adding tasks to existing list, use "todo-add" instead.
251
+
252
+ ## 💡 REMEMBER: MUST call with other tools - never alone!`,
246
253
  inputSchema: {
247
254
  type: 'object',
248
255
  properties: {
@@ -270,15 +277,19 @@ This REPLACES the entire TODO list. Never use it to "add more tasks" - use "todo
270
277
  },
271
278
  {
272
279
  name: 'todo-get',
273
- description: `Get the current TODO list for this session.
280
+ description: `Get current TODO list with task IDs, status, and hierarchy.
281
+
282
+ ⚠️ MANDATORY RULE - PARALLEL CALLS ONLY:
283
+ 🚫 NEVER call todo-get alone! MUST call with other tools in the SAME function call block.
284
+ ✅ ALWAYS: todo-get + filesystem-read/terminal-execute/etc in parallel
285
+ ❌ FORBIDDEN: Call todo-get alone to check status
274
286
 
275
- ## WHEN TO USE:
276
- - Before making any updates to check current task status and IDs
277
- - To verify what tasks exist before deciding to add/delete/update
278
- - To inspect the TODO structure before planning next steps
287
+ ## 🔄 WHEN TO USE IN DIALOGUE:
288
+ - **User provides additional info**: Use todo-get + filesystem-read to check what's done
289
+ - **User requests modifications**: Check current progress before adding/updating tasks
290
+ - **Continuing work**: Always check status first to avoid redoing completed tasks
279
291
 
280
- ## RETURNS:
281
- Complete TODO list with all task IDs, content, status, and hierarchy.`,
292
+ USAGE: Combine with filesystem-read, terminal-execute, or other actions to check progress while working.`,
282
293
  inputSchema: {
283
294
  type: 'object',
284
295
  properties: {},
@@ -286,26 +297,17 @@ Complete TODO list with all task IDs, content, status, and hierarchy.`,
286
297
  },
287
298
  {
288
299
  name: 'todo-update',
289
- description: `Update TODO status or content - USE ONLY WHEN COMPLETING TASKS.
290
-
291
- ## CORE PRINCIPLE - WORK FIRST, completed in an orderly manner:
292
-
293
- ## STATUS MODEL:
294
- - **pending**: Task not yet completed (default)
295
- - **completed**: Task is 100% finished and verified
300
+ description: `Update TODO status/content - USE THIS FREQUENTLY to track progress!
296
301
 
297
- ## WHEN TO UPDATE:
298
- **Mark "completed"** ONLY when:
299
- - When completing a task in the List
300
- - No errors or blockers
301
- - You've actually verified it works
302
+ ⚠️ MANDATORY RULE - PARALLEL CALLS ONLY:
303
+ 🚫 NEVER call todo-update alone! MUST call with other tools in the SAME function call block.
304
+ ALWAYS: todo-update + filesystem-edit/terminal-execute/etc in parallel
305
+ ❌ FORBIDDEN: Call todo-update, wait for result, then proceed
302
306
 
303
- ## WHEN NOT TO UPDATE:
304
- Don't update status to track "in progress" - just do the work
305
- ❌ Don't update before verifying the work is complete
307
+ BEST PRACTICE: Mark "completed" ONLY after task is verified.
308
+ Example: todo-update(task1, completed) + filesystem-edit(task2) Update while working!
306
309
 
307
- ## BEST PRACTICE:
308
- Every time you complete a task in Task, it will be updated to "Completed" immediately.`,
310
+ 💡 This ensures efficient workflow and prevents unnecessary wait times.`,
309
311
  inputSchema: {
310
312
  type: 'object',
311
313
  properties: {
@@ -328,24 +330,19 @@ Every time you complete a task in Task, it will be updated to "Completed" immedi
328
330
  },
329
331
  {
330
332
  name: 'todo-add',
331
- description: `Add tasks to existing TODO list (use sparingly).
333
+ description: `Add new task to existing TODO list when requirements expand.
332
334
 
333
- ## CORE PRINCIPLE - AVOID TODO BLOAT:
334
- Don't constantly add TODO items while working. If you discover small steps during execution, JUST DO THEM instead of creating TODO items. Only add to TODO if it's genuinely complex or user-requested.
335
+ ⚠️ MANDATORY RULE - PARALLEL CALLS ONLY:
336
+ 🚫 NEVER call todo-add alone! MUST call with other tools in the SAME function call block.
337
+ ✅ ALWAYS: todo-add + filesystem-edit/filesystem-read/etc in parallel
338
+ ❌ FORBIDDEN: Call todo-add alone to add task
335
339
 
336
- ## WHEN TO USE (Rare):
337
- 1. **User Adds Requirements**: User explicitly requests additional tasks
338
- 2. **Major Discovery**: You find a significant, complex step that wasn't initially planned
339
- 3. **Blocking Issue**: You discover a prerequisite that requires substantial separate work
340
+ USE WHEN:
341
+ - User adds new requirements during work
342
+ - You discover additional necessary steps
343
+ - Breaking down a complex task into subtasks
340
344
 
341
- ## WHEN NOT TO USE (Common):
342
- - ❌ Discovered a small 5-minute task while working (just do it, don't track it)
343
- - ❌ Breaking down an existing task into micro-steps (over-planning)
344
- - ❌ "Organizing" or "clarifying" existing tasks (maintain original structure)
345
- - ❌ New unrelated requirement (use todo-delete + todo-create instead)
346
-
347
- ## GUIDELINE:
348
- If a task takes less than 10 minutes, just do it instead of adding it to TODO. The goal is progress, not perfect tracking.`,
345
+ DO NOT use for initial planning - use todo-create instead.`,
349
346
  inputSchema: {
350
347
  type: 'object',
351
348
  properties: {
@@ -363,27 +360,14 @@ If a task takes less than 10 minutes, just do it instead of adding it to TODO. T
363
360
  },
364
361
  {
365
362
  name: 'todo-delete',
366
- description: `Delete TODO items from the current session.
367
-
368
- ## WHEN TO USE:
369
- 1. **Task No Longer Needed**: Requirement changed, task became irrelevant
370
- 2. **Mistake Correction**: Task was added by error or duplicated
371
- 3. **Clearing for New Requirement**: User provides COMPLETELY NEW requirement - delete all old todos first, then create new list
372
- 4. **Cascade Deletion**: Delete parent task with subtasks (automatically removes children)
373
-
374
- ## LIFECYCLE PATTERN FOR NEW REQUIREMENTS:
375
- When user asks for something completely different:
376
- 1. Use todo-get to see current list
377
- 2. Use todo-delete on root items (children auto-delete via parentId cascade)
378
- 3. Use todo-create for the new requirement
363
+ description: `Delete TODO item from the list.
379
364
 
380
- ## WHEN NOT TO USE:
381
- - Do NOT delete completed tasks just for "cleanup" (keep as history)
382
- - Do NOT delete in-progress tasks unless requirement truly changed
383
- - Do NOT use for "reorganizing" (maintain original structure)
365
+ ⚠️ MANDATORY RULE - PARALLEL CALLS ONLY:
366
+ 🚫 NEVER call todo-delete alone! MUST call with other tools in the SAME function call block.
367
+ ALWAYS: todo-delete + filesystem-edit/todo-get/etc in parallel
368
+ FORBIDDEN: Call todo-delete alone
384
369
 
385
- ## CASCADE BEHAVIOR:
386
- Deleting a parent task automatically deletes all its subtasks (parentId relationship).`,
370
+ NOTE: Deleting a parent task will cascade delete all its children automatically.`,
387
371
  inputSchema: {
388
372
  type: 'object',
389
373
  properties: {
@@ -354,15 +354,13 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
354
354
  ' ',
355
355
  "cached"))))));
356
356
  })()))),
357
- React.createElement(Box, { marginTop: 1 },
357
+ (showCommands && getFilteredCommands().length > 0) ||
358
+ showFilePicker ? (React.createElement(Box, { marginTop: 1 },
358
359
  React.createElement(Text, null, showCommands && getFilteredCommands().length > 0
359
360
  ? 'Type to filter commands'
360
361
  : showFilePicker
361
362
  ? searchMode === 'content'
362
363
  ? 'Content search • Tab/Enter to select • ESC to cancel'
363
364
  : 'Type to filter files • Tab/Enter to select • ESC to cancel'
364
- : (() => {
365
- const pasteKey = process.platform === 'darwin' ? 'Ctrl+V' : 'Alt+V';
366
- return `Ctrl+L: delete to start • Ctrl+R: delete to end • ${pasteKey}: paste images • '@': files • '@@': search content • '/': commands`;
367
- })()))))));
365
+ : ''))) : null))));
368
366
  }
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export default function HelpPanel(): React.JSX.Element;
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { Box, Text } from 'ink';
3
+ // Get platform-specific paste key
4
+ const getPasteKey = () => {
5
+ return process.platform === 'darwin' ? 'Ctrl+V' : 'Alt+V';
6
+ };
7
+ export default function HelpPanel() {
8
+ const pasteKey = getPasteKey();
9
+ return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1 },
10
+ React.createElement(Box, { marginBottom: 1 },
11
+ React.createElement(Text, { bold: true, color: "cyan" }, "\uD83D\uDD30 Keyboard Shortcuts & Help")),
12
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
13
+ React.createElement(Text, { bold: true, color: "yellow" }, "\uD83D\uDCDD Text Editing:"),
14
+ React.createElement(Text, null, " \u2022 Ctrl+L - Delete from cursor to start"),
15
+ React.createElement(Text, null, " \u2022 Ctrl+R - Delete from cursor to end"),
16
+ React.createElement(Text, null,
17
+ " \u2022 ",
18
+ pasteKey,
19
+ " - Paste images from clipboard")),
20
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
21
+ React.createElement(Text, { bold: true, color: "green" }, "\uD83D\uDD0D Quick Access:"),
22
+ React.createElement(Text, null, " \u2022 @ - Insert files from project"),
23
+ React.createElement(Text, null, " \u2022 @@ - Search file content"),
24
+ React.createElement(Text, null, " \u2022 / - Show available commands")),
25
+ React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
26
+ React.createElement(Text, { bold: true, color: "blue" }, "\uD83D\uDCCB Navigation:"),
27
+ React.createElement(Text, null, " \u2022 \u2191/\u2193 - Navigate command/message history"),
28
+ React.createElement(Text, null, " \u2022 Tab/Enter - Select item in pickers"),
29
+ React.createElement(Text, null, " \u2022 ESC - Cancel/close pickers or interrupt AI response"),
30
+ React.createElement(Text, null, " \u2022 Shift+Tab - Toggle YOLO mode (auto-approve tools)")),
31
+ React.createElement(Box, { flexDirection: "column" },
32
+ React.createElement(Text, { bold: true, color: "magenta" }, "\uD83D\uDCA1 Tips:"),
33
+ React.createElement(Text, null, " \u2022 Use /help anytime to see this information"),
34
+ React.createElement(Text, null, " \u2022 Type / to see all available commands"),
35
+ React.createElement(Text, null, " \u2022 Press ESC during AI response to interrupt")),
36
+ React.createElement(Box, { marginTop: 1 },
37
+ React.createElement(Text, { dimColor: true, color: "gray" }, "Press ESC to close this help panel"))));
38
+ }
@@ -1,19 +1,23 @@
1
1
  import React from 'react';
2
- import { Text } from 'ink';
2
+ import { Text, Box } from 'ink';
3
3
  // @ts-expect-error - cli-markdown doesn't have TypeScript definitions
4
4
  import cliMarkdown from 'cli-markdown';
5
+ import logger from '../../utils/logger.js';
5
6
  export default function MarkdownRenderer({ content }) {
6
7
  // Use cli-markdown for elegant markdown rendering with syntax highlighting
7
8
  // The patched highlight function will gracefully handle unknown languages
8
9
  const rendered = cliMarkdown(content);
9
- // Remove excessive trailing newlines and whitespace from cli-markdown output
10
- // Keep single blank lines for paragraph spacing (better readability)
11
- const trimmedRendered = rendered
12
- .split('\n')
13
- .map((line) => line.trimEnd()) // Remove trailing spaces from each line
14
- .join('\n')
15
- .replace(/\n{3,}/g, '\n\n') // Replace 3+ consecutive newlines with 2 (paragraph spacing)
16
- .replace(/^\n+/g, '') // Remove leading newlines
17
- .replace(/\n+$/g, ''); // Remove trailing newlines
18
- return React.createElement(Text, null, trimmedRendered);
10
+ // Split into lines and render each separately
11
+ // This prevents Ink's Text component from creating mysterious whitespace
12
+ // when handling multi-line content with \n characters
13
+ const lines = rendered.split('\n');
14
+ // Safety check: prevent rendering issues with excessively long output
15
+ if (lines.length > 500) {
16
+ logger.warn('[MarkdownRenderer] Rendered output has too many lines', {
17
+ totalLines: lines.length,
18
+ truncatedTo: 500,
19
+ });
20
+ return (React.createElement(Box, { flexDirection: "column" }, lines.slice(0, 500).map((line, index) => (React.createElement(Text, { key: index }, line)))));
21
+ }
22
+ return (React.createElement(Box, { flexDirection: "column" }, lines.map((line, index) => (React.createElement(Text, { key: index }, line)))));
19
23
  }
@@ -20,9 +20,11 @@ export default function TodoTree({ todos }) {
20
20
  const getStatusIcon = (status) => {
21
21
  switch (status) {
22
22
  case 'completed':
23
- return '[x]';
23
+ return '[]';
24
24
  case 'pending':
25
25
  return '[ ]';
26
+ default:
27
+ return '[ ]';
26
28
  }
27
29
  };
28
30
  const getStatusColor = (status) => {
@@ -52,5 +54,5 @@ export default function TodoTree({ todos }) {
52
54
  React.createElement(Text, { bold: true, color: "cyan" }, "TODO List")),
53
55
  rootTodos.map(todo => renderTodo(todo)),
54
56
  React.createElement(Box, { marginTop: 0 },
55
- React.createElement(Text, { dimColor: true, color: "gray" }, "[ ] Pending \u00B7 [x] Completed"))));
57
+ React.createElement(Text, { dimColor: true, color: "gray" }, "[ ] Pending \u00B7 [\u2713] Completed"))));
56
58
  }
@@ -13,6 +13,7 @@ import '../../utils/commands/usage.js';
13
13
  import '../../utils/commands/export.js';
14
14
  import '../../utils/commands/agent.js';
15
15
  import '../../utils/commands/todoPicker.js';
16
+ import '../../utils/commands/help.js';
16
17
  type Props = {
17
18
  skipWelcome?: boolean;
18
19
  };
@@ -9,6 +9,7 @@ import MCPInfoScreen from '../components/MCPInfoScreen.js';
9
9
  import MCPInfoPanel from '../components/MCPInfoPanel.js';
10
10
  import SessionListPanel from '../components/SessionListPanel.js';
11
11
  import UsagePanel from '../components/UsagePanel.js';
12
+ import HelpPanel from '../components/HelpPanel.js';
12
13
  import MarkdownRenderer from '../components/MarkdownRenderer.js';
13
14
  import ToolConfirmation from '../components/ToolConfirmation.js';
14
15
  import DiffViewer from '../components/DiffViewer.js';
@@ -46,6 +47,7 @@ import '../../utils/commands/usage.js';
46
47
  import '../../utils/commands/export.js';
47
48
  import '../../utils/commands/agent.js';
48
49
  import '../../utils/commands/todoPicker.js';
50
+ import '../../utils/commands/help.js';
49
51
  export default function ChatScreen({ skipWelcome }) {
50
52
  const [messages, setMessages] = useState([]);
51
53
  const [isSaving] = useState(false);
@@ -77,6 +79,7 @@ export default function ChatScreen({ skipWelcome }) {
77
79
  const [showSessionPanel, setShowSessionPanel] = useState(false);
78
80
  const [showMcpPanel, setShowMcpPanel] = useState(false);
79
81
  const [showUsagePanel, setShowUsagePanel] = useState(false);
82
+ const [showHelpPanel, setShowHelpPanel] = useState(false);
80
83
  const [restoreInputContent, setRestoreInputContent] = useState(null);
81
84
  const { columns: terminalWidth, rows: terminalHeight } = useTerminalSize();
82
85
  const { stdout } = useStdout();
@@ -188,6 +191,7 @@ export default function ChatScreen({ skipWelcome }) {
188
191
  setShowMcpInfo,
189
192
  setShowMcpPanel,
190
193
  setShowUsagePanel,
194
+ setShowHelpPanel,
191
195
  setMcpPanelKey,
192
196
  setYoloMode,
193
197
  setContextUsage: streamingState.setContextUsage,
@@ -256,6 +260,12 @@ export default function ChatScreen({ skipWelcome }) {
256
260
  }
257
261
  return;
258
262
  }
263
+ if (showHelpPanel) {
264
+ if (key.escape) {
265
+ setShowHelpPanel(false);
266
+ }
267
+ return;
268
+ }
259
269
  if (showMcpInfo) {
260
270
  if (key.escape) {
261
271
  setShowMcpInfo(false);
@@ -889,7 +899,11 @@ export default function ChatScreen({ skipWelcome }) {
889
899
  React.createElement(Text, null, "\u2022 Ask for code explanations and debugging help"),
890
900
  React.createElement(Text, null, "\u2022 Press ESC during response to interrupt"),
891
901
  React.createElement(Text, null, "\u2022 Press Shift+Tab: toggle YOLO"),
892
- React.createElement(Text, null,
902
+ React.createElement(Text, null, (() => {
903
+ const pasteKey = process.platform === 'darwin' ? 'Ctrl+V' : 'Alt+V';
904
+ return `• Shortcuts: Ctrl+L (delete to start) • Ctrl+R (delete to end) • ${pasteKey} (paste images) • '@' (files) • '@@' (search content) • '/' (commands)`;
905
+ })()),
906
+ React.createElement(Text, { color: "gray", dimColor: true },
893
907
  "\u2022 Working directory: ",
894
908
  workingDirectory)))),
895
909
  ...messages
@@ -1069,12 +1083,15 @@ export default function ChatScreen({ skipWelcome }) {
1069
1083
  React.createElement(UsagePanel, null),
1070
1084
  React.createElement(Box, { marginTop: 1 },
1071
1085
  React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to close")))),
1086
+ showHelpPanel && (React.createElement(Box, { paddingX: 1, flexDirection: "column", width: terminalWidth },
1087
+ React.createElement(HelpPanel, null))),
1072
1088
  snapshotState.pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: snapshotState.pendingRollback.fileCount, filePaths: snapshotState.pendingRollback.filePaths || [], onConfirm: handleRollbackConfirm })),
1073
1089
  !pendingToolConfirmation &&
1074
1090
  !isCompressing &&
1075
1091
  !showSessionPanel &&
1076
1092
  !showMcpPanel &&
1077
1093
  !showUsagePanel &&
1094
+ !showHelpPanel &&
1078
1095
  !snapshotState.pendingRollback && (React.createElement(React.Fragment, null,
1079
1096
  React.createElement(ChatInput, { onSubmit: handleMessageSubmit, onCommand: handleCommandExecution, placeholder: "Ask me anything about coding...", disabled: !!pendingToolConfirmation, isProcessing: streamingState.isStreaming || isSaving, chatHistory: messages, onHistorySelect: handleHistorySelect, yoloMode: yoloMode, contextUsage: streamingState.contextUsage
1080
1097
  ? {
@@ -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' | 'showAgentPicker' | 'showTodoPicker';
4
+ action?: 'clear' | 'resume' | 'info' | 'showMcpInfo' | 'toggleYolo' | 'initProject' | 'compact' | 'showSessionPanel' | 'showMcpPanel' | 'showUsagePanel' | 'home' | 'review' | 'exportChat' | 'showAgentPicker' | 'showTodoPicker' | 'showHelpPanel';
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,11 @@
1
+ import { registerCommand } from '../commandExecutor.js';
2
+ // Help command handler - show keyboard shortcuts and help information
3
+ registerCommand('help', {
4
+ execute: () => {
5
+ return {
6
+ success: true,
7
+ action: 'showHelpPanel',
8
+ };
9
+ },
10
+ });
11
+ export default {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {