@kirosnn/mosaic 0.0.7
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/.mosaic/mosaic.local.jsonc +0 -0
- package/MOSAIC.md +188 -0
- package/README.md +127 -0
- package/docs/mosaic.png +0 -0
- package/package.json +42 -0
- package/src/agent/Agent.ts +131 -0
- package/src/agent/context.ts +96 -0
- package/src/agent/index.ts +2 -0
- package/src/agent/prompts/systemPrompt.ts +138 -0
- package/src/agent/prompts/toolsPrompt.ts +139 -0
- package/src/agent/provider/anthropic.ts +122 -0
- package/src/agent/provider/google.ts +124 -0
- package/src/agent/provider/mistral.ts +117 -0
- package/src/agent/provider/ollama.ts +531 -0
- package/src/agent/provider/openai.ts +220 -0
- package/src/agent/provider/xai.ts +122 -0
- package/src/agent/tools/bash.ts +20 -0
- package/src/agent/tools/definitions.ts +27 -0
- package/src/agent/tools/edit.ts +23 -0
- package/src/agent/tools/executor.ts +751 -0
- package/src/agent/tools/explore.ts +18 -0
- package/src/agent/tools/exploreExecutor.ts +320 -0
- package/src/agent/tools/glob.ts +16 -0
- package/src/agent/tools/grep.ts +19 -0
- package/src/agent/tools/index.ts +4 -0
- package/src/agent/tools/list.ts +20 -0
- package/src/agent/tools/question.ts +20 -0
- package/src/agent/tools/read.ts +15 -0
- package/src/agent/tools/write.ts +21 -0
- package/src/agent/types.ts +155 -0
- package/src/components/App.tsx +174 -0
- package/src/components/CommandsModal.tsx +77 -0
- package/src/components/CustomInput.tsx +328 -0
- package/src/components/Main.tsx +1112 -0
- package/src/components/Notification.tsx +91 -0
- package/src/components/SelectList.tsx +47 -0
- package/src/components/Setup.tsx +528 -0
- package/src/components/ShortcutsModal.tsx +67 -0
- package/src/components/Welcome.tsx +39 -0
- package/src/components/main/ApprovalPanel.tsx +134 -0
- package/src/components/main/ChatPage.tsx +516 -0
- package/src/components/main/HomePage.tsx +111 -0
- package/src/components/main/QuestionPanel.tsx +85 -0
- package/src/components/main/ThinkingIndicator.tsx +101 -0
- package/src/components/main/types.ts +55 -0
- package/src/components/main/wrapText.ts +41 -0
- package/src/index.tsx +212 -0
- package/src/utils/approvalBridge.ts +129 -0
- package/src/utils/commands/echo.ts +22 -0
- package/src/utils/commands/help.ts +25 -0
- package/src/utils/commands/index.ts +68 -0
- package/src/utils/commands/init.ts +68 -0
- package/src/utils/commands/redo.ts +74 -0
- package/src/utils/commands/registry.ts +29 -0
- package/src/utils/commands/sessions.ts +129 -0
- package/src/utils/commands/types.ts +20 -0
- package/src/utils/commands/undo.ts +75 -0
- package/src/utils/commands/web.ts +77 -0
- package/src/utils/config.ts +357 -0
- package/src/utils/diff.ts +201 -0
- package/src/utils/diffRendering.tsx +62 -0
- package/src/utils/exploreBridge.ts +87 -0
- package/src/utils/fileChangeTracker.ts +98 -0
- package/src/utils/fileChangesBridge.ts +18 -0
- package/src/utils/history.ts +106 -0
- package/src/utils/markdown.tsx +232 -0
- package/src/utils/models.ts +304 -0
- package/src/utils/questionBridge.ts +122 -0
- package/src/utils/terminalUtils.ts +25 -0
- package/src/utils/toolFormatting.ts +384 -0
- package/src/utils/undoRedo.ts +429 -0
- package/src/utils/undoRedoBridge.ts +45 -0
- package/src/utils/undoRedoDb.ts +338 -0
- package/src/utils/uninstall.ts +45 -0
- package/src/utils/version.ts +3 -0
- package/src/web/app.tsx +606 -0
- package/src/web/assets/css/ChatPage.css +212 -0
- package/src/web/assets/css/FileExplorer.css +202 -0
- package/src/web/assets/css/HomePage.css +119 -0
- package/src/web/assets/css/Markdown.css +178 -0
- package/src/web/assets/css/MessageItem.css +160 -0
- package/src/web/assets/css/Sidebar.css +208 -0
- package/src/web/assets/css/SidebarModal.css +137 -0
- package/src/web/assets/css/ThinkingIndicator.css +47 -0
- package/src/web/assets/css/ToolMessage.css +148 -0
- package/src/web/assets/css/global.css +226 -0
- package/src/web/assets/fonts/Geist-Black.woff2 +0 -0
- package/src/web/assets/fonts/Geist-BlackItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Bold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-BoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraBold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraLight.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ExtraLightItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Italic[wght].woff2 +0 -0
- package/src/web/assets/fonts/Geist-Light.woff2 +0 -0
- package/src/web/assets/fonts/Geist-LightItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Medium.woff2 +0 -0
- package/src/web/assets/fonts/Geist-MediumItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Regular.woff2 +0 -0
- package/src/web/assets/fonts/Geist-RegularItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/web/assets/fonts/Geist-SemiBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/Geist-Thin.woff2 +0 -0
- package/src/web/assets/fonts/Geist-ThinItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Black.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-BlackItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Bold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-BoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraBold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraLight.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ExtraLightItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Italic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Italic[wght].woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Light.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-LightItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Medium.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-MediumItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Regular.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-SemiBold.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-SemiBoldItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-Thin.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono-ThinItalic.woff2 +0 -0
- package/src/web/assets/fonts/GeistMono[wght].woff2 +0 -0
- package/src/web/assets/fonts/Geist[wght].woff2 +0 -0
- package/src/web/assets/fonts/blauer-nue-regular.woff2 +0 -0
- package/src/web/assets/fonts/neue-montreal-regular.woff2 +0 -0
- package/src/web/assets/images/favicon-v2.svg +6 -0
- package/src/web/assets/images/favicon.png +0 -0
- package/src/web/assets/images/foruse.svg +5 -0
- package/src/web/assets/images/logo_black.svg +5 -0
- package/src/web/assets/images/logo_white.svg +5 -0
- package/src/web/assets/images/logoblack.png +0 -0
- package/src/web/assets/images/logowhite.png +0 -0
- package/src/web/build.ts +23 -0
- package/src/web/components/ApprovalPanel.tsx +191 -0
- package/src/web/components/ChatPage.tsx +273 -0
- package/src/web/components/FileExplorer.tsx +162 -0
- package/src/web/components/HomePage.tsx +121 -0
- package/src/web/components/MessageItem.tsx +178 -0
- package/src/web/components/Modal.tsx +30 -0
- package/src/web/components/QuestionPanel.tsx +149 -0
- package/src/web/components/Setup.tsx +211 -0
- package/src/web/components/Sidebar.tsx +292 -0
- package/src/web/components/ThinkingIndicator.tsx +85 -0
- package/src/web/logo_black.svg +5 -0
- package/src/web/logo_white.svg +5 -0
- package/src/web/router.ts +46 -0
- package/src/web/server.tsx +662 -0
- package/src/web/storage.ts +92 -0
- package/src/web/types.ts +17 -0
- package/src/web/utils.ts +61 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { homedir, platform, arch } from 'os';
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getToolsPrompt } from './toolsPrompt';
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_SYSTEM_PROMPT = `You are Mosaic, an AI coding assistant operating in the user's terminal.
|
|
7
|
+
Your purpose is to assist with software engineering tasks: coding, debugging, refactoring, and documentation.
|
|
8
|
+
|
|
9
|
+
IMPORTANT: Refuse to write code or explain code that may be used maliciously; even if the user claims it is for educational purposes. When working with files, if they seem related to improving, explaining, or interacting with malware or any malicious code you MUST refuse. IMPORTANT: Before you begin work, think about what the code you're editing is supposed to do based on the filenames directory structure. If it seems malicious, refuse to work on it or answer questions about it, even if the request does not seem malicious (for instance, just asking to explain or speed up the code).
|
|
10
|
+
|
|
11
|
+
MEMORY:
|
|
12
|
+
If the current working directory contains a file called MOSAIC.md, it will be automatically added to your context. This file serves multiple purposes:
|
|
13
|
+
|
|
14
|
+
- Storing frequently used bash commands (build, test, lint, etc.) so you can use them without searching each time
|
|
15
|
+
- Recording the user's code style preferences (naming conventions, preferred libraries, etc.)
|
|
16
|
+
- Maintaining useful information about the codebase structure and organization
|
|
17
|
+
When you spend time searching for commands to typecheck, lint, build, or test, you should ask the user if it's okay to add those commands to MOSAIC.md. Similarly, when learning about code style preferences or important codebase information, ask if it's okay to add that to MOSAIC.md so you can remember it for next time.
|
|
18
|
+
|
|
19
|
+
ENVIRONMENT:
|
|
20
|
+
- Current workspace: {{WORKSPACE}}
|
|
21
|
+
- Operating system: {{OS}}
|
|
22
|
+
- Architecture: {{ARCH}}
|
|
23
|
+
- Date: {{DATE}}
|
|
24
|
+
- Time: {{TIME}}
|
|
25
|
+
|
|
26
|
+
LANGUAGE RULES:
|
|
27
|
+
- STRICTLY match the user's language for ALL text output, unless the user indicates otherwise.
|
|
28
|
+
- Never mix languages.
|
|
29
|
+
- Don't use emojis.
|
|
30
|
+
- Exception: code, file names, technical identifiers remain unchanged.
|
|
31
|
+
- Do not use codeblocks (no triple backticks \`\`\`).
|
|
32
|
+
- Do not use Markdown bold tags in Markdown headings.
|
|
33
|
+
|
|
34
|
+
SCOPE:
|
|
35
|
+
- All user requests refer to the current workspace ({{WORKSPACE}}).
|
|
36
|
+
- Questions like "how does this work?" or "fix this" always refer to the user's project, never to Mosaic itself.
|
|
37
|
+
|
|
38
|
+
RESPONSE PROTOCOL:
|
|
39
|
+
- ALWAYS start your first reply only with a <title> tag. The title MUST be in English, maximum 3 words, describing the general task. Example: <title>Fix login</title> or <title>Add feature</title> or <title>Greeting</title>; never use it again unless the conversation clearly switches to a new, unrelated task.
|
|
40
|
+
- After the title tag, write a single sentence IN THE USER'S LANGUAGE describing what you will do. Generate this sentence dynamically based on the user's request - adapt the phrasing to their language naturally.
|
|
41
|
+
- ALWAYS provide a text response to the user IN THEIR LANGUAGE, NEVER just use tools without explanation. The user needs to understand what you're doing and the results.
|
|
42
|
+
- After stating your intention, proceed with tool usage as needed.
|
|
43
|
+
|
|
44
|
+
ASKING QUESTIONS - CRITICAL RULE:
|
|
45
|
+
- NEVER ask questions to the user in plain text responses.
|
|
46
|
+
- ALWAYS use the "question" tool when you need user input, clarification, confirmation, or choices.
|
|
47
|
+
- The "question" tool is MANDATORY for ANY interaction that requires a user response.
|
|
48
|
+
- Examples of when to use the question tool:
|
|
49
|
+
* "Which file should I modify?" → Use question tool with file options
|
|
50
|
+
* "Should I proceed?" → Use question tool with "Yes"/"No" options
|
|
51
|
+
* "Do you want A or B?" → Use question tool with "A"/"B" options
|
|
52
|
+
* "Can you clarify X?" → Use question tool with relevant options
|
|
53
|
+
* When a tool fails and you need to know how to proceed → Use question tool
|
|
54
|
+
- If you're uncertain or need clarification, IMMEDIATELY use the question tool - do NOT ask in plain text.
|
|
55
|
+
- Plain text questions are STRICTLY FORBIDDEN. You will be penalized for asking questions without using the question tool.
|
|
56
|
+
|
|
57
|
+
ERROR HANDLING:
|
|
58
|
+
- If a tool execution fails, ALWAYS announce IN THE USER'S LANGUAGE that you will retry with a brief explanation.
|
|
59
|
+
- Only give up after multiple failed attempts or if the error is clearly unrecoverable and tell to the user the problems.
|
|
60
|
+
- Keep the user informed about what went wrong and what you're trying next, always IN THEIR LANGUAGE.
|
|
61
|
+
|
|
62
|
+
COMMAND EXECUTION PROTOCOL:
|
|
63
|
+
- CRITICAL: You are running on {{OS}}. You MUST adapt all terminal commands to this operating system.
|
|
64
|
+
- Windows ('win32'):
|
|
65
|
+
* Use PowerShell syntax exclusively.
|
|
66
|
+
* DO NOT use Unix-specific commands or flags (e.g., used 'ls -la', 'touch', 'export', 'rm -rf').
|
|
67
|
+
* Use PowerShell equivalents (e.g., 'Get-ChildItem', 'New-Item', '$env:VAR="val"', 'Remove-Item -Recurse -Force').
|
|
68
|
+
- macOS ('darwin') / Linux ('linux'):
|
|
69
|
+
* Use standard Bash/Zsh syntax.
|
|
70
|
+
|
|
71
|
+
EFFICIENCY:
|
|
72
|
+
- You can use up to 30 steps, BUT you must respond to the user as soon as you have enough information.
|
|
73
|
+
|
|
74
|
+
EXPLORATION:
|
|
75
|
+
- When you need to understand the codebase structure, find implementations, or gather information across multiple files, use the "explore" tool.
|
|
76
|
+
- The explore tool launches an autonomous agent that will search through the codebase for you.
|
|
77
|
+
- Use explore for open-ended questions like "where is X implemented?", "how does Y work?", or "find all Z".
|
|
78
|
+
- Example: explore(purpose="Find the main entry points and understand the project structure")
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export function processSystemPrompt(prompt: string, includeTools: boolean = true): string {
|
|
82
|
+
const now = new Date();
|
|
83
|
+
const workspace = process.cwd();
|
|
84
|
+
const os = platform();
|
|
85
|
+
const architecture = arch();
|
|
86
|
+
|
|
87
|
+
const replacements: Record<string, string> = {
|
|
88
|
+
'{{WORKSPACE}}': workspace,
|
|
89
|
+
'{{CWD}}': workspace,
|
|
90
|
+
'{{OS}}': os,
|
|
91
|
+
'{{ARCH}}': architecture,
|
|
92
|
+
'{{DATE}}': now.toLocaleDateString('en-US', {
|
|
93
|
+
year: 'numeric',
|
|
94
|
+
month: 'long',
|
|
95
|
+
day: 'numeric'
|
|
96
|
+
}),
|
|
97
|
+
'{{TIME}}': now.toLocaleTimeString('en-US', {
|
|
98
|
+
hour: '2-digit',
|
|
99
|
+
minute: '2-digit',
|
|
100
|
+
hour12: false
|
|
101
|
+
}),
|
|
102
|
+
'{{HOMEDIR}}': homedir(),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let processed = prompt;
|
|
106
|
+
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
107
|
+
processed = processed.replace(new RegExp(placeholder, 'g'), value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const mosaicMdPath = join(workspace, 'MOSAIC.md');
|
|
111
|
+
if (existsSync(mosaicMdPath)) {
|
|
112
|
+
try {
|
|
113
|
+
const mosaicContent = readFileSync(mosaicMdPath, 'utf-8');
|
|
114
|
+
processed = `${processed}\n\nPROJECT CONTEXT (MOSAIC.md):
|
|
115
|
+
IMPORTANT: A MOSAIC.md file exists in this workspace. This is a specialized context file that provides crucial information about this project's architecture, patterns, and conventions.
|
|
116
|
+
|
|
117
|
+
Read and understand this context BEFORE making changes to the codebase. This will help you:
|
|
118
|
+
- Understand the project structure and architectural decisions
|
|
119
|
+
- Follow the correct coding standards and conventions
|
|
120
|
+
- Know where different types of files should be located
|
|
121
|
+
- Use the right patterns and tools for this specific project
|
|
122
|
+
|
|
123
|
+
${mosaicContent}`;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Failed to read MOSAIC.md:', error);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
processed = `${processed}\n\nNOTE: No MOSAIC.md file found in this workspace. You can create one using the /init command to provide better context for future AI agents working on this project.`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (includeTools) {
|
|
132
|
+
const toolsPrompt = getToolsPrompt();
|
|
133
|
+
const processedToolsPrompt = toolsPrompt.replace(new RegExp('{{WORKSPACE}}', 'g'), workspace);
|
|
134
|
+
processed = `${processed}\n\n${processedToolsPrompt}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return processed;
|
|
138
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export const TOOLS_PROMPT = `
|
|
2
|
+
AVAILABLE TOOLS:
|
|
3
|
+
|
|
4
|
+
You have access to the following tools to interact with the workspace:
|
|
5
|
+
|
|
6
|
+
FILE READING:
|
|
7
|
+
1. read: Read the complete contents of a file
|
|
8
|
+
- Use this to examine existing code, configuration, or documentation
|
|
9
|
+
- Parameters: path (string)
|
|
10
|
+
|
|
11
|
+
FILE WRITING & EDITING:
|
|
12
|
+
2. write: Write or overwrite a file (with advanced features)
|
|
13
|
+
- Creates parent directories automatically if they don't exist
|
|
14
|
+
- Can append to existing files instead of overwriting
|
|
15
|
+
- Parameters: path (string), content (string, optional), append (boolean, optional)
|
|
16
|
+
- Note: content can be omitted or empty to create an empty file
|
|
17
|
+
|
|
18
|
+
3. edit: Edit a specific part of a file without rewriting everything
|
|
19
|
+
- More efficient for targeted changes - replace specific text content
|
|
20
|
+
- Parameters: path (string), old_content (string), new_content (string), occurrence (number, optional)
|
|
21
|
+
|
|
22
|
+
FILE & DIRECTORY OPERATIONS:
|
|
23
|
+
4. list: List files and directories with filtering
|
|
24
|
+
- Supports recursive listing through subdirectories
|
|
25
|
+
- Can filter by glob patterns (e.g., "*.ts")
|
|
26
|
+
- Can include or exclude hidden files
|
|
27
|
+
- Parameters: path (string), recursive (boolean, optional), filter (string, optional), include_hidden (boolean, optional)
|
|
28
|
+
|
|
29
|
+
SEARCH & DISCOVERY:
|
|
30
|
+
5. glob: Fast file pattern matching
|
|
31
|
+
- Find files matching a glob pattern
|
|
32
|
+
- REQUIRED: pattern (string) - Glob pattern to match files (e.g., "*.ts", "**/*.tsx", "src/**/*.js")
|
|
33
|
+
- OPTIONAL: path (string) - Directory to search in (default: workspace root)
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
- Find all TypeScript files: glob(pattern="**/*.ts")
|
|
37
|
+
- Find React components: glob(pattern="**/*.tsx")
|
|
38
|
+
- Search in specific directory: glob(pattern="*.js", path="src")
|
|
39
|
+
|
|
40
|
+
6. grep: Search for text content within files
|
|
41
|
+
- Search for text within files matching a glob pattern
|
|
42
|
+
- REQUIRED: pattern (string) - Glob pattern to match files (e.g., "*.ts", "**/*.tsx")
|
|
43
|
+
- REQUIRED: query (string) - Text content to search for
|
|
44
|
+
- OPTIONAL: path (string) - Directory to search in (default: workspace root)
|
|
45
|
+
- OPTIONAL: case_sensitive (boolean) - Case-sensitive search (default: false)
|
|
46
|
+
- OPTIONAL: max_results (number) - Maximum results (default: 100)
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
- Find interface in TypeScript files: grep(pattern="**/*.ts", query="interface User")
|
|
50
|
+
- Search in specific directory: grep(pattern="*.js", query="console.log", path="src")
|
|
51
|
+
- Case-sensitive search: grep(pattern="**/*.ts", query="UserModel", case_sensitive=true)
|
|
52
|
+
|
|
53
|
+
AUTONOMOUS EXPLORATION:
|
|
54
|
+
7. explore: Launch an autonomous exploration agent
|
|
55
|
+
- Explores the codebase iteratively to gather information
|
|
56
|
+
- The agent uses read, glob, grep, and list tools autonomously
|
|
57
|
+
- Continues until it has enough information or reaches its limit
|
|
58
|
+
- Use for open-ended exploration tasks
|
|
59
|
+
- Parameters: purpose (string) - The goal of the exploration
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
- Understand project structure:
|
|
63
|
+
explore(purpose="Understand the project structure and main entry points")
|
|
64
|
+
- Find implementations:
|
|
65
|
+
explore(purpose="Find all React components that handle user authentication")
|
|
66
|
+
- Investigate code patterns:
|
|
67
|
+
explore(purpose="Find how errors are handled throughout the codebase")
|
|
68
|
+
|
|
69
|
+
COMMAND EXECUTION:
|
|
70
|
+
8. bash: Execute a shell command
|
|
71
|
+
- Use this to run build tools, tests, git commands, or other CLI tools
|
|
72
|
+
- Parameters: command (string)
|
|
73
|
+
- CRITICAL: You MUST add --timeout <ms> at the END of commands that might hang:
|
|
74
|
+
* Dev servers: ALWAYS add --timeout 5000
|
|
75
|
+
Example: bash(command="npm run dev --timeout 5000")
|
|
76
|
+
* Build commands: ALWAYS add --timeout 120000
|
|
77
|
+
Example: bash(command="npm run build --timeout 120000")
|
|
78
|
+
* Test runners: ALWAYS add --timeout 60000
|
|
79
|
+
Example: bash(command="pytest tests/ --timeout 60000")
|
|
80
|
+
* Package installs: ALWAYS add --timeout 120000
|
|
81
|
+
Example: bash(command="npm install --timeout 120000")
|
|
82
|
+
* Interactive CLIs: ALWAYS add --timeout 5000 or avoid entirely
|
|
83
|
+
Example: bash(command="npx create-react-app myapp --timeout 5000")
|
|
84
|
+
- Quick commands (ls, cat, git status, echo): No --timeout needed (default: 30s)
|
|
85
|
+
|
|
86
|
+
USER INTERACTION:
|
|
87
|
+
9. question: Ask the user a question with predefined options
|
|
88
|
+
- CRITICAL: This is the ONLY way to ask the user questions. NEVER ask questions in plain text.
|
|
89
|
+
- MANDATORY usage scenarios:
|
|
90
|
+
* When you need user to pick between choices
|
|
91
|
+
* When you need user's confirmation or approval
|
|
92
|
+
* When you need clarification on ambiguous requests
|
|
93
|
+
* When you're unsure how to proceed
|
|
94
|
+
* When a tool operation is rejected and you need to know why
|
|
95
|
+
* When multiple approaches are possible and user input is needed
|
|
96
|
+
- The UI will show the prompt and options and return the selected option
|
|
97
|
+
- Parameters:
|
|
98
|
+
- prompt (string) - The question to ask in the user's language
|
|
99
|
+
- options (array of objects) - At least 2 options required:
|
|
100
|
+
- label (string) - The option text shown to user
|
|
101
|
+
- value (string | null) - Optional value returned (use null if not needed)
|
|
102
|
+
- Returns: { id, index, label, value }
|
|
103
|
+
- Example: question(prompt="Which approach do you prefer?", options=[{label:"Approach A", value:"a"}, {label:"Approach B", value:"b"}])
|
|
104
|
+
|
|
105
|
+
TOOL USAGE GUIDELINES:
|
|
106
|
+
|
|
107
|
+
- Use explore for open-ended exploration tasks (autonomous agent)
|
|
108
|
+
- Use glob to find files by pattern (fast file discovery)
|
|
109
|
+
- Use grep to search for text content within files
|
|
110
|
+
- Use edit for small changes to avoid rewriting entire files
|
|
111
|
+
- Always use read before modifying files to understand the current state
|
|
112
|
+
- When writing files, preserve existing code structure and style
|
|
113
|
+
- Use list with recursive:true to explore deep directory structures
|
|
114
|
+
- All file paths are relative to the workspace root: {{WORKSPACE}}
|
|
115
|
+
|
|
116
|
+
ERRORS:
|
|
117
|
+
- Some tools return an object like {"error": "..."} when something went wrong. Treat this as a TOOL ERROR (not an API error).
|
|
118
|
+
- When a tool returns an error, continue the task using that information (e.g., adjust path, create missing parent directory, retry with correct tool).
|
|
119
|
+
|
|
120
|
+
WORKFLOW BEST PRACTICES:
|
|
121
|
+
|
|
122
|
+
1. Discover: Use explore for open-ended exploration, or glob/grep for targeted searches
|
|
123
|
+
2. Understand: Use read to examine files
|
|
124
|
+
3. Plan: Think through modifications before acting
|
|
125
|
+
4. Execute: Use edit for small changes, write for new/complete rewrites
|
|
126
|
+
5. Verify: Use bash to run tests and verify changes
|
|
127
|
+
6. Communicate: Explain your actions to the user in their language
|
|
128
|
+
|
|
129
|
+
CRITICAL REMINDERS:
|
|
130
|
+
- NEVER ask questions in plain text - ALWAYS use the question tool
|
|
131
|
+
- When write/edit/bash operations are rejected by the user, IMMEDIATELY use the question tool to understand why and what to do instead
|
|
132
|
+
- The question tool is NOT optional - it's MANDATORY for any user interaction requiring a response
|
|
133
|
+
- If you catch yourself about to ask something in text, STOP and use the question tool instead
|
|
134
|
+
|
|
135
|
+
Remember: The user can see your tool usage, so be transparent about what you're doing and why.`;
|
|
136
|
+
|
|
137
|
+
export function getToolsPrompt(): string {
|
|
138
|
+
return TOOLS_PROMPT;
|
|
139
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { streamText, CoreMessage } from 'ai';
|
|
2
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
3
|
+
import { AgentEvent, Provider, ProviderConfig, ProviderSendOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
export class AnthropicProvider implements Provider {
|
|
6
|
+
async *sendMessage(
|
|
7
|
+
messages: CoreMessage[],
|
|
8
|
+
config: ProviderConfig,
|
|
9
|
+
options?: ProviderSendOptions
|
|
10
|
+
): AsyncGenerator<AgentEvent> {
|
|
11
|
+
const cleanApiKey = config.apiKey?.trim().replace(/[\r\n]+/g, '');
|
|
12
|
+
const cleanModel = config.model.trim().replace(/[\r\n]+/g, '');
|
|
13
|
+
|
|
14
|
+
const anthropic = createAnthropic({
|
|
15
|
+
apiKey: cleanApiKey,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const result = streamText({
|
|
19
|
+
model: anthropic(cleanModel),
|
|
20
|
+
messages: messages,
|
|
21
|
+
system: config.systemPrompt,
|
|
22
|
+
tools: config.tools,
|
|
23
|
+
maxSteps: config.maxSteps || 10,
|
|
24
|
+
abortSignal: options?.abortSignal,
|
|
25
|
+
experimental_providerMetadata: {
|
|
26
|
+
anthropic: {
|
|
27
|
+
thinkingBudgetTokens: 10000,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
let stepCounter = 0;
|
|
34
|
+
|
|
35
|
+
for await (const chunk of result.fullStream as any) {
|
|
36
|
+
const c: any = chunk;
|
|
37
|
+
switch (c.type) {
|
|
38
|
+
case 'reasoning':
|
|
39
|
+
if (c.textDelta) {
|
|
40
|
+
yield {
|
|
41
|
+
type: 'reasoning-delta',
|
|
42
|
+
content: c.textDelta,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'text-delta':
|
|
48
|
+
yield {
|
|
49
|
+
type: 'text-delta',
|
|
50
|
+
content: c.textDelta,
|
|
51
|
+
};
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case 'step-start':
|
|
55
|
+
yield {
|
|
56
|
+
type: 'step-start',
|
|
57
|
+
stepNumber: typeof c.stepIndex === 'number' ? c.stepIndex : stepCounter,
|
|
58
|
+
};
|
|
59
|
+
stepCounter++;
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case 'step-finish':
|
|
63
|
+
yield {
|
|
64
|
+
type: 'step-finish',
|
|
65
|
+
stepNumber:
|
|
66
|
+
typeof c.stepIndex === 'number' ? c.stepIndex : Math.max(0, stepCounter - 1),
|
|
67
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
68
|
+
};
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case 'tool-call':
|
|
72
|
+
yield {
|
|
73
|
+
type: 'tool-call-end',
|
|
74
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
75
|
+
toolName: String(c.toolName ?? ''),
|
|
76
|
+
args: (c.args ?? {}) as Record<string, unknown>,
|
|
77
|
+
};
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 'tool-result':
|
|
81
|
+
yield {
|
|
82
|
+
type: 'tool-result',
|
|
83
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
84
|
+
toolName: String(c.toolName ?? ''),
|
|
85
|
+
result: c.result,
|
|
86
|
+
};
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'finish':
|
|
90
|
+
yield {
|
|
91
|
+
type: 'finish',
|
|
92
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
93
|
+
usage: c.usage,
|
|
94
|
+
};
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case 'error':
|
|
98
|
+
{
|
|
99
|
+
const err = c.error;
|
|
100
|
+
const msg =
|
|
101
|
+
err instanceof Error
|
|
102
|
+
? err.message
|
|
103
|
+
: typeof err === 'string'
|
|
104
|
+
? err
|
|
105
|
+
: 'Unknown error';
|
|
106
|
+
yield {
|
|
107
|
+
type: 'error',
|
|
108
|
+
error: msg,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (options?.abortSignal?.aborted) return;
|
|
116
|
+
yield {
|
|
117
|
+
type: 'error',
|
|
118
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { streamText, CoreMessage } from 'ai';
|
|
2
|
+
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
3
|
+
import { AgentEvent, Provider, ProviderConfig, ProviderSendOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
export class GoogleProvider implements Provider {
|
|
6
|
+
async *sendMessage(
|
|
7
|
+
messages: CoreMessage[],
|
|
8
|
+
config: ProviderConfig,
|
|
9
|
+
options?: ProviderSendOptions
|
|
10
|
+
): AsyncGenerator<AgentEvent> {
|
|
11
|
+
const cleanApiKey = config.apiKey?.trim().replace(/[\r\n]+/g, '');
|
|
12
|
+
const cleanModel = config.model.trim().replace(/[\r\n]+/g, '');
|
|
13
|
+
|
|
14
|
+
const google = createGoogleGenerativeAI({
|
|
15
|
+
apiKey: cleanApiKey,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const result = streamText({
|
|
19
|
+
model: google(cleanModel),
|
|
20
|
+
messages: messages,
|
|
21
|
+
system: config.systemPrompt,
|
|
22
|
+
tools: config.tools,
|
|
23
|
+
maxSteps: config.maxSteps || 10,
|
|
24
|
+
abortSignal: options?.abortSignal,
|
|
25
|
+
providerOptions: {
|
|
26
|
+
google: {
|
|
27
|
+
thinkingConfig: {
|
|
28
|
+
style: 'THINKING_STYLE_DETAILED',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
let stepCounter = 0;
|
|
36
|
+
|
|
37
|
+
for await (const chunk of result.fullStream as any) {
|
|
38
|
+
const c: any = chunk;
|
|
39
|
+
switch (c.type) {
|
|
40
|
+
case 'reasoning':
|
|
41
|
+
if (c.textDelta) {
|
|
42
|
+
yield {
|
|
43
|
+
type: 'reasoning-delta',
|
|
44
|
+
content: c.textDelta,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case 'text-delta':
|
|
50
|
+
yield {
|
|
51
|
+
type: 'text-delta',
|
|
52
|
+
content: c.textDelta,
|
|
53
|
+
};
|
|
54
|
+
break;
|
|
55
|
+
|
|
56
|
+
case 'step-start':
|
|
57
|
+
yield {
|
|
58
|
+
type: 'step-start',
|
|
59
|
+
stepNumber: typeof c.stepIndex === 'number' ? c.stepIndex : stepCounter,
|
|
60
|
+
};
|
|
61
|
+
stepCounter++;
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'step-finish':
|
|
65
|
+
yield {
|
|
66
|
+
type: 'step-finish',
|
|
67
|
+
stepNumber:
|
|
68
|
+
typeof c.stepIndex === 'number' ? c.stepIndex : Math.max(0, stepCounter - 1),
|
|
69
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
70
|
+
};
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case 'tool-call':
|
|
74
|
+
yield {
|
|
75
|
+
type: 'tool-call-end',
|
|
76
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
77
|
+
toolName: String(c.toolName ?? ''),
|
|
78
|
+
args: (c.args ?? {}) as Record<string, unknown>,
|
|
79
|
+
};
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case 'tool-result':
|
|
83
|
+
yield {
|
|
84
|
+
type: 'tool-result',
|
|
85
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
86
|
+
toolName: String(c.toolName ?? ''),
|
|
87
|
+
result: c.result,
|
|
88
|
+
};
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'finish':
|
|
92
|
+
yield {
|
|
93
|
+
type: 'finish',
|
|
94
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
95
|
+
usage: c.usage,
|
|
96
|
+
};
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case 'error':
|
|
100
|
+
{
|
|
101
|
+
const err = c.error;
|
|
102
|
+
const msg =
|
|
103
|
+
err instanceof Error
|
|
104
|
+
? err.message
|
|
105
|
+
: typeof err === 'string'
|
|
106
|
+
? err
|
|
107
|
+
: 'Unknown error';
|
|
108
|
+
yield {
|
|
109
|
+
type: 'error',
|
|
110
|
+
error: msg,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (options?.abortSignal?.aborted) return;
|
|
118
|
+
yield {
|
|
119
|
+
type: 'error',
|
|
120
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { streamText, CoreMessage } from 'ai';
|
|
2
|
+
import { createMistral } from '@ai-sdk/mistral';
|
|
3
|
+
import { AgentEvent, Provider, ProviderConfig, ProviderSendOptions } from '../types';
|
|
4
|
+
|
|
5
|
+
export class MistralProvider implements Provider {
|
|
6
|
+
async *sendMessage(
|
|
7
|
+
messages: CoreMessage[],
|
|
8
|
+
config: ProviderConfig,
|
|
9
|
+
options?: ProviderSendOptions
|
|
10
|
+
): AsyncGenerator<AgentEvent> {
|
|
11
|
+
const cleanApiKey = config.apiKey?.trim().replace(/[\r\n]+/g, '');
|
|
12
|
+
const cleanModel = config.model.trim().replace(/[\r\n]+/g, '');
|
|
13
|
+
|
|
14
|
+
const mistral = createMistral({
|
|
15
|
+
apiKey: cleanApiKey,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const result = streamText({
|
|
19
|
+
model: mistral(cleanModel),
|
|
20
|
+
messages: messages,
|
|
21
|
+
system: config.systemPrompt,
|
|
22
|
+
tools: config.tools,
|
|
23
|
+
maxSteps: config.maxSteps || 10,
|
|
24
|
+
abortSignal: options?.abortSignal
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
let stepCounter = 0;
|
|
29
|
+
|
|
30
|
+
for await (const chunk of result.fullStream as any) {
|
|
31
|
+
const c: any = chunk;
|
|
32
|
+
switch (c.type) {
|
|
33
|
+
case 'reasoning':
|
|
34
|
+
if (c.textDelta) {
|
|
35
|
+
yield {
|
|
36
|
+
type: 'reasoning-delta',
|
|
37
|
+
content: c.textDelta,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case 'text-delta':
|
|
43
|
+
yield {
|
|
44
|
+
type: 'text-delta',
|
|
45
|
+
content: c.textDelta,
|
|
46
|
+
};
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case 'step-start':
|
|
50
|
+
yield {
|
|
51
|
+
type: 'step-start',
|
|
52
|
+
stepNumber: typeof c.stepIndex === 'number' ? c.stepIndex : stepCounter,
|
|
53
|
+
};
|
|
54
|
+
stepCounter++;
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
case 'step-finish':
|
|
58
|
+
yield {
|
|
59
|
+
type: 'step-finish',
|
|
60
|
+
stepNumber:
|
|
61
|
+
typeof c.stepIndex === 'number' ? c.stepIndex : Math.max(0, stepCounter - 1),
|
|
62
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
63
|
+
};
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
case 'tool-call':
|
|
67
|
+
yield {
|
|
68
|
+
type: 'tool-call-end',
|
|
69
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
70
|
+
toolName: String(c.toolName ?? ''),
|
|
71
|
+
args: (c.args ?? {}) as Record<string, unknown>,
|
|
72
|
+
};
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
case 'tool-result':
|
|
76
|
+
yield {
|
|
77
|
+
type: 'tool-result',
|
|
78
|
+
toolCallId: String(c.toolCallId ?? ''),
|
|
79
|
+
toolName: String(c.toolName ?? ''),
|
|
80
|
+
result: c.result,
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case 'finish':
|
|
85
|
+
yield {
|
|
86
|
+
type: 'finish',
|
|
87
|
+
finishReason: String(c.finishReason ?? 'stop'),
|
|
88
|
+
usage: c.usage,
|
|
89
|
+
};
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case 'error':
|
|
93
|
+
{
|
|
94
|
+
const err = c.error;
|
|
95
|
+
const msg =
|
|
96
|
+
err instanceof Error
|
|
97
|
+
? err.message
|
|
98
|
+
: typeof err === 'string'
|
|
99
|
+
? err
|
|
100
|
+
: 'Unknown error';
|
|
101
|
+
yield {
|
|
102
|
+
type: 'error',
|
|
103
|
+
error: msg,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (options?.abortSignal?.aborted) return;
|
|
111
|
+
yield {
|
|
112
|
+
type: 'error',
|
|
113
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|