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.
- package/dist/agents/codebaseIndexAgent.js +1 -0
- package/dist/agents/codebaseReviewAgent.d.ts +61 -0
- package/dist/agents/codebaseReviewAgent.js +301 -0
- package/dist/agents/promptOptimizeAgent.d.ts +54 -0
- package/dist/agents/promptOptimizeAgent.js +268 -0
- package/dist/api/anthropic.js +1 -0
- package/dist/api/chat.js +1 -0
- package/dist/api/embedding.js +1 -0
- package/dist/api/gemini.js +2 -1
- package/dist/api/responses.js +1 -0
- package/dist/api/systemPrompt.d.ts +1 -5
- package/dist/api/systemPrompt.js +168 -100
- package/dist/app.js +14 -6
- package/dist/cli.js +1 -1
- package/dist/hooks/useCommandPanel.js +48 -46
- package/dist/hooks/useConversation.d.ts +2 -1
- package/dist/hooks/useConversation.js +116 -30
- package/dist/hooks/useGlobalExit.js +4 -2
- package/dist/hooks/useStreamingState.d.ts +9 -0
- package/dist/hooks/useStreamingState.js +3 -0
- package/dist/i18n/I18nContext.d.ts +14 -0
- package/dist/i18n/I18nContext.js +24 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/lang/en.d.ts +2 -0
- package/dist/i18n/lang/en.js +483 -0
- package/dist/i18n/lang/es.d.ts +2 -0
- package/dist/i18n/lang/es.js +483 -0
- package/dist/i18n/lang/ja.d.ts +2 -0
- package/dist/i18n/lang/ja.js +483 -0
- package/dist/i18n/lang/ko.d.ts +2 -0
- package/dist/i18n/lang/ko.js +483 -0
- package/dist/i18n/lang/zh-TW.d.ts +2 -0
- package/dist/i18n/lang/zh-TW.js +483 -0
- package/dist/i18n/lang/zh.d.ts +2 -0
- package/dist/i18n/lang/zh.js +483 -0
- package/dist/i18n/translations.d.ts +2 -0
- package/dist/i18n/translations.js +14 -0
- package/dist/i18n/types.d.ts +459 -0
- package/dist/i18n/types.js +1 -0
- package/dist/mcp/aceCodeSearch.d.ts +17 -48
- package/dist/mcp/aceCodeSearch.js +24 -56
- package/dist/mcp/bash.js +8 -1
- package/dist/mcp/codebaseSearch.d.ts +1 -1
- package/dist/mcp/codebaseSearch.js +159 -30
- package/dist/mcp/filesystem.d.ts +3 -80
- package/dist/mcp/filesystem.js +23 -103
- package/dist/mcp/subagent.d.ts +2 -1
- package/dist/mcp/subagent.js +54 -5
- package/dist/ui/components/ChatInput.js +22 -25
- package/dist/ui/components/CommandPanel.d.ts +1 -1
- package/dist/ui/components/CommandPanel.js +20 -13
- package/dist/ui/components/DiffViewer.d.ts +1 -1
- package/dist/ui/components/DiffViewer.js +101 -91
- package/dist/ui/components/FileList.js +22 -11
- package/dist/ui/components/HelpPanel.js +47 -21
- package/dist/ui/components/Menu.js +6 -2
- package/dist/ui/components/MessageList.d.ts +6 -0
- package/dist/ui/components/MessageList.js +1 -1
- package/dist/ui/components/ToolConfirmation.d.ts +4 -1
- package/dist/ui/components/ToolConfirmation.js +28 -2
- package/dist/ui/components/ToolResultPreview.d.ts +2 -1
- package/dist/ui/components/ToolResultPreview.js +41 -25
- package/dist/ui/pages/ChatScreen.js +177 -56
- package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
- package/dist/ui/pages/ConfigScreen.js +138 -98
- package/dist/ui/pages/CustomHeadersScreen.js +75 -69
- package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
- package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
- package/dist/ui/pages/ProxyConfigScreen.js +27 -23
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
- package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
- package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
- package/dist/ui/pages/WelcomeScreen.js +40 -26
- package/dist/utils/apiConfig.d.ts +2 -0
- package/dist/utils/codebaseConfig.d.ts +1 -5
- package/dist/utils/codebaseConfig.js +2 -10
- package/dist/utils/codebaseSearchEvents.d.ts +16 -0
- package/dist/utils/codebaseSearchEvents.js +13 -0
- package/dist/utils/commands/agent.js +2 -2
- package/dist/utils/commands/init.js +1 -1
- package/dist/utils/configManager.js +26 -5
- package/dist/utils/contextCompressor.js +1 -1
- package/dist/utils/languageConfig.d.ts +21 -0
- package/dist/utils/languageConfig.js +61 -0
- package/dist/utils/mcpToolsManager.js +0 -9
- package/dist/utils/notebookManager.js +11 -4
- package/dist/utils/sessionConverter.js +13 -3
- package/dist/utils/sessionManager.d.ts +1 -0
- package/dist/utils/subAgentConfig.d.ts +10 -5
- package/dist/utils/subAgentConfig.js +112 -19
- package/dist/utils/subAgentExecutor.d.ts +9 -1
- package/dist/utils/subAgentExecutor.js +122 -9
- package/dist/utils/toolExecutor.d.ts +2 -1
- package/dist/utils/toolExecutor.js +1 -2
- package/dist/utils/usageLogger.js +18 -3
- package/package.json +2 -1
package/dist/mcp/filesystem.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import
|
|
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
|
-
*
|
|
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
|
-
|
|
726
|
-
|
|
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
|
-
//
|
|
729
|
-
|
|
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
|
-
|
|
957
|
-
|
|
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
|
-
//
|
|
960
|
-
|
|
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=
|
|
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: '
|
|
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.
|
|
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: {
|
package/dist/mcp/subagent.d.ts
CHANGED
|
@@ -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<
|
|
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;
|
package/dist/mcp/subagent.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { executeSubAgent } from '../utils/subAgentExecutor.js';
|
|
2
|
-
import {
|
|
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
|
-
|
|
42
|
-
|
|
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: '
|
|
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 ?
|
|
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 },
|
|
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 },
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
369
|
+
t.chatScreen.newCache))))),
|
|
364
370
|
isOpenAI && (React.createElement(Text, { color: "cyan" },
|
|
365
371
|
"\u21AF ",
|
|
366
372
|
formatNumber(contextUsage.cachedTokens || 0),
|
|
367
373
|
' ',
|
|
368
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 },
|
|
44
|
+
React.createElement(Text, { color: "yellow", bold: true }, t.commandPanel.title)),
|
|
41
45
|
React.createElement(Box, { marginTop: 1 },
|
|
42
|
-
React.createElement(Alert, { variant: "info" },
|
|
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
|
-
|
|
54
|
-
|
|
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 ?
|
|
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 ?
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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 {};
|