From fd754dd6168fddb768011290bea30cb23501bb1a Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 18 Jan 2026 02:15:20 +0100 Subject: [PATCH 1/3] Initial commit with task details Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/link-assistant/agent-commander/issues/15 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..68fc62a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/link-assistant/agent-commander/issues/15 +Your prepared branch: issue-15-47049ece53b4 +Your prepared working directory: /tmp/gh-issue-solver-1768698918720 + +Proceed. From 00348fd79810c076aaa4544ab34484b174681669 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 18 Jan 2026 02:20:54 +0100 Subject: [PATCH 2/3] Add Gemini CLI to supported tools and fix Agent JSON Input status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added Gemini CLI to the Supported Tools table with JSON Output support - Fixed Agent CLI JSON Input status from ❌ to ✅ (verified against upstream) - Added Gemini-specific features documentation section - Added Agent-specific features documentation section - Updated all tool references to include Gemini - Added case study for issue #15 with investigation evidence Fixes #15 Co-Authored-By: Claude Opus 4.5 --- README.md | 48 ++- docs/case-studies/issue-15/README.md | 121 +++++++ .../case-studies/issue-15/agent-cli-readme.md | 209 ++++++++++++ .../issue-15/current-agent-tool-config.mjs | 258 ++++++++++++++ .../issue-15/current-gemini-tool-config.mjs | 317 ++++++++++++++++++ .../issue-15/evidence-timeline.md | 65 ++++ .../issue-15/gemini-cli-info.json | 16 + docs/case-studies/issue-15/issue-details.txt | 13 + 8 files changed, 1042 insertions(+), 5 deletions(-) create mode 100644 docs/case-studies/issue-15/README.md create mode 100644 docs/case-studies/issue-15/agent-cli-readme.md create mode 100644 docs/case-studies/issue-15/current-agent-tool-config.mjs create mode 100644 docs/case-studies/issue-15/current-gemini-tool-config.mjs create mode 100644 docs/case-studies/issue-15/evidence-timeline.md create mode 100644 docs/case-studies/issue-15/gemini-cli-info.json create mode 100644 docs/case-studies/issue-15/issue-details.txt diff --git a/README.md b/README.md index db70e2a..97ec66d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # agent-commander -A JavaScript library to control agents enclosed in CLI commands like Anthropic Claude Code CLI, OpenAI Codex, OpenCode, Qwen Code, and @link-assistant/agent. +A JavaScript library to control agents enclosed in CLI commands like Anthropic Claude Code CLI, OpenAI Codex, OpenCode, Qwen Code, Gemini CLI, and @link-assistant/agent. Built on the success of [hive-mind](https://github.com/link-assistant/hive-mind), `agent-commander` provides a flexible JavaScript interface and CLI tools for managing agent processes with various isolation levels. @@ -12,6 +12,7 @@ Built on the success of [hive-mind](https://github.com/link-assistant/hive-mind) - `codex` - OpenAI Codex CLI - `opencode` - OpenCode CLI - `qwen` - Qwen Code CLI (Alibaba's AI coding agent) + - `gemini` - Gemini CLI (Google's AI coding agent) - `agent` - @link-assistant/agent (unrestricted OpenCode fork) - **Multiple Isolation Modes**: - No isolation (direct execution) @@ -58,7 +59,8 @@ bun add agent-commander | `codex` | OpenAI Codex CLI | ✅ | ❌ | `gpt5`, `o3`, `gpt4o` | | `opencode` | OpenCode CLI | ✅ | ❌ | `grok`, `gemini`, `sonnet` | | `qwen` | Qwen Code CLI | ✅ (stream-json) | ✅ (stream-json) | `qwen3-coder`, `coder`, `gpt-4o` | -| `agent` | @link-assistant/agent | ✅ | ❌ | `grok`, `sonnet`, `haiku` | +| `gemini` | Gemini CLI | ✅ (stream-json) | ❌ | `flash`, `pro`, `lite` | +| `agent` | @link-assistant/agent | ✅ | ✅ | `grok`, `sonnet`, `haiku` | ### Claude-specific Features @@ -83,6 +85,28 @@ The [Qwen Code CLI](https://github.com/QwenLM/qwen-code) supports additional fea - **Partial messages**: Use `--include-partial-messages` with stream-json for real-time UI updates - **Model flexibility**: Supports Qwen3-Coder models plus OpenAI-compatible models via API +### Gemini-specific Features + +The [Gemini CLI](https://github.com/google-gemini/gemini-cli) supports additional features: + +- **Stream JSON format**: Uses `--output-format stream-json` for real-time NDJSON streaming +- **Auto-approval mode**: Use `--yolo` flag for automatic action approval (enabled by default) +- **Sandbox mode**: Use `--sandbox` flag for secure tool execution in isolated environment +- **Checkpointing**: Use `--checkpointing` to save project snapshots before file modifications +- **Debug output**: Use `-d` flag for detailed debug output +- **Model selection**: Use `-m` flag to select model (e.g., `gemini-2.5-flash`, `gemini-2.5-pro`) +- **Interactive mode**: Use `-i` flag with prompt to start interactive session + +### Agent-specific Features + +The [@link-assistant/agent](https://github.com/link-assistant/agent) supports additional features: + +- **JSON Input/Output**: Accepts JSON via stdin, outputs JSON event streams (OpenCode-compatible) +- **Unrestricted access**: No sandbox, no permissions system - full autonomous execution +- **13 built-in tools**: Including websearch, codesearch, batch - all enabled by default +- **MCP support**: Model Context Protocol for extending functionality with MCP servers +- **OpenCode compatibility**: 100% compatible with OpenCode's JSON event streaming format + ## CLI Usage ### start-agent @@ -95,7 +119,7 @@ start-agent --tool claude --working-directory "/tmp/dir" --prompt "Solve the iss #### Options -- `--tool ` - CLI tool to use (e.g., 'claude', 'codex', 'opencode', 'qwen', 'agent') [required] +- `--tool ` - CLI tool to use (e.g., 'claude', 'codex', 'opencode', 'qwen', 'gemini', 'agent') [required] - `--working-directory ` - Working directory for the agent [required] - `--prompt ` - Prompt for the agent - `--system-prompt ` - System prompt for the agent @@ -136,6 +160,11 @@ start-agent --tool agent --working-directory "/tmp/dir" --prompt "Analyze code" start-agent --tool qwen --working-directory "/tmp/dir" --prompt "Review this code" --model qwen3-coder ``` +**Using Gemini** +```bash +start-agent --tool gemini --working-directory "/tmp/dir" --prompt "Explain this code" --model flash +``` + **With model fallback (Claude)** ```bash start-agent --tool claude --working-directory "/tmp/dir" \ @@ -259,6 +288,14 @@ const qwenAgent = agent({ prompt: 'Review this code', model: 'qwen3-coder', }); + +// Using Gemini CLI +const geminiAgent = agent({ + tool: 'gemini', + workingDirectory: '/tmp/project', + prompt: 'Explain this code', + model: 'flash', +}); ``` ### Streaming JSON Messages @@ -373,7 +410,7 @@ await myAgent.start({ dryRun: true }); import { getTool, listTools, isToolSupported } from 'agent-commander'; // List all available tools -console.log(listTools()); // ['claude', 'codex', 'opencode', 'agent', 'qwen'] +console.log(listTools()); // ['claude', 'codex', 'opencode', 'agent', 'gemini', 'qwen'] // Check if a tool is supported console.log(isToolSupported({ toolName: 'claude' })); // true @@ -394,7 +431,7 @@ console.log(fullId); // 'claude-opus-4-5-20251101' Creates an agent controller. **Parameters:** -- `options.tool` (string, required) - CLI tool to use ('claude', 'codex', 'opencode', 'qwen', 'agent') +- `options.tool` (string, required) - CLI tool to use ('claude', 'codex', 'opencode', 'qwen', 'gemini', 'agent') - `options.workingDirectory` (string, required) - Working directory - `options.prompt` (string, optional) - Prompt for the agent - `options.systemPrompt` (string, optional) - System prompt @@ -562,6 +599,7 @@ agent-commander/ │ │ ├── codex.mjs # Codex CLI config │ │ ├── opencode.mjs # OpenCode CLI config │ │ ├── qwen.mjs # Qwen Code CLI config +│ │ ├── gemini.mjs # Gemini CLI config │ │ └── agent.mjs # @link-assistant/agent config │ ├── streaming/ # JSON streaming utilities │ │ ├── index.mjs # Stream exports diff --git a/docs/case-studies/issue-15/README.md b/docs/case-studies/issue-15/README.md new file mode 100644 index 0000000..a26ab83 --- /dev/null +++ b/docs/case-studies/issue-15/README.md @@ -0,0 +1,121 @@ +# Case Study: JSON Output/Input Documentation Accuracy + +**Issue:** [#15 - Double check for JSON Output and JSON Input for all our tools](https://github.com/link-assistant/agent-commander/issues/15) + +This document captures the investigation and resolution of documentation discrepancies in the agent-commander README.md regarding JSON input/output support for supported CLI tools. + +## Overview + +The issue identified two documentation problems: +1. **Missing Gemini CLI** - Not listed in the Supported Tools table despite being fully implemented +2. **Incorrect Agent CLI JSON Input status** - Marked as ❌ but actually supports JSON input + +## Investigation Methodology + +### Data Collection + +1. **Repository Analysis**: Examined the codebase for actual tool configurations +2. **External Verification**: Checked upstream repositories (@link-assistant/agent, google-gemini/gemini-cli) +3. **Code Review**: Analyzed `js/src/tools/*.mjs` configuration files +4. **Documentation Review**: Cross-referenced README.md claims with actual capabilities + +### Evidence Sources + +| Source | Location | Key Finding | +|--------|----------|-------------| +| agent.mjs | js/src/tools/agent.mjs:246 | `supportsJsonInput: true` | +| gemini.mjs | js/src/tools/gemini.mjs | Full Gemini CLI implementation | +| tools/index.mjs | js/src/tools/index.mjs:21 | Exports `geminiTool` | +| @link-assistant/agent | GitHub README | "JSON Input/Output: Accepts JSON via stdin" | +| google-gemini/gemini-cli | GitHub README | Supports `--output-format stream-json` | + +## Root Cause Analysis + +### Problem 1: Missing Gemini in README + +**Root Cause**: The Gemini CLI tool was added to the codebase in a previous update (gemini.mjs created, index.mjs updated), but the README.md Supported Tools table was not updated to include this new tool. + +**Evidence**: +- `js/src/tools/gemini.mjs` exists with full implementation (318 lines) +- `js/src/tools/index.mjs` imports and exports `geminiTool` +- README.md table only lists: claude, codex, opencode, qwen, agent + +### Problem 2: Incorrect Agent JSON Input Status + +**Root Cause**: Documentation lag - the Agent CLI's JSON input capability was either overlooked during initial documentation or the capability was added after documentation was written. + +**Evidence**: +- `js/src/tools/agent.mjs:246` explicitly states `supportsJsonInput: true` +- @link-assistant/agent README states: "JSON Input/Output: Accepts JSON via stdin, outputs JSON event streams (OpenCode-compatible)" +- The code comments in agent.mjs line 71 confirm: "Agent uses stdin for prompt input (NDJSON streaming supported)" + +## Timeline of Events + +1. **Initial Release**: agent-commander released with support for claude, codex, opencode, agent, qwen +2. **Agent JSON Support**: Agent CLI implemented full JSON input/output (based on OpenCode compatibility) +3. **Gemini Addition**: Gemini CLI tool added to codebase (gemini.mjs created) +4. **Documentation Gap**: README.md not updated to reflect Gemini addition or Agent JSON input capability +5. **Issue #15**: Documentation inconsistency discovered and reported + +## Solution Implementation + +### Changes Made to README.md + +1. **Added Gemini to Supported Tools Table**: + ```markdown + | `gemini` | Gemini CLI | ✅ (stream-json) | ❌ | `flash`, `pro`, `lite` | + ``` + +2. **Fixed Agent JSON Input Status**: + ```markdown + | `agent` | @link-assistant/agent | ✅ | ✅ | `grok`, `sonnet`, `haiku` | + ``` + +3. **Added Gemini-specific Features Section**: + - Stream JSON format documentation + - Yolo mode (auto-approval) documentation + - Sandbox mode documentation + - Checkpointing support documentation + - Model configuration documentation + +4. **Updated Feature List**: + - Added `gemini` to the Multiple CLI Agents list + - Updated description to include Gemini CLI + +### Verification + +| Tool | JSON Output | JSON Input | Verified Source | +|------|-------------|------------|-----------------| +| claude | ✅ (stream-json) | ✅ (stream-json) | Code + README | +| codex | ✅ | ❌ | Code analysis | +| opencode | ✅ | ❌ | Code analysis | +| qwen | ✅ (stream-json) | ✅ (stream-json) | Code + README | +| agent | ✅ | ✅ | Code + upstream README | +| gemini | ✅ (stream-json) | ❌ | Code + upstream README | + +## Lessons Learned + +1. **Documentation-Code Sync**: When adding new tools or capabilities, update documentation in the same commit/PR +2. **Verification Process**: Cross-reference code configurations with upstream tool documentation +3. **Case Studies**: Documenting the investigation helps prevent similar issues and provides institutional knowledge + +## Files Modified + +| File | Changes | +|------|---------| +| README.md | Added Gemini to table, fixed Agent JSON Input, added Gemini features section | +| docs/case-studies/issue-15/README.md | This case study document | +| docs/case-studies/issue-15/evidence-timeline.md | Investigation evidence and timeline | + +## References + +- [google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) - Official Gemini CLI repository +- [@link-assistant/agent](https://github.com/link-assistant/agent) - Agent CLI repository +- [Gemini CLI Headless Mode](https://geminicli.com/docs/cli/headless/) - JSON output documentation +- [Issue #15](https://github.com/link-assistant/agent-commander/issues/15) - Original issue report + +## Conclusion + +The documentation discrepancies were caused by documentation lag rather than technical issues. The actual code implementations were correct - only the README.md needed updating to accurately reflect the supported tools and their capabilities. + +Key insight: Maintaining accurate documentation requires systematic verification against both the codebase and upstream tool capabilities. Creating case studies for such investigations helps prevent similar issues and provides a reference for future contributors. diff --git a/docs/case-studies/issue-15/agent-cli-readme.md b/docs/case-studies/issue-15/agent-cli-readme.md new file mode 100644 index 0000000..e5ce6d7 --- /dev/null +++ b/docs/case-studies/issue-15/agent-cli-readme.md @@ -0,0 +1,209 @@ +# Link Assistant Agent + +**A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface** + +[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](http://unlicense.org/) + +> 🚨 **SECURITY WARNING: 100% UNSAFE AND AUTONOMOUS** 🚨 +> +> This agent operates with **ZERO RESTRICTIONS** and **FULL AUTONOMY**: +> +> - ❌ **No Sandbox** - Complete unrestricted file system access +> - ❌ **No Permissions System** - No approval required for any actions +> - ❌ **No Safety Guardrails** - Can execute ANY command with full privileges +> - ⚠️ **Autonomous Execution** - Makes decisions and executes actions independently +> +> **ONLY use in isolated environments** (VMs, Docker containers) where AI agents can have unrestricted access. **NOT SAFE** for personal computers, production servers, or systems with sensitive data. + +## Implementations + +This repository contains two implementations of the agent: + +| Implementation | Status | Package Manager | Install Command | +|---------------|--------|-----------------|-----------------| +| [JavaScript/Bun](js/README.md) | **Production Ready** | npm | `bun install -g @link-assistant/agent` | +| [Rust](rust/README.md) | Work in Progress | cargo | `cargo install agent` (when published) | + +Both implementations aim to be fully compatible with [OpenCode](https://github.com/sst/opencode)'s `run --format json` mode. + +### JavaScript/Bun Implementation + +[![npm version](https://badge.fury.io/js/@link-assistant%2Fagent.svg)](https://www.npmjs.com/package/@link-assistant/agent) + +The primary implementation, feature-complete and production-ready. Requires [Bun](https://bun.sh) >= 1.0.0. + +```bash +# Install +bun install -g @link-assistant/agent + +# Usage +echo "hi" | agent +``` + +See [js/README.md](js/README.md) for full documentation including: +- Complete CLI options reference +- Model selection examples +- Session resume functionality +- MCP (Model Context Protocol) configuration +- JSON output standards (OpenCode and Claude formats) + +### Rust Implementation + +The Rust implementation provides core functionality but is still under active development. + +```bash +# Build from source +cd rust +cargo build --release +./target/release/agent -p "hello" +``` + +See [rust/README.md](rust/README.md) for full documentation. + +## Project Vision + +We're creating a slimmed-down, public domain version of OpenCode CLI focused on the "agentic run mode" for use in virtual machines, Docker containers, and other environments where unrestricted AI agent access is acceptable. This is **not** for general desktop use - it's for isolated environments where you want maximum AI agent freedom. + +**OpenCode Compatibility**: We maintain 100% compatibility with OpenCode's JSON event streaming format, so tools expecting `opencode run --format json --model opencode/grok-code` output will work with our agent-cli. + +## Features + +- **JSON Input/Output**: Accepts JSON via stdin, outputs JSON event streams (OpenCode-compatible) +- **Plain Text Input**: Also accepts plain text messages (auto-converted to JSON format) +- **Unrestricted Access**: Full file system and command execution access (no sandbox, no restrictions) +- **Tool Support**: 13 tools including websearch, codesearch, batch - all enabled by default +- **Flexible Model Selection**: Supports [OpenCode Zen](https://opencode.ai/docs/zen/), [Claude OAuth](docs/claude-oauth.md), [Groq](docs/groq.md), and more - see [MODELS.md](MODELS.md) +- **Public Domain**: Unlicense - use it however you want + +## Quick Start + +**Plain text (easiest):** + +```bash +echo "hi" | agent +``` + +**Simple JSON message:** + +```bash +echo '{"message":"hi"}' | agent +``` + +**With custom model:** + +```bash +echo "hi" | agent --model opencode/grok-code +``` + +**Direct prompt mode:** + +```bash +agent -p "What is 2+2?" +``` + +See [js/README.md](js/README.md#usage) for more usage examples including model selection, session resume, and JSON output standards. + +## Supported Tools + +All 13 tools are **enabled by default** with **no configuration required**. See [TOOLS.md](TOOLS.md) for complete documentation. + +### File Operations + +- **`read`** - Read file contents +- **`write`** - Write files +- **`edit`** - Edit files with string replacement +- **`list`** - List directory contents + +### Search Tools + +- **`glob`** - File pattern matching (`**/*.js`) +- **`grep`** - Text search with regex support +- **`websearch`** ✨ - Web search via Exa API (no config needed!) +- **`codesearch`** ✨ - Code search via Exa API (no config needed!) + +### Execution Tools + +- **`bash`** - Execute shell commands +- **`batch`** ✨ - Batch multiple tool calls (no config needed!) +- **`task`** - Launch subagent tasks + +### Utility Tools + +- **`todo`** - Task tracking +- **`webfetch`** - Fetch and process URLs + +✨ = Always enabled (no experimental flags or environment variables needed) + +## Architecture + +This agent reproduces OpenCode's `run --format json` command architecture: + +- **Streaming JSON Events**: Real-time event stream output +- **Event Types**: `tool_use`, `text`, `step_start`, `step_finish`, `error` +- **Session Management**: Unique session IDs for each request +- **Tool Execution**: Tools with unrestricted access +- **Compatible Format**: Events match OpenCode's JSON schema exactly + +## MCP (Model Context Protocol) Support + +The agent supports the Model Context Protocol (MCP), allowing you to extend functionality with MCP servers such as browser automation via Playwright. + +**Quick setup for Playwright MCP:** + +```bash +agent mcp add playwright npx @playwright/mcp@latest +``` + +See [js/README.md](js/README.md#mcp-model-context-protocol-support) for full MCP documentation including: +- Available Playwright tools (22+ browser automation capabilities) +- MCP server configuration +- Usage examples + +## Documentation + +| Document | Description | +|----------|-------------| +| [MODELS.md](MODELS.md) | Available models, providers, and pricing | +| [TOOLS.md](TOOLS.md) | Complete tool documentation | +| [EXAMPLES.md](EXAMPLES.md) | Usage examples for each tool | +| [TESTING.md](TESTING.md) | Testing guide | +| [js/README.md](js/README.md) | JavaScript/Bun implementation (full docs) | +| [rust/README.md](rust/README.md) | Rust implementation | + +## Files + +### JavaScript Implementation (js/) + +- `js/src/index.js` - Main entry point with JSON/plain text input support +- `js/src/session/` - Session management and agent implementation +- `js/src/tool/` - Tool implementations +- `js/tests/` - Comprehensive test suite +- `js/package.json` - npm package configuration + +### Rust Implementation (rust/) + +- `rust/src/main.rs` - Main entry point +- `rust/src/cli.rs` - CLI argument parsing +- `rust/src/tool/` - Tool implementations +- `rust/Cargo.toml` - Cargo package configuration + +## Reference Implementations + +This repository includes official reference implementations as git submodules to provide best-in-class examples: + +- **original-opencode** - [OpenCode](https://github.com/sst/opencode) - The original OpenCode implementation we maintain compatibility with +- **reference-gemini-cookbook** - [Google Gemini Cookbook](https://github.com/google-gemini/cookbook) - Official examples and guides for using the Gemini API +- **reference-gemini-cli** - [Google Gemini CLI](https://github.com/google-gemini/gemini-cli) - Official AI agent bringing Gemini directly to the terminal +- **reference-qwen3-coder** - [Qwen3-Coder](https://github.com/QwenLM/Qwen3-Coder) - Official Qwen3 code model from Alibaba Cloud + +To initialize all submodules: + +```bash +git submodule update --init --recursive +``` + +These reference implementations provide valuable insights into different approaches for building AI agents and can serve as learning resources for developers working with this codebase. + +## License + +Unlicense (Public Domain) diff --git a/docs/case-studies/issue-15/current-agent-tool-config.mjs b/docs/case-studies/issue-15/current-agent-tool-config.mjs new file mode 100644 index 0000000..96e4c5f --- /dev/null +++ b/docs/case-studies/issue-15/current-agent-tool-config.mjs @@ -0,0 +1,258 @@ +/** + * Agent CLI tool configuration (@link-assistant/agent) + * Based on hive-mind's agent.lib.mjs implementation + * Agent is a fork of OpenCode with unrestricted permissions for autonomous execution + */ + +/** + * Available Agent model configurations + * Maps aliases to full model IDs (uses OpenCode's provider/model format) + */ +export const modelMap = { + grok: 'opencode/grok-code', + 'grok-code': 'opencode/grok-code', + 'grok-code-fast-1': 'opencode/grok-code', + 'big-pickle': 'opencode/big-pickle', + 'gpt-5-nano': 'openai/gpt-5-nano', + sonnet: 'anthropic/claude-3-5-sonnet', + haiku: 'anthropic/claude-3-5-haiku', + opus: 'anthropic/claude-3-opus', + 'gemini-3-pro': 'google/gemini-3-pro', +}; + +/** + * Map model alias to full model ID + * @param {Object} options - Options + * @param {string} options.model - Model alias or full ID + * @returns {string} Full model ID + */ +export function mapModelToId(options) { + const { model } = options; + return modelMap[model] || model; +} + +/** + * Build command line arguments for Agent + * @param {Object} options - Options + * @param {string} [options.prompt] - User prompt + * @param {string} [options.systemPrompt] - System prompt (combined with user prompt) + * @param {string} [options.model] - Model to use + * @param {boolean} [options.compactJson] - Use compact JSON output + * @param {boolean} [options.useExistingClaudeOAuth] - Use existing Claude OAuth credentials + * @returns {string[]} Array of CLI arguments + */ +export function buildArgs(options) { + const { + model, + compactJson = false, + useExistingClaudeOAuth = false, + } = options; + + const args = []; + + if (model) { + const mappedModel = mapModelToId({ model }); + args.push('--model', mappedModel); + } + + if (compactJson) { + args.push('--compact-json'); + } + + if (useExistingClaudeOAuth) { + args.push('--use-existing-claude-oauth'); + } + + return args; +} + +/** + * Build complete command string for Agent + * Agent uses stdin for prompt input (NDJSON streaming supported) + * @param {Object} options - Options + * @param {string} options.workingDirectory - Working directory + * @param {string} [options.prompt] - User prompt + * @param {string} [options.systemPrompt] - System prompt + * @param {string} [options.model] - Model to use + * @param {boolean} [options.compactJson] - Use compact JSON output + * @param {boolean} [options.useExistingClaudeOAuth] - Use existing Claude OAuth + * @returns {string} Complete command string + */ +export function buildCommand(options) { + // eslint-disable-next-line no-unused-vars + const { workingDirectory, prompt, systemPrompt, ...argOptions } = options; + const args = buildArgs(argOptions); + + // Agent expects prompt via stdin, combine system and user prompts + const combinedPrompt = systemPrompt + ? `${systemPrompt}\n\n${prompt || ''}` + : prompt || ''; + + // Build command with stdin piping + const escapedPrompt = combinedPrompt.replace(/'/g, "'\\''"); + return `printf '%s' '${escapedPrompt}' | agent ${args.map(escapeArg).join(' ')}`.trim(); +} + +/** + * Escape an argument for shell usage + * @param {string} arg - Argument to escape + * @returns {string} Escaped argument + */ +function escapeArg(arg) { + if (/["\s$`\\]/.test(arg)) { + return `"${arg.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`').replace(/\\/g, '\\\\')}"`; + } + return arg; +} + +/** + * Parse JSON messages from Agent output + * Agent outputs NDJSON format with specific event types + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object[]} Array of parsed JSON messages + */ +export function parseOutput(options) { + const { output } = options; + const messages = []; + const lines = output.split('\n'); + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || !trimmed.startsWith('{')) { + continue; + } + + try { + const parsed = JSON.parse(trimmed); + messages.push(parsed); + } catch { + // Skip lines that aren't valid JSON + } + } + + return messages; +} + +/** + * Extract session ID from Agent output + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {string|null} Session ID or null + */ +export function extractSessionId(options) { + const { output } = options; + const messages = parseOutput({ output }); + + for (const msg of messages) { + if (msg.session_id) { + return msg.session_id; + } + } + + return null; +} + +/** + * Parse token usage from Agent output + * Agent outputs step_finish events with token data + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object} Token usage statistics + */ +export function extractUsage(options) { + const { output } = options; + const messages = parseOutput({ output }); + + const usage = { + inputTokens: 0, + outputTokens: 0, + reasoningTokens: 0, + cacheReadTokens: 0, + cacheWriteTokens: 0, + totalCost: 0, + stepCount: 0, + }; + + for (const msg of messages) { + // Look for step_finish events which contain token usage + if (msg.type === 'step_finish' && msg.part?.tokens) { + const tokens = msg.part.tokens; + usage.stepCount++; + + // Add token counts + if (tokens.input) { + usage.inputTokens += tokens.input; + } + if (tokens.output) { + usage.outputTokens += tokens.output; + } + if (tokens.reasoning) { + usage.reasoningTokens += tokens.reasoning; + } + + // Handle cache tokens + if (tokens.cache) { + if (tokens.cache.read) { + usage.cacheReadTokens += tokens.cache.read; + } + if (tokens.cache.write) { + usage.cacheWriteTokens += tokens.cache.write; + } + } + + // Add cost from step_finish + if (msg.part.cost !== undefined) { + usage.totalCost += msg.part.cost; + } + } + } + + return usage; +} + +/** + * Detect errors in Agent output + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object} Error detection result + */ +export function detectErrors(options) { + const { output } = options; + const messages = parseOutput({ output }); + + for (const msg of messages) { + // Check for explicit error message types from agent + if (msg.type === 'error' || msg.type === 'step_error') { + return { + hasError: true, + errorType: msg.type, + message: msg.message || 'Unknown error', + }; + } + } + + return { hasError: false }; +} + +/** + * Agent tool configuration + */ +export const agentTool = { + name: 'agent', + displayName: '@link-assistant/agent', + executable: 'agent', + supportsJsonOutput: true, + supportsJsonInput: true, // Agent supports full JSON streaming input + supportsSystemPrompt: false, // System prompt is combined with user prompt + supportsResume: false, // Agent doesn't have explicit resume like Claude + defaultModel: 'grok-code-fast-1', + modelMap, + mapModelToId, + buildArgs, + buildCommand, + parseOutput, + extractSessionId, + extractUsage, + detectErrors, +}; diff --git a/docs/case-studies/issue-15/current-gemini-tool-config.mjs b/docs/case-studies/issue-15/current-gemini-tool-config.mjs new file mode 100644 index 0000000..2c154d9 --- /dev/null +++ b/docs/case-studies/issue-15/current-gemini-tool-config.mjs @@ -0,0 +1,317 @@ +/** + * Gemini CLI tool configuration + * Based on Google's official gemini-cli: https://github.com/google-gemini/gemini-cli + */ + +/** + * Available Gemini model configurations + * Maps aliases to full model IDs + */ +export const modelMap = { + // Gemini 2.5 models (current stable) + flash: 'gemini-2.5-flash', + '2.5-flash': 'gemini-2.5-flash', + pro: 'gemini-2.5-pro', + '2.5-pro': 'gemini-2.5-pro', + lite: 'gemini-2.5-flash-lite', + '2.5-lite': 'gemini-2.5-flash-lite', + // Gemini 3 models (latest generation) + '3-flash': 'gemini-3-flash-preview', + '3-pro': 'gemini-3-pro-preview', + // Legacy aliases + 'gemini-flash': 'gemini-2.5-flash', + 'gemini-pro': 'gemini-2.5-pro', +}; + +/** + * Map model alias to full model ID + * @param {Object} options - Options + * @param {string} options.model - Model alias or full ID + * @returns {string} Full model ID + */ +export function mapModelToId(options) { + const { model } = options; + return modelMap[model] || model; +} + +/** + * Build command line arguments for Gemini CLI + * @param {Object} options - Options + * @param {string} [options.prompt] - User prompt (for non-interactive mode) + * @param {string} [options.systemPrompt] - System prompt (combined with user prompt) + * @param {string} [options.model] - Model to use + * @param {boolean} [options.json] - JSON output mode (stream-json format) + * @param {boolean} [options.yolo] - Auto-approve all tool calls (autonomous mode) + * @param {boolean} [options.sandbox] - Run tools in secure sandbox + * @param {boolean} [options.debug] - Enable debug output + * @param {boolean} [options.checkpointing] - Save project snapshot before file modifications + * @param {boolean} [options.interactive] - Start interactive session with initial prompt + * @returns {string[]} Array of CLI arguments + */ +export function buildArgs(options) { + const { + prompt, + model, + json = false, + yolo = true, // Enable autonomous mode by default for agent use + sandbox = false, + debug = false, + checkpointing = false, + interactive = false, + } = options; + + const args = []; + + if (model) { + const mappedModel = mapModelToId({ model }); + args.push('-m', mappedModel); + } + + // Enable yolo mode for autonomous execution (auto-approve all tool calls) + if (yolo) { + args.push('--yolo'); + } + + // Sandbox mode for secure execution + if (sandbox) { + args.push('--sandbox'); + } + + // Debug output + if (debug) { + args.push('-d'); + } + + // Checkpointing for file modifications + if (checkpointing) { + args.push('--checkpointing'); + } + + // JSON output mode - use stream-json for streaming events + if (json) { + args.push('--output-format', 'stream-json'); + } + + // Add prompt for non-interactive mode + if (prompt) { + if (interactive) { + args.push('-i', prompt); + } else { + args.push('-p', prompt); + } + } + + return args; +} + +/** + * Build complete command string for Gemini CLI + * @param {Object} options - Options + * @param {string} options.workingDirectory - Working directory + * @param {string} [options.prompt] - User prompt + * @param {string} [options.systemPrompt] - System prompt + * @param {string} [options.model] - Model to use + * @param {boolean} [options.json] - JSON output mode + * @param {boolean} [options.yolo] - Auto-approve all tool calls + * @param {boolean} [options.sandbox] - Run tools in secure sandbox + * @param {boolean} [options.debug] - Enable debug output + * @param {boolean} [options.checkpointing] - Save project snapshot + * @param {boolean} [options.interactive] - Start interactive session + * @returns {string} Complete command string + */ +export function buildCommand(options) { + // eslint-disable-next-line no-unused-vars + const { workingDirectory, systemPrompt, prompt, ...argOptions } = options; + + // Gemini CLI supports system prompt via GEMINI_SYSTEM_PROMPT env var + // or via .gemini/system.md file. For now, combine with user prompt. + const combinedPrompt = systemPrompt + ? `${systemPrompt}\n\n${prompt || ''}` + : prompt || ''; + + const args = buildArgs({ ...argOptions, prompt: combinedPrompt }); + return `gemini ${args.map(escapeArg).join(' ')}`.trim(); +} + +/** + * Escape an argument for shell usage + * @param {string} arg - Argument to escape + * @returns {string} Escaped argument + */ +function escapeArg(arg) { + // If argument contains spaces, quotes, or special chars, wrap in quotes + if (/["\s$`\\]/.test(arg)) { + return `"${arg.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`').replace(/\\/g, '\\\\')}"`; + } + return arg; +} + +/** + * Parse JSON messages from Gemini CLI output + * Gemini CLI outputs NDJSON (newline-delimited JSON) in stream-json mode + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object[]} Array of parsed JSON messages + */ +export function parseOutput(options) { + const { output } = options; + const messages = []; + const lines = output.split('\n'); + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || !trimmed.startsWith('{')) { + continue; + } + + try { + const parsed = JSON.parse(trimmed); + messages.push(parsed); + } catch { + // Skip lines that aren't valid JSON + } + } + + return messages; +} + +/** + * Extract session ID from Gemini CLI output + * Gemini CLI may include session information in its output + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {string|null} Session ID or null + */ +export function extractSessionId(options) { + const { output } = options; + const messages = parseOutput({ output }); + + for (const msg of messages) { + if (msg.session_id) { + return msg.session_id; + } + // Gemini might use different session identifier + if (msg.conversation_id) { + return msg.conversation_id; + } + } + + return null; +} + +/** + * Extract usage statistics from Gemini CLI output + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object} Usage statistics + */ +export function extractUsage(options) { + const { output } = options; + const messages = parseOutput({ output }); + + const usage = { + inputTokens: 0, + outputTokens: 0, + totalTokens: 0, + }; + + for (const msg of messages) { + // Check for usage metadata in different possible formats + if (msg.usage) { + const u = msg.usage; + if (u.input_tokens !== undefined) { + usage.inputTokens += u.input_tokens; + } + if (u.output_tokens !== undefined) { + usage.outputTokens += u.output_tokens; + } + if (u.total_tokens !== undefined) { + usage.totalTokens += u.total_tokens; + } + // Also check camelCase variants + if (u.inputTokens !== undefined) { + usage.inputTokens += u.inputTokens; + } + if (u.outputTokens !== undefined) { + usage.outputTokens += u.outputTokens; + } + if (u.totalTokens !== undefined) { + usage.totalTokens += u.totalTokens; + } + } + + // Also check for Gemini-specific token metrics + if (msg.usageMetadata) { + const u = msg.usageMetadata; + if (u.promptTokenCount !== undefined) { + usage.inputTokens += u.promptTokenCount; + } + if (u.candidatesTokenCount !== undefined) { + usage.outputTokens += u.candidatesTokenCount; + } + if (u.totalTokenCount !== undefined) { + usage.totalTokens += u.totalTokenCount; + } + } + } + + // Calculate total if not provided + if ( + usage.totalTokens === 0 && + (usage.inputTokens > 0 || usage.outputTokens > 0) + ) { + usage.totalTokens = usage.inputTokens + usage.outputTokens; + } + + return usage; +} + +/** + * Detect errors in Gemini CLI output + * @param {Object} options - Options + * @param {string} options.output - Raw output string + * @returns {Object} Error detection result + */ +export function detectErrors(options) { + const { output } = options; + const messages = parseOutput({ output }); + + for (const msg of messages) { + // Check for explicit error message types + if (msg.type === 'error' || msg.error) { + return { + hasError: true, + errorType: msg.type || 'error', + message: msg.message || msg.error || 'Unknown error', + }; + } + } + + return { hasError: false }; +} + +/** + * Gemini CLI tool configuration + */ +export const geminiTool = { + name: 'gemini', + displayName: 'Gemini CLI', + executable: 'gemini', + supportsJsonOutput: true, + supportsJsonInput: false, // Gemini CLI uses -p flag for prompts, not stdin JSON + supportsSystemPrompt: false, // System prompt via env var or file, combined with user prompt + supportsResume: true, // Via /chat resume command in interactive mode + supportsYolo: true, // Supports --yolo for autonomous execution + supportsSandbox: true, // Supports --sandbox for secure execution + supportsCheckpointing: true, // Supports --checkpointing + supportsDebug: true, // Supports -d for debug output + defaultModel: 'gemini-2.5-flash', + modelMap, + mapModelToId, + buildArgs, + buildCommand, + parseOutput, + extractSessionId, + extractUsage, + detectErrors, +}; diff --git a/docs/case-studies/issue-15/evidence-timeline.md b/docs/case-studies/issue-15/evidence-timeline.md new file mode 100644 index 0000000..ff2be2c --- /dev/null +++ b/docs/case-studies/issue-15/evidence-timeline.md @@ -0,0 +1,65 @@ +# Evidence Timeline for Issue #15 + +## Data Collection Date +2026-01-18 + +## Timeline of Evidence + +### 1. Repository State Analysis + +**Agent Commander README.md** (Current State): +- Shows Agent CLI with JSON Input: ❌ (incorrect) +- Does NOT list Gemini CLI in supported tools table + +**Agent Commander js/src/tools/** (Current State): +- Contains `gemini.mjs` - fully implemented Gemini CLI support +- Contains `agent.mjs` with `supportsJsonInput: true` +- `index.mjs` exports all 6 tools including Gemini + +### 2. External Repository Evidence + +**@link-assistant/agent README** (verified 2026-01-18): +- Explicitly states: "JSON Input/Output: Accepts JSON via stdin, outputs JSON event streams (OpenCode-compatible)" +- Confirms full JSON streaming support + +**google-gemini/gemini-cli** (verified 2026-01-18): +- Supports `--output-format json` for structured output +- Supports `--output-format stream-json` for NDJSON streaming +- Official Google repository for Gemini CLI + +### 3. Code Configuration Evidence + +**js/src/tools/agent.mjs:246**: +```javascript +supportsJsonInput: true, // Agent supports full JSON streaming input +``` + +**js/src/tools/gemini.mjs:300-301**: +```javascript +supportsJsonOutput: true, +supportsJsonInput: false, // Gemini CLI uses -p flag for prompts, not stdin JSON +``` + +**js/src/tools/index.mjs:16-23**: +```javascript +export const tools = { + claude: claudeTool, + codex: codexTool, + opencode: opencodeTool, + agent: agentTool, + gemini: geminiTool, // Present but not in README + qwen: qwenTool, +}; +``` + +## Root Cause Analysis + +1. **Missing Gemini in README**: The Gemini tool was added to the codebase but the README.md was not updated to reflect this addition in the supported tools table. + +2. **Incorrect Agent JSON Input Status**: The README shows ❌ for Agent JSON Input, but the code configuration (`agent.mjs`) and the actual @link-assistant/agent repository confirm that JSON Input is fully supported. + +## Solution + +1. Add Gemini to the Supported Tools table in README.md +2. Update Agent's JSON Input from ❌ to ✅ +3. Add Gemini-specific features documentation section (similar to Claude and Qwen sections) diff --git a/docs/case-studies/issue-15/gemini-cli-info.json b/docs/case-studies/issue-15/gemini-cli-info.json new file mode 100644 index 0000000..aceab60 --- /dev/null +++ b/docs/case-studies/issue-15/gemini-cli-info.json @@ -0,0 +1,16 @@ +{ + "name": "gemini-cli", + "description": "An open-source AI agent that brings the power of Gemini directly into your terminal.", + "html_url": "https://github.com/google-gemini/gemini-cli", + "topics": [ + "ai", + "ai-agents", + "cli", + "gemini", + "gemini-api", + "mcp-client", + "mcp-server" + ], + "stargazers_count": 91378, + "updated_at": "2026-01-18T01:07:37Z" +} diff --git a/docs/case-studies/issue-15/issue-details.txt b/docs/case-studies/issue-15/issue-details.txt new file mode 100644 index 0000000..390a039 --- /dev/null +++ b/docs/case-studies/issue-15/issue-details.txt @@ -0,0 +1,13 @@ +title: Double check for JSON Output and JSON Input for all our tools +state: OPEN +author: konard +labels: bug, documentation, enhancement +comments: 0 +assignees: +projects: +milestone: +number: 15 +-- +We have missing listing in README.md of Gemini, also our Agent CLI (https://github.com/link-assistant/agent) actually supports JSON input/output. + +Please download all logs and data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), in which we will reconstruct timeline/sequence of events, find root causes of the problem, and propose possible solutions. From b502589c963343ac79078a5852551c0d9a685f97 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 18 Jan 2026 02:21:40 +0100 Subject: [PATCH 3/3] Revert "Initial commit with task details" This reverts commit fd754dd6168fddb768011290bea30cb23501bb1a. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 68fc62a..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/link-assistant/agent-commander/issues/15 -Your prepared branch: issue-15-47049ece53b4 -Your prepared working directory: /tmp/gh-issue-solver-1768698918720 - -Proceed.