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][framework_element]>
+
+ IF [condition]:
+ [action_path_a]
+ ELSE:
+ [action_path_b]
+
+[step_specific_framework]>
+
+
+ <[template_name]>
+ [STRUCTURED_TEMPLATE_CONTENT]
+ [template_name]>
+
+
+
+ - [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][framework_element]>
-
- IF [condition]:
- [action_path_a]
- ELSE:
- [action_path_b]
-
-[step_specific_framework]>
+
+ [WORKFLOW WITH ROUTING USING @ SYMBOLS]
+
-
- <[template_name]>
- [STRUCTURED_TEMPLATE_CONTENT]
- [template_name]>
-
-
-
- - [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"
---