snow-ai 0.3.36 → 0.4.0

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/agents/codebaseIndexAgent.js +1 -0
  2. package/dist/agents/codebaseReviewAgent.d.ts +61 -0
  3. package/dist/agents/codebaseReviewAgent.js +301 -0
  4. package/dist/agents/promptOptimizeAgent.d.ts +54 -0
  5. package/dist/agents/promptOptimizeAgent.js +268 -0
  6. package/dist/api/anthropic.js +1 -0
  7. package/dist/api/chat.js +1 -0
  8. package/dist/api/embedding.js +1 -0
  9. package/dist/api/gemini.js +2 -1
  10. package/dist/api/responses.js +1 -0
  11. package/dist/api/systemPrompt.d.ts +1 -5
  12. package/dist/api/systemPrompt.js +168 -100
  13. package/dist/app.js +14 -6
  14. package/dist/cli.js +1 -1
  15. package/dist/hooks/useCommandPanel.js +48 -46
  16. package/dist/hooks/useConversation.d.ts +2 -1
  17. package/dist/hooks/useConversation.js +116 -30
  18. package/dist/hooks/useGlobalExit.js +4 -2
  19. package/dist/hooks/useStreamingState.d.ts +9 -0
  20. package/dist/hooks/useStreamingState.js +3 -0
  21. package/dist/i18n/I18nContext.d.ts +14 -0
  22. package/dist/i18n/I18nContext.js +24 -0
  23. package/dist/i18n/index.d.ts +3 -0
  24. package/dist/i18n/index.js +2 -0
  25. package/dist/i18n/lang/en.d.ts +2 -0
  26. package/dist/i18n/lang/en.js +483 -0
  27. package/dist/i18n/lang/es.d.ts +2 -0
  28. package/dist/i18n/lang/es.js +483 -0
  29. package/dist/i18n/lang/ja.d.ts +2 -0
  30. package/dist/i18n/lang/ja.js +483 -0
  31. package/dist/i18n/lang/ko.d.ts +2 -0
  32. package/dist/i18n/lang/ko.js +483 -0
  33. package/dist/i18n/lang/zh-TW.d.ts +2 -0
  34. package/dist/i18n/lang/zh-TW.js +483 -0
  35. package/dist/i18n/lang/zh.d.ts +2 -0
  36. package/dist/i18n/lang/zh.js +483 -0
  37. package/dist/i18n/translations.d.ts +2 -0
  38. package/dist/i18n/translations.js +14 -0
  39. package/dist/i18n/types.d.ts +459 -0
  40. package/dist/i18n/types.js +1 -0
  41. package/dist/mcp/aceCodeSearch.d.ts +17 -48
  42. package/dist/mcp/aceCodeSearch.js +24 -56
  43. package/dist/mcp/bash.js +8 -1
  44. package/dist/mcp/codebaseSearch.d.ts +1 -1
  45. package/dist/mcp/codebaseSearch.js +159 -30
  46. package/dist/mcp/filesystem.d.ts +3 -80
  47. package/dist/mcp/filesystem.js +23 -103
  48. package/dist/mcp/subagent.d.ts +2 -1
  49. package/dist/mcp/subagent.js +54 -5
  50. package/dist/ui/components/ChatInput.js +22 -25
  51. package/dist/ui/components/CommandPanel.d.ts +1 -1
  52. package/dist/ui/components/CommandPanel.js +20 -13
  53. package/dist/ui/components/DiffViewer.d.ts +1 -1
  54. package/dist/ui/components/DiffViewer.js +101 -91
  55. package/dist/ui/components/FileList.js +22 -11
  56. package/dist/ui/components/HelpPanel.js +47 -21
  57. package/dist/ui/components/Menu.js +6 -2
  58. package/dist/ui/components/MessageList.d.ts +6 -0
  59. package/dist/ui/components/MessageList.js +1 -1
  60. package/dist/ui/components/ToolConfirmation.d.ts +4 -1
  61. package/dist/ui/components/ToolConfirmation.js +28 -2
  62. package/dist/ui/components/ToolResultPreview.d.ts +2 -1
  63. package/dist/ui/components/ToolResultPreview.js +41 -25
  64. package/dist/ui/pages/ChatScreen.js +177 -56
  65. package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
  66. package/dist/ui/pages/ConfigScreen.js +138 -98
  67. package/dist/ui/pages/CustomHeadersScreen.js +75 -69
  68. package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
  69. package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
  70. package/dist/ui/pages/ProxyConfigScreen.js +27 -23
  71. package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
  72. package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
  73. package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
  74. package/dist/ui/pages/WelcomeScreen.js +40 -26
  75. package/dist/utils/apiConfig.d.ts +2 -0
  76. package/dist/utils/codebaseConfig.d.ts +1 -5
  77. package/dist/utils/codebaseConfig.js +2 -10
  78. package/dist/utils/codebaseSearchEvents.d.ts +16 -0
  79. package/dist/utils/codebaseSearchEvents.js +13 -0
  80. package/dist/utils/commands/agent.js +2 -2
  81. package/dist/utils/commands/init.js +1 -1
  82. package/dist/utils/configManager.js +26 -5
  83. package/dist/utils/contextCompressor.js +1 -1
  84. package/dist/utils/languageConfig.d.ts +21 -0
  85. package/dist/utils/languageConfig.js +61 -0
  86. package/dist/utils/mcpToolsManager.js +0 -9
  87. package/dist/utils/notebookManager.js +11 -4
  88. package/dist/utils/sessionConverter.js +13 -3
  89. package/dist/utils/sessionManager.d.ts +1 -0
  90. package/dist/utils/subAgentConfig.d.ts +10 -5
  91. package/dist/utils/subAgentConfig.js +112 -19
  92. package/dist/utils/subAgentExecutor.d.ts +9 -1
  93. package/dist/utils/subAgentExecutor.js +122 -9
  94. package/dist/utils/toolExecutor.d.ts +2 -1
  95. package/dist/utils/toolExecutor.js +1 -2
  96. package/dist/utils/usageLogger.js +18 -3
  97. package/package.json +2 -1
@@ -1,7 +1,6 @@
1
1
  import { promises as fs } from 'fs';
2
2
  import * as path from 'path';
3
- import { exec } from 'child_process';
4
- import { promisify } from 'util';
3
+ import * as prettier from 'prettier';
5
4
  // IDE connection supports both VSCode and JetBrains IDEs
6
5
  import { vscodeConnection } from '../utils/vscodeConnection.js';
7
6
  import { incrementalSnapshotManager } from '../utils/incrementalSnapshot.js';
@@ -16,7 +15,6 @@ import { parseFileSymbols } from './utils/aceCodeSearch/symbol.utils.js';
16
15
  // Notebook utilities for automatic note retrieval
17
16
  import { queryNotebook } from '../utils/notebookManager.js';
18
17
  const { resolve, dirname, isAbsolute } = path;
19
- const execAsync = promisify(exec);
20
18
  /**
21
19
  * Filesystem MCP Service
22
20
  * Provides basic file operations: read, create, and delete files
@@ -382,52 +380,11 @@ export class FilesystemMCPService {
382
380
  }
383
381
  }
384
382
  /**
385
- * Delete one or multiple files
386
- * @param filePaths - Single file path or array of file paths to delete
387
- * @returns Success message with details
388
- * @throws Error if file deletion fails
389
- */
390
- async deleteFile(filePaths) {
391
- try {
392
- const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
393
- const results = [];
394
- const errors = [];
395
- for (const filePath of paths) {
396
- try {
397
- const fullPath = this.resolvePath(filePath);
398
- await this.validatePath(fullPath);
399
- const stats = await fs.stat(fullPath);
400
- if (!stats.isFile()) {
401
- throw new Error(`Path is not a file: ${filePath}`);
402
- }
403
- // Backup file before deletion
404
- await incrementalSnapshotManager.backupFile(fullPath);
405
- await fs.unlink(fullPath);
406
- results.push(`✅ ${filePath}`);
407
- }
408
- catch (error) {
409
- const errorMsg = error instanceof Error ? error.message : 'Unknown error';
410
- errors.push(`❌ ${filePath}: ${errorMsg}`);
411
- }
412
- }
413
- const summary = [];
414
- if (results.length > 0) {
415
- summary.push(`Successfully deleted ${results.length} file(s):\n${results.join('\n')}`);
416
- }
417
- if (errors.length > 0) {
418
- summary.push(`Failed to delete ${errors.length} file(s):\n${errors.join('\n')}`);
419
- }
420
- return summary.join('\n\n');
421
- }
422
- catch (error) {
423
- throw new Error(`Failed to delete files: ${error instanceof Error ? error.message : 'Unknown error'}`);
424
- }
425
- }
426
- /**
427
- * List files in a directory
383
+ * List files in a directory (internal use for read tool)
428
384
  * @param dirPath - Directory path relative to base path or absolute path
429
385
  * @returns Array of file names
430
386
  * @throws Error if directory cannot be read
387
+ * @private
431
388
  */
432
389
  async listFiles(dirPath = '.') {
433
390
  try {
@@ -722,11 +679,14 @@ export class FilesystemMCPService {
722
679
  const shouldFormat = this.prettierSupportedExtensions.includes(fileExtension);
723
680
  if (shouldFormat) {
724
681
  try {
725
- await execAsync(`npx prettier --write "${fullPath}"`, {
726
- encoding: 'utf-8',
682
+ // Use Prettier API for better performance (avoids npx overhead)
683
+ const prettierConfig = await prettier.resolveConfig(fullPath);
684
+ finalContent = await prettier.format(modifiedContent, {
685
+ filepath: fullPath,
686
+ ...prettierConfig,
727
687
  });
728
- // Re-read the file after formatting
729
- finalContent = await fs.readFile(fullPath, 'utf-8');
688
+ // Write formatted content back to file
689
+ await fs.writeFile(fullPath, finalContent, 'utf-8');
730
690
  finalLines = finalContent.split('\n');
731
691
  finalTotalLines = finalLines.length;
732
692
  finalContextEnd = Math.min(finalTotalLines, contextStart + (contextEnd - contextStart) + lineDifference);
@@ -953,11 +913,15 @@ export class FilesystemMCPService {
953
913
  const shouldFormat = this.prettierSupportedExtensions.includes(fileExtension);
954
914
  if (shouldFormat) {
955
915
  try {
956
- await execAsync(`npx prettier --write "${fullPath}"`, {
957
- encoding: 'utf-8',
916
+ // Use Prettier API for better performance (avoids npx overhead)
917
+ const prettierConfig = await prettier.resolveConfig(fullPath);
918
+ const newContent = modifiedLines.join('\n');
919
+ const formattedContent = await prettier.format(newContent, {
920
+ filepath: fullPath,
921
+ ...prettierConfig,
958
922
  });
959
- // Re-read the file after formatting to get the formatted content
960
- const formattedContent = await fs.readFile(fullPath, 'utf-8');
923
+ // Write formatted content back to file
924
+ await fs.writeFile(fullPath, formattedContent, 'utf-8');
961
925
  finalLines = formattedContent.split('\n');
962
926
  finalTotalLines = finalLines.length;
963
927
  // Recalculate the context end line based on formatted content
@@ -1113,7 +1077,7 @@ export const filesystemService = new FilesystemMCPService();
1113
1077
  export const mcpTools = [
1114
1078
  {
1115
1079
  name: 'filesystem-read',
1116
- description: 'Read file content with line numbers. **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=50 reads lines 1-50 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:30}, {path:"b.ts", startLine:100, endLine:150}] reads different ranges from each file.',
1080
+ description: 'Read file content with line numbers. **Read only when the actual file or folder path is found or provided by the user, do not make random guesses,Search for specific documents or line numbers before reading more accurately** **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. **INTEGRATED DIRECTORY LISTING**: When filePath is a directory, automatically lists its contents instead of throwing error. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=500 reads lines 1-500 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:300}, {path:"b.ts", startLine:100, endLine:550}] reads different ranges from each file. (C) Directory: filePath="./src" returns list of files in src/.',
1117
1081
  inputSchema: {
1118
1082
  type: 'object',
1119
1083
  properties: {
@@ -1121,7 +1085,7 @@ export const mcpTools = [
1121
1085
  oneOf: [
1122
1086
  {
1123
1087
  type: 'string',
1124
- description: 'Path to a single file to read',
1088
+ description: 'Path to a single file to read or directory to list',
1125
1089
  },
1126
1090
  {
1127
1091
  type: 'array',
@@ -1153,7 +1117,7 @@ export const mcpTools = [
1153
1117
  description: 'Array of file config objects with per-file line ranges. Each file can have its own startLine/endLine.',
1154
1118
  },
1155
1119
  ],
1156
- description: 'Path to the file(s) to read: string, array of strings, or array of {path, startLine?, endLine?} objects',
1120
+ description: 'Path to the file(s) to read or directory to list: string, array of strings, or array of {path, startLine?, endLine?} objects',
1157
1121
  },
1158
1122
  startLine: {
1159
1123
  type: 'number',
@@ -1190,53 +1154,9 @@ export const mcpTools = [
1190
1154
  required: ['filePath', 'content'],
1191
1155
  },
1192
1156
  },
1193
- {
1194
- name: 'filesystem-delete',
1195
- description: 'Delete one or multiple files. Supports both single file and batch deletion.',
1196
- inputSchema: {
1197
- type: 'object',
1198
- properties: {
1199
- filePath: {
1200
- type: 'string',
1201
- description: 'Path to a single file to delete (deprecated: use filePaths for single or multiple files)',
1202
- },
1203
- filePaths: {
1204
- oneOf: [
1205
- {
1206
- type: 'string',
1207
- description: 'Path to a single file to delete',
1208
- },
1209
- {
1210
- type: 'array',
1211
- items: {
1212
- type: 'string',
1213
- },
1214
- description: 'Array of file paths to delete',
1215
- },
1216
- ],
1217
- description: 'Single file path or array of file paths to delete',
1218
- },
1219
- },
1220
- // Make both optional, but at least one is required (validated in code)
1221
- },
1222
- },
1223
- {
1224
- name: 'filesystem-list',
1225
- description: 'List files in a directory',
1226
- inputSchema: {
1227
- type: 'object',
1228
- properties: {
1229
- dirPath: {
1230
- type: 'string',
1231
- description: 'Directory path to list files from',
1232
- default: '.',
1233
- },
1234
- },
1235
- },
1236
- },
1237
1157
  {
1238
1158
  name: 'filesystem-edit_search',
1239
- description: '**RECOMMENDED** for most edits: Search-and-replace with SMART FUZZY MATCHING. **SUPPORTS BATCH EDITING**: Pass (1) single file with search/replace, (2) array of file paths with unified search/replace, or (3) array of {path, searchContent, replaceContent, occurrence?} for per-file edits. **WORKFLOW**: (1) Use ace-text_search/ace-search_symbols to locate code, (2) Use filesystem-read to view content, (3) Copy code blocks (without line numbers), (4) Use THIS tool. **WHY**: No line tracking, auto-handles spacing/tabs, finds best match. **BATCH EXAMPLE**: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
1159
+ description: 'RECOMMENDED for most edits: Search-and-replace with SMART FUZZY MATCHING. SUPPORTS BATCH EDITING: Pass (1) single file with search/replace, (2) array of file paths with unified search/replace, or (3) array of {path, searchContent, replaceContent, occurrence?} for per-file edits. CRITICAL WORKFLOW FOR CODE SAFETY: (1) Use ace-text_search/ace-search_symbols to locate code, (2) MUST use filesystem-read to identify COMPLETE code boundaries (entire function body with all braces, complete markup tags with opening/closing pairs, full code blocks), (3) Copy the COMPLETE code block (without line numbers), (4) Verify boundaries are intact (matching braces/brackets/tags), (5) Use THIS tool. WHY: No line tracking, auto-handles spacing/tabs, finds best match. COMMON ERRORS TO AVOID: Modifying only part of a function (missing closing brace), incomplete markup tags (HTML/Vue/JSX), partial code blocks. Always include complete syntactic units. BATCH EXAMPLE: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}]',
1240
1160
  inputSchema: {
1241
1161
  type: 'object',
1242
1162
  properties: {
@@ -1306,7 +1226,7 @@ export const mcpTools = [
1306
1226
  },
1307
1227
  {
1308
1228
  name: 'filesystem-edit',
1309
- description: 'Line-based editing for precise control. **SUPPORTS BATCH EDITING**: Pass (1) single file with line range, (2) array of file paths with unified line range, or (3) array of {path, startLine, endLine, newContent} for per-file edits. **WHEN TO USE**: (1) Adding new code sections, (2) Deleting specific line ranges, (3) When search-replace not suitable. **WORKFLOW**: (1) Use ace-text_search/ace-file_outline to locate area, (2) Use filesystem-read to get line numbers, (3) Use THIS tool. **RECOMMENDATION**: For modifying existing code, use filesystem-edit_search - safer. **BATCH EXAMPLE**: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
1229
+ description: 'Line-based editing for precise control. SUPPORTS BATCH EDITING: Pass (1) single file with line range, (2) array of file paths with unified line range, or (3) array of {path, startLine, endLine, newContent} for per-file edits. WHEN TO USE: (1) Adding new code sections, (2) Deleting specific line ranges, (3) When search-replace not suitable. CRITICAL WORKFLOW FOR CODE SAFETY: (1) Use ace-text_search/ace-file_outline to locate area, (2) MUST use filesystem-read to identify COMPLETE code boundaries - for functions: include opening line to closing brace; for markup tags (HTML/Vue/JSX): include opening tag to closing tag; for code blocks: include all braces/brackets, (3) Verify line range covers the ENTIRE syntactic unit (check indentation levels, matching pairs), (4) Use THIS tool with exact startLine/endLine. RECOMMENDATION: For modifying existing code, use filesystem-edit_search - safer and no line tracking needed. COMMON ERRORS TO AVOID: Line range stops mid-function (missing closing brace), partial markup tags, incomplete code blocks. Always verify boundaries with filesystem-read first. BATCH EXAMPLE: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}]',
1310
1230
  inputSchema: {
1311
1231
  type: 'object',
1312
1232
  properties: {
@@ -1,11 +1,12 @@
1
1
  import type { SubAgentMessage } from '../utils/subAgentExecutor.js';
2
2
  import type { ToolCall } from '../utils/toolExecutor.js';
3
+ import type { ConfirmationResult } from '../ui/components/ToolConfirmation.js';
3
4
  export interface SubAgentToolExecutionOptions {
4
5
  agentId: string;
5
6
  prompt: string;
6
7
  onMessage?: (message: SubAgentMessage) => void;
7
8
  abortSignal?: AbortSignal;
8
- requestToolConfirmation?: (toolCall: ToolCall, batchToolNames?: string, allTools?: ToolCall[]) => Promise<string>;
9
+ requestToolConfirmation?: (toolCall: ToolCall, batchToolNames?: string, allTools?: ToolCall[]) => Promise<ConfirmationResult>;
9
10
  isToolAutoApproved?: (toolName: string) => boolean;
10
11
  yoloMode?: boolean;
11
12
  addToAlwaysApproved?: (toolName: string) => void;
@@ -1,5 +1,5 @@
1
1
  import { executeSubAgent } from '../utils/subAgentExecutor.js';
2
- import { getSubAgents } from '../utils/subAgentConfig.js';
2
+ import { getUserSubAgents } from '../utils/subAgentConfig.js';
3
3
  /**
4
4
  * Sub-Agent MCP Service
5
5
  * Provides tools for executing sub-agents with their own specialized system prompts and tool access
@@ -32,14 +32,62 @@ export class SubAgentService {
32
32
  return {
33
33
  success: true,
34
34
  result: result.result,
35
+ usage: result.usage,
35
36
  };
36
37
  }
37
38
  /**
38
39
  * Get all available sub-agents as MCP tools
39
40
  */
40
41
  getTools() {
41
- const subAgents = getSubAgents();
42
- return subAgents.map(agent => ({
42
+ // Get only user-configured agents (built-in agents are hardcoded below)
43
+ const userAgents = getUserSubAgents();
44
+ // Built-in agents (hardcoded, always available)
45
+ const tools = [
46
+ {
47
+ name: 'agent_explore',
48
+ description: 'Explore Agent: Specialized for quickly exploring and understanding codebases. Excels at searching code, finding definitions, analyzing code structure and dependencies. Read-only operations, will not modify files or execute commands.',
49
+ inputSchema: {
50
+ type: 'object',
51
+ properties: {
52
+ prompt: {
53
+ type: 'string',
54
+ description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full task description with business requirements, (2) Known file locations and code paths, (3) Relevant code snippets or patterns already discovered, (4) Any constraints or important context. Example: "Explore authentication implementation. Main flow uses OAuth in src/auth/oauth.ts, need to find all related error handling. User mentioned JWT tokens are validated in middleware."',
55
+ },
56
+ },
57
+ required: ['prompt'],
58
+ },
59
+ },
60
+ {
61
+ name: 'agent_plan',
62
+ description: 'Plan Agent: Specialized for planning complex tasks. Analyzes requirements, explores code, identifies relevant files, and creates detailed implementation plans. Read-only operations, outputs structured implementation proposals.',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ prompt: {
67
+ type: 'string',
68
+ description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full requirement details and business objectives, (2) Current architecture/file structure understanding, (3) Known dependencies and constraints, (4) Files/modules already identified that need changes, (5) User preferences or specific implementation approaches mentioned. Example: "Plan caching implementation. Current API uses Express in src/server.ts, data layer in src/models/. Need Redis caching, user wants minimal changes to existing controllers in src/controllers/."',
69
+ },
70
+ },
71
+ required: ['prompt'],
72
+ },
73
+ },
74
+ {
75
+ name: 'agent_general',
76
+ description: 'General Purpose Agent: General-purpose multi-step task execution agent. Has full tool access for searching, modifying files, and executing commands. Best for complex tasks requiring actual operations.',
77
+ inputSchema: {
78
+ type: 'object',
79
+ properties: {
80
+ prompt: {
81
+ type: 'string',
82
+ description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full task description with step-by-step requirements, (2) Exact file paths and locations to modify, (3) Code patterns/snippets to follow or replicate, (4) Dependencies between files/changes, (5) Testing/verification requirements, (6) Any business logic or constraints discovered in main session. Example: "Update error handling across API. Files: src/api/users.ts, src/api/posts.ts, src/api/comments.ts. Replace old pattern try-catch with new ErrorHandler class from src/utils/errorHandler.ts. Must preserve existing error codes. Run npm test after changes."',
83
+ },
84
+ },
85
+ required: ['prompt'],
86
+ },
87
+ },
88
+ ];
89
+ // Add user-configured agents (avoid duplicates with built-in)
90
+ tools.push(...userAgents.map(agent => ({
43
91
  name: agent.id,
44
92
  description: `${agent.name}: ${agent.description}`,
45
93
  inputSchema: {
@@ -47,12 +95,13 @@ export class SubAgentService {
47
95
  properties: {
48
96
  prompt: {
49
97
  type: 'string',
50
- description: 'The task prompt to send to the sub-agent',
98
+ description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include all relevant: (1) Task requirements and objectives, (2) Known file locations and code structure, (3) Business logic and constraints, (4) Code patterns or examples, (5) Dependencies and relationships. Be specific and comprehensive - sub-agent cannot ask for clarification from main session.',
51
99
  },
52
100
  },
53
101
  required: ['prompt'],
54
102
  },
55
- }));
103
+ })));
104
+ return tools;
56
105
  }
57
106
  }
58
107
  // Export a default instance
@@ -15,6 +15,7 @@ import { useTerminalSize } from '../../hooks/useTerminalSize.js';
15
15
  import { useTerminalFocus } from '../../hooks/useTerminalFocus.js';
16
16
  import { useAgentPicker } from '../../hooks/useAgentPicker.js';
17
17
  import { useTodoPicker } from '../../hooks/useTodoPicker.js';
18
+ import { useI18n } from '../../i18n/index.js';
18
19
  /**
19
20
  * Calculate context usage percentage
20
21
  * This is the same logic used in ChatInput to display usage
@@ -33,6 +34,8 @@ export function calculateContextPercentage(contextUsage) {
33
34
  return Math.min(100, (totalInputTokens / contextUsage.maxContextTokens) * 100);
34
35
  }
35
36
  export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type your message...', disabled = false, isProcessing = false, chatHistory = [], onHistorySelect, yoloMode = false, contextUsage, initialContent = null, onContextPercentageChange, }) {
37
+ // Use i18n hook for translations
38
+ const { t } = useI18n();
36
39
  // Use terminal size hook to listen for resize events
37
40
  const { columns: terminalWidth } = useTerminalSize();
38
41
  const prevTerminalWidthRef = useRef(terminalWidth);
@@ -239,7 +242,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
239
242
  else {
240
243
  return (React.createElement(React.Fragment, null,
241
244
  renderCursor(' '),
242
- React.createElement(Text, { color: disabled ? 'darkGray' : 'gray', dimColor: true }, disabled ? 'Waiting for response...' : placeholder)));
245
+ React.createElement(Text, { color: disabled ? 'darkGray' : 'gray', dimColor: true }, disabled ? t.chatScreen.waitingForResponse : placeholder)));
243
246
  }
244
247
  };
245
248
  return (React.createElement(Box, { flexDirection: "column", paddingX: 1, width: terminalWidth },
@@ -260,10 +263,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
260
263
  const hasMoreAbove = startIndex > 0;
261
264
  const hasMoreBelow = endIndex < userMessages.length;
262
265
  return (React.createElement(React.Fragment, null,
263
- React.createElement(Box, { height: 1 }, hasMoreAbove ? (React.createElement(Text, { color: "gray", dimColor: true },
264
- "\u2191 ",
265
- startIndex,
266
- " more above...")) : (React.createElement(Text, null, " "))),
266
+ React.createElement(Box, { height: 1 }, hasMoreAbove ? (React.createElement(Text, { color: "gray", dimColor: true }, t.chatScreen.moreAbove.replace('{count}', startIndex.toString()))) : (React.createElement(Text, null, " "))),
267
267
  visibleMessages.map((message, displayIndex) => {
268
268
  const actualIndex = startIndex + displayIndex;
269
269
  // Ensure single line by removing all newlines and control characters
@@ -284,13 +284,10 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
284
284
  actualIndex === historySelectedIndex ? '❯ ' : ' ',
285
285
  truncatedLabel)));
286
286
  }),
287
- React.createElement(Box, { height: 1 }, hasMoreBelow ? (React.createElement(Text, { color: "gray", dimColor: true },
288
- "\u2193 ",
289
- userMessages.length - endIndex,
290
- " more below...")) : (React.createElement(Text, null, " ")))));
287
+ React.createElement(Box, { height: 1 }, hasMoreBelow ? (React.createElement(Text, { color: "gray", dimColor: true }, t.chatScreen.moreBelow.replace('{count}', (userMessages.length - endIndex).toString()))) : (React.createElement(Text, null, " ")))));
291
288
  })()),
292
289
  React.createElement(Box, { marginBottom: 1 },
293
- React.createElement(Text, { color: "cyan", dimColor: true }, "\u2191\u2193 navigate \u00B7 Enter select \u00B7 ESC close")))),
290
+ React.createElement(Text, { color: "cyan", dimColor: true }, t.chatScreen.historyNavigateHint)))),
294
291
  !showHistoryMenu && (React.createElement(React.Fragment, null,
295
292
  React.createElement(Box, { flexDirection: "column", width: terminalWidth - 2 },
296
293
  React.createElement(Text, { color: "gray" }, '─'.repeat(terminalWidth - 2)),
@@ -300,13 +297,22 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
300
297
  ' '),
301
298
  React.createElement(Box, { flexGrow: 1 }, renderContent())),
302
299
  React.createElement(Text, { color: "gray" }, '─'.repeat(terminalWidth - 2))),
300
+ (showCommands && getFilteredCommands().length > 0) ||
301
+ showFilePicker ? (React.createElement(Box, { marginTop: 1 },
302
+ React.createElement(Text, null, showCommands && getFilteredCommands().length > 0
303
+ ? t.chatScreen.typeToFilterCommands
304
+ : showFilePicker
305
+ ? searchMode === 'content'
306
+ ? t.chatScreen.contentSearchHint
307
+ : t.chatScreen.fileSearchHint
308
+ : ''))) : null,
303
309
  React.createElement(CommandPanel, { commands: getFilteredCommands(), selectedIndex: commandSelectedIndex, query: buffer.getFullText().slice(1), visible: showCommands, isProcessing: commandPanelIsProcessing }),
304
310
  React.createElement(Box, null,
305
311
  React.createElement(FileList, { ref: fileListRef, query: fileQuery, selectedIndex: fileSelectedIndex, visible: showFilePicker, maxItems: 10, rootPath: process.cwd(), onFilteredCountChange: handleFilteredCountChange, searchMode: searchMode })),
306
312
  React.createElement(AgentPickerPanel, { selectedIndex: agentSelectedIndex, visible: showAgentPicker, maxHeight: 5 }),
307
313
  React.createElement(TodoPickerPanel, { todos: todos, selectedIndex: todoSelectedIndex, selectedTodos: selectedTodos, visible: showTodoPicker, maxHeight: 5, isLoading: todoIsLoading, searchQuery: todoSearchQuery, totalCount: totalTodoCount }),
308
314
  yoloMode && (React.createElement(Box, { marginTop: 1 },
309
- React.createElement(Text, { color: "yellow", dimColor: true }, "\u2741 YOLO MODE ACTIVE - All tools will be auto-approved without confirmation"))),
315
+ React.createElement(Text, { color: "yellow", dimColor: true }, t.chatScreen.yoloModeActive))),
310
316
  contextUsage && (React.createElement(Box, { marginTop: 1 },
311
317
  React.createElement(Text, { color: "gray", dimColor: true }, (() => {
312
318
  // Determine which caching system is being used
@@ -342,7 +348,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
342
348
  "%"),
343
349
  React.createElement(Text, null, " \u00B7 "),
344
350
  React.createElement(Text, { color: color }, formatNumber(totalInputTokens)),
345
- React.createElement(Text, null, " tokens"),
351
+ React.createElement(Text, null, t.chatScreen.tokens),
346
352
  hasCacheMetrics && (React.createElement(React.Fragment, null,
347
353
  React.createElement(Text, null, " \u00B7 "),
348
354
  isAnthropic && (React.createElement(React.Fragment, null,
@@ -352,7 +358,7 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
352
358
  ' ',
353
359
  formatNumber(contextUsage.cacheReadTokens || 0),
354
360
  ' ',
355
- "cached"))),
361
+ t.chatScreen.cached))),
356
362
  (contextUsage.cacheCreationTokens || 0) > 0 && (React.createElement(React.Fragment, null,
357
363
  (contextUsage.cacheReadTokens || 0) > 0 && (React.createElement(Text, null, " \u00B7 ")),
358
364
  React.createElement(Text, { color: "magenta" },
@@ -360,20 +366,11 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
360
366
  ' ',
361
367
  formatNumber(contextUsage.cacheCreationTokens || 0),
362
368
  ' ',
363
- "new cache"))))),
369
+ t.chatScreen.newCache))))),
364
370
  isOpenAI && (React.createElement(Text, { color: "cyan" },
365
371
  "\u21AF ",
366
372
  formatNumber(contextUsage.cachedTokens || 0),
367
373
  ' ',
368
- "cached"))))));
369
- })()))),
370
- (showCommands && getFilteredCommands().length > 0) ||
371
- showFilePicker ? (React.createElement(Box, { marginTop: 1 },
372
- React.createElement(Text, null, showCommands && getFilteredCommands().length > 0
373
- ? 'Type to filter commands'
374
- : showFilePicker
375
- ? searchMode === 'content'
376
- ? 'Content search • Tab/Enter to select • ESC to cancel'
377
- : 'Type to filter files • Tab/Enter to select • ESC to cancel'
378
- : ''))) : null))));
374
+ t.chatScreen.cached))))));
375
+ })())))))));
379
376
  }
@@ -11,5 +11,5 @@ interface Props {
11
11
  maxHeight?: number;
12
12
  isProcessing?: boolean;
13
13
  }
14
- declare const CommandPanel: React.MemoExoticComponent<({ commands, selectedIndex, visible, maxHeight, isProcessing }: Props) => React.JSX.Element | null>;
14
+ declare const CommandPanel: React.MemoExoticComponent<({ commands, selectedIndex, visible, maxHeight, isProcessing, }: Props) => React.JSX.Element | null>;
15
15
  export default CommandPanel;
@@ -1,10 +1,14 @@
1
1
  import React, { memo, useMemo } from 'react';
2
2
  import { Box, Text } from 'ink';
3
3
  import { Alert } from '@inkjs/ui';
4
- const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProcessing = false }) => {
4
+ import { useI18n } from '../../i18n/index.js';
5
+ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProcessing = false, }) => {
6
+ const { t } = useI18n();
5
7
  // Fixed maximum display items to prevent rendering issues
6
8
  const MAX_DISPLAY_ITEMS = 5;
7
- const effectiveMaxItems = maxHeight ? Math.min(maxHeight, MAX_DISPLAY_ITEMS) : MAX_DISPLAY_ITEMS;
9
+ const effectiveMaxItems = maxHeight
10
+ ? Math.min(maxHeight, MAX_DISPLAY_ITEMS)
11
+ : MAX_DISPLAY_ITEMS;
8
12
  // Limit displayed commands
9
13
  const displayedCommands = useMemo(() => {
10
14
  if (commands.length <= effectiveMaxItems) {
@@ -22,7 +26,7 @@ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProc
22
26
  }, [commands, selectedIndex, effectiveMaxItems]);
23
27
  // Calculate actual selected index in the displayed subset
24
28
  const displayedSelectedIndex = useMemo(() => {
25
- return displayedCommands.findIndex((cmd) => {
29
+ return displayedCommands.findIndex(cmd => {
26
30
  const originalIndex = commands.indexOf(cmd);
27
31
  return originalIndex === selectedIndex;
28
32
  });
@@ -37,9 +41,9 @@ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProc
37
41
  React.createElement(Box, { width: "100%" },
38
42
  React.createElement(Box, { flexDirection: "column", width: "100%" },
39
43
  React.createElement(Box, null,
40
- React.createElement(Text, { color: "yellow", bold: true }, "Command Panel")),
44
+ React.createElement(Text, { color: "yellow", bold: true }, t.commandPanel.title)),
41
45
  React.createElement(Box, { marginTop: 1 },
42
- React.createElement(Alert, { variant: "info" }, "Please wait for the conversation to complete before using commands"))))));
46
+ React.createElement(Alert, { variant: "info" }, t.commandPanel.processingMessage))))));
43
47
  }
44
48
  // Don't show panel if no commands found
45
49
  if (commands.length === 0) {
@@ -50,22 +54,25 @@ const CommandPanel = memo(({ commands, selectedIndex, visible, maxHeight, isProc
50
54
  React.createElement(Box, { flexDirection: "column", width: "100%" },
51
55
  React.createElement(Box, null,
52
56
  React.createElement(Text, { color: "yellow", bold: true },
53
- "Available Commands ",
54
- commands.length > effectiveMaxItems && `(${selectedIndex + 1}/${commands.length})`)),
57
+ t.commandPanel.availableCommands,
58
+ ' ',
59
+ commands.length > effectiveMaxItems &&
60
+ `(${selectedIndex + 1}/${commands.length})`)),
55
61
  displayedCommands.map((command, index) => (React.createElement(Box, { key: command.name, flexDirection: "column", width: "100%" },
56
- React.createElement(Text, { color: index === displayedSelectedIndex ? "green" : "gray", bold: true },
57
- index === displayedSelectedIndex ? "" : " ",
62
+ React.createElement(Text, { color: index === displayedSelectedIndex ? 'green' : 'gray', bold: true },
63
+ index === displayedSelectedIndex ? '' : ' ',
58
64
  "/",
59
65
  command.name),
60
66
  React.createElement(Box, { marginLeft: 3 },
61
- React.createElement(Text, { color: index === displayedSelectedIndex ? "green" : "gray", dimColor: true },
67
+ React.createElement(Text, { color: index === displayedSelectedIndex ? 'green' : 'gray', dimColor: true },
62
68
  "\u2514\u2500 ",
63
69
  command.description))))),
64
70
  commands.length > effectiveMaxItems && (React.createElement(Box, { marginTop: 1 },
65
71
  React.createElement(Text, { color: "gray", dimColor: true },
66
- "\u2191\u2193 to scroll \u00B7 ",
67
- commands.length - effectiveMaxItems,
68
- " more hidden")))))));
72
+ t.commandPanel.scrollHint,
73
+ " \u00B7",
74
+ ' ',
75
+ t.commandPanel.moreHidden.replace('{count}', (commands.length - effectiveMaxItems).toString()))))))));
69
76
  });
70
77
  CommandPanel.displayName = 'CommandPanel';
71
78
  export default CommandPanel;
@@ -7,5 +7,5 @@ interface Props {
7
7
  completeNewContent?: string;
8
8
  startLineNumber?: number;
9
9
  }
10
- export default function DiffViewer({ oldContent, newContent, filename, completeOldContent, completeNewContent, startLineNumber, }: Props): React.JSX.Element;
10
+ export default function DiffViewer({ oldContent, newContent, filename, completeOldContent, completeNewContent, startLineNumber, }: Props): React.JSX.Element | null;
11
11
  export {};