diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d0397a3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,85 @@ +# AGENTS.md + +This file provides guidance to AI agents working with code in this repository. + +## Build Commands + +- `make build` - Build the binary to `out/agentapi` (includes chat UI build) +- `make embed` - Build the chat UI and embed it into Go +- `go build -o out/agentapi main.go` - Direct Go build without chat UI +- `go generate ./...` - Generate OpenAPI schema and version info + +## Testing + +- `go test ./...` - Run all Go tests +- Tests are located alongside source files (e.g., `lib/httpapi/server_test.go`) + +## Development Commands + +- `agentapi server -- claude` - Start server with Claude Code agent +- `agentapi server -- aider --model sonnet` - Start server with Aider agent +- `agentapi server -- goose` - Start server with Goose agent +- `agentapi server --type=codex -- codex` - Start server with Codex (requires explicit type) +- `agentapi server --type=gemini -- gemini` - Start server with Gemini (requires explicit type) +- `agentapi attach --url localhost:3284` - Attach to running agent terminal +- Server runs on port 3284 by default +- Chat UI available at http://localhost:3284/chat +- API documentation at http://localhost:3284/docs + +## Architecture + +This is a Go HTTP API server that controls coding agents (Claude Code, Aider, Goose, etc.) through terminal emulation. + +**Core Components:** +- `main.go` - Entry point using cobra CLI framework +- `cmd/` - CLI command definitions (server, attach) +- `lib/httpapi/` - HTTP server, routes, and OpenAPI schema +- `lib/screentracker/` - Terminal output parsing and message splitting +- `lib/termexec/` - Terminal process execution and management +- `lib/msgfmt/` - Message formatting for different agent types (claude, goose, aider, codex, gemini, amp, cursor-agent, cursor, auggie, custom) +- `chat/` - Next.js web UI (embedded into Go binary) + +**Key Architecture:** +- Runs agents in an in-memory terminal emulator +- Translates API calls to terminal keystrokes +- Parses terminal output into structured messages +- Supports multiple agent types with different message formats +- Embeds Next.js chat UI as static assets in Go binary + +**Message Flow:** +1. User sends message via HTTP API +2. Server takes terminal snapshot +3. Message sent to agent as terminal input +4. Terminal output changes tracked and parsed +5. New content becomes agent response message +6. SSE events stream updates to clients + +## API Endpoints + +- GET `/messages` - Get all messages in conversation +- POST `/message` - Send message to agent (content, type fields) +- GET `/status` - Get agent status ("stable" or "running") +- GET `/events` - SSE stream of agent events +- GET `/openapi.json` - OpenAPI schema +- GET `/docs` - API documentation UI +- GET `/chat` - Web chat interface + +## Supported Agents + +Agents with explicit type requirement (use `--type=`): +- `codex` - OpenAI Codex +- `gemini` - Google Gemini CLI +- `amp` - Sourcegraph Amp CLI +- `cursor` - Cursor CLI + +Agents with auto-detection: +- `claude` - Claude Code (default) +- `goose` - Goose +- `aider` - Aider + +## Project Structure + +- Go module with standard layout +- Chat UI in `chat/` directory (Next.js + TypeScript) +- OpenAPI schema auto-generated to `openapi.json` +- Version managed via `version.sh` script diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b4e19..8e6783f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## v0.7.0 + +### Features +- Add Support for Opencode. +- Add support for Agent aliases +- Explicitly support AmazonQ +- Bump NEXT.JS version + +## v0.6.3 + +- CI fixes. + ## v0.6.2 - Fix incorrect version string. diff --git a/Makefile b/Makefile index e74757c..97d78c2 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ embed: $(CHAT_SOURCES_STAMP) @echo "Chat build is up to date." .PHONY: build -build: gen embed +build: embed CGO_ENABLED=0 go build -o ${BINPATH} main.go .PHONY: gen diff --git a/README.md b/README.md index 6bfb02a..792813a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AgentAPI -Control [Claude Code](https://github.com/anthropics/claude-code), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), [Gemini](https://github.com/google-gemini/gemini-cli), [Sourcegraph Amp](https://github.com/sourcegraph/amp-cli), [Codex](https://github.com/openai/codex), [Auggie](https://docs.augmentcode.com/cli/overview), and [Cursor CLI](https://cursor.com/en/cli) with an HTTP API. +Control [Claude Code](https://github.com/anthropics/claude-code), [AmazonQ](https://aws.amazon.com/developer/learning/q-developer-cli/), [Opencode](https://opencode.ai/), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), [Gemini](https://github.com/google-gemini/gemini-cli), [Sourcegraph Amp](https://github.com/sourcegraph/amp-cli), [Codex](https://github.com/openai/codex), [Auggie](https://docs.augmentcode.com/cli/overview), and [Cursor CLI](https://cursor.com/en/cli) with an HTTP API. ![agentapi-chat](https://github.com/user-attachments/assets/57032c9f-4146-4b66-b219-09e38ab7690d) @@ -65,7 +65,7 @@ agentapi server -- goose ``` > [!NOTE] -> When using Codex, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break. +> When using Codex, Opencode, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break. An OpenAPI schema is available in [openapi.json](openapi.json). diff --git a/chat/bun.lock b/chat/bun.lock index 0cb649c..df3ebcb 100644 --- a/chat/bun.lock +++ b/chat/bun.lock @@ -10,7 +10,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.511.0", - "next": "15.2.4", + "next": "15.4.7", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -250,7 +250,7 @@ "@emnapi/core": ["@emnapi/core@1.4.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.1", "tslib": "^2.4.0" } }, "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg=="], - "@emnapi/runtime": ["@emnapi/runtime@1.4.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw=="], + "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw=="], @@ -340,43 +340,49 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="], - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="], - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="], - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="], - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="], - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="], - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="], - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="], - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="], - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="], - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="], - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="], - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="], - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="], - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="], - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="], - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="], - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="], - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="], - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], @@ -390,25 +396,25 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.8", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg=="], - "@next/env": ["@next/env@15.2.4", "", {}, "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g=="], + "@next/env": ["@next/env@15.4.7", "", {}, "sha512-PrBIpO8oljZGTOe9HH0miix1w5MUiGJ/q83Jge03mHEE0E3pyqzAy2+l5G6aJDbXoobmxPJTVhbCuwlLtjSHwg=="], "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.2.4", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2Dkb+VUTp9kHHkSqtws4fDl2Oxms29HcZBwFIda1X7Ztudzy7M6XF9HDS2dq85TmdN47VpuhjE+i6wgnIboVzQ=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-qaMnEozKdWezlmh1OGDVFueFv2z9lWTcLvt7e39QA3YOvZHNpN2rLs/IQLwZaUiw2jSvxW07LxMCWtOqsWFNQg=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-ny7lODPE7a15Qms8LZiN9wjNWIeI+iAZOFDOnv2pcHStncUr7cr9lD5XF81mdhrBXLUP9yT9RzlmSWKIazWoDw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-4SaCjlFR/2hGJqZLLWycccy1t+wBrE/vyJWnYaZJhUVHccpGLG5q0C+Xkw4iRzUIkE+/dr90MJRUym3s1+vO8A=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.7", "", { "os": "linux", "cpu": "x64" }, "sha512-2uNXjxvONyRidg00VwvlTYDwC9EgCGNzPAPYbttIATZRxmOZ3hllk/YYESzHZb65eyZfBR5g9xgCZjRAl9YYGg=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.7", "", { "os": "linux", "cpu": "x64" }, "sha512-ceNbPjsFgLscYNGKSu4I6LYaadq2B8tcK116nVuInpHHdAWLWSwVK6CHNvCi0wVS9+TTArIFKJGsEyVD1H+4Kg=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-pZyxmY1iHlZJ04LUL7Css8bNvsYAMYOY9JRwFA3HZgpaNKsJSowD09Vg2R9734GxAcLJc2KDQHSCR91uD6/AAw=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-HjuwPJ7BeRzgl3KrjKqD2iDng0eQIpIReyhpF5r4yeAHFwWRuAhfW92rWv/r3qeQHEwHsLRzFDvMqRjyM5DI6A=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -496,8 +502,6 @@ "@storybook/react-dom-shim": ["@storybook/react-dom-shim@9.0.17", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "storybook": "^9.0.17" } }, "sha512-ak/x/m6MDDxdE6rCDymTltaiQF3oiKrPHSwfM+YPgQR6MVmzTTs4+qaPfeev7FZEHq23IkfDMTmSTTJtX7Vs9A=="], - "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], - "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@tailwindcss/node": ["@tailwindcss/node@4.1.3", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", "tailwindcss": "4.1.3" } }, "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA=="], @@ -784,8 +788,6 @@ "builtin-status-codes": ["builtin-status-codes@3.0.0", "", {}, "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ=="], - "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -796,7 +798,7 @@ "camel-case": ["camel-case@4.1.2", "", { "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw=="], - "caniuse-lite": ["caniuse-lite@1.0.30001712", "", {}, "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig=="], + "caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="], "case-sensitive-paths-webpack-plugin": ["case-sensitive-paths-webpack-plugin@2.4.0", "", {}, "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw=="], @@ -904,7 +906,7 @@ "des.js": ["des.js@1.1.0", "", { "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg=="], - "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], @@ -1346,7 +1348,7 @@ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], - "next": ["next@15.2.4", "", { "dependencies": { "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.2.4", "@next/swc-darwin-x64": "15.2.4", "@next/swc-linux-arm64-gnu": "15.2.4", "@next/swc-linux-arm64-musl": "15.2.4", "@next/swc-linux-x64-gnu": "15.2.4", "@next/swc-linux-x64-musl": "15.2.4", "@next/swc-win32-arm64-msvc": "15.2.4", "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ=="], + "next": ["next@15.4.7", "", { "dependencies": { "@next/env": "15.4.7", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.7", "@next/swc-darwin-x64": "15.4.7", "@next/swc-linux-arm64-gnu": "15.4.7", "@next/swc-linux-arm64-musl": "15.4.7", "@next/swc-linux-x64-gnu": "15.4.7", "@next/swc-linux-x64-musl": "15.4.7", "@next/swc-win32-arm64-msvc": "15.4.7", "@next/swc-win32-x64-msvc": "15.4.7", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-OcqRugwF7n7mC8OSYjvsZhhG1AYSvulor1EIUsIkbbEbf1qoE5EbH36Swj8WhF4cHqmDgkiam3z1c1W0J1Wifg=="], "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], @@ -1572,7 +1574,7 @@ "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], - "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -1608,8 +1610,6 @@ "stream-http": ["stream-http@3.2.0", "", { "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", "readable-stream": "^3.6.0", "xtend": "^4.0.2" } }, "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A=="], - "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], - "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], @@ -1794,6 +1794,8 @@ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + "@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.4.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw=="], + "@pmmmwh/react-refresh-webpack-plugin/loader-utils": ["loader-utils@2.0.4", "", { "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^2.1.2" } }, "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw=="], "@storybook/preset-react-webpack/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="], @@ -1832,8 +1834,6 @@ "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "browserslist/caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -1870,6 +1870,8 @@ "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "lightningcss/detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + "make-dir/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1910,6 +1912,8 @@ "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "sharp/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], diff --git a/chat/package.json b/chat/package.json index e6e79e0..6372af2 100644 --- a/chat/package.json +++ b/chat/package.json @@ -1,18 +1,4 @@ { - "name": "chat", - "version": "0.6.2", - "private": true, - "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "next lint", - "export": "GITHUB_PAGES=true next build", - "serve-static": "npx serve out", - "deploy-gh-pages": "GITHUB_PAGES=true next build && gh-pages -d out --nojekyll -e chat -f", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" - }, "dependencies": { "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-slot": "^1.2.2", @@ -20,7 +6,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.511.0", - "next": "15.2.4", + "next": "15.4.7", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -44,5 +30,19 @@ "tailwindcss": "^4", "tw-animate-css": "^1.3.0", "typescript": "^5" - } + }, + "name": "chat", + "private": true, + "scripts": { + "build": "next build", + "build-storybook": "storybook build", + "deploy-gh-pages": "GITHUB_PAGES=true next build && gh-pages -d out --nojekyll -e chat -f", + "dev": "next dev --turbopack", + "export": "GITHUB_PAGES=true next build", + "lint": "next lint", + "serve-static": "npx serve out", + "start": "next start", + "storybook": "storybook dev -p 6006" + }, + "version": "0.7.0" } diff --git a/cmd/server/server.go b/cmd/server/server.go index 912ab66..6581e37 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -23,44 +23,46 @@ import ( type AgentType = msgfmt.AgentType const ( - AgentTypeClaude AgentType = msgfmt.AgentTypeClaude - AgentTypeGoose AgentType = msgfmt.AgentTypeGoose - AgentTypeAider AgentType = msgfmt.AgentTypeAider - AgentTypeCodex AgentType = msgfmt.AgentTypeCodex - AgentTypeGemini AgentType = msgfmt.AgentTypeGemini - AgentTypeAmp AgentType = msgfmt.AgentTypeAmp - AgentTypeCursorAgent AgentType = msgfmt.AgentTypeCursorAgent - AgentTypeCursor AgentType = msgfmt.AgentTypeCursor - AgentTypeAuggie AgentType = msgfmt.AgentTypeAuggie - AgentTypeCustom AgentType = msgfmt.AgentTypeCustom + AgentTypeClaude AgentType = msgfmt.AgentTypeClaude + AgentTypeGoose AgentType = msgfmt.AgentTypeGoose + AgentTypeAider AgentType = msgfmt.AgentTypeAider + AgentTypeCodex AgentType = msgfmt.AgentTypeCodex + AgentTypeGemini AgentType = msgfmt.AgentTypeGemini + AgentTypeAmp AgentType = msgfmt.AgentTypeAmp + AgentTypeCursor AgentType = msgfmt.AgentTypeCursor + AgentTypeAuggie AgentType = msgfmt.AgentTypeAuggie + AgentTypeAmazonQ AgentType = msgfmt.AgentTypeAmazonQ + AgentTypeOpencode AgentType = msgfmt.AgentTypeOpencode + AgentTypeCustom AgentType = msgfmt.AgentTypeCustom ) -// exhaustiveness of this map is checked by the exhaustive linter -var agentTypeMap = map[AgentType]bool{ - AgentTypeClaude: true, - AgentTypeGoose: true, - AgentTypeAider: true, - AgentTypeCodex: true, - AgentTypeGemini: true, - AgentTypeAmp: true, - AgentTypeCursorAgent: true, - AgentTypeCursor: true, - AgentTypeAuggie: true, - AgentTypeCustom: true, +// agentTypeAliases contains the mapping of possible input agent type strings to their canonical AgentType values +var agentTypeAliases = map[string]AgentType{ + "claude": AgentTypeClaude, + "goose": AgentTypeGoose, + "aider": AgentTypeAider, + "codex": AgentTypeCodex, + "gemini": AgentTypeGemini, + "amp": AgentTypeAmp, + "auggie": AgentTypeAuggie, + "cursor": AgentTypeCursor, + "cursor-agent": AgentTypeCursor, + "q": AgentTypeAmazonQ, + "amazonq": AgentTypeAmazonQ, + "opencode": AgentTypeOpencode, + "custom": AgentTypeCustom, } func parseAgentType(firstArg string, agentTypeVar string) (AgentType, error) { // if the agent type is provided, use it - castedAgentType := AgentType(agentTypeVar) - if _, ok := agentTypeMap[castedAgentType]; ok { + if castedAgentType, ok := agentTypeAliases[agentTypeVar]; ok { return castedAgentType, nil } if agentTypeVar != "" { return AgentTypeCustom, fmt.Errorf("invalid agent type: %s", agentTypeVar) } // if the agent type is not provided, guess it from the first argument - castedFirstArg := AgentType(firstArg) - if _, ok := agentTypeMap[castedFirstArg]; ok { + if castedFirstArg, ok := agentTypeAliases[firstArg]; ok { return castedFirstArg, nil } return AgentTypeCustom, nil @@ -144,9 +146,9 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er } var agentNames = (func() []string { - names := make([]string, 0, len(agentTypeMap)) - for agentType := range agentTypeMap { - names = append(names, string(agentType)) + names := make([]string, 0, len(agentTypeAliases)) + for agentType := range agentTypeAliases { + names = append(names, agentType) } sort.Strings(names) return names diff --git a/cmd/server/server_test.go b/cmd/server/server_test.go index f61d2c0..03c52ff 100644 --- a/cmd/server/server_test.go +++ b/cmd/server/server_test.go @@ -50,13 +50,28 @@ func TestParseAgentType(t *testing.T) { { firstArg: "cursor-agent", agentTypeVar: "", - want: AgentTypeCursorAgent, + want: AgentTypeCursor, }, { firstArg: "cursor", agentTypeVar: "", want: AgentTypeCursor, }, + { + firstArg: "amazonq", + agentTypeVar: "", + want: AgentTypeAmazonQ, + }, + { + firstArg: "q", + agentTypeVar: "", + want: AgentTypeAmazonQ, + }, + { + firstArg: "opencode", + agentTypeVar: "", + want: AgentTypeOpencode, + }, { firstArg: "auggie", agentTypeVar: "", @@ -97,10 +112,25 @@ func TestParseAgentType(t *testing.T) { agentTypeVar: "gemini", want: AgentTypeGemini, }, + { + firstArg: "claude", + agentTypeVar: "amazonq", + want: AgentTypeAmazonQ, + }, + { + firstArg: "claude", + agentTypeVar: "q", + want: AgentTypeAmazonQ, + }, + { + firstArg: "claude", + agentTypeVar: "opencode", + want: AgentTypeOpencode, + }, { firstArg: "claude", agentTypeVar: "cursor-agent", - want: AgentTypeCursorAgent, + want: AgentTypeCursor, }, { firstArg: "claude", diff --git a/internal/version/version.go b/internal/version/version.go index cb53b66..3fcde94 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -2,4 +2,4 @@ package version -var Version = "0.6.2" +var Version = "0.7.0" diff --git a/lib/httpapi/server.go b/lib/httpapi/server.go index e8abab9..b72b4c3 100644 --- a/lib/httpapi/server.go +++ b/lib/httpapi/server.go @@ -196,7 +196,8 @@ func NewServer(ctx context.Context, config ServerConfig) (*Server, error) { return mf.FormatAgentMessage(config.AgentType, message, userInput) } conversation := st.NewConversation(ctx, st.ConversationConfig{ - AgentIO: config.Process, + AgentType: config.AgentType, + AgentIO: config.Process, GetTime: func() time.Time { return time.Now() }, diff --git a/lib/msgfmt/message_box.go b/lib/msgfmt/message_box.go index 9c42468..ce4eeee 100644 --- a/lib/msgfmt/message_box.go +++ b/lib/msgfmt/message_box.go @@ -60,3 +60,22 @@ func removeCodexInputBox(msg string) string { } return strings.Join(lines, "\n") } + +func removeOpencodeMessageBox(msg string) string { + lines := strings.Split(msg, "\n") + // Check the last 3 lines for + // + // ┃ ┃ + // ┃ > ┃ + // ┃ ┃ + // We only check for the first ┃ and then an empty line above it - as sometimes the full input block does not load within a snapshot, + // this leads to displaying a bunch of newlines. + for i := len(lines) - 1; i >= 1; i-- { + if strings.TrimSpace(lines[i-1]) == "" && + strings.ReplaceAll(lines[i], " ", "") == "┃┃" { + lines = lines[:i-1] + break + } + } + return strings.Join(lines, "\n") +} diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index 3c88200..4844b3f 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -192,10 +192,17 @@ func RemoveUserInput(msgRaw string, userInputRaw string, agentType AgentType) st if idx, found := skipTrailingInputBoxLine(msgLines, lastUserInputLineIdx, "╯", "╰"); found { lastUserInputLineIdx = idx } - } else if agentType == AgentTypeCursorAgent || agentType == AgentTypeCursor { + } else if agentType == AgentTypeCursor { if idx, found := skipTrailingInputBoxLine(msgLines, lastUserInputLineIdx, "┘", "└"); found { lastUserInputLineIdx = idx } + } else if agentType == AgentTypeOpencode { + // skip +2 lines after the input + // ┃ jkmr (08:46 PM) ┃ + // ┃ ┃ + if lastUserInputLineIdx+2 < len(msgLines) { + lastUserInputLineIdx += 2 + } } return strings.Join(msgLines[lastUserInputLineIdx+1:], "\n") @@ -225,16 +232,17 @@ func trimEmptyLines(message string) string { type AgentType string const ( - AgentTypeClaude AgentType = "claude" - AgentTypeGoose AgentType = "goose" - AgentTypeAider AgentType = "aider" - AgentTypeCodex AgentType = "codex" - AgentTypeGemini AgentType = "gemini" - AgentTypeAmp AgentType = "amp" - AgentTypeCursorAgent AgentType = "cursor-agent" - AgentTypeCursor AgentType = "cursor" - AgentTypeAuggie AgentType = "auggie" - AgentTypeCustom AgentType = "custom" + AgentTypeClaude AgentType = "claude" + AgentTypeGoose AgentType = "goose" + AgentTypeAider AgentType = "aider" + AgentTypeCodex AgentType = "codex" + AgentTypeGemini AgentType = "gemini" + AgentTypeAmp AgentType = "amp" + AgentTypeCursor AgentType = "cursor" + AgentTypeAuggie AgentType = "auggie" + AgentTypeAmazonQ AgentType = "amazonq" + AgentTypeOpencode AgentType = "opencode" + AgentTypeCustom AgentType = "custom" ) func formatGenericMessage(message string, userInput string, agentType AgentType) string { @@ -251,6 +259,13 @@ func formatCodexMessage(message string, userInput string) string { return message } +func formatOpencodeMessage(message string, userInput string) string { + message = RemoveUserInput(message, userInput, AgentTypeOpencode) + message = removeOpencodeMessageBox(message) + message = trimEmptyLines(message) + return message +} + func FormatAgentMessage(agentType AgentType, message string, userInput string) string { switch agentType { case AgentTypeClaude: @@ -265,12 +280,14 @@ func FormatAgentMessage(agentType AgentType, message string, userInput string) s return formatGenericMessage(message, userInput, agentType) case AgentTypeAmp: return formatGenericMessage(message, userInput, agentType) - case AgentTypeCursorAgent: - return formatGenericMessage(message, userInput, agentType) case AgentTypeCursor: return formatGenericMessage(message, userInput, agentType) case AgentTypeAuggie: return formatGenericMessage(message, userInput, agentType) + case AgentTypeAmazonQ: + return formatGenericMessage(message, userInput, agentType) + case AgentTypeOpencode: + return formatOpencodeMessage(message, userInput) case AgentTypeCustom: return formatGenericMessage(message, userInput, agentType) default: diff --git a/lib/msgfmt/msgfmt_test.go b/lib/msgfmt/msgfmt_test.go index c29d6b9..9cbb5ca 100644 --- a/lib/msgfmt/msgfmt_test.go +++ b/lib/msgfmt/msgfmt_test.go @@ -218,7 +218,7 @@ func TestTrimEmptyLines(t *testing.T) { func TestFormatAgentMessage(t *testing.T) { dir := "testdata/format" - agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeGemini, AgentTypeAmp, AgentTypeCodex, AgentTypeCursorAgent, AgentTypeCursor, AgentTypeAuggie, AgentTypeCustom} + agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeGemini, AgentTypeAmp, AgentTypeCodex, AgentTypeCursor, AgentTypeAuggie, AgentTypeAmazonQ, AgentTypeOpencode, AgentTypeCustom} for _, agentType := range agentTypes { t.Run(string(agentType), func(t *testing.T) { cases, err := testdataDir.ReadDir(path.Join(dir, string(agentType))) diff --git a/lib/msgfmt/testdata/format/amazonq/confirmation_box/expected.txt b/lib/msgfmt/testdata/format/amazonq/confirmation_box/expected.txt new file mode 100644 index 0000000..be32e1d --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/confirmation_box/expected.txt @@ -0,0 +1,13 @@ +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Checking current directory to identify repository", + ⋮ "link": "", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/confirmation_box/msg.txt b/lib/msgfmt/testdata/format/amazonq/confirmation_box/msg.txt new file mode 100644 index 0000000..71cda2c --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/confirmation_box/msg.txt @@ -0,0 +1,20 @@ +> what repo is this ? + + + + +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Checking current directory to identify repository", + ⋮ "link": "", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/confirmation_box/user.txt b/lib/msgfmt/testdata/format/amazonq/confirmation_box/user.txt new file mode 100644 index 0000000..4cce2f7 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/confirmation_box/user.txt @@ -0,0 +1 @@ +what repo is this ? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/first_message/expected.txt b/lib/msgfmt/testdata/format/amazonq/first_message/expected.txt new file mode 100644 index 0000000..464886e --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/first_message/expected.txt @@ -0,0 +1,12 @@ +✓ coder loaded in 0.03 s + +Welcome to Amazon Q! + +💡 Run /prompts to learn how to build & run repeatable workflows + +/help all commands +ctrl + j new lines +ctrl + s fuzzy search + + +🤖 You are chatting with claude-sonnet-4 \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/first_message/msg.txt b/lib/msgfmt/testdata/format/amazonq/first_message/msg.txt new file mode 100644 index 0000000..4f43700 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/first_message/msg.txt @@ -0,0 +1,14 @@ +✓ coder loaded in 0.03 s + +Welcome to Amazon Q! + +💡 Run /prompts to learn how to build & run repeatable workflows + +/help all commands +ctrl + j new lines +ctrl + s fuzzy search + + +🤖 You are chatting with claude-sonnet-4 + +> \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/first_message/user.txt b/lib/msgfmt/testdata/format/amazonq/first_message/user.txt new file mode 100644 index 0000000..e69de29 diff --git a/lib/msgfmt/testdata/format/amazonq/multi-line-input/expected.txt b/lib/msgfmt/testdata/format/amazonq/multi-line-input/expected.txt new file mode 100644 index 0000000..112f6f9 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/multi-line-input/expected.txt @@ -0,0 +1,62 @@ +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Searching for the formatCodexMessage function in the codebase", + ⋮ "link": "", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> y + + + ⋮ + ● Completed in 0.0s + + + + + +Tool validation failed: +Failed to validate tool parameters: Path is not a file: + + + +🛠️ Using tool: fs_read (trusted) + ⋮ + ● Reading directory: /Users/jkmr/Documents/work/agentapi with maximum depth of 0 + ✓ Successfully read directory /Users/jkmr/Documents/work/agentapi (29 entries) + + ⋮ + ● Completed in 0.1s + + + + + +🛠️ Using tool: execute_bash + ⋮ + ● I will run the following shell command: +find /Users/jkmr/Documents/work/agentapi -name "*.go" -exec grep -l "formatCodexMessage" {} \; + ⋮ + ↳ Purpose: Search for Go files containing formatCodexMessage function + + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> y + +/Users/jkmr/Documents/work/agentapi/lib/msgfmt/msgfmt.go + + ⋮ + ● Completed in 0.449s + + +> The formatCodexMessage function is located in the file: + +/Users/jkmr/Documents/work/agentapi/lib/msgfmt/msgfmt.go \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/multi-line-input/msg.txt b/lib/msgfmt/testdata/format/amazonq/multi-line-input/msg.txt new file mode 100644 index 0000000..a004f51 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/multi-line-input/msg.txt @@ -0,0 +1,75 @@ +> Which file has this snippet +func formatCodexMessage(message string, userInput string) string { + message = RemoveUserInput(message, userInput) + message = removeCodexInputBox(message) + message = trimEmptyLines(message) + return message +} + + + + +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Searching for the formatCodexMessage function in the codebase", + ⋮ "link": "", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> y + + + ⋮ + ● Completed in 0.0s + + + + + +Tool validation failed: +Failed to validate tool parameters: Path is not a file: + + + +🛠️ Using tool: fs_read (trusted) + ⋮ + ● Reading directory: /Users/jkmr/Documents/work/agentapi with maximum depth of 0 + ✓ Successfully read directory /Users/jkmr/Documents/work/agentapi (29 entries) + + ⋮ + ● Completed in 0.1s + + + + + +🛠️ Using tool: execute_bash + ⋮ + ● I will run the following shell command: +find /Users/jkmr/Documents/work/agentapi -name "*.go" -exec grep -l "formatCodexMessage" {} \; + ⋮ + ↳ Purpose: Search for Go files containing formatCodexMessage function + + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> y + +/Users/jkmr/Documents/work/agentapi/lib/msgfmt/msgfmt.go + + ⋮ + ● Completed in 0.449s + + +> The formatCodexMessage function is located in the file: + +/Users/jkmr/Documents/work/agentapi/lib/msgfmt/msgfmt.go + +> \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/multi-line-input/user.txt b/lib/msgfmt/testdata/format/amazonq/multi-line-input/user.txt new file mode 100644 index 0000000..e85fa2a --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/multi-line-input/user.txt @@ -0,0 +1,7 @@ +Which file has this snippet +func formatCodexMessage(message string, userInput string) string { + message = RemoveUserInput(message, userInput) + message = removeCodexInputBox(message) + message = trimEmptyLines(message) + return message +} \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/second_message/expected.txt b/lib/msgfmt/testdata/format/amazonq/second_message/expected.txt new file mode 100644 index 0000000..57def31 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/second_message/expected.txt @@ -0,0 +1,59 @@ +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Checking for untracked files in the repository", + ⋮ "link": "/Users/jkmr/Documents/work/agentapi", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> t + + + ⋮ + ● Completed in 0.0s + + + + + +🛠️ Using tool: execute_bash + ⋮ + ● I will run the following shell command: +git status --porcelain | grep '^??' | wc -l + ⋮ + ↳ Purpose: Count untracked files using git status + + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> t + + 8 + + ⋮ + ● Completed in 0.24s + + +> There are 8 untracked files in the repository. + + +🛠️ Using tool: coder_report_task (trusted) from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "link": "/Users/jkmr/Documents/work/agentapi", + ⋮ "summary": "Found 8 untracked files in the repository", + ⋮ "state": "idle" + ⋮ } + ⋮ } + + ⋮ + ● Completed in 0.0s \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/second_message/msg.txt b/lib/msgfmt/testdata/format/amazonq/second_message/msg.txt new file mode 100644 index 0000000..e02a08b --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/second_message/msg.txt @@ -0,0 +1,69 @@ +> How many untracked files are there? + + + + +🛠️ Using tool: coder_report_task from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "summary": "Checking for untracked files in the repository", + ⋮ "link": "/Users/jkmr/Documents/work/agentapi", + ⋮ "state": "working" + ⋮ } + ⋮ } + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> t + + + ⋮ + ● Completed in 0.0s + + + + + +🛠️ Using tool: execute_bash + ⋮ + ● I will run the following shell command: +git status --porcelain | grep '^??' | wc -l + ⋮ + ↳ Purpose: Count untracked files using git status + + +Allow this action? Use 't' to trust (always allow) this tool for the session. [y/n/t]: + +> t + + 8 + + ⋮ + ● Completed in 0.24s + + +> There are 8 untracked files in the repository. + + +🛠️ Using tool: coder_report_task (trusted) from mcp server coder + ⋮ + ● Running coder_report_task with the param: + ⋮ { + ⋮ "name": "coder_report_task", + ⋮ "arguments": { + ⋮ "link": "/Users/jkmr/Documents/work/agentapi", + ⋮ "summary": "Found 8 untracked files in the repository", + ⋮ "state": "idle" + ⋮ } + ⋮ } + + ⋮ + ● Completed in 0.0s + + + + +> \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/second_message/user.txt b/lib/msgfmt/testdata/format/amazonq/second_message/user.txt new file mode 100644 index 0000000..f739c91 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/second_message/user.txt @@ -0,0 +1 @@ +How many untracked files are there? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/thinking/expected.txt b/lib/msgfmt/testdata/format/amazonq/thinking/expected.txt new file mode 100644 index 0000000..64c5b8a --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/thinking/expected.txt @@ -0,0 +1 @@ +⠋ Thinking... \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/thinking/msg.txt b/lib/msgfmt/testdata/format/amazonq/thinking/msg.txt new file mode 100644 index 0000000..71b55de --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/thinking/msg.txt @@ -0,0 +1,3 @@ +> How many untracked files are there? + +⠋ Thinking... \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/amazonq/thinking/user.txt b/lib/msgfmt/testdata/format/amazonq/thinking/user.txt new file mode 100644 index 0000000..f739c91 --- /dev/null +++ b/lib/msgfmt/testdata/format/amazonq/thinking/user.txt @@ -0,0 +1 @@ +How many untracked files are there? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/first_message/expected.txt b/lib/msgfmt/testdata/format/opencode/first_message/expected.txt new file mode 100644 index 0000000..8882891 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/first_message/expected.txt @@ -0,0 +1,12 @@ + █▀▀█ █▀▀█ █▀▀ █▀▀▄ █▀▀ █▀▀█ █▀▀▄ █▀▀ + █░░█ █░░█ █▀▀ █░░█ █░░ █░░█ █░░█ █▀▀ + ▀▀▀▀ █▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀ + v0.6.8 + + /new new session ctrl+x n + /help show help ctrl+x h + /share share session ctrl+x s + /models list models ctrl+x m + + + Grok Code is free for a limited time \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/first_message/msg.txt b/lib/msgfmt/testdata/format/opencode/first_message/msg.txt new file mode 100644 index 0000000..7b797b1 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/first_message/msg.txt @@ -0,0 +1,604 @@ + █▀▀█ █▀▀█ █▀▀ █▀▀▄ █▀▀ █▀▀█ █▀▀▄ █▀▀ + █░░█ █░░█ █▀▀ █░░█ █░░ █░░█ █░░█ █▀▀ + ▀▀▀▀ █▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀ + v0.6.8 + + /new new session ctrl+x n + /help show help ctrl+x h + /share share session ctrl+x s + /models list models ctrl+x m + + + Grok Code is free for a limited time + + + ┃ ┃ + ┃ > ┃ + ┃ ┃ + enter send + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + opencode v0.6.8 ~/Documents/work/agentapi tab ┃ BUILD AGENT \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/first_message/user.txt b/lib/msgfmt/testdata/format/opencode/first_message/user.txt new file mode 100644 index 0000000..e69de29 diff --git a/lib/msgfmt/testdata/format/opencode/second_message/expected.txt b/lib/msgfmt/testdata/format/opencode/second_message/expected.txt new file mode 100644 index 0000000..8767a12 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/second_message/expected.txt @@ -0,0 +1,14 @@ + ┃ + ┃ Shell Check git remote URLs to identify repository + ┃ + ┃ $ git remote -v + ┃ origin https://github.com/35C4n0r/agentapi (fetch) + ┃ origin https://github.com/35C4n0r/agentapi (push) + ┃ upstream https://github.com/coder/agentapi.git (fetch) + ┃ upstream https://github.com/coder/agentapi.git (push) + ┃ + + + This is the 35C4n0r/agentapi repository, which is a fork of + coder/agentapi. + Build claude-sonnet-4-20250514 (09:19 PM) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/second_message/msg.txt b/lib/msgfmt/testdata/format/opencode/second_message/msg.txt new file mode 100644 index 0000000..2350003 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/second_message/msg.txt @@ -0,0 +1,1185 @@ + ┃ ┃ + ┃ Which repo is this ? ┃ + ┃ jkmr (09:19 PM) ┃ + ┃ ┃ + + ┃ + ┃ Shell Check git remote URLs to identify repository + ┃ + ┃ $ git remote -v + ┃ origin https://github.com/35C4n0r/agentapi (fetch) + ┃ origin https://github.com/35C4n0r/agentapi (push) + ┃ upstream https://github.com/coder/agentapi.git (fetch) + ┃ upstream https://github.com/coder/agentapi.git (push) + ┃ + + + This is the 35C4n0r/agentapi repository, which is a fork of + coder/agentapi. + Build claude-sonnet-4-20250514 (09:19 PM) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ┃ ┃ + ┃ > ┃ + ┃ ┃ + enter send Anthropic Claude Sonnet 4 + + opencode v0.6.8 ~/Documents/work/agentapi:feat-opencode tab ┃ BUILD AGENT \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/second_message/user.txt b/lib/msgfmt/testdata/format/opencode/second_message/user.txt new file mode 100644 index 0000000..c7d3776 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/second_message/user.txt @@ -0,0 +1 @@ +Which repo is this ? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/thinking/expected.txt b/lib/msgfmt/testdata/format/opencode/thinking/expected.txt new file mode 100644 index 0000000..cbc0e92 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/thinking/expected.txt @@ -0,0 +1,2 @@ + Generating... + Build claude-sonnet-4-20250514 (09:23 PM) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/thinking/msg.txt b/lib/msgfmt/testdata/format/opencode/thinking/msg.txt new file mode 100644 index 0000000..57bc242 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/thinking/msg.txt @@ -0,0 +1,1185 @@ + ┃ ┃ + ┃ Which repo is this ? ┃ + ┃ jkmr (09:23 PM) ┃ + ┃ ┃ + + + Generating... + Build claude-sonnet-4-20250514 (09:23 PM) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ┃ ┃ + ┃ > ┃ + ┃ ┃ + working esc interrupt Anthropic Claude Sonnet 4 + + opencode v0.6.8 ~/Documents/work/agentapi:feat-opencode tab ┃ BUILD AGENT \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/opencode/thinking/user.txt b/lib/msgfmt/testdata/format/opencode/thinking/user.txt new file mode 100644 index 0000000..c7d3776 --- /dev/null +++ b/lib/msgfmt/testdata/format/opencode/thinking/user.txt @@ -0,0 +1 @@ +Which repo is this ? \ No newline at end of file diff --git a/lib/screentracker/conversation.go b/lib/screentracker/conversation.go index d7b12d0..7777e04 100644 --- a/lib/screentracker/conversation.go +++ b/lib/screentracker/conversation.go @@ -24,7 +24,8 @@ type AgentIO interface { } type ConversationConfig struct { - AgentIO AgentIO + AgentType msgfmt.AgentType + AgentIO AgentIO // GetTime returns the current time GetTime func() time.Time // How often to take a snapshot for the stability check @@ -133,15 +134,29 @@ func (c *Conversation) StartSnapshotLoop(ctx context.Context) { }() } -func FindNewMessage(oldScreen, newScreen string) string { +func FindNewMessage(oldScreen, newScreen string, agentType msgfmt.AgentType) string { oldLines := strings.Split(oldScreen, "\n") newLines := strings.Split(newScreen, "\n") oldLinesMap := make(map[string]bool) + + // -1 indicates no header + dynamicHeaderEnd := -1 + + // Skip header lines for Opencode agent type to avoid false positives + // The header contains dynamic content (token count, context percentage, cost) + // that changes between screens, causing line comparison mismatches: + // + // ┃ # Getting Started with Claude CLI ┃ + // ┃ /share to create a shareable link 12.6K/6% ($0.05) ┃ + if len(newLines) >= 2 && agentType == msgfmt.AgentTypeOpencode { + dynamicHeaderEnd = 2 + } + for _, line := range oldLines { oldLinesMap[line] = true } firstNonMatchingLine := len(newLines) - for i, line := range newLines { + for i, line := range newLines[dynamicHeaderEnd+1:] { if !oldLinesMap[line] { firstNonMatchingLine = i break @@ -178,7 +193,7 @@ func (c *Conversation) lastMessage(role ConversationRole) ConversationMessage { // This function assumes that the caller holds the lock func (c *Conversation) updateLastAgentMessage(screen string, timestamp time.Time) { - agentMessage := FindNewMessage(c.screenBeforeLastUserMessage, screen) + agentMessage := FindNewMessage(c.screenBeforeLastUserMessage, screen, c.cfg.AgentType) lastUserMessage := c.lastMessage(ConversationRoleUser) if c.cfg.FormatMessage != nil { agentMessage = c.cfg.FormatMessage(agentMessage, lastUserMessage.Message) diff --git a/lib/screentracker/conversation_test.go b/lib/screentracker/conversation_test.go index 61b28df..53c77fd 100644 --- a/lib/screentracker/conversation_test.go +++ b/lib/screentracker/conversation_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/coder/agentapi/lib/msgfmt" "github.com/stretchr/testify/assert" st "github.com/coder/agentapi/lib/screentracker" @@ -353,12 +354,12 @@ func TestMessages(t *testing.T) { var testdataDir embed.FS func TestFindNewMessage(t *testing.T) { - assert.Equal(t, "", st.FindNewMessage("123456", "123456")) - assert.Equal(t, "1234567", st.FindNewMessage("123456", "1234567")) - assert.Equal(t, "42", st.FindNewMessage("123", "123\n \n \n \n42")) - assert.Equal(t, "12342", st.FindNewMessage("123", "12342\n \n \n \n")) - assert.Equal(t, "42", st.FindNewMessage("123", "123\n \n \n \n42\n \n \n \n")) - assert.Equal(t, "42", st.FindNewMessage("89", "42")) + assert.Equal(t, "", st.FindNewMessage("123456", "123456", msgfmt.AgentTypeCustom)) + assert.Equal(t, "1234567", st.FindNewMessage("123456", "1234567", msgfmt.AgentTypeCustom)) + assert.Equal(t, "42", st.FindNewMessage("123", "123\n \n \n \n42", msgfmt.AgentTypeCustom)) + assert.Equal(t, "12342", st.FindNewMessage("123", "12342\n \n \n \n", msgfmt.AgentTypeCustom)) + assert.Equal(t, "42", st.FindNewMessage("123", "123\n \n \n \n42\n \n \n \n", msgfmt.AgentTypeCustom)) + assert.Equal(t, "42", st.FindNewMessage("89", "42", msgfmt.AgentTypeCustom)) dir := "testdata/diff" cases, err := testdataDir.ReadDir(dir) @@ -371,7 +372,7 @@ func TestFindNewMessage(t *testing.T) { assert.NoError(t, err) expected, err := testdataDir.ReadFile(path.Join(dir, c.Name(), "expected.txt")) assert.NoError(t, err) - assert.Equal(t, string(expected), st.FindNewMessage(string(before), string(after))) + assert.Equal(t, string(expected), st.FindNewMessage(string(before), string(after), msgfmt.AgentTypeCustom)) }) } } diff --git a/openapi.json b/openapi.json index c0ee716..9d26def 100644 --- a/openapi.json +++ b/openapi.json @@ -307,7 +307,7 @@ "info": { "description": "HTTP API for Claude Code, Goose, and Aider.\n\nhttps://github.com/coder/agentapi", "title": "AgentAPI", - "version": "0.6.2" + "version": "0.7.0" }, "openapi": "3.1.0", "paths": { diff --git a/version.sh b/version.sh index 7a0d187..a3fda1a 100755 --- a/version.sh +++ b/version.sh @@ -9,7 +9,7 @@ fi version=$(go run main.go --version | awk '{print $3}') -jq --arg version "${version}" '.info.version = $version' openapi.json > openapi.json.tmp && mv openapi.json.tmp openapi.json -jq --arg version "${version}" '.version = $version' chat/package.json > chat/package.json.tmp && mv chat/package.json.tmp chat/package.json +jq --sort-keys --arg version "${version}" '.info.version = $version' openapi.json > openapi.json.tmp && mv openapi.json.tmp openapi.json +jq --sort-keys --arg version "${version}" '.version = $version' chat/package.json > chat/package.json.tmp && mv chat/package.json.tmp chat/package.json echo -n "${version}"