diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a79fc08..5898c57 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,35 +1,42 @@ --- name: Bug Report about: Report a bug with taracode -title: '[Bug] ' +title: '' labels: bug -assignees: dayanstef +assignees: '' --- ## Description -A clear description of the bug. + +A clear and concise description of the bug. ## Steps to Reproduce -1. Run `taracode ...` + +1. Run `taracode` 2. Enter '...' 3. See error ## Expected Behavior + What you expected to happen. ## Actual Behavior + What actually happened. ## Environment -- taracode version: [e.g., v0.4.5] -- OS: [e.g., macOS 14, Ubuntu 22.04] -- Ollama version: [e.g., 0.1.32] -- Model: [e.g., gemma3:27b] -## Logs +- **taracode version**: [run `taracode --version`] +- **OS**: [e.g., macOS 14.2, Ubuntu 22.04] +- **Ollama version**: [run `ollama --version`] +- **Model**: [e.g., gemma3:27b] + +## Logs / Error Output + ``` Paste relevant output here ``` ## Additional Context -Any other relevant information. + +Any other relevant information, screenshots, or context about the problem. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..f4f4c87 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Documentation + url: https://code.tara.vision/documentation + about: Check the documentation for usage guides and examples + - name: Discussions + url: https://github.com/tara-vision/taracode/discussions + about: Ask questions and share ideas in GitHub Discussions diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 3a028cc..9c37067 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,22 +1,34 @@ --- name: Feature Request about: Suggest a new feature for taracode -title: '[Feature] ' +title: '' labels: enhancement -assignees: dayanstef +assignees: '' --- -## Problem -Describe the problem this feature would solve. +## Problem Statement + +A clear description of the problem this feature would solve. + +Example: "I'm always frustrated when..." ## Proposed Solution -Describe your proposed solution. + +Describe your proposed solution or feature. ## Use Case -How would you use this feature? + +How would you use this feature in practice? + +```bash +# Example usage +> /new-command ... +``` ## Alternatives Considered -Any alternative solutions you've considered. + +Have you considered any alternative solutions or features? ## Additional Context -Any other relevant information. + +Any other context, screenshots, or examples about the feature request. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ade1f6f..82d3471 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,22 @@ ## Description -Brief description of changes. + +Brief description of what this PR does. + +## Related Issue + +Fixes #(issue number) or Related to #(issue number) ## Type of Change -- [ ] Bug fix -- [ ] New feature + +- [ ] Bug fix (non-breaking change that fixes an issue) +- [ ] New feature (non-breaking change that adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update -- [ ] Refactoring -- [ ] Other (describe): +- [ ] Refactoring (no functional changes) + +## How Has This Been Tested? -## Testing -How were these changes tested? +Describe the tests you ran: - [ ] Tested locally with Ollama - [ ] Added/updated unit tests @@ -17,8 +24,12 @@ How were these changes tested? - [ ] Tested on Linux ## Checklist -- [ ] Code follows project style guidelines -- [ ] Self-reviewed the code + +- [ ] My code follows the project style guidelines +- [ ] I have performed a self-review of my code +- [ ] `gofmt -s -l .` shows no files (code is formatted) +- [ ] `go vet ./...` passes - [ ] `make test` passes - [ ] `make build` succeeds -- [ ] Documentation updated if needed +- [ ] I have updated documentation if needed +- [ ] My changes generate no new warnings diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 429337f..ee158e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,76 @@ -name: CI (Build and Test) +name: CI on: - workflow_dispatch: - branches: - - main + push: + branches: [main] + pull_request: + branches: [main] jobs: test: + name: Test runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: true + + - name: Download dependencies + run: go mod download + + - name: Check formatting + run: | + if [ -n "$(gofmt -s -l .)" ]; then + echo "Code is not formatted with gofmt -s:" + gofmt -s -l . + exit 1 + fi + + - name: Run go vet + run: go vet ./... - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' - cache: true + - name: Run tests + run: go test -v -race -coverprofile=coverage.out ./... + + - name: Upload coverage + uses: codecov/codecov-action@v4 + with: + files: coverage.out + fail_ci_if_error: false + + build: + name: Build + runs-on: ubuntu-latest + needs: test + strategy: + matrix: + goos: [linux, darwin] + goarch: [amd64, arm64] + exclude: + - goos: linux + goarch: arm64 + steps: + - uses: actions/checkout@v4 - - name: Download dependencies - run: go mod download + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: true - - name: Run tests - run: make test + - name: Build + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + run: | + go build -ldflags "-s -w" -o taracode-${{ matrix.goos }}-${{ matrix.goarch }} . - - name: Build Check - run: make build \ No newline at end of file + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: taracode-${{ matrix.goos }}-${{ matrix.goarch }} + path: taracode-${{ matrix.goos }}-${{ matrix.goarch }} diff --git a/.gitignore b/.gitignore index fbbf041..753737c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Binaries taracode -taracode-linux +taracode-* *.exe *.exe~ *.dll diff --git a/CHANGELOG.md b/CHANGELOG.md index 9495662..37db9e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,27 @@ All notable changes to taracode will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +## [1.0.1] - 2026-02-04 + +### Fixed + +- **Memory manager not initializing after `/init`** - `/remember` and `/memory` commands now work immediately after + running `/init` in a fresh session +- **History manager not initializing after `/init`** - `/history` and `/undo` commands now work immediately after + running `/init` in a fresh session + +### Changed + +- Applied `gofmt -s` formatting across 32 files for Go Report Card compliance (89.3% → 100%) + ## [1.0.0] - 2026-01-31 ### Changed - **Open Source Release** - taracode is now open source under the MIT License -- **No Authentication Required** - removed login/logout, use immediately after install +- **No Authentication Required** - removed login/logout, use immediately after installation - **Security Mode for All** - security mode available to all users, no plan restrictions - **Simplified Configuration** - removed backend API integration @@ -36,9 +51,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Security audit logging - Operation history and undo - Context budget display -- Edit preview mode with diff display +- Edit preview mode with a diff display ## Pre-1.0 History -The project evolved from v0.1.0 through v0.4.5 as a commercial product before -being open-sourced in v1.0.0. +The project evolved through the following milestones before being open-sourced: + +- **v0.4.5** - Screen monitoring (`/watch`), multi-agent system enhancements +- **v0.4.2** - Multi-agent system with 7 specialized agents +- **v0.3.30** - Persistent project memory (`/remember`, `/memory`) +- **v0.3.27** - Autonomous task execution (`/task`) +- **v0.3.24** - MCP (Model Context Protocol) support +- **v0.3.18** - Security mode with audit logging +- **v0.3.15** - Web search resilience, context budget display +- **v0.3.12** - File reference autocomplete, permissions system +- **v0.3.8** - Native OpenAI function calling, security tools + +[Unreleased]: https://github.com/tara-vision/taracode/compare/v1.0.1...HEAD + +[1.0.1]: https://github.com/tara-vision/taracode/compare/v1.0.0...v1.0.1 + +[1.0.0]: https://github.com/tara-vision/taracode/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a267ced..e0a9266 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,11 +51,21 @@ ollama pull gemma3:27b ### Code Style - Follow standard Go conventions and formatting -- Run `go fmt` before committing -- Run `go vet` to catch common issues +- Run `gofmt -s -w .` before committing (the `-s` flag simplifies code) +- Run `go vet ./...` to catch common issues - Keep functions focused and well-documented - Write tests for new functionality +**Formatting check:** + +```bash +# Check if any files need formatting +gofmt -s -l . + +# Auto-format all files +gofmt -s -w . +``` + ## Submitting Changes ### Pull Request Process diff --git a/README.md b/README.md index 4c3b2ef..1f3a4e7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,33 @@ # taracode -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Go Version](https://img.shields.io/badge/Go-1.23-blue.svg)](https://go.dev/) -[![Go Report Card](https://goreportcard.com/badge/github.com/tara-vision/taracode)](https://goreportcard.com/report/github.com/tara-vision/taracode) - -**DevOps & Cloud AI Assistant** - Expert guidance for Kubernetes, Terraform, Docker, and multi-cloud deployments. Runs -locally with Ollama for complete privacy. +

+ DevOps & Cloud AI Assistant
+ Expert guidance for Kubernetes, Terraform, Docker, and multi-cloud deployments.
+ Runs locally with Ollama for complete privacy. +

+ +

+ Release + License: MIT + Go Report Card + Go Version +

+ +

+ Stars + Forks + Issues +

+ +

+ Quick Start • + Features • + Commands • + Documentation • + Contributing +

+ +--- ## Why taracode? @@ -13,7 +35,7 @@ locally with Ollama for complete privacy. - **58 Built-in Tools** - DevOps, security scanning, file operations, git, web search - **Multi-Agent System** - 7 specialized agents for complex tasks - **Privacy-first** - Runs fully local with Ollama, your data never leaves your machine -- **No Account Required** - Just install and use +- **No Account Required** - Open source, just install and use ## Quick Start @@ -44,8 +66,7 @@ curl -fsSL https://code.tara.vision/install.sh | bash **Homebrew (macOS / Linux):** ```bash -brew tap tara-vision/tap -brew install taracode +brew install tara-vision/tap/taracode ``` **Go install:** @@ -55,6 +76,7 @@ go install github.com/tara-vision/taracode@latest ``` **Manual download:** + Download binaries from [GitHub Releases](https://github.com/tara-vision/taracode/releases) ### 4. Run @@ -62,7 +84,7 @@ Download binaries from [GitHub Releases](https://github.com/tara-vision/taracode ```bash cd your-project taracode -> /init # Enable project features +> /init # Initialize project features ``` That's it! Start asking questions about your infrastructure. @@ -187,6 +209,8 @@ agents: enabled: true ``` +See [config.example.yaml](config.example.yaml) for all options. + ## Supported LLM Backends | Backend | Setup | Notes | @@ -204,14 +228,23 @@ make test # Run tests make install # Install to /usr/local/bin ``` +See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines. + ## Contributing -Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. +Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) +and [Code of Conduct](CODE_OF_CONDUCT.md). + +## Security + +For security issues, please see our [Security Policy](SECURITY.md). ## License MIT License - see [LICENSE](LICENSE) for details. -## About +--- -Built by [Tara Vision, LLC](https://tara.vision) | Created by [Dejan Stefanoski](https://stefanoski.nl) +

+ Built with ❤️ by Tara Vision · Created by Dejan Stefanoski +

diff --git a/cmd/repl.go b/cmd/repl.go index 0b79218..9a61be8 100644 --- a/cmd/repl.go +++ b/cmd/repl.go @@ -57,9 +57,9 @@ func startREPL() { workingDir, _ := os.Getwd() // Sandbox directory tracking - projectRoot := workingDir // Fixed at startup, becomes sandbox root after init - currentRelDir := "" // Relative path from project root (empty = at root) - currentAbsDir := workingDir // Current absolute path for file operations + projectRoot := workingDir // Fixed at startup, becomes sandbox root after init + currentRelDir := "" // Relative path from project root (empty = at root) + currentAbsDir := workingDir // Current absolute path for file operations // Create renderer for styled output renderer := ui.NewRenderer() @@ -345,13 +345,36 @@ func startREPL() { // Check if init succeeded if isInitializedProject(projectRoot) { isProjectInitialized = true + taracodeDir := filepath.Join(projectRoot, ".taracode") + // Initialize memory manager if not already done + if memoryManager == nil { + mm, err := memory.NewManager(taracodeDir) + if err == nil { + memoryManager = mm + } + } + // Initialize history manager if not already done + if historyManager == nil { + session := asst.GetSession() + sessionID := "default" + if session != nil { + sessionID = session.ID + } + hm, err := history.NewManager(taracodeDir, sessionID) + if err == nil { + historyManager = hm + // Set history manager on tool registry for automatic tracking + if registry := asst.GetToolRegistry(); registry != nil { + registry.SetHistoryManager(hm) + } + } + } // Auto-connect MCP servers after init if mcpManager != nil { mcpManager.AutoConnect(context.Background()) } // Initialize TaskBridge if not already done if taskBridge == nil { - taracodeDir := filepath.Join(projectRoot, ".taracode") taskMgr, err := storage.NewTaskManager(taracodeDir) if err == nil { taskBridge = orchestrator.NewTaskBridgeFromProvider( @@ -1342,10 +1365,10 @@ func extractFilesRead(messages []storage.ConversationMessage) []string { func exportAuditJSON(session *storage.Session, log *storage.AuditLog) { // Create export structure export := struct { - SessionID string `json:"session_id"` - SessionName string `json:"session_name"` - ExportedAt string `json:"exported_at"` - Mode string `json:"mode"` + SessionID string `json:"session_id"` + SessionName string `json:"session_name"` + ExportedAt string `json:"exported_at"` + Mode string `json:"mode"` Summary struct { TotalEntries int `json:"total_entries"` TotalAllow int `json:"total_allow"` diff --git a/cmd/watch_cmd.go b/cmd/watch_cmd.go index 240b030..cf329bb 100644 --- a/cmd/watch_cmd.go +++ b/cmd/watch_cmd.go @@ -15,24 +15,24 @@ import ( var ( watchHeaderStyle = lipgloss.NewStyle(). - Bold(true). - Foreground(lipgloss.Color("#A78BFA")) + Bold(true). + Foreground(lipgloss.Color("#A78BFA")) watchActiveStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#2dd4bf")) + Foreground(lipgloss.Color("#2dd4bf")) watchInactiveStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#94A3B8")) + Foreground(lipgloss.Color("#94A3B8")) watchErrorStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FCA5A5")). - Bold(true) + Foreground(lipgloss.Color("#FCA5A5")). + Bold(true) watchWarningStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FCD34D")) + Foreground(lipgloss.Color("#FCD34D")) watchImprovementStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#93C5FD")) + Foreground(lipgloss.Color("#93C5FD")) ) // handleWatchCommand handles the /watch command and subcommands diff --git a/internal/agent/config_test.go b/internal/agent/config_test.go index 772d24f..33f5033 100644 --- a/internal/agent/config_test.go +++ b/internal/agent/config_test.go @@ -35,11 +35,11 @@ func TestGetAgentConfig(t *testing.T) { expectedTemp float32 }{ {TypePlanner, 0.3}, - {TypeCoder, 0.4}, // Coder uses higher temperature for creative code generation + {TypeCoder, 0.4}, // Coder uses higher temperature for creative code generation {TypeTester, 0.2}, - {TypeReviewer, 0.5}, // Reviewer uses higher temperature for varied feedback - {TypeDevOps, 0.3}, // DevOps temperature for infrastructure tasks - {TypeSecurity, 0.2}, // Security uses low temperature for precise analysis + {TypeReviewer, 0.5}, // Reviewer uses higher temperature for varied feedback + {TypeDevOps, 0.3}, // DevOps temperature for infrastructure tasks + {TypeSecurity, 0.2}, // Security uses low temperature for precise analysis {TypeDiagnostics, 0.2}, } diff --git a/internal/agent/planner.go b/internal/agent/planner.go index 2d0e82b..3e71222 100644 --- a/internal/agent/planner.go +++ b/internal/agent/planner.go @@ -33,18 +33,18 @@ type TaskPlan struct { // PlanStep represents a single step in the plan type PlanStep struct { - Index int `json:"index"` - Name string `json:"name"` - Description string `json:"description"` - AgentType Type `json:"agent_type"` - ActionType string `json:"action_type"` // tool, command, analyze - Tool string `json:"tool,omitempty"` - Command string `json:"command,omitempty"` - Prompt string `json:"prompt,omitempty"` - DependsOn []int `json:"depends_on,omitempty"` - Checkpoint bool `json:"checkpoint,omitempty"` - Verification string `json:"verification,omitempty"` - OnFailure string `json:"on_failure,omitempty"` // retry, skip, abort, rollback + Index int `json:"index"` + Name string `json:"name"` + Description string `json:"description"` + AgentType Type `json:"agent_type"` + ActionType string `json:"action_type"` // tool, command, analyze + Tool string `json:"tool,omitempty"` + Command string `json:"command,omitempty"` + Prompt string `json:"prompt,omitempty"` + DependsOn []int `json:"depends_on,omitempty"` + Checkpoint bool `json:"checkpoint,omitempty"` + Verification string `json:"verification,omitempty"` + OnFailure string `json:"on_failure,omitempty"` // retry, skip, abort, rollback } // CanHandle returns true for planning-related tasks diff --git a/internal/agent/router.go b/internal/agent/router.go index 58ed789..49a8e7b 100644 --- a/internal/agent/router.go +++ b/internal/agent/router.go @@ -16,9 +16,9 @@ const ( // RoutingRule defines a rule for routing tasks to agents type RoutingRule struct { - Pattern string `yaml:"pattern" json:"pattern"` // Regex pattern to match + Pattern string `yaml:"pattern" json:"pattern"` // Regex pattern to match AgentType Type `yaml:"agent_type" json:"agent_type"` // Agent to route to - Priority int `yaml:"priority" json:"priority"` // Higher = checked first + Priority int `yaml:"priority" json:"priority"` // Higher = checked first } // Router handles agent selection based on task content and routing rules @@ -177,11 +177,11 @@ func (r *Router) RouteByToolName(toolName string) Type { // GetRoutingInfo returns information about how a prompt would be routed type RoutingInfo struct { - Prompt string `json:"prompt"` - MatchedRule string `json:"matched_rule,omitempty"` - MatchedAgent Type `json:"matched_agent"` - Confidence string `json:"confidence"` // high, medium, low - Alternatives []Type `json:"alternatives,omitempty"` + Prompt string `json:"prompt"` + MatchedRule string `json:"matched_rule,omitempty"` + MatchedAgent Type `json:"matched_agent"` + Confidence string `json:"confidence"` // high, medium, low + Alternatives []Type `json:"alternatives,omitempty"` } // ExplainRouting explains how a prompt would be routed diff --git a/internal/assistant/task_planner.go b/internal/assistant/task_planner.go index ca0ea7f..1023165 100644 --- a/internal/assistant/task_planner.go +++ b/internal/assistant/task_planner.go @@ -54,9 +54,9 @@ func (tp *TaskPlanner) PlanTask(taskDescription string) (*storage.TaskExecution, // TaskPlan is the intermediate plan structure from LLM type TaskPlan struct { - Name string `json:"name"` - Description string `json:"description"` - Steps []storage.TaskStep `json:"steps"` + Name string `json:"name"` + Description string `json:"description"` + Steps []storage.TaskStep `json:"steps"` } // generatePlan uses the LLM to create an execution plan @@ -176,9 +176,9 @@ func (tp *TaskPlanner) parsePlanResponse(response string) (*TaskPlan, error) { Name string `json:"name"` Description string `json:"description"` Steps []struct { - Name string `json:"name"` - Description string `json:"description"` - Action struct { + Name string `json:"name"` + Description string `json:"description"` + Action struct { Type string `json:"type"` Tool string `json:"tool,omitempty"` Params map[string]interface{} `json:"params,omitempty"` diff --git a/internal/context/detector.go b/internal/context/detector.go index 5f94721..f0545fa 100644 --- a/internal/context/detector.go +++ b/internal/context/detector.go @@ -19,7 +19,7 @@ type ProjectTypeInfo struct { // ToolMapping maps frameworks/tools to relevant taracode tools var ToolMapping = map[string][]string{ // Infrastructure as Code - "terraform": {"terraform_init", "terraform_plan", "terraform_apply", "terraform_destroy", "terraform_output", "terraform_state", "tfsec_scan"}, + "terraform": {"terraform_init", "terraform_plan", "terraform_apply", "terraform_destroy", "terraform_output", "terraform_state", "tfsec_scan"}, "kubernetes": {"kubectl_get", "kubectl_apply", "kubectl_delete", "kubectl_describe", "kubectl_logs", "kubectl_exec", "kubesec_scan"}, "helm": {"helm_list", "helm_install", "kubectl_get", "kubectl_apply"}, "docker": {"docker_build", "docker_ps", "docker_logs", "docker_compose", "docker_exec", "trivy_scan"}, diff --git a/internal/history/manager.go b/internal/history/manager.go index 1f4fe6e..9c0cab2 100644 --- a/internal/history/manager.go +++ b/internal/history/manager.go @@ -547,10 +547,10 @@ func (m *Manager) GetStats() map[string]int { // FileDiff represents a diff for a single file type FileDiff struct { - Path string - Operation string // "modified", "created", "deleted", "moved", "copied" + Path string + Operation string // "modified", "created", "deleted", "moved", "copied" OriginalPath string // For move operations - Diff string // Unified diff content + Diff string // Unified diff content } // GenerateDiff generates unified diffs for all file changes in the session diff --git a/internal/history/types.go b/internal/history/types.go index 3626083..8533abe 100644 --- a/internal/history/types.go +++ b/internal/history/types.go @@ -6,31 +6,31 @@ import "time" type OperationType string const ( - OpTypeWrite OperationType = "write" // write_file, append_file - OpTypeEdit OperationType = "edit" // edit_file, insert_lines, replace_lines, delete_lines - OpTypeDelete OperationType = "delete" // delete_file - OpTypeMove OperationType = "move" // move_file - OpTypeCopy OperationType = "copy" // copy_file - OpTypeRead OperationType = "read" // read_file (non-mutating, for context) - OpTypeCreate OperationType = "create" // create_directory - OpTypeExecute OperationType = "execute" // execute_command (non-undoable) + OpTypeWrite OperationType = "write" // write_file, append_file + OpTypeEdit OperationType = "edit" // edit_file, insert_lines, replace_lines, delete_lines + OpTypeDelete OperationType = "delete" // delete_file + OpTypeMove OperationType = "move" // move_file + OpTypeCopy OperationType = "copy" // copy_file + OpTypeRead OperationType = "read" // read_file (non-mutating, for context) + OpTypeCreate OperationType = "create" // create_directory + OpTypeExecute OperationType = "execute" // execute_command (non-undoable) ) // Operation represents a single tool operation in the history type Operation struct { - ID int `json:"id"` // Sequential ID within session - Timestamp time.Time `json:"timestamp"` // When operation occurred - Tool string `json:"tool"` // Tool name (e.g., "edit_file") - Type OperationType `json:"type"` // Operation category - Params map[string]interface{} `json:"params"` // Tool parameters - Target string `json:"target"` // Primary target (file path) - BackupPath string `json:"backup_path,omitempty"` // Path to backup file (for undoable ops) - Result string `json:"result"` // "success" or error message - Success bool `json:"success"` // Whether operation succeeded - LinesChanged int `json:"lines_changed,omitempty"` // Number of lines affected - BytesWritten int64 `json:"bytes_written,omitempty"` // Bytes written/modified - Undone bool `json:"undone,omitempty"` // Whether this op was undone - UndoneAt *time.Time `json:"undone_at,omitempty"` // When it was undone + ID int `json:"id"` // Sequential ID within session + Timestamp time.Time `json:"timestamp"` // When operation occurred + Tool string `json:"tool"` // Tool name (e.g., "edit_file") + Type OperationType `json:"type"` // Operation category + Params map[string]interface{} `json:"params"` // Tool parameters + Target string `json:"target"` // Primary target (file path) + BackupPath string `json:"backup_path,omitempty"` // Path to backup file (for undoable ops) + Result string `json:"result"` // "success" or error message + Success bool `json:"success"` // Whether operation succeeded + LinesChanged int `json:"lines_changed,omitempty"` // Number of lines affected + BytesWritten int64 `json:"bytes_written,omitempty"` // Bytes written/modified + Undone bool `json:"undone,omitempty"` // Whether this op was undone + UndoneAt *time.Time `json:"undone_at,omitempty"` // When it was undone // For delete operations, store content to restore DeletedContent string `json:"deleted_content,omitempty"` @@ -95,10 +95,10 @@ func ToolToOperationType(toolName string) OperationType { // UndoResult contains the result of an undo operation type UndoResult struct { - OperationID int `json:"operation_id"` - Tool string `json:"tool"` - Target string `json:"target"` - Success bool `json:"success"` - Message string `json:"message"` - RestoredFrom string `json:"restored_from,omitempty"` // Backup path used + OperationID int `json:"operation_id"` + Tool string `json:"tool"` + Target string `json:"target"` + Success bool `json:"success"` + Message string `json:"message"` + RestoredFrom string `json:"restored_from,omitempty"` // Backup path used } diff --git a/internal/mcp/client.go b/internal/mcp/client.go index cb45d04..777bb5a 100644 --- a/internal/mcp/client.go +++ b/internal/mcp/client.go @@ -16,10 +16,10 @@ import ( // Client is an MCP client that communicates with an MCP server via stdio type Client struct { - cmd *exec.Cmd - stdin io.WriteCloser - stdout *bufio.Reader - stderr io.ReadCloser + cmd *exec.Cmd + stdin io.WriteCloser + stdout *bufio.Reader + stderr io.ReadCloser mu sync.Mutex nextID int64 @@ -53,8 +53,8 @@ type jsonRPCResponse struct { // jsonRPCError represents a JSON-RPC 2.0 error type jsonRPCError struct { - Code int `json:"code"` - Message string `json:"message"` + Code int `json:"code"` + Message string `json:"message"` Data interface{} `json:"data,omitempty"` } @@ -166,7 +166,7 @@ func (c *Client) Connect(ctx context.Context) error { // Parse server info var initResult struct { - ProtocolVersion string `json:"protocolVersion"` + ProtocolVersion string `json:"protocolVersion"` Capabilities interface{} `json:"capabilities"` ServerInfo *ServerInfo `json:"serverInfo"` } diff --git a/internal/mcp/types.go b/internal/mcp/types.go index 967a569..ff0e5e5 100644 --- a/internal/mcp/types.go +++ b/internal/mcp/types.go @@ -22,11 +22,11 @@ type MCPConfig struct { // MCPTool represents a tool discovered from an MCP server type MCPTool struct { - Name string // Prefixed name: "github.list_repos" - ServerName string // Server that provides this tool - OriginalName string // Original name without prefix - Description string - InputSchema map[string]interface{} + Name string // Prefixed name: "github.list_repos" + ServerName string // Server that provides this tool + OriginalName string // Original name without prefix + Description string + InputSchema map[string]interface{} } // MCPConnection represents a connection to an MCP server diff --git a/internal/orchestrator/diagnostics.go b/internal/orchestrator/diagnostics.go index 6f0a1aa..d0fb1f5 100644 --- a/internal/orchestrator/diagnostics.go +++ b/internal/orchestrator/diagnostics.go @@ -10,17 +10,17 @@ import ( // FailureDiagnostics provides detailed analysis of failures type FailureDiagnostics struct { - ToolName string `json:"tool_name"` + ToolName string `json:"tool_name"` Params map[string]interface{} `json:"params"` - Error string `json:"error"` - ExitCode int `json:"exit_code,omitempty"` - Stderr string `json:"stderr,omitempty"` - Stdout string `json:"stdout,omitempty"` - RootCause string `json:"root_cause"` - Suggestion string `json:"suggestion"` - Context string `json:"context,omitempty"` - Severity string `json:"severity"` // low, medium, high, critical - Recoverable bool `json:"recoverable"` + Error string `json:"error"` + ExitCode int `json:"exit_code,omitempty"` + Stderr string `json:"stderr,omitempty"` + Stdout string `json:"stdout,omitempty"` + RootCause string `json:"root_cause"` + Suggestion string `json:"suggestion"` + Context string `json:"context,omitempty"` + Severity string `json:"severity"` // low, medium, high, critical + Recoverable bool `json:"recoverable"` } // DiagnoseFailure analyzes a tool call failure and provides actionable information diff --git a/internal/orchestrator/types.go b/internal/orchestrator/types.go index 3331f5f..5e3bcc7 100644 --- a/internal/orchestrator/types.go +++ b/internal/orchestrator/types.go @@ -9,12 +9,12 @@ import ( // OrchestratorConfig defines orchestrator configuration type OrchestratorConfig struct { - Enabled bool `yaml:"enabled" json:"enabled"` - DefaultRouting string `yaml:"default_routing" json:"default_routing"` // auto, manual, task-based - FallbackModel string `yaml:"fallback_model" json:"fallback_model"` - TimeoutMultiplier float64 `yaml:"timeout_multiplier" json:"timeout_multiplier"` - MaxReplans int `yaml:"max_replans" json:"max_replans"` - AutoDiagnostics bool `yaml:"auto_diagnostics" json:"auto_diagnostics"` + Enabled bool `yaml:"enabled" json:"enabled"` + DefaultRouting string `yaml:"default_routing" json:"default_routing"` // auto, manual, task-based + FallbackModel string `yaml:"fallback_model" json:"fallback_model"` + TimeoutMultiplier float64 `yaml:"timeout_multiplier" json:"timeout_multiplier"` + MaxReplans int `yaml:"max_replans" json:"max_replans"` + AutoDiagnostics bool `yaml:"auto_diagnostics" json:"auto_diagnostics"` } // DefaultOrchestratorConfig returns default configuration @@ -31,16 +31,16 @@ func DefaultOrchestratorConfig() OrchestratorConfig { // TaskState tracks the state of a task during orchestration type TaskState struct { - TaskID string `json:"task_id"` - Status storage.TaskExecutionStatus `json:"status"` - CurrentStep int `json:"current_step"` - TotalSteps int `json:"total_steps"` - ActiveAgent agent.Type `json:"active_agent"` - StartedAt time.Time `json:"started_at"` - UpdatedAt time.Time `json:"updated_at"` - ReplanCount int `json:"replan_count"` - Context *SharedContext `json:"context"` - Handoffs []agent.Handoff `json:"handoffs"` + TaskID string `json:"task_id"` + Status storage.TaskExecutionStatus `json:"status"` + CurrentStep int `json:"current_step"` + TotalSteps int `json:"total_steps"` + ActiveAgent agent.Type `json:"active_agent"` + StartedAt time.Time `json:"started_at"` + UpdatedAt time.Time `json:"updated_at"` + ReplanCount int `json:"replan_count"` + Context *SharedContext `json:"context"` + Handoffs []agent.Handoff `json:"handoffs"` } // SharedContext manages context shared between agents @@ -137,11 +137,11 @@ func (sc *SharedContext) Clear() { // StepResult represents the result of executing a step type StepResult struct { - StepIndex int `json:"step_index"` - AgentType agent.Type `json:"agent_type"` + StepIndex int `json:"step_index"` + AgentType agent.Type `json:"agent_type"` Result *agent.ExecutionResult `json:"result"` - StartedAt time.Time `json:"started_at"` - CompletedAt time.Time `json:"completed_at"` + StartedAt time.Time `json:"started_at"` + CompletedAt time.Time `json:"completed_at"` } // ExecutionEvent represents an event during task execution diff --git a/internal/permissions/permissions.go b/internal/permissions/permissions.go index 2288a36..8db2b03 100644 --- a/internal/permissions/permissions.go +++ b/internal/permissions/permissions.go @@ -32,9 +32,9 @@ const ( // PermissionConfig represents the structure stored in .taracode/permissions.json type PermissionConfig struct { - Version int `json:"version"` + Version int `json:"version"` Categories map[PermissionCategory]Permission `json:"categories"` - Tools map[string]Permission `json:"tools"` + Tools map[string]Permission `json:"tools"` } // toolCategoryMap maps tool names to their categories diff --git a/internal/search/brave.go b/internal/search/brave.go index a0f1097..0f8e452 100644 --- a/internal/search/brave.go +++ b/internal/search/brave.go @@ -11,7 +11,7 @@ import ( ) const ( - braveAPIEndpoint = "https://api.search.brave.com/res/v1/web/search" + braveAPIEndpoint = "https://api.search.brave.com/res/v1/web/search" braveDefaultTimeout = 10 * time.Second ) diff --git a/internal/search/searxng.go b/internal/search/searxng.go index 027cd9e..f0ab13f 100644 --- a/internal/search/searxng.go +++ b/internal/search/searxng.go @@ -126,8 +126,8 @@ type searxngResponse struct { PrettyURL string `json:"pretty_url"` PublishedDate string `json:"publishedDate,omitempty"` } `json:"results"` - Answers []string `json:"answers"` - Infoboxes []struct { + Answers []string `json:"answers"` + Infoboxes []struct { Infobox string `json:"infobox"` ID string `json:"id"` Content string `json:"content"` diff --git a/internal/storage/memory_types.go b/internal/storage/memory_types.go index 78d8ab5..9b73fa5 100644 --- a/internal/storage/memory_types.go +++ b/internal/storage/memory_types.go @@ -27,8 +27,8 @@ type Memory struct { ID string `json:"id"` Category MemoryCategory `json:"category"` Content string `json:"content"` - Context string `json:"context,omitempty"` // Additional context about when/why - Tags []string `json:"tags,omitempty"` // Searchable tags + Context string `json:"context,omitempty"` // Additional context about when/why + Tags []string `json:"tags,omitempty"` // Searchable tags Source MemorySource `json:"source"` CreatedAt time.Time `json:"created_at"` LastUsedAt time.Time `json:"last_used_at"` @@ -46,7 +46,7 @@ type MemoryIndex struct { type MemoryMetadata struct { ID string `json:"id"` Category MemoryCategory `json:"category"` - Preview string `json:"preview"` // First 80 chars of content + Preview string `json:"preview"` // First 80 chars of content Tags []string `json:"tags,omitempty"` CreatedAt time.Time `json:"created_at"` LastUsedAt time.Time `json:"last_used_at"` @@ -57,9 +57,9 @@ type MemoryMetadata struct { type MemoryConfig struct { Enabled bool `json:"enabled"` MaxMemories int `json:"max_memories"` - MaxContextTokens int `json:"max_context_tokens"` // Max tokens to inject into prompt - RetentionDays int `json:"retention_days"` // Auto-cleanup after N days of non-use - AutoCapture bool `json:"auto_capture"` // Detect and suggest memories + MaxContextTokens int `json:"max_context_tokens"` // Max tokens to inject into prompt + RetentionDays int `json:"retention_days"` // Auto-cleanup after N days of non-use + AutoCapture bool `json:"auto_capture"` // Detect and suggest memories Categories []MemoryCategory `json:"categories,omitempty"` // Enabled categories (nil = all) } @@ -84,15 +84,15 @@ type MemoryExport struct { // MemoryStats contains statistics about the memory store type MemoryStats struct { - TotalMemories int `json:"total_memories"` - ByCategory map[string]int `json:"by_category"` - BySource map[string]int `json:"by_source"` - TotalUseCount int `json:"total_use_count"` - OldestMemory *time.Time `json:"oldest_memory,omitempty"` - NewestMemory *time.Time `json:"newest_memory,omitempty"` - MostUsedID string `json:"most_used_id,omitempty"` - MostUsedContent string `json:"most_used_content,omitempty"` - MostUsedCount int `json:"most_used_count"` - UnusedCount int `json:"unused_count"` // Memories never injected - EstimatedTokens int `json:"estimated_tokens"` + TotalMemories int `json:"total_memories"` + ByCategory map[string]int `json:"by_category"` + BySource map[string]int `json:"by_source"` + TotalUseCount int `json:"total_use_count"` + OldestMemory *time.Time `json:"oldest_memory,omitempty"` + NewestMemory *time.Time `json:"newest_memory,omitempty"` + MostUsedID string `json:"most_used_id,omitempty"` + MostUsedContent string `json:"most_used_content,omitempty"` + MostUsedCount int `json:"most_used_count"` + UnusedCount int `json:"unused_count"` // Memories never injected + EstimatedTokens int `json:"estimated_tokens"` } diff --git a/internal/storage/types.go b/internal/storage/types.go index 384fd56..4415a4b 100644 --- a/internal/storage/types.go +++ b/internal/storage/types.go @@ -17,13 +17,13 @@ type Session struct { // ConversationMessage represents a single message in conversation type ConversationMessage struct { - Role string `json:"role"` // user, assistant, system, tool - Content string `json:"content"` - Timestamp time.Time `json:"timestamp"` - ToolCalls []ToolCallRecord `json:"tool_calls,omitempty"` // Multiple tool calls per message (native function calling) - ToolCallID string `json:"tool_call_id,omitempty"` // For tool response messages - ToolCall *ToolCallRecord `json:"tool_call,omitempty"` // Deprecated: kept for backward compatibility with old sessions - Usage *TokenUsage `json:"usage,omitempty"` + Role string `json:"role"` // user, assistant, system, tool + Content string `json:"content"` + Timestamp time.Time `json:"timestamp"` + ToolCalls []ToolCallRecord `json:"tool_calls,omitempty"` // Multiple tool calls per message (native function calling) + ToolCallID string `json:"tool_call_id,omitempty"` // For tool response messages + ToolCall *ToolCallRecord `json:"tool_call,omitempty"` // Deprecated: kept for backward compatibility with old sessions + Usage *TokenUsage `json:"usage,omitempty"` } // ToolCallRecord captures tool execution details @@ -229,7 +229,7 @@ type TaskStep struct { CompletedAt *time.Time `json:"completed_at,omitempty"` Duration int64 `json:"duration_ms,omitempty"` RetryCount int `json:"retry_count,omitempty"` - Checkpoint bool `json:"checkpoint,omitempty"` // Create checkpoint before this step + Checkpoint bool `json:"checkpoint,omitempty"` // Create checkpoint before this step Verification *TaskVerify `json:"verification,omitempty"` // Optional verification after step } @@ -237,13 +237,13 @@ type TaskStep struct { type TaskStepStatus string const ( - StepStatusPending TaskStepStatus = "pending" - StepStatusRunning TaskStepStatus = "running" - StepStatusCompleted TaskStepStatus = "completed" - StepStatusFailed TaskStepStatus = "failed" - StepStatusSkipped TaskStepStatus = "skipped" - StepStatusVerifying TaskStepStatus = "verifying" - StepStatusRetrying TaskStepStatus = "retrying" + StepStatusPending TaskStepStatus = "pending" + StepStatusRunning TaskStepStatus = "running" + StepStatusCompleted TaskStepStatus = "completed" + StepStatusFailed TaskStepStatus = "failed" + StepStatusSkipped TaskStepStatus = "skipped" + StepStatusVerifying TaskStepStatus = "verifying" + StepStatusRetrying TaskStepStatus = "retrying" ) // TaskAction defines what a step should do @@ -267,13 +267,13 @@ const ( // TaskVerify defines how to verify a step completed successfully type TaskVerify struct { - Type TaskVerifyType `json:"type"` - Command string `json:"command,omitempty"` // Shell command to run - Expected string `json:"expected,omitempty"` // Expected output (substring match) - Tool string `json:"tool,omitempty"` // Tool to run for verification + Type TaskVerifyType `json:"type"` + Command string `json:"command,omitempty"` // Shell command to run + Expected string `json:"expected,omitempty"` // Expected output (substring match) + Tool string `json:"tool,omitempty"` // Tool to run for verification Params map[string]interface{} `json:"params,omitempty"` - Timeout int `json:"timeout,omitempty"` // Timeout in seconds - OnFailure string `json:"on_failure,omitempty"` // "retry", "skip", "abort", "rollback" + Timeout int `json:"timeout,omitempty"` // Timeout in seconds + OnFailure string `json:"on_failure,omitempty"` // "retry", "skip", "abort", "rollback" } // TaskVerifyType defines the verification method @@ -288,11 +288,11 @@ const ( // TaskCheckpoint captures the state at a point in time for rollback type TaskCheckpoint struct { - ID string `json:"id"` - StepIndex int `json:"step_index"` // Step index this checkpoint was created before - CreatedAt time.Time `json:"created_at"` + ID string `json:"id"` + StepIndex int `json:"step_index"` // Step index this checkpoint was created before + CreatedAt time.Time `json:"created_at"` Files []FileBackup `json:"files,omitempty"` // Files backed up at this checkpoint - Note string `json:"note,omitempty"` + Note string `json:"note,omitempty"` } // FileBackup tracks a file backup for rollback @@ -304,8 +304,8 @@ type FileBackup struct { // TaskIndex tracks all task executions type TaskIndex struct { - ActiveTaskID string `json:"active_task_id,omitempty"` - Tasks []TaskMetadata `json:"tasks"` + ActiveTaskID string `json:"active_task_id,omitempty"` + Tasks []TaskMetadata `json:"tasks"` } // TaskMetadata contains summary info about a task execution @@ -321,21 +321,21 @@ type TaskMetadata struct { // TaskTemplate defines a reusable task definition (YAML) type TaskTemplate struct { - Name string `yaml:"name" json:"name"` - Description string `yaml:"description,omitempty" json:"description,omitempty"` - Variables map[string]string `yaml:"variables,omitempty" json:"variables,omitempty"` - Steps []TaskTemplateStep `yaml:"steps" json:"steps"` + Name string `yaml:"name" json:"name"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Variables map[string]string `yaml:"variables,omitempty" json:"variables,omitempty"` + Steps []TaskTemplateStep `yaml:"steps" json:"steps"` } // TaskTemplateStep defines a step in a task template type TaskTemplateStep struct { - Name string `yaml:"name" json:"name"` - Action string `yaml:"action" json:"action"` // Tool name or "command" - Params map[string]interface{} `yaml:"params,omitempty" json:"params,omitempty"` - Verify *TaskTemplateVerify `yaml:"verify,omitempty" json:"verify,omitempty"` - Checkpoint bool `yaml:"checkpoint,omitempty" json:"checkpoint,omitempty"` - OnFailure string `yaml:"on_failure,omitempty" json:"on_failure,omitempty"` - Timeout int `yaml:"timeout,omitempty" json:"timeout,omitempty"` + Name string `yaml:"name" json:"name"` + Action string `yaml:"action" json:"action"` // Tool name or "command" + Params map[string]interface{} `yaml:"params,omitempty" json:"params,omitempty"` + Verify *TaskTemplateVerify `yaml:"verify,omitempty" json:"verify,omitempty"` + Checkpoint bool `yaml:"checkpoint,omitempty" json:"checkpoint,omitempty"` + OnFailure string `yaml:"on_failure,omitempty" json:"on_failure,omitempty"` + Timeout int `yaml:"timeout,omitempty" json:"timeout,omitempty"` } // TaskTemplateVerify defines verification in a template diff --git a/internal/tools/definitions.go b/internal/tools/definitions.go index 44034e6..dc59954 100644 --- a/internal/tools/definitions.go +++ b/internal/tools/definitions.go @@ -1457,64 +1457,64 @@ func GetToolDefinitionsByCategory(category string) []openai.Tool { // getDefinitionsMap returns a map of tool name to definition for category lookups func getDefinitionsMap() map[string]ToolDefinition { return map[string]ToolDefinition{ - "read_file": {Category: "file"}, - "write_file": {Category: "file"}, - "append_file": {Category: "file"}, - "edit_file": {Category: "file"}, - "insert_lines": {Category: "file"}, - "replace_lines": {Category: "file"}, - "delete_lines": {Category: "file"}, - "copy_file": {Category: "file"}, - "move_file": {Category: "file"}, - "delete_file": {Category: "file"}, - "create_directory": {Category: "file"}, - "list_files": {Category: "file"}, - "find_files": {Category: "file"}, - "execute_command": {Category: "command"}, - "search_files": {Category: "command"}, - "git_status": {Category: "git"}, - "git_diff": {Category: "git"}, - "git_log": {Category: "git"}, - "git_add": {Category: "git"}, - "git_commit": {Category: "git"}, - "git_branch": {Category: "git"}, - "git_stash": {Category: "git"}, - "web_search": {Category: "web"}, - "web_fetch": {Category: "web"}, - "get_datetime": {Category: "utility"}, - "kubectl_get": {Category: "kubernetes"}, - "kubectl_apply": {Category: "kubernetes"}, - "kubectl_delete": {Category: "kubernetes"}, - "kubectl_describe": {Category: "kubernetes"}, - "kubectl_logs": {Category: "kubernetes"}, - "kubectl_exec": {Category: "kubernetes"}, - "helm_list": {Category: "kubernetes"}, - "helm_install": {Category: "kubernetes"}, - "terraform_init": {Category: "terraform"}, - "terraform_plan": {Category: "terraform"}, - "terraform_apply": {Category: "terraform"}, + "read_file": {Category: "file"}, + "write_file": {Category: "file"}, + "append_file": {Category: "file"}, + "edit_file": {Category: "file"}, + "insert_lines": {Category: "file"}, + "replace_lines": {Category: "file"}, + "delete_lines": {Category: "file"}, + "copy_file": {Category: "file"}, + "move_file": {Category: "file"}, + "delete_file": {Category: "file"}, + "create_directory": {Category: "file"}, + "list_files": {Category: "file"}, + "find_files": {Category: "file"}, + "execute_command": {Category: "command"}, + "search_files": {Category: "command"}, + "git_status": {Category: "git"}, + "git_diff": {Category: "git"}, + "git_log": {Category: "git"}, + "git_add": {Category: "git"}, + "git_commit": {Category: "git"}, + "git_branch": {Category: "git"}, + "git_stash": {Category: "git"}, + "web_search": {Category: "web"}, + "web_fetch": {Category: "web"}, + "get_datetime": {Category: "utility"}, + "kubectl_get": {Category: "kubernetes"}, + "kubectl_apply": {Category: "kubernetes"}, + "kubectl_delete": {Category: "kubernetes"}, + "kubectl_describe": {Category: "kubernetes"}, + "kubectl_logs": {Category: "kubernetes"}, + "kubectl_exec": {Category: "kubernetes"}, + "helm_list": {Category: "kubernetes"}, + "helm_install": {Category: "kubernetes"}, + "terraform_init": {Category: "terraform"}, + "terraform_plan": {Category: "terraform"}, + "terraform_apply": {Category: "terraform"}, "terraform_destroy": {Category: "terraform"}, - "terraform_output": {Category: "terraform"}, - "terraform_state": {Category: "terraform"}, - "docker_build": {Category: "docker"}, - "docker_ps": {Category: "docker"}, - "docker_logs": {Category: "docker"}, - "docker_compose": {Category: "docker"}, - "docker_exec": {Category: "docker"}, - "aws_cli": {Category: "cloud"}, - "aws_ecs": {Category: "cloud"}, - "aws_eks": {Category: "cloud"}, - "az_cli": {Category: "cloud"}, - "az_aks": {Category: "cloud"}, - "gcloud": {Category: "cloud"}, - "gke": {Category: "cloud"}, - "trivy_scan": {Category: "security"}, - "gitleaks_scan": {Category: "security"}, - "secrets_scan": {Category: "security"}, - "dependency_audit": {Category: "security"}, - "sast_scan": {Category: "security"}, - "tfsec_scan": {Category: "security"}, - "kubesec_scan": {Category: "security"}, + "terraform_output": {Category: "terraform"}, + "terraform_state": {Category: "terraform"}, + "docker_build": {Category: "docker"}, + "docker_ps": {Category: "docker"}, + "docker_logs": {Category: "docker"}, + "docker_compose": {Category: "docker"}, + "docker_exec": {Category: "docker"}, + "aws_cli": {Category: "cloud"}, + "aws_ecs": {Category: "cloud"}, + "aws_eks": {Category: "cloud"}, + "az_cli": {Category: "cloud"}, + "az_aks": {Category: "cloud"}, + "gcloud": {Category: "cloud"}, + "gke": {Category: "cloud"}, + "trivy_scan": {Category: "security"}, + "gitleaks_scan": {Category: "security"}, + "secrets_scan": {Category: "security"}, + "dependency_audit": {Category: "security"}, + "sast_scan": {Category: "security"}, + "tfsec_scan": {Category: "security"}, + "kubesec_scan": {Category: "security"}, } } diff --git a/internal/tools/registry.go b/internal/tools/registry.go index 313404c..91b94ad 100644 --- a/internal/tools/registry.go +++ b/internal/tools/registry.go @@ -408,9 +408,9 @@ func (r *Registry) GetToolList() []ToolInfo { // securityToolsWithSeverity maps security tools to their severity parameter name // Only tools that support severity filtering are included var securityToolsWithSeverity = map[string]string{ - "trivy_scan": "severity", // --severity HIGH,CRITICAL - "sast_scan": "severity", // --severity ERROR,WARNING - "tfsec_scan": "minimum_severity", // --minimum-severity HIGH + "trivy_scan": "severity", // --severity HIGH,CRITICAL + "sast_scan": "severity", // --severity ERROR,WARNING + "tfsec_scan": "minimum_severity", // --minimum-severity HIGH } // InjectSecurityDefaults injects default security configuration into tool parameters diff --git a/internal/tools/web_tools.go b/internal/tools/web_tools.go index a83095d..193852c 100644 --- a/internal/tools/web_tools.go +++ b/internal/tools/web_tools.go @@ -15,16 +15,16 @@ import ( ) const ( - defaultNumResults = 5 - maxNumResults = 10 - defaultMaxLength = 50000 - webFetchTimeout = 15 * time.Second - userAgent = "taracode/1.0 (+https://tara.vision)" + defaultNumResults = 5 + maxNumResults = 10 + defaultMaxLength = 50000 + webFetchTimeout = 15 * time.Second + userAgent = "taracode/1.0 (+https://tara.vision)" ) var ( // searchOrchestrator manages search providers with fallback - searchOrchestrator *search.Orchestrator + searchOrchestrator *search.Orchestrator searchOrchestratorOnce sync.Once // onProviderSwitch is called when search falls back to another provider @@ -338,23 +338,23 @@ func extractTextContent(html string) string { // decodeHTMLEntities decodes common HTML entities func decodeHTMLEntities(s string) string { entities := map[string]string{ - "&": "&", - "<": "<", - ">": ">", - """: "\"", - "'": "'", - "'": "'", - " ": " ", - "–": "-", - "—": "-", - "‘": "'", - "’": "'", - "“": "\"", - "”": "\"", + "&": "&", + "<": "<", + ">": ">", + """: "\"", + "'": "'", + "'": "'", + " ": " ", + "–": "-", + "—": "-", + "‘": "'", + "’": "'", + "“": "\"", + "”": "\"", "…": "...", - "©": "(c)", - "®": "(R)", - "™": "(TM)", + "©": "(c)", + "®": "(R)", + "™": "(TM)", } for entity, replacement := range entities { diff --git a/internal/ui/errors.go b/internal/ui/errors.go index 5d65d93..a18112e 100644 --- a/internal/ui/errors.go +++ b/internal/ui/errors.go @@ -324,7 +324,6 @@ func FormatConnectionError(host string, err error) string { return sb.String() } - // VerboseToolError represents a detailed tool error for verbose mode type VerboseToolError struct { ToolName string diff --git a/internal/ui/errors_test.go b/internal/ui/errors_test.go index e24d906..02a0ac4 100644 --- a/internal/ui/errors_test.go +++ b/internal/ui/errors_test.go @@ -204,4 +204,3 @@ func TestFormatConnectionError(t *testing.T) { t.Error("Expected suggestion to start ollama") } } - diff --git a/internal/ui/preview.go b/internal/ui/preview.go index ed2f5b1..75dfacc 100644 --- a/internal/ui/preview.go +++ b/internal/ui/preview.go @@ -9,12 +9,12 @@ import ( // EditPreview represents a preview of an edit operation type EditPreview struct { - FilePath string - OldContent string - NewContent string - OldString string - NewString string - LinesAdded int + FilePath string + OldContent string + NewContent string + OldString string + NewString string + LinesAdded int LinesRemoved int } diff --git a/internal/ui/preview_test.go b/internal/ui/preview_test.go index 7420650..36a9324 100644 --- a/internal/ui/preview_test.go +++ b/internal/ui/preview_test.go @@ -7,11 +7,11 @@ import ( func TestGenerateUnifiedDiff(t *testing.T) { tests := []struct { - name string - oldContent string - newContent string - oldString string - newString string + name string + oldContent string + newContent string + oldString string + newString string wantContains []string }{ { diff --git a/internal/ui/prompt.go b/internal/ui/prompt.go index e3f02bc..f2d18a2 100644 --- a/internal/ui/prompt.go +++ b/internal/ui/prompt.go @@ -11,10 +11,10 @@ import ( // PermissionChoice represents a user's permission choice type PermissionChoice struct { - Allowed bool // Whether this execution is allowed - SavePerm permissions.Permission // Permission to save (empty if not saving) - SaveScope string // "tool" or "category" (empty if not saving) - Category permissions.PermissionCategory // The category (for category saves) + Allowed bool // Whether this execution is allowed + SavePerm permissions.Permission // Permission to save (empty if not saving) + SaveScope string // "tool" or "category" (empty if not saving) + Category permissions.PermissionCategory // The category (for category saves) } // PromptToolPermission asks the user for permission to execute a tool diff --git a/internal/ui/spinner.go b/internal/ui/spinner.go index 7b4c266..62e302d 100644 --- a/internal/ui/spinner.go +++ b/internal/ui/spinner.go @@ -20,7 +20,7 @@ type Spinner struct { frames []string interval time.Duration message string - baseMessage string // Original message without elapsed time + baseMessage string // Original message without elapsed time stop chan struct{} done chan struct{} mu sync.Mutex diff --git a/internal/ui/styles.go b/internal/ui/styles.go index 3e54d20..81477c6 100644 --- a/internal/ui/styles.go +++ b/internal/ui/styles.go @@ -53,19 +53,19 @@ var ( // Icon constants const ( - IconSuccess = "✓" - IconError = "✗" - IconArrow = "→" - IconWarning = "⚠" - IconInfo = "ℹ" - IconFolder = "📁" - IconSession = "📝" - IconTip = "💡" - IconStar = "🌟" - IconThinking = "⠋" - IconImage = "📷" - IconCloud = "☁️" - IconLock = "🔒" + IconSuccess = "✓" + IconError = "✗" + IconArrow = "→" + IconWarning = "⚠" + IconInfo = "ℹ" + IconFolder = "📁" + IconSession = "📝" + IconTip = "💡" + IconStar = "🌟" + IconThinking = "⠋" + IconImage = "📷" + IconCloud = "☁️" + IconLock = "🔒" IconShield = "🛡" IconDanger = "⛔" IconDiagnostics = "🔬" @@ -76,55 +76,55 @@ const ( var ( // Destructive operations - high risk (soft red on light red) SecurityDestructive = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#991B1B")). - Background(lipgloss.Color("#FEE2E2")). - Bold(true). - Padding(0, 1) + Foreground(lipgloss.Color("#991B1B")). + Background(lipgloss.Color("#FEE2E2")). + Bold(true). + Padding(0, 1) // Execute operations - elevated risk (soft orange on light orange) SecurityExecute = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#9A3412")). - Background(lipgloss.Color("#FFEDD5")). - Bold(true). - Padding(0, 1) + Foreground(lipgloss.Color("#9A3412")). + Background(lipgloss.Color("#FFEDD5")). + Bold(true). + Padding(0, 1) // Write/Git operations - moderate risk (soft amber on light amber) SecurityWrite = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#92400E")). - Background(lipgloss.Color("#FEF3C7")). - Bold(true). - Padding(0, 1) + Foreground(lipgloss.Color("#92400E")). + Background(lipgloss.Color("#FEF3C7")). + Bold(true). + Padding(0, 1) // Security audit box style SecurityAuditBox = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(Warning). - Padding(0, 1). - Width(60) + Border(lipgloss.RoundedBorder()). + BorderForeground(Warning). + Padding(0, 1). + Width(60) // Security audit header SecurityAuditHeader = lipgloss.NewStyle(). - Foreground(Warning). - Bold(true) + Foreground(Warning). + Bold(true) // Security audit label style SecurityAuditLabel = lipgloss.NewStyle(). - Foreground(Muted). - Width(12) + Foreground(Muted). + Width(12) // Security audit value style SecurityAuditValue = lipgloss.NewStyle(). - Bold(true) + Bold(true) // Security implication style SecurityImplication = lipgloss.NewStyle(). - Foreground(Warning). - Italic(true) + Foreground(Warning). + Italic(true) // Batch indicator style BatchIndicator = lipgloss.NewStyle(). - Foreground(Info). - Bold(true) + Foreground(Info). + Bold(true) ) // Security mode colors (softer pastel variants) @@ -137,29 +137,29 @@ var ( var ( // Security mode banner box - prominent red/orange border SecurityModeBannerBox = lipgloss.NewStyle(). - Border(lipgloss.DoubleBorder()). - BorderForeground(SecurityOrange). - Padding(0, 2). - Width(64) + Border(lipgloss.DoubleBorder()). + BorderForeground(SecurityOrange). + Padding(0, 2). + Width(64) // Security mode title - bold with shield icon SecurityModeTitle = lipgloss.NewStyle(). - Foreground(SecurityOrange). - Bold(true) + Foreground(SecurityOrange). + Bold(true) // Security mode subtitle SecurityModeSubtitle = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FBBF24")). - Italic(true) + Foreground(lipgloss.Color("#FBBF24")). + Italic(true) // Security mode feature bullet SecurityModeBullet = lipgloss.NewStyle(). - Foreground(lipgloss.Color("#F97316")) + Foreground(lipgloss.Color("#F97316")) // Security mode prompt indicator SecurityModePrompt = lipgloss.NewStyle(). - Foreground(SecurityOrange). - Bold(true) + Foreground(SecurityOrange). + Bold(true) ) // Display constants diff --git a/internal/ui/task.go b/internal/ui/task.go index 45156b3..8408f5c 100644 --- a/internal/ui/task.go +++ b/internal/ui/task.go @@ -12,63 +12,63 @@ import ( var ( // Task header style TaskHeaderStyle = lipgloss.NewStyle(). - Bold(true). - Foreground(Primary). - Padding(0, 1) + Bold(true). + Foreground(Primary). + Padding(0, 1) // Task box style TaskBoxStyle = lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(Primary). - Padding(0, 1). - Width(70) + Border(lipgloss.RoundedBorder()). + BorderForeground(Primary). + Padding(0, 1). + Width(70) // Step status styles StepPending = lipgloss.NewStyle(). - Foreground(Muted) + Foreground(Muted) StepRunning = lipgloss.NewStyle(). - Foreground(Info). - Bold(true) + Foreground(Info). + Bold(true) StepCompleted = lipgloss.NewStyle(). - Foreground(Success) + Foreground(Success) StepFailed = lipgloss.NewStyle(). - Foreground(Error) + Foreground(Error) StepSkipped = lipgloss.NewStyle(). - Foreground(Warning) + Foreground(Warning) // Step number style StepNumber = lipgloss.NewStyle(). - Foreground(Muted). - Width(4) + Foreground(Muted). + Width(4) // Step name style StepName = lipgloss.NewStyle(). - Bold(true) + Bold(true) // Task status styles TaskStatusPending = lipgloss.NewStyle(). - Foreground(Muted). - Bold(true) + Foreground(Muted). + Bold(true) TaskStatusRunning = lipgloss.NewStyle(). - Foreground(Info). - Bold(true) + Foreground(Info). + Bold(true) TaskStatusCompleted = lipgloss.NewStyle(). - Foreground(Success). - Bold(true) + Foreground(Success). + Bold(true) TaskStatusFailed = lipgloss.NewStyle(). - Foreground(Error). - Bold(true) + Foreground(Error). + Bold(true) TaskStatusPaused = lipgloss.NewStyle(). - Foreground(Warning). - Bold(true) + Foreground(Warning). + Bold(true) ) // Step status icons diff --git a/internal/watch/detector_test.go b/internal/watch/detector_test.go index 3911436..a26c3f9 100644 --- a/internal/watch/detector_test.go +++ b/internal/watch/detector_test.go @@ -60,8 +60,8 @@ func TestHasSignificantChange(t *testing.T) { expected bool }{ {"identical below threshold", 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0.15, false}, - {"5% change below 15% threshold", 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFC, 0.15, false}, // 2 bits = 3.1% - {"20% change above 15% threshold", 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFF00000, 0.15, true}, // 20 bits = 31% + {"5% change below 15% threshold", 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFC, 0.15, false}, // 2 bits = 3.1% + {"20% change above 15% threshold", 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFF00000, 0.15, true}, // 20 bits = 31% {"completely different", 0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0.15, true}, } diff --git a/internal/watch/monitor.go b/internal/watch/monitor.go index 17c5e9c..572a42f 100644 --- a/internal/watch/monitor.go +++ b/internal/watch/monitor.go @@ -16,19 +16,19 @@ type AnalyzeFunc func(prompt string, images []*assistant.ImageData) (string, err // WatchMonitor coordinates screen monitoring and analysis type WatchMonitor struct { - config WatchConfig + config WatchConfig analyzeFunc AnalyzeFunc - tempDir string + tempDir string // State management - mu sync.RWMutex - state WatchState - startTime time.Time - lastCapture time.Time - lastAnalysis time.Time - totalCaptures int - totalAnalyses int - displayCount int + mu sync.RWMutex + state WatchState + startTime time.Time + lastCapture time.Time + lastAnalysis time.Time + totalCaptures int + totalAnalyses int + displayCount int // Previous capture hash for change detection lastHash uint64 diff --git a/internal/watch/types.go b/internal/watch/types.go index 0d455d1..53f67b0 100644 --- a/internal/watch/types.go +++ b/internal/watch/types.go @@ -45,10 +45,10 @@ type Finding struct { // AnalysisResult contains the LLM's findings from analyzing a screenshot type AnalysisResult struct { - Timestamp time.Time `json:"timestamp"` - ScreenCount int `json:"screen_count"` - Findings []Finding `json:"findings"` - RawResponse string `json:"raw_response,omitempty"` + Timestamp time.Time `json:"timestamp"` + ScreenCount int `json:"screen_count"` + Findings []Finding `json:"findings"` + RawResponse string `json:"raw_response,omitempty"` AnalysisTime time.Duration `json:"analysis_time"` } @@ -81,12 +81,12 @@ func (r *AnalysisResult) WarningCount() int { // ScreenCapture represents a captured screenshot type ScreenCapture struct { - Path string `json:"path"` - DisplayID int `json:"display_id"` - Width int `json:"width"` - Height int `json:"height"` - Timestamp time.Time `json:"timestamp"` - Hash uint64 `json:"hash"` + Path string `json:"path"` + DisplayID int `json:"display_id"` + Width int `json:"width"` + Height int `json:"height"` + Timestamp time.Time `json:"timestamp"` + Hash uint64 `json:"hash"` } // WatchConfig holds configuration for the watch monitor