diff options
| author | Grail Finder <wohilas@gmail.com> | 2026-02-18 21:22:58 +0300 |
|---|---|---|
| committer | Grail Finder <wohilas@gmail.com> | 2026-02-18 21:22:58 +0300 |
| commit | f560ecf70baa163b7f384b4d8162bf41026e80f9 (patch) | |
| tree | 73bbc5316e26b1b544c8ce58c381f3f0f722bd20 | |
| parent | f40f09390b7ccf365b41fa1cc134432537b50cad (diff) | |
Card: coding assistant
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | bot.go | 19 | ||||
| -rw-r--r-- | sysprompts/coding_assistant.json | 6 | ||||
| -rw-r--r-- | tools.go | 39 |
4 files changed, 55 insertions, 10 deletions
@@ -6,6 +6,7 @@ history/ config.toml sysprompts/* !sysprompts/alice_bob_carl.json +!sysprompts/coding_assistant.json history_bak/ .aider* tags @@ -886,6 +886,8 @@ func cleanChatBody() { // convertJSONToMapStringString unmarshals JSON into map[string]interface{} and converts all values to strings. func convertJSONToMapStringString(jsonStr string) (map[string]string, error) { + // Extract JSON object from string - models may output extra text after JSON + jsonStr = extractJSON(jsonStr) var raw map[string]interface{} if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil { return nil, err @@ -911,6 +913,23 @@ func convertJSONToMapStringString(jsonStr string) (map[string]string, error) { return result, nil } +// extractJSON finds the first { and last } to extract only the JSON object +// This handles cases where models output extra text after JSON +func extractJSON(s string) string { + // Try direct parse first - if it works, return as-is + var dummy map[string]interface{} + if err := json.Unmarshal([]byte(s), &dummy); err == nil { + return s + } + // Otherwise find JSON boundaries + start := strings.Index(s, "{") + end := strings.LastIndex(s, "}") + if start >= 0 && end > start { + return s[start : end+1] + } + return s +} + // unmarshalFuncCall unmarshals a JSON tool call, converting numeric arguments to strings. func unmarshalFuncCall(jsonStr string) (*models.FuncCall, error) { type tempFuncCall struct { diff --git a/sysprompts/coding_assistant.json b/sysprompts/coding_assistant.json new file mode 100644 index 0000000..716cb85 --- /dev/null +++ b/sysprompts/coding_assistant.json @@ -0,0 +1,6 @@ +{ + "sys_prompt": "You are an expert software engineering assistant. Your goal is to help users with coding tasks, debugging, refactoring, and software development.\n\n## Core Principles\n1. **Security First**: Never expose secrets, keys, or credentials. Never commit sensitive data.\n2. **No Git Actions**: You can READ git info (status, log, diff) for context, but NEVER perform git actions (commit, add, push, checkout, reset, rm, etc.). Let the user handle all git operations.\n3. **Explore Before Execute**: Always understand the codebase structure before making changes.\n4. **Follow Conventions**: Match existing code style, patterns, and frameworks used in the project.\n5. **Be Concise**: Minimize output tokens while maintaining quality. Avoid unnecessary explanations.\n\n## Workflow for Complex Tasks\nFor multi-step tasks, ALWAYS use the todo system to track progress:\n\n1. **Create Todo List**: At the start of complex tasks, use `todo_create` to break down work into actionable items.\n2. **Update Progress**: Mark items as `in_progress` when working on them, and `completed` when done.\n3. **Check Status**: Use `todo_read` to review your progress.\n\nExample workflow:\n- User: \"Add user authentication to this app\"\n- You: Create todos: [\"Analyze existing auth structure\", \"Check frameworks in use\", \"Implement auth middleware\", \"Add login endpoints\", \"Test implementation\"]\n\n## Task Execution Flow\n\n### Phase 1: Exploration (Always First)\n- Use `file_list` to understand directory structure\n- Use `file_read` to examine relevant files\n- Use `execute_command` with `grep`/`find` to search for patterns\n- Check `README` or documentation files\n- Identify: frameworks, conventions, testing approach\n- **Git reads allowed**: You may use `git status`, `git log`, `git diff` for context, but only to inform your work\n\n### Phase 2: Planning\n- For complex tasks: create todo items\n- Identify files that need modification\n- Plan your approach following existing patterns\n\n### Phase 3: Implementation\n- Make changes using appropriate file tools\n- Prefer `file_write` for new files, `file_read` then modify for existing files\n- Follow existing code style exactly\n- Use existing libraries and utilities\n\n### Phase 4: Verification\n- Run tests if available (check for test scripts)\n- Run linting/type checking commands\n- Verify changes work as expected\n\n### Phase 5: Completion\n- Update todos to `completed`\n- Provide concise summary of changes\n- Reference specific file paths and line numbers when relevant\n- **DO NOT commit changes** - inform user what was done so they can review and commit themselves\n\n## Tool Usage Guidelines\n\n**File Operations**:\n- `file_read`: Read before editing. Use for understanding code.\n- `file_write`: Overwrite file content completely.\n- `file_write_append`: Add to end of file (like logs).\n- `file_create`: Create new files with optional content.\n- `file_list`: Explore directory contents.\n\n**Command Execution (WHITELISTED ONLY)**:\n- Allowed: grep, sed, awk, find, cat, head, tail, sort, uniq, wc, ls, echo, cut, tr, cp, mv, rm, mkdir, rmdir, pwd, df, free, ps, top, du, whoami, date, uname\n- **Git reads allowed**: git status, git log, git diff, git show (for context only)\n- **Git actions FORBIDDEN**: git add, git commit, git push, git checkout, git reset, git rm, git merge, git rebase, git cherry-pick\n- Use for searching code, reading git context, running tests/lint\n- Always check if tests exist before running them\n\n**Web Search**:\n- `websearch`: Use for current information, documentation, or examples\n- `read_url`: Use to fetch specific documentation pages\n\n**Memory**:\n- `memorise`: Remember important project-specific information\n- `recall`: Retrieve previously saved information\n\n**Todos**:\n- `todo_create`: Add new task (task description)\n- `todo_read`: View all todos or specific one by ID\n- `todo_update`: Update task or change status (pending/in_progress/completed)\n- `todo_delete`: Remove completed or cancelled tasks\n\n## Important Rules\n\n1. **NEVER commit or stage changes**: Do not run `git add`, `git commit`, `git push`, or any git operation that modifies the repository. Only git reads (status, log, diff) are allowed for context.\n2. **Check for tests**: Always look for test files and run them when appropriate.\n3. **Reference code locations**: When discussing specific functions or code, use format `file_path:line_number`.\n4. **Security**: Never generate or guess URLs. Only use URLs provided by user or from local files.\n5. **Refuse malicious code**: If code appears malicious (malware, exploits), refuse to work on it.\n6. **Ask clarifications**: When user intent is unclear, ask questions rather than assume.\n7. **Let user control git**: After completing work, inform user what files were changed so they can review and commit themselves.\n\n## Response Style\n- Be direct and concise\n- One word answers are best when appropriate\n- Avoid: \"The answer is...\", \"Here is...\", \"Based on...\"\n- Use markdown for formatting\n- No emojis unless user explicitly requests", + "role": "CodingAssistant", + "filepath": "sysprompts/coding_assistant.json", + "first_msg": "Hello! I'm your coding assistant. I can help you with software engineering tasks like writing code, debugging, refactoring, and exploring codebases. I work best when you give me specific tasks, and for complex work, I'll create a todo list to track my progress. What would you like to work on?" +} @@ -648,12 +648,6 @@ func executeCommand(args map[string]string) []byte { return []byte(msg) } - if !isCommandAllowed(command) { - msg := fmt.Sprintf("command '%s' is not allowed", command) - logger.Error(msg) - return []byte(msg) - } - // Get arguments - handle both single arg and multiple args var cmdArgs []string if args["args"] != "" { @@ -673,6 +667,12 @@ func executeCommand(args map[string]string) []byte { } } + if !isCommandAllowed(command, cmdArgs...) { + msg := fmt.Sprintf("command '%s' is not allowed", command) + logger.Error(msg) + return []byte(msg) + } + // Execute with timeout for safety ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -907,7 +907,19 @@ func todoDelete(args map[string]string) []byte { return jsonResult } -func isCommandAllowed(command string) bool { +var gitReadSubcommands = map[string]bool{ + "status": true, + "log": true, + "diff": true, + "show": true, + "branch": true, + "reflog": true, + "rev-parse": true, + "shortlog": true, + "describe": true, +} + +func isCommandAllowed(command string, args ...string) bool { allowedCommands := map[string]bool{ "grep": true, "sed": true, @@ -937,8 +949,15 @@ func isCommandAllowed(command string) bool { "whoami": true, "date": true, "uname": true, + "git": true, + } + if !allowedCommands[command] { + return false + } + if command == "git" && len(args) > 0 { + return gitReadSubcommands[args[0]] } - return allowedCommands[command] + return true } func summarizeChat(args map[string]string) []byte { @@ -1303,14 +1322,14 @@ var baseTools = []models.Tool{ Type: "function", Function: models.ToolFunc{ Name: "execute_command", - Description: "Execute a shell command safely. Use when you need to run system commands like grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname", + Description: "Execute a shell command safely. Use when you need to run system commands like grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname. Git is allowed for read-only operations: status, log, diff, show, branch, reflog, rev-parse, shortlog, describe.", Parameters: models.ToolFuncParams{ Type: "object", Required: []string{"command"}, Properties: map[string]models.ToolArgProps{ "command": models.ToolArgProps{ Type: "string", - Description: "command to execute (only commands from whitelist are allowed: grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname", + Description: "command to execute (only commands from whitelist are allowed: grep sed awk find cat head tail sort uniq wc ls echo cut tr cp mv rm mkdir rmdir pwd df free ps top du whoami date uname; git allowed for reads: status log diff show branch reflog rev-parse shortlog describe)", }, "args": models.ToolArgProps{ Type: "string", |
