From b4927fc1b00e699ca0c46e13394ab056f9b6a540 Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Thu, 18 Sep 2025 16:02:28 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=94=A7=20chore:=20update=20agent=20do?= =?UTF-8?q?cumentation=20and=20enhance=20prompt=20architecture=20with=20st?= =?UTF-8?q?ructured=20XML=20methodology?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .opencode/AGENT-SYSTEM-BLUEPRINT.md | 760 +++++++++++++++++++ .opencode/agent/codebase-agent.md | 5 +- .opencode/agent/reviewer.md | 12 +- .opencode/agent/subagents/build-agent.md | 42 + .opencode/agent/{ => subagents}/tester.md | 0 .opencode/agent/workflow-orchestrator.md | 56 ++ .opencode/command/prompter.md | 238 ++++-- .opencode/context/core/essential-patterns.md | 250 ++++++ .opencode/context/project/project-context.md | 97 +++ 9 files changed, 1404 insertions(+), 56 deletions(-) create mode 100644 .opencode/AGENT-SYSTEM-BLUEPRINT.md create mode 100644 .opencode/agent/subagents/build-agent.md rename .opencode/agent/{ => subagents}/tester.md (100%) create mode 100644 .opencode/agent/workflow-orchestrator.md create mode 100644 .opencode/context/core/essential-patterns.md create mode 100644 .opencode/context/project/project-context.md diff --git a/.opencode/AGENT-SYSTEM-BLUEPRINT.md b/.opencode/AGENT-SYSTEM-BLUEPRINT.md new file mode 100644 index 0000000..0d7770b --- /dev/null +++ b/.opencode/AGENT-SYSTEM-BLUEPRINT.md @@ -0,0 +1,760 @@ +# The OpenCode Agent System Blueprint + +_Build Intelligent Workflow Systems with Context-Aware AI_ + +## The Golden Rule + +**Context flows in one direction: Commands load context immediately, Agents can look up additional context deterministically.** + +Like a well-organized library: the librarian (command) brings you the right books immediately, but you (agent) can look up specific references when needed. + +## How @ Symbol Context Loading Works (FUNDAMENTAL) + +### The Magic of Automatic Context Injection + +When you create a slash command with @ references, OpenCode automatically loads that context into the agent's memory BEFORE the agent starts thinking: + +```markdown +# .opencode/command/create-component.md + +--- + +name: create-component +agent: simple-coder + +--- + +You are creating React components following our patterns. + +**Request:** $ARGUMENTS + +@.opencode/context/frontend/react-patterns.md +@.opencode/context/styling/design-system.md +@.opencode/context/core/validation-patterns.md + +Create the component now. +``` + +**What happens when user types `/create-component "user profile card"`:** + +1. ✅ OpenCode reads the command file +2. ✅ **Automatically loads** react-patterns.md, design-system.md, validation-patterns.md +3. ✅ **Injects all context** into the agent's working memory +4. ✅ Agent receives: user request + all loaded context + instructions +5. ✅ Agent can immediately use patterns without looking them up + +### Why This Mechanism Is Powerful + +**❌ Without @ loading:** + +``` +Agent: "I need to create a component but don't know your patterns. Let me search..." +→ Agent uses generic patterns or has to look up files +→ Inconsistent results, slower execution +``` + +**✅ With @ loading:** + +``` +Agent: "I have your React patterns, design system, and validation rules loaded. Creating component..." +→ Agent immediately follows your exact patterns +→ Consistent, fast, high-quality results +``` + +### Context Loading Strategy + +**Base Context (Always Load):** + +```markdown +@.opencode/context/core/essential-patterns.md +@.opencode/context/architecture/project-structure.md +``` + +**Domain-Specific Context (Load Based on Command Purpose):** + +```markdown +# Frontend commands + +@.opencode/context/frontend/react-patterns.md +@.opencode/context/styling/design-system.md + +# Backend commands + +@.opencode/context/backend/server-patterns.md +@.opencode/context/database/query-patterns.md + +# Testing commands + +@.opencode/context/testing/test-patterns.md +``` + +### Dynamic Context Loading + +```markdown +# Advanced: Conditional context based on request analysis + +@.opencode/context/core/essential-patterns.md +!`if echo "$ARGUMENTS" | grep -i "component\|ui" > /dev/null; then echo "@.opencode/context/frontend/react-patterns.md"; fi` +!`if echo "$ARGUMENTS" | grep -i "database\|query" > /dev/null; then echo "@.opencode/context/database/query-patterns.md"; fi` +``` + +### Best Practices for @ Context Loading + +1. **Load 2-4 context files maximum** (prevent cognitive overload) +2. **Always include core patterns** (essential-patterns.md) +3. **Load domain-specific patterns** based on command purpose +4. **Keep context files focused** (50-150 lines each) +5. **Use conditional loading** for dynamic context selection + +### Why This Architecture Matters + +Understanding @ symbol auto-loading is crucial because: + +- **It's the foundation** of how agents get consistent context +- **It determines command design** - you must anticipate what context agents need +- **It explains the architecture** - why commands are "smart loaders" and agents are "focused executors" +- **It guides best practices** - load the right context, not too much, not too little + +**This mechanism is what makes the system "intelligent" - agents always start with the right knowledge instead of having to discover it.** + +## Core Principles + +### 1. Single-Level Context Loading + +**OpenCode processes `@` references only in command templates, NOT recursively in file contents.** + +```markdown +# ✅ This works (in command template) + +@.opencode/context/frontend/patterns.md +@.opencode/context/styling/design-system.md + +# ❌ This doesn't work (inside patterns.md file) + +# If patterns.md contains @other-file.md, it's treated as plain text +``` + +**Critical Implication**: Commands must load ALL necessary context upfront using @ references. Agents can look up additional files using tools (read, grep, glob), but cannot use @ loading themselves. + +### 2. Deterministic vs Non-Deterministic Behavior + +- **Commands**: Non-deterministic - analyze requests and load appropriate context +- **Agents**: Deterministic - predictable behavior, can look up additional context + +### 3. Context Optimization + +- **Maximum 4 context files per command** (250-450 lines total) +- **50-150 lines per context file** (optimal range) +- **Always load core patterns** + request-specific context + +## The 5-Part Intelligent System + +### 1. Commands (.opencode/command/) + +**What:** Entry points that load immediate context based on request analysis +**Rule:** Commands are non-deterministic - they adapt context to the request + +``` +/workflow → Intelligent routing with dynamic context +/plan-task → Task planning with architecture context +/execute-task → Step execution with progress tracking +/create-frontend-component → UI development with styling + frontend context +/create-backend-operation → Server logic with backend + database context +/create-database-operation → Database queries with focused database context +/review-completion → Quality assurance with minimal context +/validate-with-tests → Automated testing with testing context +``` + +**Command Structure Pattern:** + +```markdown +--- +name: command-name +agent: target-agent +description: "What this command does" +--- + +You are [doing specific task]. [Direct instructions to agent]. + +**Request:** $ARGUMENTS + +**Your Instructions:** + +1. **ANALYZE** the request... +2. **IMPLEMENT** following patterns... +3. **VALIDATE** the results... + +**Context Loaded:** +@.opencode/context/core/essential-patterns.md +@.opencode/context/[domain]/[specific-patterns].md +@.opencode/context/architecture/architecture-lite.md + +**Success Criteria:** + +- [Specific measurable outcomes] + +Execute this [task type] now. +``` + +### 2. Agents (.opencode/agent/) + +**What:** Deterministic AI workers with specific capabilities +**Rule:** Agents can look up additional context but are predictable in behavior + +``` +Primary Agents: +├── workflow-orchestrator.md → Routes requests and analyzes complexity +├── task-planner.md → Creates detailed task plans with file tracking +├── task-executor.md → Executes planned steps systematically +├── post-flight-reviewer.md → Reviews completed work for compliance +└── simple-coder.md → Implements focused development tasks + +Subagents (Specialized): +├── subagents/code-reviewer.md → Security and quality reviews (read-only) +├── subagents/test-writer.md → Test creation and validation (reports issues) +├── subagents/pattern-finder.md → Discovers existing implementations +└── subagents/doc-writer.md → Documentation creation and updates +``` + +**Agent Structure Pattern:** + +```markdown +--- +description: "What this agent does" +mode: primary|subagent +model: claude-4-sonnet +temperature: 0.1-0.3 +tools: + read: true|false + edit: true|false + write: true|false + bash: true|false + task: true|false +permissions: + edit: + "**/*.env*": "deny" + "**/*.secret": "deny" +--- + +You are [specific role]. [Direct instructions for behavior]. + +**EXECUTE** this [process type] for every [task type]: + +**1. [ACTION]** the [subject]: + +- [Specific instruction 1] +- [Specific instruction 2] + +**2. [ACTION]** the [subject]: + +- [Specific instruction 1] +- [Specific instruction 2] + +**RULES**: + +- **ALWAYS** [critical requirement] +- **NEVER** [forbidden action] +- **ONLY** [scope limitation] + +Execute [task type] now. +``` + +### 3. Context (.opencode/context/) + +**What:** Layered knowledge system with focused domains +**Rule:** Single-level loading - no recursive context references + +``` +context/ +├── core/ +│ └── essential-patterns.md (76 lines - always loaded) +├── architecture/ +│ └── architecture-lite.md (74 lines - project structure) +├── frontend/ +│ ├── frontend-patterns.md (230 lines - React patterns) +│ └── styling/design-system.md (122 lines - UI standards) +├── backend/ +│ ├── backend-patterns.md (326 lines → split into focused files) +│ └── server-actions.md (100 lines - focused server action patterns) +├── database/ +│ └── database-queries.md (207 lines - query patterns) +├── security/ +│ └── security-patterns.md (205 lines - auth patterns) +├── testing/ +│ ├── testing-patterns.md (322 lines → split) +│ └── unit-testing.md (100 lines - focused test patterns) +└── debugging/ + └── common-errors.md (304 lines → split by error type) +``` + +**Context File Structure Pattern:** + +````markdown +**[ACTION]** [subject] using these exact patterns: + +**[PATTERN NAME]** - [When to use]: + +```[language] +// Example code with clear comments +// Showing exactly what to do +``` +```` + +**[ANOTHER PATTERN]** - [When to use]: + +```[language] +// Another example +``` + +**RULES**: + +- **ALWAYS** [critical requirement] +- **NEVER** [forbidden action] +- **USE** [specific tools/methods] + +``` + +### 4. Task Management (tasks/) +**What:** File-based progress tracking with checkbox systems +**Rule:** Every complex task gets a plan file with step-by-step tracking + +``` + +tasks/ +├── features/ # Feature development +│ └── [feature-name]/ +│ ├── task-plan.md # Main plan with checkboxes +│ ├── execution-log.md # Detailed progress log +│ └── review-report.md # Post-flight review +├── fixes/ # Bug fixes +│ └── [issue-name]/ +│ ├── task-plan.md +│ └── fix-report.md +├── improvements/ # Code improvements +│ └── [improvement-name]/ +└── single/ # Simple tasks +└── [task-name].md + +```` + +**Task Plan File Structure:** +```markdown +# Task: [Task Name] + +## Overview +**Request**: [Original user request] +**Complexity**: [Simple/Medium/Complex] +**Estimated Duration**: [Time estimate] +**Dependencies**: [List any blocking requirements] + +## Task Breakdown + +### Phase 1: [Phase Name] +- [ ] **Step 1.1**: [Specific action] + - **Agent**: @[agent-name] + - **Context**: [Required context files] + - **Validation**: [How to verify completion] + - **Duration**: [Time estimate] + +- [ ] **Step 1.2**: [Specific action] + - **Agent**: @[agent-name] + - **Context**: [Required context files] + - **Validation**: [How to verify completion] + - **Duration**: [Time estimate] + +## Quality Gates +- [ ] **Build Validation**: TypeScript compilation passes +- [ ] **Code Review**: Security and quality review completed +- [ ] **Testing**: All tests pass and coverage adequate +- [ ] **Integration**: Feature works end-to-end + +## Acceptance Criteria +- [ ] [Specific requirement 1] +- [ ] [Specific requirement 2] + +## Progress Tracking +**Started**: [Date/Time] +**Last Updated**: [Date/Time] +**Status**: [Planning/In Progress/Review/Complete] +**Completed Steps**: 0/[Total Steps] +```` + +### 5. Workflow Orchestration + +**What:** Multi-agent coordination with quality gates +**Rule:** Complex workflows use multiple agents with validation checkpoints + +``` +Workflow Types: +├── Simple (< 30 min) → Direct execution with focused context +├── Medium (30min-2hrs) → Task planning with step tracking +└── Complex (> 2hrs) → Multi-phase with quality gates + +Quality Gates: +├── Build Validation → TypeScript, linting, build checks +├── Code Review → Security and quality assessment +├── Testing → Automated test execution +└── Post-Flight Review → Instruction compliance verification +``` + +## System Flow (Intelligent Workflow Management) + +```mermaid +flowchart TD + A[User Request] --> B[Workflow Orchestrator] + B --> C{Analyze Request} + C --> D{Complexity?} + D -->|Simple| E[Direct Execution] + D -->|Complex| F[Task Planning] + + E --> G[Load Focused Context] + G --> H[Execute with Agent] + H --> I[Validate Results] + I --> J[Complete] + + F --> K[Create Task Plan File] + K --> L[Step-by-Step Execution] + L --> M[Update Progress Tracking] + M --> N{More Steps?} + N -->|Yes| O[Next Step] + N -->|No| P[Quality Gates] + O --> L + P --> Q[Post-Flight Review] + Q --> R[Mark Complete] + + style A fill:#E6F3FF + style J fill:#E6FFE6 + style R fill:#E6FFE6 +``` + +## Context Loading Strategy + +### Dynamic Context Loading Pattern + +```markdown +# In workflow orchestrator + +**ANALYZE** request: "$ARGUMENTS" + +**BASE CONTEXT** (always loaded): +@.opencode/context/core/essential-patterns.md +@.opencode/context/architecture/architecture-lite.md + +**CONDITIONAL CONTEXT** (based on request analysis): +!`if echo "$ARGUMENTS" | grep -i -E "(component|ui|frontend)" > /dev/null; then echo "@.opencode/context/styling/design-system.md"; fi` +!`if echo "$ARGUMENTS" | grep -i -E "(server|backend|action)" > /dev/null; then echo "@.opencode/context/backend/server-actions.md"; fi` +!`if echo "$ARGUMENTS" | grep -i -E "(database|query|data)" > /dev/null; then echo "@.opencode/context/database/database-queries.md"; fi` +``` + +### Context Size Guidelines + +- **✅ Optimal**: 50-150 lines (focused, actionable patterns) +- **⚠️ Acceptable**: 150-250 lines (comprehensive but manageable) +- **❌ Too Large**: 250+ lines (should be split into focused files) + +### Context Loading Rules + +1. **Always load core patterns** (essential-patterns.md + architecture-lite.md) +2. **Maximum 4 context files** per command (prevent overload) +3. **Load based on request analysis** (dynamic, not static) +4. **Use bash commands** for conditional loading + +## Implementation Guide + +### Step 1: Create Your Workflow Commands + +```markdown +# Essential Commands to Create: + +/workflow # Main entry with intelligent routing +/plan-task # Complex task breakdown +/execute-task # Step-by-step execution +/create-frontend-component # UI development +/create-backend-operation # Server logic +/review-completion # Quality assurance +``` + +### Step 2: Build Your Agent System + +```markdown +# Core Agents Needed: + +workflow-orchestrator.md # Request analysis and routing +task-planner.md # Detailed planning with file tracking +task-executor.md # Step execution with progress updates +post-flight-reviewer.md # Compliance and quality review +simple-coder.md # Focused implementation work + +# Specialized Subagents: + +subagents/code-reviewer.md # Security and quality (read-only) +subagents/test-writer.md # Test creation (reports issues) +``` + +### Step 3: Structure Your Context + +```markdown +# Context Organization: + +core/essential-patterns.md # Always loaded (76 lines) +architecture/architecture-lite.md # Project structure (74 lines) +frontend/frontend-patterns.md # React patterns (230 lines) +styling/design-system.md # UI standards (122 lines) +backend/server-actions.md # Server patterns (100 lines) +database/database-queries.md # Query patterns (207 lines) +``` + +### Step 4: Implement Task Management + +```markdown +# Task File Structure: + +tasks/features/[name]/task-plan.md # Main planning file +tasks/features/[name]/review-report.md # Post-completion review +tasks/fixes/[name]/task-plan.md # Bug fix planning +tasks/single/[name].md # Simple tasks +``` + +### Step 5: Add Quality Gates + +```markdown +# Automated Validation: + +!`pnpm tsc --noEmit` # TypeScript check +!`pnpm lint` # Code quality +!`pnpm build` # Build validation +!`pnpm test` # Test execution +``` + +## Best Practices + +### Context Management + +1. **Keep context files focused** (50-150 lines) +2. **Use single-level loading** (no recursive `@` references) +3. **Load dynamically** based on request analysis +4. **Always include core patterns** + architecture + +### Agent Design + +1. **Make agents deterministic** (predictable behavior) +2. **Give clear, direct instructions** (not documentation) +3. **Separate concerns** (one agent, one responsibility) +4. **Use structured response formats** + +### Task Management + +1. **Break complex work into steps** (15-30 minutes each) +2. **Use checkbox tracking** for progress visibility +3. **Include validation criteria** for each step +4. **Implement quality gates** at key milestones + +### Workflow Orchestration + +1. **Analyze before routing** (complexity and domain) +2. **Use appropriate workflows** (simple vs complex) +3. **Coordinate multiple agents** for complex tasks +4. **Validate at every step** (build, test, review) + +## System Architecture + +```mermaid +graph TB + subgraph "User Interface" + CMD["/workflow
/plan-task
/execute-task"] + end + + subgraph ".opencode/command/" + C1[workflow.md] + C2[plan-task.md] + C3[execute-task.md] + C4[create-frontend-component.md] + C5[create-backend-operation.md] + end + + subgraph ".opencode/agent/" + A1[workflow-orchestrator.md] + A2[task-planner.md] + A3[task-executor.md] + A4[simple-coder.md] + A5[post-flight-reviewer.md] + end + + subgraph ".opencode/context/" + CTX1[core/essential-patterns.md] + CTX2[architecture/architecture-lite.md] + CTX3[frontend/frontend-patterns.md] + CTX4[backend/server-actions.md] + CTX5[database/database-queries.md] + end + + subgraph "tasks/" + T1[features/[name]/task-plan.md] + T2[fixes/[name]/task-plan.md] + T3[single/[name].md] + end + + CMD --> C1 + CMD --> C2 + CMD --> C3 + + C1 --> A1 + C2 --> A2 + C3 --> A3 + C4 --> A4 + C5 --> A4 + + A1 --> CTX1 + A1 --> CTX2 + A2 --> CTX2 + A3 --> CTX1 + A4 --> CTX3 + A4 --> CTX4 + + A2 --> T1 + A3 --> T1 + A3 --> T2 +``` + +## Using the System (User Guide) + +### How Slash Commands Work + +**Slash commands automatically inject context into agents using @ references:** + +```bash +# User types this: +/create-component "user profile card with avatar upload" + +# OpenCode automatically: +# 1. Loads the command file: .opencode/command/create-component.md +# 2. Reads all @ referenced context files +# 3. Injects context + user request into the agent +# 4. Agent starts with full knowledge of your patterns +``` + +### Basic Commands + +- `/workflow "your request"` - Main entry point for any development task +- `/plan-task "complex feature"` - For multi-step planning +- `/execute-task` - Continue working on planned tasks + +### Command Examples + +- `/workflow "Create a user profile component with form validation"` +- `/create-frontend-component "Dashboard header with user menu"` +- `/review-completion` - Check completed work for quality + +### When to Use Which Command + +- Simple tasks (< 30 min) → `/workflow` +- Complex features → `/plan-task` then `/execute-task` +- Specialized work → `/create-frontend-component`, `/create-backend-operation` + +### Why This Works Better Than Chat + +- **Consistent patterns**: Agents always use YOUR coding standards +- **Faster execution**: No time spent discovering or asking about patterns +- **Quality results**: Context-aware from the first response +- **Scalable knowledge**: Add new patterns once, use everywhere + +## Quick Start Implementation + +### 1. Start with Core System + +```bash +# Create essential structure +mkdir -p .opencode/{command,agent,context/{core,architecture}} +mkdir -p tasks/{features,fixes,single} +``` + +### 2. Create Main Entry Point + +```markdown +# .opencode/command/workflow.md + +--- + +name: workflow +agent: workflow-orchestrator + +--- + +You are analyzing requests and routing to optimal workflows. + +**Request:** $ARGUMENTS + +**Context Loaded:** +@.opencode/context/core/essential-patterns.md +@.opencode/context/architecture/architecture-lite.md + +Route to appropriate specialized workflow now. +``` + +### 3. Build Workflow Orchestrator + +```markdown +# .opencode/agent/workflow-orchestrator.md + +--- + +description: "Routes requests to specialized workflows" +mode: primary +tools: [read, grep, glob, task] + +--- + +You are analyzing requests and routing to specialized workflows. + +**ANALYZE** the request and **ROUTE** to appropriate command: + +- Feature development → /plan-task or /create-frontend-component +- Bug fixes → /fix-issue +- Code review → /review-code + +Execute routing now. +``` + +### 4. Add Essential Context + +````markdown +# .opencode/context/core/essential-patterns.md + +**CORE PATTERNS** - Essential patterns for all development: + +**AUTHENTICATION PATTERN**: + +```typescript +const user = await getCurrentUser(); +if (!user) return { error: "Unauthorized" }; +``` +```` + +**VALIDATION PATTERN**: + +```typescript +const validated = schema.safeParse(data); +if (!validated.success) return { error: "Validation failed" }; +``` + +```` + +### 5. Test Your System +```bash +# Test the workflow +/workflow "Create a user profile component" +# Should route to appropriate workflow with right context +```` + +## Remember + +1. **Context flows one direction** - Commands load immediately, Agents look up deterministically +2. **Keep context focused** - 50-150 lines per file, maximum 4 files per command +3. **Make agents predictable** - Deterministic behavior with clear instructions +4. **Track everything** - File-based task management with checkbox progress +5. **Validate continuously** - Quality gates and post-flight reviews +6. **Start simple** - Build core system first, add complexity gradually + +--- + +_Think of this system like a professional development team: each member has a specific role, they communicate clearly, they track their work systematically, and they validate quality at every step._ diff --git a/.opencode/agent/codebase-agent.md b/.opencode/agent/codebase-agent.md index 60abd23..367b036 100644 --- a/.opencode/agent/codebase-agent.md +++ b/.opencode/agent/codebase-agent.md @@ -33,9 +33,8 @@ Always start with phrase "DIGGING IN..." You have access to the following subagents: - `@task-manager` -- `@subagents/coder-agent` -- `@subagents/tester` -- `@subagents/documentation` +- `@subagents/tester` @tester +- `@subagents/documentation` @documentation Focus: You are a TypeScript coding specialist focused on writing clean, maintainable, and scalable code. Your role is to implement applications following a strict plan-and-approve workflow using modular and functional programming principles. diff --git a/.opencode/agent/reviewer.md b/.opencode/agent/reviewer.md index 7559dfb..b65a314 100644 --- a/.opencode/agent/reviewer.md +++ b/.opencode/agent/reviewer.md @@ -1,4 +1,5 @@ --- + description: "Code review, security, and quality assurance agent" mode: subagent model: claude-4-sonnet @@ -25,15 +26,22 @@ Responsibilities: - Check alignment with naming conventions and modular patterns - Identify and flag potential security vulnerabilities (e.g., XSS, injection, insecure dependencies) - Flag potential performance and maintainability issues +- Load project-specific context for accurate pattern validation - First sentence should be Start with "Reviewing..., what would you devs do if I didn't check up on you?" Workflow: -1. Share a short review plan (files/concerns to inspect, including security aspects) and ask to proceed. -2. Provide concise review notes with suggested diffs (do not apply changes), including any security concerns. +1. **ANALYZE** request and load relevant project context +2. Share a short review plan (files/concerns to inspect, including security aspects) and ask to proceed. +3. Provide concise review notes with suggested diffs (do not apply changes), including any security concerns. Output: Start with "Reviewing..., what would you devs do if I didn't check up on you?" Then give a short summary of the review. - Risk level (including security risk) and recommended follow-ups + +**Context Loading:** +- Load project patterns and security guidelines +- Analyze code against established conventions +- Flag deviations from team standards \ No newline at end of file diff --git a/.opencode/agent/subagents/build-agent.md b/.opencode/agent/subagents/build-agent.md new file mode 100644 index 0000000..39a66ac --- /dev/null +++ b/.opencode/agent/subagents/build-agent.md @@ -0,0 +1,42 @@ +--- + +description: "Type check and build validation agent" +mode: subagent +model: claude-4-sonnet +temperature: 0.1 +tools: + bash: true + read: true + grep: true +permissions: + bash: + "tsc": "allow" + "npm run build": "allow" + "yarn build": "allow" + "pnpm build": "allow" + "*": "deny" + edit: + "**/*": "deny" +--- + +# Build Agent + +You are a build validation agent. For every request, perform the following steps: + +1. **Type Check** + - Run the TypeScript compiler (`tsc`). + - If there are any type errors, return the error output and stop. + +2. **Build Check** + - If type checking passes, run the build command (`npm run build`, `yarn build`, or `pnpm build` as appropriate). + - If there are any build errors, return the error output. + +3. **Success** + - If both steps complete without errors, return a success message. + +**Rules:** +- Only run type check and build check. +- Only report errors if they occur; otherwise, report success. +- Do not modify any code. + +Execute type check and build validation now. \ No newline at end of file diff --git a/.opencode/agent/tester.md b/.opencode/agent/subagents/tester.md similarity index 100% rename from .opencode/agent/tester.md rename to .opencode/agent/subagents/tester.md diff --git a/.opencode/agent/workflow-orchestrator.md b/.opencode/agent/workflow-orchestrator.md new file mode 100644 index 0000000..da80827 --- /dev/null +++ b/.opencode/agent/workflow-orchestrator.md @@ -0,0 +1,56 @@ +--- + +description: "Routes requests to specialized workflows with selective context loading" +mode: primary +model: claude-4-sonnet +temperature: 0.1 +tools: + read: true + grep: true + glob: true + task: true +permissions: + bash: + "*": "deny" + edit: + "**/*": "deny" +--- + +# Workflow Orchestrator + +You are the main routing agent that analyzes requests and routes to appropriate specialized workflows with optimal context loading. + +**ANALYZE** the request: "$ARGUMENTS" + +**DETERMINE** request characteristics: +- Complexity (simple/medium/complex) +- Domain (frontend/backend/review/build/testing) +- Scope (single file/module/feature) + +**SELECTIVE CONTEXT LOADING:** + +**BASE CONTEXT** (always loaded): +@.opencode/context/core/essential-patterns.md +@.opencode/context/project/project-context.md + +**CONDITIONAL CONTEXT** (based on analysis): +!`if echo "$ARGUMENTS" | grep -i -E "(review|security|quality)" > /dev/null; then echo "@.opencode/context/project/project-context.md"; fi` +!`if echo "$ARGUMENTS" | grep -i -E "(build|type|lint|compile)" > /dev/null; then echo "@.opencode/context/project/project-context.md"; fi` +!`if echo "$ARGUMENTS" | grep -i -E "(test|spec|unit|integration)" > /dev/null; then echo "@.opencode/context/project/project-context.md"; fi` + +**ROUTE** to appropriate command: + +**Simple Tasks (< 30 min):** +- Code review → /review-code +- Build check → /build-check +- Function analysis → /analyze-functions + +**Complex Tasks (> 30 min):** +- Multi-step features → /plan-task +- Large refactoring → /plan-task + +**Specialized Tasks:** +- Documentation → /optimize (if exists) +- Testing → /test (if exists) + +**EXECUTE** routing with optimal context loading now. \ No newline at end of file diff --git a/.opencode/command/prompter.md b/.opencode/command/prompter.md index a8bf959..9aecc08 100644 --- a/.opencode/command/prompter.md +++ b/.opencode/command/prompter.md @@ -1,72 +1,208 @@ --- -description: Transform basic requests into comprehensive, well-structured prompts using the 10-step framework +description: Transform basic requests into comprehensive, systematic prompt architectures using structured XML methodology --- -You are a prompt enhancement specialist. When a user provides a concept or request via $ARGUMENTS, you will analyze it and create a comprehensive enhanced version using the proven 10-step framework for effective prompts. +# Advanced Prompt Architecture Generator -## Your Enhancement Process: +## Overview +Transform basic requests into comprehensive, systematic prompt frameworks using structured XML methodology and the 10-step enhancement principles. -**Step 1: Analyze the Original Request** -Examine the provided concept and identify: -- Task type and scope -- Required expertise domain -- End goal and success criteria -- Potential challenges or complexity + -**Step 2: Apply the 10-Step Enhancement Framework** -Transform the request by incorporating these essential elements: + +### Step 1: Request Analysis +Analyze the user's concept ($ARGUMENTS) to identify core requirements and optimal prompt architecture. -1. **Task Context** - Expert role and main objective -2. **Tone Context** - Communication style and audience level -3. **Background Knowledge** - Required expertise and resources -4. **Task Rules & Standards** - Quality criteria and constraints -5. **Examples & Patterns** - Concrete illustrations of approaches -6. **Context Management** - Conversation continuity handling -7. **Immediate Deliverables** - Expected outputs -8. **Reasoning Approach** - Systematic thinking methodology -9. **Output Structure** - Format and organization requirements -10. **Response Framework** - Templates and structured approaches + + + - task_type: [classification of work required] + - complexity_level: [simple/moderate/complex/enterprise] + - domain_expertise: [required knowledge areas] + - output_requirements: [expected deliverables] + - success_criteria: [measurable outcomes] + + + + - process_flow: [sequential/parallel/conditional steps] + - decision_points: [branching logic required] + - template_requirements: [structured outputs needed] + - subagent_opportunities: [specialized role assignments] + + -**Step 3: Present Your Analysis** + -Use this format: + +### Step 2: 10-Step Framework Integration +Map the enhanced framework elements to structured XML components. + + + + [specialized role definition] + [primary goal statement] + + + + [communication style parameters] + [technical sophistication requirements] + + + + [domain knowledge needed] + [external knowledge sources] + + + + [operational constraints] + [measurable criteria] + + + + [concrete illustrations] + [conversation continuity] + + + + [expected outputs] + [systematic methodology] + [format organization] + [templates and patterns] + + + + + + +### Step 3: Generate Structured Prompt Architecture +Create the comprehensive prompt using systematic XML structure. + + +# [PROMPT_TITLE] + +## Overview +[CONCISE_MISSION_STATEMENT_WITH_SCOPE] + + + [INITIAL_VALIDATION_REQUIREMENTS] + + + + + +### Step 1: [STEP_TITLE] +[DETAILED_STEP_DESCRIPTION] + +<[step_specific_framework]> + <[framework_element]>[specific_requirements] + + IF [condition]: + [action_path_a] + ELSE: + [action_path_b] + + + + + <[template_name]> + [STRUCTURED_TEMPLATE_CONTENT] + + + + + - [SPECIFIC_CONSTRAINT_1] + - [SPECIFIC_CONSTRAINT_2] + + + + [CONCRETE_EXAMPLES_FOR_CLARITY] + + + + +[ADDITIONAL_STEPS_AS_NEEDED] + + + + + [VALIDATION_AND_QUALITY_ASSURANCE] + + + + + + + +### Step 4: Framework Enhancement Documentation +Document how each 10-step element was systematically integrated. + + + integrated via: [specific XML structures used] + integrated via: [communication framework elements] + integrated via: [knowledge requirement specifications] + integrated via: [constraint and validation systems] + integrated via: [template and example structures] + integrated via: [pre/post flight and continuity systems] + integrated via: [output specification frameworks] + integrated via: [decision trees and process flows] + integrated via: [template systems and formatting] + integrated via: [structured response patterns] + + + + + + +## Presentation Format + +Use this exact structure for your response: --- -## 📋 Request Analysis -**Original concept**: "[USER'S CONCEPT]" -**Task type**: [Brief description of work involved] -**Required expertise**: [Domain knowledge needed] -**Complexity level**: [Simple/Moderate/Complex] -## 🎯 10-Step Enhanced Framework -Instead of a basic request, here's a structured approach using all 10 framework elements: +## 📋 Architecture Analysis + +**Original concept**: "[USER'S_CONCEPT]" +**Task classification**: [SYSTEMATIC_WORK_TYPE] +**Architectural complexity**: [SIMPLE/MODERATE/COMPLEX/ENTERPRISE] +**Process flow type**: [SEQUENTIAL/CONDITIONAL/PARALLEL] + +## 🏗️ Generated Prompt Architecture -### ✨ Enhanced Request: ``` -[Create a comprehensive request that naturally incorporates all 10 elements, written as a single cohesive prompt that the user could copy and use. Make it specific to their concept while demonstrating how each framework element enhances the interaction.] +[COMPLETE_STRUCTURED_PROMPT_WITH_XML_TAGS_AND_PROCESS_FLOWS] ``` -## 🔧 Framework Elements Applied: -- **Task Context**: [How expert role was defined] -- **Tone Context**: [Communication style set] -- **Background Knowledge**: [Expertise requirements specified] -- **Task Rules & Standards**: [Quality criteria added] -- **Examples & Patterns**: [Illustrations provided] -- **Context Management**: [Continuity handled] -- **Immediate Deliverables**: [Expected outputs clarified] -- **Reasoning Approach**: [Thinking methodology specified] -- **Output Structure**: [Format requirements defined] -- **Response Framework**: [Templates/structure provided] +## 🔧 Enhancement Integration Map -### ▶️ Suggested Next Step -"Would you like me to proceed with this enhanced 10-step approach? Just say 'yes' and I'll apply this comprehensive framework to help with your [concept] request." +- **Task Context**: [How expert role and objectives were systematically defined] +- **Operational Framework**: [How tone and audience parameters were structured] +- **Knowledge Architecture**: [How expertise requirements were systematized] +- **Quality Systems**: [How standards and constraints were implemented] +- **Guidance Framework**: [How examples and patterns were integrated] +- **Process Management**: [How context and continuity were handled] +- **Output Systems**: [How deliverables and reasoning were structured] +- **Response Architecture**: [How templates and frameworks were organized] + +### ▶️ Implementation Ready + +"This structured prompt architecture is ready for immediate deployment. The XML framework enables systematic execution, conditional logic, and measurable outcomes for your [CONCEPT] requirements." --- -## Important Guidelines: -- Make the enhanced request feel natural, not like a checklist -- Ensure all 10 framework elements are meaningfully integrated -- Tailor the complexity and tone to match the original concept -- The enhanced request should be immediately usable by the user -- Focus on practical improvements that will genuinely enhance their interaction + + + - systematic_structure: XML-based organization with clear hierarchies + - conditional_logic: decision trees and branching workflows + - template_systems: reusable patterns and structured outputs + - process_flows: numbered steps with clear dependencies + - validation_frameworks: pre/post flight checks and quality gates + + + + - all_10_elements: seamlessly integrated into XML structure + - natural_flow: feels systematic rather than checklist-driven + - immediate_usability: ready for deployment without modification + - scalable_complexity: appropriate sophistication for task scope + - measurable_outcomes: clear success criteria and validation points + + diff --git a/.opencode/context/core/essential-patterns.md b/.opencode/context/core/essential-patterns.md new file mode 100644 index 0000000..689df3f --- /dev/null +++ b/.opencode/context/core/essential-patterns.md @@ -0,0 +1,250 @@ +# Essential Patterns - Core Knowledge Base + +## Error Handling Pattern + +**ALWAYS** handle errors gracefully: + +```typescript +try { + const result = await riskyOperation(); + return { success: true, data: result }; +} catch (error) { + console.error('Operation failed:', error); + return { success: false, error: error.message }; +} +``` + +## Validation Pattern + +**ALWAYS** validate input data: + +```typescript +function validateInput(input: any): { valid: boolean; errors?: string[] } { + const errors: string[] = []; + + if (!input) errors.push('Input is required'); + if (typeof input !== 'string') errors.push('Input must be a string'); + if (input.length < 3) errors.push('Input must be at least 3 characters'); + + return { + valid: errors.length === 0, + errors: errors.length > 0 ? errors : undefined + }; +} +``` + +## Logging Pattern + +**USE** consistent logging levels: + +```typescript +// Debug information (development only) +console.debug('Processing request:', requestId); + +// Info for important events +console.info('User authenticated:', userId); + +// Warning for potential issues +console.warn('Rate limit approaching for user:', userId); + +// Error for failures +console.error('Database connection failed:', error); +``` + +## Security Pattern + +**NEVER** expose sensitive information: + +```typescript +// ❌ BAD: Exposing internal errors +return { error: 'Internal server error: ' + error.message }; + +// ✅ GOOD: Generic error message +return { error: 'An unexpected error occurred. Please try again.' }; +``` + +## File System Safety Pattern + +**ALWAYS** validate file paths: + +```typescript +import path from 'path'; + +function safeReadFile(userPath: string): string | null { + const resolvedPath = path.resolve(userPath); + const allowedDir = path.resolve('./allowed-directory'); + + // Ensure path is within allowed directory + if (!resolvedPath.startsWith(allowedDir)) { + throw new Error('Access denied: Invalid path'); + } + + return fs.readFileSync(resolvedPath, 'utf8'); +} +``` + +## Type Safety Pattern + +**ALWAYS** use strict TypeScript types: + +```typescript +interface User { + id: string; + name: string; + email: string; + createdAt: Date; +} + +interface ApiResponse { + success: boolean; + data?: T; + error?: string; +} + +// Use generics for type-safe responses +function createUser(userData: Omit): ApiResponse { + // Implementation +} +``` + +## Async/Await Pattern + +**ALWAYS** handle promises properly: + +```typescript +// ❌ BAD: Nested promises +fetchUser().then(user => { + return fetchPosts(user.id).then(posts => { + return { user, posts }; + }); +}); + +// ✅ GOOD: Async/await with error handling +async function getUserWithPosts(userId: string) { + try { + const user = await fetchUser(userId); + const posts = await fetchPosts(user.id); + return { user, posts }; + } catch (error) { + console.error('Failed to fetch user data:', error); + throw error; + } +} +``` + +## Configuration Pattern + +**ALWAYS** use environment variables for configuration: + +```typescript +// config.ts +export const config = { + port: parseInt(process.env.PORT || '3000'), + databaseUrl: process.env.DATABASE_URL, + jwtSecret: process.env.JWT_SECRET, + nodeEnv: process.env.NODE_ENV || 'development', +}; + +// Validate required config +if (!config.databaseUrl) { + throw new Error('DATABASE_URL environment variable is required'); +} +``` + +## Testing Pattern + +**ALWAYS** write testable code: + +```typescript +// ❌ BAD: Hard to test +export function processPayment(amount: number) { + const apiKey = process.env.STRIPE_KEY; + // Direct API call +} + +// ✅ GOOD: Dependency injection +export interface PaymentService { + processPayment(amount: number): Promise; +} + +export function createPaymentProcessor(service: PaymentService) { + return { + async process(amount: number) { + return service.processPayment(amount); + } + }; +} +``` + +## Documentation Pattern + +**ALWAYS** document complex logic: + +```typescript +/** + * Calculates the total price including tax and discounts + * @param basePrice - The original price before modifications + * @param taxRate - Tax rate as decimal (e.g., 0.08 for 8%) + * @param discountPercent - Discount percentage (0-100) + * @returns The final price after tax and discount + */ +function calculateTotalPrice( + basePrice: number, + taxRate: number = 0.08, + discountPercent: number = 0 +): number { + const discountAmount = basePrice * (discountPercent / 100); + const discountedPrice = basePrice - discountAmount; + const taxAmount = discountedPrice * taxRate; + return discountedPrice + taxAmount; +} +``` + +## Performance Pattern + +**AVOID** unnecessary operations in loops: + +```typescript +// ❌ BAD: Repeated calculations +const results = []; +for (let i = 0; i < items.length; i++) { + results.push(items[i] * calculateTax(items[i])); // calculateTax called repeatedly +} + +// ✅ GOOD: Pre-calculate or cache +const results = []; +const taxRate = getCurrentTaxRate(); // Calculate once +for (let i = 0; i < items.length; i++) { + results.push(items[i] * taxRate); +} +``` + +## Code Organization Pattern + +**KEEP** functions focused and small: + +```typescript +// ❌ BAD: One function doing too much +function processOrder(orderData) { + // Validate input + // Calculate pricing + // Save to database + // Send email + // Log analytics +} + +// ✅ GOOD: Separated concerns +function validateOrder(orderData) { /* validation logic */ } +function calculatePricing(orderData) { /* pricing logic */ } +function saveOrder(orderData) { /* database logic */ } +function sendConfirmation(orderData) { /* email logic */ } +function logAnalytics(orderData) { /* analytics logic */ } + +async function processOrder(orderData) { + validateOrder(orderData); + const pricing = calculatePricing(orderData); + await saveOrder({ ...orderData, pricing }); + await sendConfirmation(orderData); + logAnalytics(orderData); +} +``` \ No newline at end of file diff --git a/.opencode/context/project/project-context.md b/.opencode/context/project/project-context.md new file mode 100644 index 0000000..c90da68 --- /dev/null +++ b/.opencode/context/project/project-context.md @@ -0,0 +1,97 @@ +# OpenCode Agent System Project Context + +## Technology Stack + +**Primary Language:** TypeScript +**Runtime:** Node.js/Bun +**Package Manager:** npm/pnpm/yarn +**Build Tools:** TypeScript Compiler (tsc) +**Testing:** Jest/Vitest (if configured) +**Linting:** ESLint (if configured) + +## Project Structure + +``` +.opencode/ +├── agent/ # AI agents for specific tasks +│ ├── subagents/ # Specialized subagents +│ └── *.md # Primary agents +├── command/ # Slash commands +├── context/ # Knowledge base for agents +└── plugin/ # Extensions and integrations + +tasks/ # Task management files +``` + +## Core Patterns + +### Agent Structure Pattern +```markdown +--- +description: "What this agent does" +mode: primary|subagent +tools: [read, edit, bash, etc.] +permissions: [security restrictions] +--- + +# Agent Name + +[Direct instructions for behavior] + +**EXECUTE** this [process type] for every [task type]: + +**1. [ACTION]** the [subject]: +- [Specific instruction 1] +- [Specific instruction 2] + +**RULES:** +- **ALWAYS** [critical requirement] +- **NEVER** [forbidden action] +``` + +### Command Structure Pattern +```markdown +--- +name: command-name +agent: target-agent +--- + +You are [doing specific task]. + +**Request:** $ARGUMENTS + +**Context Loaded:** +@.opencode/context/core/essential-patterns.md +@[additional context files] + +Execute [task] now. +``` + +### Context Loading Rules +- Commands load context immediately using @ references +- Agents can look up additional context deterministically +- Maximum 4 context files per command (250-450 lines total) +- Keep context files focused (50-150 lines each) + +## Security Guidelines + +- Agents have restricted permissions by default +- Sensitive operations require explicit approval +- No direct file system modifications without validation +- Build commands limited to safe operations + +## Development Workflow + +1. **Planning:** Create detailed task plans for complex work +2. **Implementation:** Execute one step at a time with validation +3. **Review:** Code review and security checks +4. **Testing:** Automated testing and build validation +5. **Documentation:** Update docs and context files + +## Quality Gates + +- TypeScript compilation passes +- Code review completed +- Build process succeeds +- Tests pass (if available) +- Documentation updated \ No newline at end of file From dd204b5a2b53b63ae8a918e2d923eb7478e7cda8 Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:40:36 +0100 Subject: [PATCH 2/6] update to agent,md --- .opencode/AGENTS.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/.opencode/AGENTS.md b/.opencode/AGENTS.md index 1446907..2270ffd 100644 --- a/.opencode/AGENTS.md +++ b/.opencode/AGENTS.md @@ -1,24 +1,28 @@ -# OpenCode Agents for Mastra RAG Template + This repository defines task-focused agents to streamline planning, implementation, review, documentation, and testing. Agents: -- `plan-project`: Roadmaps, milestones, ADRs, risk register -- `plan-analyse`: Repo survey, external research, dependency and risk mapping -- `mastra`: Implementation for ingestion, embeddings, LanceDB, retrieval, assembly, and agents -- `review`: Code review with targeted feedback and suggested diffs -- `documentation`: Doc updates for README, specs, examples -- `write-test`: Unit/integration tests, mocks, deterministic practices +- `general`: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks +- `reviewer`: Code review, security, and quality assurance agent +- `subagents/codebase-pattern-analyst`: TypeScript implementation agent for modular and functional development +- `subagents/coder-agent`: Executes coding subtasks in sequence, ensuring completion as specified +- `subagents/build-agent`: Type check and build validation agent +- `subagents/tester`: Test authoring and TDD agent +- `subagents/documentation`: Documentation authoring agent +- `@task-manager`: Task planning and management agent +- `@tester`: Test execution and validation agent +- `@documentation`: Documentation generation and updates agent Usage: ```bash -# Start a session with an agent -opencode --agent plan-project +# Launch a task with a specific agent +task --description "Short task description" --prompt "Detailed task instructions" --subagent_type general -# One-off task -opencode run --agent mastra "Implement LanceDB schema and retrieval module" +# Example for code review +task --description "Review code" --prompt "Review the changes in pull request #123" --subagent_type reviewer ``` Safety: From 25e6e919be3b0d4e020a6b6be9feb8db26c6eef5 Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Sun, 21 Sep 2025 17:34:31 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20Gemini=20API=20ke?= =?UTF-8?q?y=20configuration=20to=20env.example=20for=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .opencode/tool/README.md | 79 +++++++++++++++ .opencode/tool/bun.lock | 160 ++++++++++++++++++++++++++++++ .opencode/tool/gemini-multiple.ts | 109 ++++++++++++++++++++ .opencode/tool/gemini.ts | 55 ++++++++++ .opencode/tool/package.json | 20 ++++ .opencode/tool/test-plugin.ts | 11 ++ .opencode/tool/tsconfig.json | 13 +++ env.example | 4 + 8 files changed, 451 insertions(+) create mode 100644 .opencode/tool/README.md create mode 100644 .opencode/tool/bun.lock create mode 100644 .opencode/tool/gemini-multiple.ts create mode 100644 .opencode/tool/gemini.ts create mode 100644 .opencode/tool/package.json create mode 100644 .opencode/tool/test-plugin.ts create mode 100644 .opencode/tool/tsconfig.json diff --git a/.opencode/tool/README.md b/.opencode/tool/README.md new file mode 100644 index 0000000..5197794 --- /dev/null +++ b/.opencode/tool/README.md @@ -0,0 +1,79 @@ +# Gemini Image Tools + +This collection of tools allows you to edit and analyze images using Google's Gemini AI directly from OpenCode. + +## Setup + +1. Get your Gemini API key from [Google AI Studio](https://makersuite.google.com/app/apikey) +2. Add it to your `.env` file: + ```bash + GEMINI_API_KEY=your_api_key_here + ``` + +## Available Tools + +### `/gemini` - Simple Image Editor +Edit an image using file path or data URL: + +```bash +/gemini "path/to/image.png" "Add a red border around the image" "output.png" +/gemini "data:image/png;base64,AAA..." "Convert to black and white" +``` + +### `/gemini_multiple_edit` - Advanced Image Editor +Same functionality as `/gemini` but from the multiple tools file: + +```bash +/gemini_multiple_edit "image.jpg" "Make it look like a watercolor painting" "watercolor.jpg" +``` + +### `/gemini_multiple_analyze` - Image Analysis +Analyze an image without editing it: + +```bash +/gemini_multiple_analyze "screenshot.png" "What programming language is shown in this code?" +/gemini_multiple_analyze "photo.jpg" "Describe the objects and colors in this image" +``` + +### `/gemini_edit` - Auto-Detection Plugin +1. Paste an image directly into your OpenCode chat +2. Use the command with just the prompt: + +```bash +/gemini_edit "Add the text 'Hello World' in cursive at top" +/gemini_edit "Make this image look like a painting" +``` + +## Features + +- **File Path Support**: Pass local image file paths +- **Data URL Support**: Use base64 data URLs from pasted images +- **Auto-Detection**: Plugin automatically captures the latest pasted image +- **Image Analysis**: Ask questions about images without editing +- **Flexible Output**: Specify custom output filenames or use defaults +- **Error Handling**: Clear error messages for missing API keys or failed requests + +## Files + +- `gemini.ts` - Simple tool that accepts image arguments +- `gemini-multiple.ts` - Multiple tools (edit + analyze) in one file +- `../plugin/gemini-edit.ts` - Plugin with auto-detection of pasted images + +## API Endpoints + +- **Image Editing**: Uses Gemini 2.5 Flash with image preview capabilities +- **Image Analysis**: Uses Gemini 2.5 Flash for text-based analysis + +## Examples + +```bash +# Edit an image +/gemini "logo.png" "Add a subtle drop shadow" "logo-shadow.png" + +# Analyze code in a screenshot +/gemini_multiple_analyze "code-screenshot.png" "What bugs can you spot in this code?" + +# Auto-edit pasted image +# (paste image first, then run:) +/gemini_edit "Remove the background and make it transparent" +``` \ No newline at end of file diff --git a/.opencode/tool/bun.lock b/.opencode/tool/bun.lock new file mode 100644 index 0000000..ba13347 --- /dev/null +++ b/.opencode/tool/bun.lock @@ -0,0 +1,160 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "opencode-gemini-tool", + "dependencies": { + "@opencode-ai/sdk": "^0.10.0", + "zod": "^4.1.9", + }, + "devDependencies": { + "@opencode-ai/plugin": "^0.10.0", + "@types/node": "^24.2.1", + "bun-types": "latest", + }, + }, + }, + "packages": { + "@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.0.6", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } }, "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w=="], + + "@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.81.0", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-PoJukNBkUfHOoMDpN33bBETX49TUhy7Hu8Sa0jslOvFndvZ5VjQr4Nl/Dzjb9LG1Lp5HjybyTJMA6a1zYk/q6A=="], + + "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + + "@opencode-ai/plugin": ["@opencode-ai/plugin@0.10.0", "", { "dependencies": { "@opencode-ai/sdk": "0.10.0", "zod": "4.1.8" } }, "sha512-qviOlxiWZchKGfJ1WWWM9q8Jz/6PokcdEUvGW0xeuC11QAkgC/RRC0DDn62PCcJUuWTOfIBkIcIQdP4qzbYNKw=="], + + "@opencode-ai/sdk": ["@opencode-ai/sdk@0.10.0", "", { "dependencies": { "@hey-api/openapi-ts": "0.81.0" } }, "sha512-2ojC3KEdf5r9rmhKJJx4cq8gxhw5LATHokIUl+VXsbE5qWaLe7yzt5Xpb2505wyieMfUlujo6XDGUtnPQXWkAw=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + + "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], + + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "c12": ["c12@2.0.1", "", { "dependencies": { "chokidar": "^4.0.1", "confbox": "^0.1.7", "defu": "^6.1.4", "dotenv": "^16.4.5", "giget": "^1.2.3", "jiti": "^2.3.0", "mlly": "^1.7.1", "ohash": "^1.1.4", "pathe": "^1.1.2", "perfect-debounce": "^1.0.0", "pkg-types": "^1.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + + "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], + + "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], + + "commander": ["commander@13.0.0", "", {}, "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ=="], + + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "default-browser": ["default-browser@5.2.1", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg=="], + + "default-browser-id": ["default-browser-id@5.0.0", "", {}, "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + + "giget": ["giget@1.2.5", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.5.4", "pathe": "^2.0.3", "tar": "^6.2.1" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug=="], + + "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], + + "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + + "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], + + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + + "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], + + "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], + + "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + + "nypm": ["nypm@0.5.4", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "tinyexec": "^0.3.2", "ufo": "^1.5.4" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA=="], + + "ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="], + + "open": ["open@10.1.2", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw=="], + + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + + "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + + "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], + + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], + + "undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + + "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], + + "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "zod": ["zod@4.1.9", "", {}, "sha512-HI32jTq0AUAC125z30E8bQNz0RQ+9Uc+4J7V97gLYjZVKRjeydPgGt6dvQzFrav7MYOUGFqqOGiHpA/fdbd0cQ=="], + + "@opencode-ai/plugin/zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], + + "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + } +} diff --git a/.opencode/tool/gemini-multiple.ts b/.opencode/tool/gemini-multiple.ts new file mode 100644 index 0000000..f32e0e2 --- /dev/null +++ b/.opencode/tool/gemini-multiple.ts @@ -0,0 +1,109 @@ +import { tool } from "@opencode-ai/plugin" + +async function parseImageInput(input: string) { + // Accepts file path ("./img.png") or data URL ("data:image/png;base64,...") + if (input.startsWith("data:")) { + const base64 = input.split(",")[1] + const mime = input.substring(5, input.indexOf(";")) + return { mime, base64 } + } + // Treat as file path + const file = Bun.file(input) + const arr = await file.arrayBuffer() + const base64 = Buffer.from(arr).toString("base64") + // Best-effort mime + const mime = file.type || "image/png" + return { mime, base64 } +} + +async function callGeminiAPI(mime: string, base64: string, prompt: string) { + const apiKey = process.env.GEMINI_API_KEY + if (!apiKey) throw new Error("Set GEMINI_API_KEY in your environment") + + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify({ + inputs: [{ mimeType: mime, data: base64 }], + contents: [{ parts: [{ text: prompt }]}], + }), + } + ) + + if (!res.ok) throw new Error(`API error: ${await res.text()}`) + const json = await res.json() + const b64 = json?.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data + if (!b64) throw new Error("No image data returned") + + return b64 +} + +export const edit = tool({ + description: "Edit an image using Gemini with file path or data URL", + args: { + image: tool.schema.string().describe("File path or data URL"), + prompt: tool.schema.string().describe("Edit instruction"), + output: tool.schema.string().optional().describe("Output filename (default edited.png)"), + }, + async execute(args, context) { + try { + const { mime, base64 } = await parseImageInput(args.image) + const resultBase64 = await callGeminiAPI(mime, base64, args.prompt) + + const out = args.output || "edited.png" + await Bun.write(out, Buffer.from(resultBase64, "base64")) + return `Saved edited image to ${out}` + } catch (error) { + return `Error: ${error.message}` + } + }, +}) + +export const analyze = tool({ + description: "Analyze an image using Gemini without editing", + args: { + image: tool.schema.string().describe("File path or data URL"), + question: tool.schema.string().describe("What to analyze about the image"), + }, + async execute(args, context) { + try { + const { mime, base64 } = await parseImageInput(args.image) + + const apiKey = process.env.GEMINI_API_KEY + if (!apiKey) return "Set GEMINI_API_KEY in your environment" + + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify({ + contents: [{ + parts: [ + { text: args.question }, + { inlineData: { mimeType: mime, data: base64 } } + ] + }], + }), + } + ) + + if (!res.ok) return `API error: ${await res.text()}` + const json = await res.json() + const text = json?.candidates?.[0]?.content?.parts?.[0]?.text + if (!text) return "No analysis returned" + + return text + } catch (error) { + return `Error: ${error.message}` + } + }, +}) \ No newline at end of file diff --git a/.opencode/tool/gemini.ts b/.opencode/tool/gemini.ts new file mode 100644 index 0000000..9dc87cf --- /dev/null +++ b/.opencode/tool/gemini.ts @@ -0,0 +1,55 @@ +import { tool } from "@opencode-ai/plugin/tool" + +async function parseImageInput(input: string) { + // Accepts file path ("./img.png") or data URL ("data:image/png;base64,...") + if (input.startsWith("data:")) { + const base64 = input.split(",")[1] + const mime = input.substring(5, input.indexOf(";")) + return { mime, base64 } + } + // Treat as file path + const file = Bun.file(input) + const arr = await file.arrayBuffer() + const base64 = Buffer.from(arr).toString("base64") + // Best-effort mime + const mime = file.type || "image/png" + return { mime, base64 } +} + +export default tool({ + description: "Edit an image using Gemini. Pass file path or data URL.", + args: { + image: tool.schema.string().describe("File path or data URL"), + prompt: tool.schema.string().describe("Edit instruction"), + output: tool.schema.string().optional().describe("Output filename (default edited.png)"), + }, + async execute(args, context) { + const apiKey = process.env.GEMINI_API_KEY + if (!apiKey) return "Set GEMINI_API_KEY in your environment" + + const { mime, base64 } = await parseImageInput(args.image) + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify({ + inputs: [{ mimeType: mime, data: base64 }], + contents: [{ parts: [{ text: args.prompt }]}], + }), + } + ) + + if (!res.ok) return `API error: ${await res.text()}` + const json = await res.json() + const b64 = json?.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data + if (!b64) return "No image data returned" + + const out = args.output || "edited.png" + await Bun.write(out, Buffer.from(b64, "base64")) + return `Saved ${out}` + }, +}) \ No newline at end of file diff --git a/.opencode/tool/package.json b/.opencode/tool/package.json new file mode 100644 index 0000000..1c4f2f2 --- /dev/null +++ b/.opencode/tool/package.json @@ -0,0 +1,20 @@ +{ + "type": "module", + "name": "opencode-gemini-tool", + "version": "1.0.0", + "description": "Gemini image editing tool for OpenCode", + "main": "gemini.ts", + "scripts": { + "build": "bun build gemini.ts --outdir dist", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@opencode-ai/sdk": "^0.10.0", + "zod": "^4.1.9" + }, + "devDependencies": { + "@opencode-ai/plugin": "^0.10.0", + "@types/node": "^24.2.1", + "bun-types": "latest" + } +} \ No newline at end of file diff --git a/.opencode/tool/test-plugin.ts b/.opencode/tool/test-plugin.ts new file mode 100644 index 0000000..60b1c2c --- /dev/null +++ b/.opencode/tool/test-plugin.ts @@ -0,0 +1,11 @@ +import * as plugin from "@opencode-ai/plugin" + +console.log("Available exports:", Object.keys(plugin)) + +// Try to import tool +try { + const { tool } = await import("@opencode-ai/plugin") + console.log("Tool function:", typeof tool) +} catch (e) { + console.log("Tool import error:", e.message) +} \ No newline at end of file diff --git a/.opencode/tool/tsconfig.json b/.opencode/tool/tsconfig.json new file mode 100644 index 0000000..dc2a13b --- /dev/null +++ b/.opencode/tool/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "resolvePackageJsonExports": true, + "resolvePackageJsonImports": true, + "target": "ES2022", + "skipLibCheck": true, + "types": ["bun-types"] + }, + "include": ["*.ts"], + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/env.example b/env.example index 16039ff..4d6ba21 100644 --- a/env.example +++ b/env.example @@ -18,3 +18,7 @@ TELEGRAM_CHECK_INTERVAL=30000 # Enable/disable the plugin (true/false) TELEGRAM_ENABLED=true + +# Gemini API Configuration +# Get your API key from https://makersuite.google.com/app/apikey +GEMINI_API_KEY=your_gemini_api_key_here From e073c5bb2e9a4dea3d73432e3930a353579a24be Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:31:36 +0100 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20chore:=20remove=20d?= =?UTF-8?q?eprecated=20Gemini=20tool=20files=20and=20update=20package=20co?= =?UTF-8?q?nfiguration=20for=20improved=20structure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .opencode/agent/image-specialist.md | 67 ++++ .opencode/agent/{ => subagents}/reviewer.md | 0 .opencode/command/prompter.md | 282 +++++---------- .opencode/tool/env/index.ts | 168 +++++++++ .opencode/tool/gemini-multiple.ts | 109 ------ .opencode/tool/gemini.ts | 55 --- .opencode/tool/gemini/index.ts | 379 ++++++++++++++++++++ .opencode/tool/index.ts | 27 ++ .opencode/tool/package.json | 8 +- .opencode/tool/template/README.md | 35 ++ .opencode/tool/template/index.ts | 25 ++ .opencode/tool/test-plugin.ts | 11 - assets/.gitignore | 6 + assets/images/.gitkeep | 1 + 14 files changed, 811 insertions(+), 362 deletions(-) create mode 100644 .opencode/agent/image-specialist.md rename .opencode/agent/{ => subagents}/reviewer.md (100%) create mode 100644 .opencode/tool/env/index.ts delete mode 100644 .opencode/tool/gemini-multiple.ts delete mode 100644 .opencode/tool/gemini.ts create mode 100644 .opencode/tool/gemini/index.ts create mode 100644 .opencode/tool/index.ts create mode 100644 .opencode/tool/template/README.md create mode 100644 .opencode/tool/template/index.ts delete mode 100644 .opencode/tool/test-plugin.ts create mode 100644 assets/.gitignore create mode 100644 assets/images/.gitkeep diff --git a/.opencode/agent/image-specialist.md b/.opencode/agent/image-specialist.md new file mode 100644 index 0000000..6536dcd --- /dev/null +++ b/.opencode/agent/image-specialist.md @@ -0,0 +1,67 @@ +--- +description: Specialized agent for image editing and analysis using Gemini AI tools +mode: primary +model: anthropic/claude-sonnet-4-20250514 +temperature: 0.3 +permission: + edit: deny + bash: deny + webfetch: allow +tools: + write: false + edit: false + bash: false + read: true + grep: true + glob: true + list: true + gemini-multiple_analyze: true + gemini-multiple_edit: true + gemini: true +environment: + GEMINI_API_KEY: required +--- + +You are an image processing specialist powered by Gemini AI's Nano Banana model. Your capabilities include: + +## Core Functions +- **Image Generation**: Creating images from text using Gemini Nano Banana +- **Image Editing**: Modifying existing images with Nano Banana +- **Image Analysis**: Analyzing images with detailed descriptions + +## Tools Available +- `gemini-multiple_edit`: Edit existing images with Nano Banana +- `gemini-multiple_analyze`: Analyze images and provide detailed descriptions +- `gemini`: Generate or edit images (legacy tool) + +## Meta-Prompt for Nano Banana Requests + +When users provide simple instructions, use this meta-prompt approach to create detailed Nano Banana prompts: + +**Process:** +1. **Identify core purpose**: Schematic/diagram, action illustration, or emotive scene? +2. **Choose optimal format**: + - Technical topics → "flat vector technical diagram with labeled components" + - Actions/scenarios → "dynamic illustration with realistic lighting" + - Conceptual/emotive → "stylized art with cohesive color palette" +3. **Determine style attributes**: Color palette, typography, composition +4. **Build final prompt**: "Create a [FORMAT] illustrating [TOPIC] in a [STYLE] style, using [COLORS], with [TYPOGRAPHY] labels, include [LAYOUT ELEMENTS]" + +**Example:** +- Input: "Visualize microservices architecture" +- Output: "Create a flat-vector technical diagram illustrating a microservices architecture with labeled service nodes and directional arrows showing service-to-service calls, in a navy & teal color palette, with Roboto sans-serif labels, include a legend box at bottom right, optimized for 1200×627 px." + +## Workflow +1. **For simple requests**: Apply meta-prompt to enhance the instruction +2. **For image generation**: Use detailed, styled prompts with Nano Banana +3. **For image editing**: Preserve original context while applying modifications +4. **For analysis**: Provide comprehensive descriptions and suggestions + +## File Organization +- Images are automatically organized by date: `assets/images/YYYY-MM-DD/` +- Generations saved to: `generations/` subdirectory +- Edits saved to: `edits/` subdirectory with auto-increment naming +- No files are overwritten - each edit creates a unique numbered version +- All images stored in repo's `assets/images/` directory for proper organization + +Always ensure you have necessary inputs and provide clear descriptions of operations performed. \ No newline at end of file diff --git a/.opencode/agent/reviewer.md b/.opencode/agent/subagents/reviewer.md similarity index 100% rename from .opencode/agent/reviewer.md rename to .opencode/agent/subagents/reviewer.md diff --git a/.opencode/command/prompter.md b/.opencode/command/prompter.md index 9aecc08..0ce85b3 100644 --- a/.opencode/command/prompter.md +++ b/.opencode/command/prompter.md @@ -1,208 +1,122 @@ --- -description: Transform basic requests into comprehensive, systematic prompt architectures using structured XML methodology +agent: prompt-enhancer +description: "Research-backed XML prompt optimizer delivering 20% performance improvement" --- -# Advanced Prompt Architecture Generator + -## Overview -Transform basic requests into comprehensive, systematic prompt frameworks using structured XML methodology and the 10-step enhancement principles. + + Prompt optimization using empirically-proven XML structures + LLM prompt engineering with Stanford/Anthropic research patterns + 20% routing accuracy, 25% consistency, 17% performance gains + - +Expert Prompt Architect specializing in evidence-based XML structure optimization - -### Step 1: Request Analysis -Analyze the user's concept ($ARGUMENTS) to identify core requirements and optimal prompt architecture. +Transform prompts into high-performance XML following proven component ordering for measurable improvements - - - - task_type: [classification of work required] - - complexity_level: [simple/moderate/complex/enterprise] - - domain_expertise: [required knowledge areas] - - output_requirements: [expected deliverables] - - success_criteria: [measurable outcomes] - + + + Assess current structure against research patterns + + - Component order (context→role→task→instructions) + - Length ratios (role 5-10%, context 15-25%, instructions 40-50%) + - Context management strategy presence + - Hierarchical routing implementation + + - - - process_flow: [sequential/parallel/conditional steps] - - decision_points: [branching logic required] - - template_requirements: [structured outputs needed] - - subagent_opportunities: [specialized role assignments] - - - - - - -### Step 2: 10-Step Framework Integration -Map the enhanced framework elements to structured XML components. - - - - [specialized role definition] - [primary goal statement] - + + Apply optimal component sequence + + 1. Context (system→domain→task→execution) + 2. Role (clear identity, first 20% of prompt) + 3. Task (primary objective) + 4. Instructions (hierarchical workflow) + 5. Examples (if needed) + 6. Constraints (boundaries) + 7. Output_format (expected structure) + + - - [communication style parameters] - [technical sophistication requirements] - + + Implement manager-worker patterns + + - LLM-based decision making + - Explicit routing criteria with @ symbol + - Fallback strategies + - Context allocation per task type + + - - [domain knowledge needed] - [external knowledge sources] - - - - [operational constraints] - [measurable criteria] - - - - [concrete illustrations] - [conversation continuity] - - - - [expected outputs] - [systematic methodology] - [format organization] - [templates and patterns] - - + + Apply 3-level context management + + Complete isolation - subagent receives only specific task + Filtered context - curated relevant background + Windowed context - last N messages only + + + - - - -### Step 3: Generate Structured Prompt Architecture -Create the comprehensive prompt using systematic XML structure. + + + - 40% improvement in response quality with descriptive tags + - 15% reduction in token overhead for complex prompts + - Universal compatibility across models + - Explicit boundaries prevent context bleeding + + + + 5-10% of total prompt + 15-25% hierarchical information + 40-50% detailed procedures + 20-30% when needed + 5-10% boundaries + + + + Always use @ symbol (e.g., @context-provider, @research-assistant-agent) + Route to @[agent-name] when [condition] + + -# [PROMPT_TITLE] +## Analysis Results +- Current Structure Score: [X/10] +- Optimization Opportunities: [LIST] +- Expected Performance Gain: [X%] -## Overview -[CONCISE_MISSION_STATEMENT_WITH_SCOPE] +## Optimized Prompt Structure - - [INITIAL_VALIDATION_REQUIREMENTS] - +```xml + + [HIERARCHICAL CONTEXT: system→domain→task→execution] + - +[AGENT IDENTITY - 5-10% of prompt] - -### Step 1: [STEP_TITLE] -[DETAILED_STEP_DESCRIPTION] +[PRIMARY OBJECTIVE] -<[step_specific_framework]> - <[framework_element]>[specific_requirements] - - IF [condition]: - [action_path_a] - ELSE: - [action_path_b] - - + + [WORKFLOW WITH ROUTING USING @ SYMBOLS] + - - <[template_name]> - [STRUCTURED_TEMPLATE_CONTENT] - - - - - - [SPECIFIC_CONSTRAINT_1] - - [SPECIFIC_CONSTRAINT_2] - - - - [CONCRETE_EXAMPLES_FOR_CLARITY] - - - - -[ADDITIONAL_STEPS_AS_NEEDED] - - - - - [VALIDATION_AND_QUALITY_ASSURANCE] - +[ADDITIONAL COMPONENTS AS NEEDED] +``` +## Implementation Notes +- Component reordering impact: +[X]% performance +- Context management efficiency: [X]% reduction +- Routing accuracy improvement: +[X]% +- Subagent references: @agent-name format maintained - + + Stanford multi-instruction study + Anthropic XML research + Measurable 20% routing improvement + 80% reduction in unnecessary context + Ready for deployment without modification + - -### Step 4: Framework Enhancement Documentation -Document how each 10-step element was systematically integrated. - - - integrated via: [specific XML structures used] - integrated via: [communication framework elements] - integrated via: [knowledge requirement specifications] - integrated via: [constraint and validation systems] - integrated via: [template and example structures] - integrated via: [pre/post flight and continuity systems] - integrated via: [output specification frameworks] - integrated via: [decision trees and process flows] - integrated via: [template systems and formatting] - integrated via: [structured response patterns] - - - - - - -## Presentation Format - -Use this exact structure for your response: - ---- - -## 📋 Architecture Analysis - -**Original concept**: "[USER'S_CONCEPT]" -**Task classification**: [SYSTEMATIC_WORK_TYPE] -**Architectural complexity**: [SIMPLE/MODERATE/COMPLEX/ENTERPRISE] -**Process flow type**: [SEQUENTIAL/CONDITIONAL/PARALLEL] - -## 🏗️ Generated Prompt Architecture - -``` -[COMPLETE_STRUCTURED_PROMPT_WITH_XML_TAGS_AND_PROCESS_FLOWS] -``` - -## 🔧 Enhancement Integration Map - -- **Task Context**: [How expert role and objectives were systematically defined] -- **Operational Framework**: [How tone and audience parameters were structured] -- **Knowledge Architecture**: [How expertise requirements were systematized] -- **Quality Systems**: [How standards and constraints were implemented] -- **Guidance Framework**: [How examples and patterns were integrated] -- **Process Management**: [How context and continuity were handled] -- **Output Systems**: [How deliverables and reasoning were structured] -- **Response Architecture**: [How templates and frameworks were organized] - -### ▶️ Implementation Ready - -"This structured prompt architecture is ready for immediate deployment. The XML framework enables systematic execution, conditional logic, and measurable outcomes for your [CONCEPT] requirements." - ---- - - - - - systematic_structure: XML-based organization with clear hierarchies - - conditional_logic: decision trees and branching workflows - - template_systems: reusable patterns and structured outputs - - process_flows: numbered steps with clear dependencies - - validation_frameworks: pre/post flight checks and quality gates - - - - - all_10_elements: seamlessly integrated into XML structure - - natural_flow: feels systematic rather than checklist-driven - - immediate_usability: ready for deployment without modification - - scalable_complexity: appropriate sophistication for task scope - - measurable_outcomes: clear success criteria and validation points - - diff --git a/.opencode/tool/env/index.ts b/.opencode/tool/env/index.ts new file mode 100644 index 0000000..8ff2ac9 --- /dev/null +++ b/.opencode/tool/env/index.ts @@ -0,0 +1,168 @@ +import { readFile } from "fs/promises" +import { resolve } from "path" + +/** + * Configuration for environment variable loading + */ +export interface EnvLoaderConfig { + /** Custom paths to search for .env files (relative to current working directory) */ + searchPaths?: string[] + /** Whether to log when environment variables are loaded */ + verbose?: boolean + /** Whether to override existing environment variables */ + override?: boolean +} + +/** + * Default search paths for .env files + */ +const DEFAULT_ENV_PATHS = [ + './.env', + '../.env', + '../../.env', + '../plugin/.env', + '../../../.env' +] + +/** + * Load environment variables from .env files + * Searches multiple common locations for .env files and loads them into process.env + * + * @param config Configuration options + * @returns Object containing loaded environment variables + */ +export async function loadEnvVariables(config: EnvLoaderConfig = {}): Promise> { + const { + searchPaths = DEFAULT_ENV_PATHS, + verbose = false, + override = false + } = config + + const loadedVars: Record = {} + + for (const envPath of searchPaths) { + try { + const fullPath = resolve(envPath) + const content = await readFile(fullPath, 'utf8') + + if (verbose) { + console.log(`Checking .env file: ${envPath}`) + } + + // Parse .env file content + const lines = content.split('\n') + for (const line of lines) { + const trimmed = line.trim() + if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) { + const [key, ...valueParts] = trimmed.split('=') + const value = valueParts.join('=').trim() + + // Remove quotes if present + const cleanValue = value.replace(/^["']|["']$/g, '') + + if (key && cleanValue && (override || !process.env[key])) { + process.env[key] = cleanValue + loadedVars[key] = cleanValue + + if (verbose) { + console.log(`Loaded ${key} from ${envPath}`) + } + } + } + } + } catch (error) { + // File doesn't exist or can't be read, continue to next + if (verbose) { + console.log(`Could not read ${envPath}: ${error.message}`) + } + } + } + + return loadedVars +} + +/** + * Get a specific environment variable with automatic .env file loading + * + * @param varName Name of the environment variable + * @param config Configuration options + * @returns The environment variable value or null if not found + */ +export async function getEnvVariable(varName: string, config: EnvLoaderConfig = {}): Promise { + // First check if it's already in the environment + let value = process.env[varName] + + if (!value) { + // Try to load from .env files + const loadedVars = await loadEnvVariables(config) + value = loadedVars[varName] || process.env[varName] + } + + return value || null +} + +/** + * Get a required environment variable with automatic .env file loading + * Throws an error if the variable is not found + * + * @param varName Name of the environment variable + * @param config Configuration options + * @returns The environment variable value + * @throws Error if the variable is not found + */ +export async function getRequiredEnvVariable(varName: string, config: EnvLoaderConfig = {}): Promise { + const value = await getEnvVariable(varName, config) + + if (!value) { + const searchPaths = config.searchPaths || DEFAULT_ENV_PATHS + throw new Error(`${varName} not found. Please set it in your environment or .env file. + +To fix this: +1. Add to .env file: ${varName}=your_value_here +2. Or export it: export ${varName}=your_value_here + +Current working directory: ${process.cwd()} +Searched paths: ${searchPaths.join(', ')} +Environment variables available: ${Object.keys(process.env).filter(k => k.includes(varName.split('_')[0])).join(', ') || 'none matching'}`) + } + + return value +} + +/** + * Load multiple required environment variables at once + * + * @param varNames Array of environment variable names + * @param config Configuration options + * @returns Object with variable names as keys and values as values + * @throws Error if any variable is not found + */ +export async function getRequiredEnvVariables(varNames: string[], config: EnvLoaderConfig = {}): Promise> { + const result: Record = {} + + // Load all .env files first + await loadEnvVariables(config) + + // Check each required variable + for (const varName of varNames) { + const value = process.env[varName] + if (!value) { + throw new Error(`Required environment variable ${varName} not found. Please set it in your environment or .env file.`) + } + result[varName] = value + } + + return result +} + +/** + * Utility function specifically for API keys + * + * @param apiKeyName Name of the API key environment variable + * @param config Configuration options + * @returns The API key value + * @throws Error if the API key is not found + */ +export async function getApiKey(apiKeyName: string, config: EnvLoaderConfig = {}): Promise { + return getRequiredEnvVariable(apiKeyName, config) +} \ No newline at end of file diff --git a/.opencode/tool/gemini-multiple.ts b/.opencode/tool/gemini-multiple.ts deleted file mode 100644 index f32e0e2..0000000 --- a/.opencode/tool/gemini-multiple.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { tool } from "@opencode-ai/plugin" - -async function parseImageInput(input: string) { - // Accepts file path ("./img.png") or data URL ("data:image/png;base64,...") - if (input.startsWith("data:")) { - const base64 = input.split(",")[1] - const mime = input.substring(5, input.indexOf(";")) - return { mime, base64 } - } - // Treat as file path - const file = Bun.file(input) - const arr = await file.arrayBuffer() - const base64 = Buffer.from(arr).toString("base64") - // Best-effort mime - const mime = file.type || "image/png" - return { mime, base64 } -} - -async function callGeminiAPI(mime: string, base64: string, prompt: string) { - const apiKey = process.env.GEMINI_API_KEY - if (!apiKey) throw new Error("Set GEMINI_API_KEY in your environment") - - const res = await fetch( - "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", - { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-goog-api-key": apiKey, - }, - body: JSON.stringify({ - inputs: [{ mimeType: mime, data: base64 }], - contents: [{ parts: [{ text: prompt }]}], - }), - } - ) - - if (!res.ok) throw new Error(`API error: ${await res.text()}`) - const json = await res.json() - const b64 = json?.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data - if (!b64) throw new Error("No image data returned") - - return b64 -} - -export const edit = tool({ - description: "Edit an image using Gemini with file path or data URL", - args: { - image: tool.schema.string().describe("File path or data URL"), - prompt: tool.schema.string().describe("Edit instruction"), - output: tool.schema.string().optional().describe("Output filename (default edited.png)"), - }, - async execute(args, context) { - try { - const { mime, base64 } = await parseImageInput(args.image) - const resultBase64 = await callGeminiAPI(mime, base64, args.prompt) - - const out = args.output || "edited.png" - await Bun.write(out, Buffer.from(resultBase64, "base64")) - return `Saved edited image to ${out}` - } catch (error) { - return `Error: ${error.message}` - } - }, -}) - -export const analyze = tool({ - description: "Analyze an image using Gemini without editing", - args: { - image: tool.schema.string().describe("File path or data URL"), - question: tool.schema.string().describe("What to analyze about the image"), - }, - async execute(args, context) { - try { - const { mime, base64 } = await parseImageInput(args.image) - - const apiKey = process.env.GEMINI_API_KEY - if (!apiKey) return "Set GEMINI_API_KEY in your environment" - - const res = await fetch( - "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent", - { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-goog-api-key": apiKey, - }, - body: JSON.stringify({ - contents: [{ - parts: [ - { text: args.question }, - { inlineData: { mimeType: mime, data: base64 } } - ] - }], - }), - } - ) - - if (!res.ok) return `API error: ${await res.text()}` - const json = await res.json() - const text = json?.candidates?.[0]?.content?.parts?.[0]?.text - if (!text) return "No analysis returned" - - return text - } catch (error) { - return `Error: ${error.message}` - } - }, -}) \ No newline at end of file diff --git a/.opencode/tool/gemini.ts b/.opencode/tool/gemini.ts deleted file mode 100644 index 9dc87cf..0000000 --- a/.opencode/tool/gemini.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { tool } from "@opencode-ai/plugin/tool" - -async function parseImageInput(input: string) { - // Accepts file path ("./img.png") or data URL ("data:image/png;base64,...") - if (input.startsWith("data:")) { - const base64 = input.split(",")[1] - const mime = input.substring(5, input.indexOf(";")) - return { mime, base64 } - } - // Treat as file path - const file = Bun.file(input) - const arr = await file.arrayBuffer() - const base64 = Buffer.from(arr).toString("base64") - // Best-effort mime - const mime = file.type || "image/png" - return { mime, base64 } -} - -export default tool({ - description: "Edit an image using Gemini. Pass file path or data URL.", - args: { - image: tool.schema.string().describe("File path or data URL"), - prompt: tool.schema.string().describe("Edit instruction"), - output: tool.schema.string().optional().describe("Output filename (default edited.png)"), - }, - async execute(args, context) { - const apiKey = process.env.GEMINI_API_KEY - if (!apiKey) return "Set GEMINI_API_KEY in your environment" - - const { mime, base64 } = await parseImageInput(args.image) - const res = await fetch( - "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", - { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-goog-api-key": apiKey, - }, - body: JSON.stringify({ - inputs: [{ mimeType: mime, data: base64 }], - contents: [{ parts: [{ text: args.prompt }]}], - }), - } - ) - - if (!res.ok) return `API error: ${await res.text()}` - const json = await res.json() - const b64 = json?.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data - if (!b64) return "No image data returned" - - const out = args.output || "edited.png" - await Bun.write(out, Buffer.from(b64, "base64")) - return `Saved ${out}` - }, -}) \ No newline at end of file diff --git a/.opencode/tool/gemini/index.ts b/.opencode/tool/gemini/index.ts new file mode 100644 index 0000000..3fe85c9 --- /dev/null +++ b/.opencode/tool/gemini/index.ts @@ -0,0 +1,379 @@ +import { tool } from "@opencode-ai/plugin/tool" +import { mkdir } from "fs/promises" +import { join, dirname, basename, extname, resolve } from "path" +import { getApiKey } from "../env" + +// Function to detect if we're in test mode +function isTestMode(): boolean { + // Only enable test mode when explicitly set + return process.env.GEMINI_TEST_MODE === 'true' +} + +// Function to get Gemini API key with automatic .env loading +async function getGeminiApiKey(): Promise { + if (isTestMode()) { + return 'test-api-key' + } + return getApiKey('GEMINI_API_KEY') +} + +interface ImageConfig { + outputDir?: string; + useTimestamp?: boolean; + preserveOriginal?: boolean; + customName?: string; +} + +async function parseImageInput(input: string) { + // Accepts file path ("./img.png") or data URL ("data:image/png;base64,...") + if (input.startsWith("data:")) { + const base64 = input.split(",")[1] + const mime = input.substring(5, input.indexOf(";")) + return { mime, base64 } + } + // Treat as file path + const file = Bun.file(input) + const arr = await file.arrayBuffer() + const base64 = Buffer.from(arr).toString("base64") + // Best-effort mime + const mime = file.type || "image/png" + return { mime, base64 } +} + +async function ensureDirectoryExists(dirPath: string) { + try { + await mkdir(dirPath, { recursive: true }) + } catch (error) { + // Directory might already exist, that's fine + } +} + +function getDateBasedPath(baseDir?: string): string { + // Default to assets/images at repo root + if (!baseDir) { + // Navigate from .opencode/tool/ to repo root, then to assets/images + baseDir = resolve(process.cwd(), "../../assets/images") + } + const today = new Date().toISOString().split('T')[0] // YYYY-MM-DD format + return join(baseDir, today) +} + +async function getUniqueFilename(directory: string, baseName: string, extension: string, isEdit: boolean = false): Promise { + await ensureDirectoryExists(directory) + + if (!isEdit) { + // For generations, use timestamp if file exists + const baseFilename = join(directory, `${baseName}${extension}`) + const fileExists = await Bun.file(baseFilename).exists() + + if (!fileExists) { + return baseFilename + } + + // Add timestamp if file exists + const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5) // Remove milliseconds and Z + return join(directory, `${baseName}_${timestamp}${extension}`) + } + + // For edits, use incremental numbering + let counter = 1 + let filename: string + + do { + const editSuffix = `_edit_${counter.toString().padStart(3, '0')}` + filename = join(directory, `${baseName}${editSuffix}${extension}`) + counter++ + } while (await Bun.file(filename).exists()) + + return filename +} + +export async function generateImage(prompt: string, config: ImageConfig = {}): Promise { + const apiKey = await getGeminiApiKey() + + // Test mode - return mock response without API call + if (isTestMode()) { + const baseDir = config.outputDir || getDateBasedPath() + const generationsDir = join(baseDir, "generations") + let baseName = config.customName || "generated" + if (baseName.endsWith('.png') || baseName.endsWith('.jpg') || baseName.endsWith('.jpeg')) { + baseName = baseName.substring(0, baseName.lastIndexOf('.')) + } + const outputPath = await getUniqueFilename(generationsDir, baseName, ".png", false) + + return `[TEST MODE] Would generate image: ${outputPath} for prompt: "${prompt.substring(0, 50)}..."` + } + + const body = { + contents: [{ + parts: [{ text: prompt }] + }], + } + + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify(body), + } + ) + + if (!res.ok) { + const errorText = await res.text() + throw new Error(`API error (${res.status}): ${errorText}`) + } + + const json = await res.json() + + // Look for image data in the response + const candidates = json?.candidates + if (!candidates || candidates.length === 0) { + throw new Error("No candidates in response") + } + + const parts = candidates[0]?.content?.parts + if (!parts || parts.length === 0) { + throw new Error("No parts in response") + } + + let b64 = null + for (const part of parts) { + if (part.inlineData?.data) { + b64 = part.inlineData.data + break + } + } + + if (!b64) { + throw new Error("No image data returned from Nano Banana model") + } + + // Determine output path + const baseDir = config.outputDir || getDateBasedPath() + const generationsDir = join(baseDir, "generations") + + // Generate filename (remove extension if already present) + let baseName = config.customName || "generated" + if (baseName.endsWith('.png') || baseName.endsWith('.jpg') || baseName.endsWith('.jpeg')) { + baseName = baseName.substring(0, baseName.lastIndexOf('.')) + } + const extension = ".png" + const outputPath = await getUniqueFilename(generationsDir, baseName, extension, false) + + console.log(`Saving generated image to: ${outputPath}`) + await Bun.write(outputPath, Buffer.from(b64, "base64")) + + const fileExists = await Bun.file(outputPath).exists() + if (!fileExists) { + throw new Error(`Failed to save file to ${outputPath}`) + } + + const stats = await Bun.file(outputPath).stat() + return `Generated image saved: ${outputPath} (${stats.size} bytes)` +} + +export async function editImage(imagePath: string, prompt: string, config: ImageConfig = {}): Promise { + const apiKey = await getGeminiApiKey() + + // Test mode - return mock response without API call + if (isTestMode()) { + const baseDir = config.outputDir || getDateBasedPath() + const editsDir = join(baseDir, "edits") + const originalName = basename(imagePath, extname(imagePath)) + let baseName = config.customName || originalName + if (baseName.endsWith('.png') || baseName.endsWith('.jpg') || baseName.endsWith('.jpeg')) { + baseName = baseName.substring(0, baseName.lastIndexOf('.')) + } + const outputPath = await getUniqueFilename(editsDir, baseName, ".png", true) + + return `[TEST MODE] Would edit image: ${imagePath} -> ${outputPath} with prompt: "${prompt.substring(0, 50)}..."` + } + + // Parse the input image + const { mime, base64 } = await parseImageInput(imagePath) + + const body = { + contents: [{ + parts: [ + { text: prompt }, + { inlineData: { mimeType: mime, data: base64 } } + ] + }], + } + + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify(body), + } + ) + + if (!res.ok) { + const errorText = await res.text() + throw new Error(`API error (${res.status}): ${errorText}`) + } + + const json = await res.json() + + // Look for image data in the response + const candidates = json?.candidates + if (!candidates || candidates.length === 0) { + throw new Error("No candidates in response") + } + + const parts = candidates[0]?.content?.parts + if (!parts || parts.length === 0) { + throw new Error("No parts in response") + } + + let b64 = null + for (const part of parts) { + if (part.inlineData?.data) { + b64 = part.inlineData.data + break + } + } + + if (!b64) { + throw new Error("No image data returned from Nano Banana model") + } + + // Determine output path + const baseDir = config.outputDir || getDateBasedPath() + const editsDir = join(baseDir, "edits") + + // Extract original filename without extension + const originalName = basename(imagePath, extname(imagePath)) + let baseName = config.customName || originalName + if (baseName.endsWith('.png') || baseName.endsWith('.jpg') || baseName.endsWith('.jpeg')) { + baseName = baseName.substring(0, baseName.lastIndexOf('.')) + } + const extension = ".png" + + const outputPath = await getUniqueFilename(editsDir, baseName, extension, true) + + console.log(`Saving edited image to: ${outputPath}`) + await Bun.write(outputPath, Buffer.from(b64, "base64")) + + const fileExists = await Bun.file(outputPath).exists() + if (!fileExists) { + throw new Error(`Failed to save file to ${outputPath}`) + } + + const stats = await Bun.file(outputPath).stat() + return `Edited image saved: ${outputPath} (${stats.size} bytes)` +} + +export async function analyzeImage(imagePath: string, question: string): Promise { + const apiKey = await getGeminiApiKey() + + // Test mode - return mock response without API call + if (isTestMode()) { + return `[TEST MODE] Would analyze image: ${imagePath} with question: "${question.substring(0, 50)}..." - Mock analysis: This is a test image analysis response.` + } + + const { mime, base64 } = await parseImageInput(imagePath) + + const res = await fetch( + "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent", + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-goog-api-key": apiKey, + }, + body: JSON.stringify({ + contents: [{ + parts: [ + { text: question }, + { inlineData: { mimeType: mime, data: base64 } } + ] + }], + }), + } + ) + + if (!res.ok) { + const errorText = await res.text() + throw new Error(`API error (${res.status}): ${errorText}`) + } + + const json = await res.json() + const text = json?.candidates?.[0]?.content?.parts?.[0]?.text + if (!text) { + throw new Error("No analysis returned") + } + + return text +} + +// Tool for generating images from text +export const generate = tool({ + description: "Generate an image using Gemini Nano Banana from text prompt", + args: { + prompt: tool.schema.string().describe("Text description of the image to generate"), + outputDir: tool.schema.string().optional().describe("Custom output directory (default: ./generated-images/YYYY-MM-DD/)"), + filename: tool.schema.string().optional().describe("Custom filename (default: generated)"), + }, + async execute(args, context) { + try { + const config: ImageConfig = { + outputDir: args.outputDir, + customName: args.filename, + } + return await generateImage(args.prompt, config) + } catch (error) { + return `Error: ${error.message}` + } + }, +}) + +// Tool for editing existing images +export const edit = tool({ + description: "Edit an existing image using Gemini Nano Banana", + args: { + image: tool.schema.string().describe("File path or data URL of image to edit"), + prompt: tool.schema.string().describe("Edit instruction"), + outputDir: tool.schema.string().optional().describe("Custom output directory (default: ./generated-images/YYYY-MM-DD/)"), + filename: tool.schema.string().optional().describe("Custom filename (default: original name with _edit_XXX)"), + }, + async execute(args, context) { + try { + const config: ImageConfig = { + outputDir: args.outputDir, + customName: args.filename, + } + return await editImage(args.image, args.prompt, config) + } catch (error) { + return `Error: ${error.message}` + } + }, +}) + +// Tool for analyzing images +export const analyze = tool({ + description: "Analyze an image using Gemini (text analysis only)", + args: { + image: tool.schema.string().describe("File path or data URL of image to analyze"), + question: tool.schema.string().describe("What to analyze about the image"), + }, + async execute(args, context) { + try { + return await analyzeImage(args.image, args.question) + } catch (error) { + return `Error: ${error.message}` + } + }, +}) + +// Default export for backward compatibility +export default edit \ No newline at end of file diff --git a/.opencode/tool/index.ts b/.opencode/tool/index.ts new file mode 100644 index 0000000..3b047df --- /dev/null +++ b/.opencode/tool/index.ts @@ -0,0 +1,27 @@ +/** + * OpenCode Gemini Tool - Main entry point + * + * This module provides image generation, editing, and analysis capabilities + * using Google's Gemini AI models, along with environment variable utilities. + */ + +// Gemini AI image tools +export { + generate, // Generate images from text prompts + edit, // Edit existing images with text instructions + analyze, // Analyze images and answer questions about them + generateImage, // Core image generation function + editImage, // Core image editing function + analyzeImage, // Core image analysis function + default as gemini // Default export (edit tool) +} from "./gemini" + +// Environment variable utilities +export { + loadEnvVariables, + getEnvVariable, + getRequiredEnvVariable, + getRequiredEnvVariables, + getApiKey, + type EnvLoaderConfig +} from "./env" \ No newline at end of file diff --git a/.opencode/tool/package.json b/.opencode/tool/package.json index 1c4f2f2..ce357d2 100644 --- a/.opencode/tool/package.json +++ b/.opencode/tool/package.json @@ -3,10 +3,12 @@ "name": "opencode-gemini-tool", "version": "1.0.0", "description": "Gemini image editing tool for OpenCode", - "main": "gemini.ts", + "main": "index.ts", "scripts": { - "build": "bun build gemini.ts --outdir dist", - "type-check": "tsc --noEmit" + "build": "bun build index.ts --outdir dist", + "type-check": "tsc --noEmit", + "test": "bun test.ts", + "test:real": "bun test.ts" }, "dependencies": { "@opencode-ai/sdk": "^0.10.0", diff --git a/.opencode/tool/template/README.md b/.opencode/tool/template/README.md new file mode 100644 index 0000000..da1eeb8 --- /dev/null +++ b/.opencode/tool/template/README.md @@ -0,0 +1,35 @@ +# Tool Template + +This is a template for creating new tools in the modular structure. + +## How to Create a New Tool + +1. **Copy this template directory:** + ```bash + cp -r template/ your-tool-name/ + ``` + +2. **Edit `index.ts`:** + - Replace `exampleFunction` with your tool's logic + - Update the tool definition with proper description and args + - Export your functions and tool + +3. **Add to main index:** + - Add exports to `/tool/index.ts`: + ```typescript + export { yourTool, yourFunction } from "./your-tool-name" + ``` + +4. **Test your tool:** + ```bash + bun -e "import('./your-tool-name/index.ts').then(m => console.log('Exports:', Object.keys(m)))" + ``` + +## Structure + +``` +your-tool-name/ +└── index.ts # All tool functionality +``` + +Keep it simple - one file per tool with all functionality included. \ No newline at end of file diff --git a/.opencode/tool/template/index.ts b/.opencode/tool/template/index.ts new file mode 100644 index 0000000..fc213e4 --- /dev/null +++ b/.opencode/tool/template/index.ts @@ -0,0 +1,25 @@ +import { tool } from "@opencode-ai/plugin/tool" + +// Example tool implementation +export async function exampleFunction(input: string): Promise { + // Your tool logic here + return `Processed: ${input}` +} + +// Tool definition for OpenCode agent system +export const exampleTool = tool({ + description: "Example tool description", + args: { + input: tool.schema.string().describe("Input parameter description"), + }, + async execute(args, context) { + try { + return await exampleFunction(args.input) + } catch (error) { + return `Error: ${error.message}` + } + }, +}) + +// Default export +export default exampleTool \ No newline at end of file diff --git a/.opencode/tool/test-plugin.ts b/.opencode/tool/test-plugin.ts deleted file mode 100644 index 60b1c2c..0000000 --- a/.opencode/tool/test-plugin.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as plugin from "@opencode-ai/plugin" - -console.log("Available exports:", Object.keys(plugin)) - -// Try to import tool -try { - const { tool } = await import("@opencode-ai/plugin") - console.log("Tool function:", typeof tool) -} catch (e) { - console.log("Tool import error:", e.message) -} \ No newline at end of file diff --git a/assets/.gitignore b/assets/.gitignore new file mode 100644 index 0000000..cc2e43f --- /dev/null +++ b/assets/.gitignore @@ -0,0 +1,6 @@ +# Ignore generated images but keep directory structure +images/*/ +!images/.gitkeep + +# Allow specific important images if needed +# !images/examples/ \ No newline at end of file diff --git a/assets/images/.gitkeep b/assets/images/.gitkeep new file mode 100644 index 0000000..6aa7453 --- /dev/null +++ b/assets/images/.gitkeep @@ -0,0 +1 @@ +# Keep this directory in git \ No newline at end of file From 318cfab2505702924ae28640f09b050dceef3fdc Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Tue, 23 Sep 2025 19:54:53 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20instruction=20?= =?UTF-8?q?for=20requesting=20images=20from=20the=20image=20specialist=20i?= =?UTF-8?q?n=20codebase-agent.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .opencode/agent/codebase-agent.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.opencode/agent/codebase-agent.md b/.opencode/agent/codebase-agent.md index 367b036..1cbc271 100644 --- a/.opencode/agent/codebase-agent.md +++ b/.opencode/agent/codebase-agent.md @@ -77,6 +77,7 @@ Do NOT proceed without explicit approval Phase 2: Implementation (After Approval Only) Implement incrementally - complete one step at a time, never implement the entire plan at once +If need images for a task, so pass it to the `@image-specialist` to make images for the task and tell it where to save the images. So you can use the images in the task. After each increment: - Use appropriate runtime (node/bun) to execute the code and check for errors before moving on to the next step - Run type checks using TypeScript compiler From cac7ee6b03d17e8c4613505fd020c04ce4978c00 Mon Sep 17 00:00:00 2001 From: darrenhinde <107584450+darrenhinde@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:35:32 +0100 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=9D=20docs:=20update=20codebase-ag?= =?UTF-8?q?ent.md=20to=20include=20@image-specialist=20subagent=20and=20re?= =?UTF-8?q?move=20GEMINI=5FAPI=5FKEY=20requirement=20from=20image-speciali?= =?UTF-8?q?st.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .opencode/agent/codebase-agent.md | 1 + .opencode/agent/image-specialist.md | 2 -- .opencode/agent/subagents/tester.md | 1 - .opencode/command/{prompter.md => prompt-enchancer.md} | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) rename .opencode/command/{prompter.md => prompt-enchancer.md} (99%) diff --git a/.opencode/agent/codebase-agent.md b/.opencode/agent/codebase-agent.md index 1cbc271..40219d8 100644 --- a/.opencode/agent/codebase-agent.md +++ b/.opencode/agent/codebase-agent.md @@ -33,6 +33,7 @@ Always start with phrase "DIGGING IN..." You have access to the following subagents: - `@task-manager` +- `@image-specialist` - `@subagents/tester` @tester - `@subagents/documentation` @documentation diff --git a/.opencode/agent/image-specialist.md b/.opencode/agent/image-specialist.md index 6536dcd..1d73a73 100644 --- a/.opencode/agent/image-specialist.md +++ b/.opencode/agent/image-specialist.md @@ -18,8 +18,6 @@ tools: gemini-multiple_analyze: true gemini-multiple_edit: true gemini: true -environment: - GEMINI_API_KEY: required --- You are an image processing specialist powered by Gemini AI's Nano Banana model. Your capabilities include: diff --git a/.opencode/agent/subagents/tester.md b/.opencode/agent/subagents/tester.md index debf13d..dedd81d 100644 --- a/.opencode/agent/subagents/tester.md +++ b/.opencode/agent/subagents/tester.md @@ -1,7 +1,6 @@ --- description: "Test authoring and TDD agent" mode: subagent -model: google/gemini-2.5-flash temperature: 0.1 tools: read: true diff --git a/.opencode/command/prompter.md b/.opencode/command/prompt-enchancer.md similarity index 99% rename from .opencode/command/prompter.md rename to .opencode/command/prompt-enchancer.md index 0ce85b3..e20c121 100644 --- a/.opencode/command/prompter.md +++ b/.opencode/command/prompt-enchancer.md @@ -1,5 +1,4 @@ --- -agent: prompt-enhancer description: "Research-backed XML prompt optimizer delivering 20% performance improvement" ---